diff options
107 files changed, 1640 insertions, 1087 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 45e33ce4b6e9..e40c78c494aa 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -401,6 +401,7 @@ java_aconfig_library { min_sdk_version: "30", apex_available: [ "//apex_available:platform", + "com.android.tethering", "com.android.wifi", ], defaults: ["framework-minus-apex-aconfig-java-defaults"], diff --git a/core/api/current.txt b/core/api/current.txt index f9e772649e43..ca4b2fae2f99 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -98,6 +98,7 @@ package android { field public static final String DUMP = "android.permission.DUMP"; field public static final String ENFORCE_UPDATE_OWNERSHIP = "android.permission.ENFORCE_UPDATE_OWNERSHIP"; field public static final String EXECUTE_APP_ACTION = "android.permission.EXECUTE_APP_ACTION"; + field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXECUTE_APP_FUNCTIONS = "android.permission.EXECUTE_APP_FUNCTIONS"; field public static final String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR"; field public static final String FACTORY_TEST = "android.permission.FACTORY_TEST"; field public static final String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE"; @@ -8892,8 +8893,8 @@ package android.app.appfunctions { } @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionManager { - method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>); - method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>); + method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>); + method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>); method public void isAppFunctionEnabled(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>); method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>); field public static final int APP_FUNCTION_STATE_DEFAULT = 0; // 0x0 @@ -24940,6 +24941,7 @@ package android.media { method @Nullable public android.net.Uri getIconUri(); method @NonNull public String getId(); method @NonNull public CharSequence getName(); + method @FlaggedApi("com.android.media.flags.enable_media_route_2_info_provider_package_name") @Nullable public String getProviderPackageName(); method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public java.util.List<java.util.Set<java.lang.String>> getRequiredPermissions(); method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getSuitabilityStatus(); method @FlaggedApi("com.android.media.flags.enable_mirroring_in_media_router_2") public int getSupportedRoutingTypes(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index e1d8fb11efb3..f0f0fc98881e 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -150,7 +150,6 @@ package android { field @FlaggedApi("com.android.window.flags.untrusted_embedding_any_app_permission") public static final String EMBED_ANY_APP_IN_UNTRUSTED_MODE = "android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE"; field @FlaggedApi("android.content.pm.emergency_install_permission") public static final String EMERGENCY_INSTALL_PACKAGES = "android.permission.EMERGENCY_INSTALL_PACKAGES"; field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED"; - field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXECUTE_APP_FUNCTIONS = "android.permission.EXECUTE_APP_FUNCTIONS"; field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXECUTE_APP_FUNCTIONS_TRUSTED = "android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED"; field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS"; field public static final String FORCE_BACK = "android.permission.FORCE_BACK"; @@ -523,7 +522,6 @@ package android { field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") public static final int config_defaultOnDeviceIntelligenceDeviceConfigNamespace; field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") public static final int config_defaultOnDeviceIntelligenceService; field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") public static final int config_defaultOnDeviceSandboxedInferenceService; - field @FlaggedApi("android.permission.flags.cross_user_role_platform_api_enabled") public static final int config_defaultReservedForTestingProfileGroupExclusivity; field @FlaggedApi("android.permission.flags.retail_demo_role_enabled") public static final int config_defaultRetailDemo = 17039432; // 0x1040048 field public static final int config_defaultSms = 17039396; // 0x1040024 field @FlaggedApi("android.permission.flags.wallet_role_enabled") public static final int config_defaultWallet = 17039433; // 0x1040049 diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java index aee1cd9b4760..a5b58f968c27 100644 --- a/core/java/android/app/supervision/SupervisionManager.java +++ b/core/java/android/app/supervision/SupervisionManager.java @@ -16,8 +16,10 @@ package android.app.supervision; +import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.annotation.UserHandleAware; +import android.annotation.UserIdInt; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.RemoteException; @@ -32,9 +34,7 @@ public class SupervisionManager { private final Context mContext; private final ISupervisionManager mService; - /** - * @hide - */ + /** @hide */ @UnsupportedAppUsage public SupervisionManager(Context context, ISupervisionManager service) { mContext = context; @@ -48,8 +48,23 @@ public class SupervisionManager { */ @UserHandleAware public boolean isSupervisionEnabled() { + return isSupervisionEnabledForUser(mContext.getUserId()); + } + + /** + * Returns whether the device is supervised. + * + * <p>The caller must be from the same user as the target or hold the {@link + * android.Manifest.permission#INTERACT_ACROSS_USERS} permission. + * + * @hide + */ + @RequiresPermission( + value = android.Manifest.permission.INTERACT_ACROSS_USERS, + conditional = true) + public boolean isSupervisionEnabledForUser(@UserIdInt int userId) { try { - return mService.isSupervisionEnabledForUser(mContext.getUserId()); + return mService.isSupervisionEnabledForUser(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index d9888ad6cd8d..1e0cc94612dd 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -754,6 +754,7 @@ public final class ContextHubManager { * @param executor the executor to invoke callbacks for this client * @return the callback interface */ + @FlaggedApi(Flags.FLAG_OFFLOAD_API) private IContextHubEndpointDiscoveryCallback createDiscoveryCallback( IHubEndpointDiscoveryCallback callback, Executor executor, @@ -767,21 +768,9 @@ public final class ContextHubManager { } executor.execute( () -> { - // TODO(b/380293951): Refactor List<HubDiscoveryInfo> discoveryList = - new ArrayList<>(hubEndpointInfoList.length); - for (HubEndpointInfo info : hubEndpointInfoList) { - if (serviceDescriptor != null) { - for (HubServiceInfo sInfo : info.getServiceInfoCollection()) { - if (sInfo.getServiceDescriptor() - .equals(serviceDescriptor)) { - discoveryList.add(new HubDiscoveryInfo(info, sInfo)); - } - } - } else { - discoveryList.add(new HubDiscoveryInfo(info)); - } - } + getMatchingEndpointDiscoveryList( + hubEndpointInfoList, serviceDescriptor); if (discoveryList.isEmpty()) { Log.w(TAG, "onEndpointsStarted: no matching service descriptor"); } else { @@ -799,19 +788,8 @@ public final class ContextHubManager { executor.execute( () -> { List<HubDiscoveryInfo> discoveryList = - new ArrayList<>(hubEndpointInfoList.length); - for (HubEndpointInfo info : hubEndpointInfoList) { - if (serviceDescriptor != null) { - for (HubServiceInfo sInfo : info.getServiceInfoCollection()) { - if (sInfo.getServiceDescriptor() - .equals(serviceDescriptor)) { - discoveryList.add(new HubDiscoveryInfo(info, sInfo)); - } - } - } else { - discoveryList.add(new HubDiscoveryInfo(info)); - } - } + getMatchingEndpointDiscoveryList( + hubEndpointInfoList, serviceDescriptor); if (discoveryList.isEmpty()) { Log.w(TAG, "onEndpointsStopped: no matching service descriptor"); } else { @@ -823,6 +801,34 @@ public final class ContextHubManager { } /** + * Generates a list of matching endpoint discovery info, given the list and an (optional) + * service descriptor. If service descriptor is null, all endpoints are added to the filtered + * output list. + * + * @param hubEndpointInfoList The hub endpoints to filter. + * @param serviceDescriptor The optional service descriptor to match, null if adding all + * endpoints. + * @return The list of filtered HubDiscoveryInfo which matches the serviceDescriptor. + */ + @FlaggedApi(Flags.FLAG_OFFLOAD_API) + private List<HubDiscoveryInfo> getMatchingEndpointDiscoveryList( + HubEndpointInfo[] hubEndpointInfoList, @Nullable String serviceDescriptor) { + List<HubDiscoveryInfo> discoveryList = new ArrayList<>(hubEndpointInfoList.length); + for (HubEndpointInfo info : hubEndpointInfoList) { + if (serviceDescriptor != null) { + for (HubServiceInfo sInfo : info.getServiceInfoCollection()) { + if (sInfo.getServiceDescriptor().equals(serviceDescriptor)) { + discoveryList.add(new HubDiscoveryInfo(info, sInfo)); + } + } + } else { + discoveryList.add(new HubDiscoveryInfo(info)); + } + } + return discoveryList; + } + + /** * Equivalent to {@link #registerEndpointDiscoveryCallback(long, IHubEndpointDiscoveryCallback, * Executor)} with the default executor in the main thread. */ diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig index 6799db3f7471..8d12b76e23ff 100644 --- a/core/java/android/net/flags.aconfig +++ b/core/java/android/net/flags.aconfig @@ -29,3 +29,11 @@ flag { } is_exported: true } + +flag { + name: "mdns_improvement_for_25q2" + is_exported: true + namespace: "android_core_networking" + description: "Flag for MDNS quality, reliability and performance improvement in 25Q2" + bug: "373270045" +} diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 84ca5ed4ab10..d54dbad9286c 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -1558,6 +1558,7 @@ public class Build { * @hide */ @SuppressWarnings("FlaggedApi") // SDK_INT_MULTIPLIER is defined in this file + @SuppressLint("InlinedApi") public static @SdkIntFull int parseFullVersion(@NonNull String version) { int index = version.indexOf('.'); int major; @@ -1569,12 +1570,22 @@ public class Build { major = Integer.parseInt(version.substring(0, index)); minor = Integer.parseInt(version.substring(index + 1)); } - if (major < 0 || minor < 0) { - throw new NumberFormatException(); + if (major < 0) { + throw new NumberFormatException("negative major version"); + } + if (major >= 21474) { + throw new NumberFormatException("major version too large, must be less than 21474"); + } + if (minor < 0) { + throw new NumberFormatException("negative minor version"); + } + if (minor >= VERSION_CODES_FULL.SDK_INT_MULTIPLIER) { + throw new NumberFormatException("minor version too large, must be less than " + + VERSION_CODES_FULL.SDK_INT_MULTIPLIER); } } catch (NumberFormatException e) { throw new IllegalArgumentException("failed to parse '" + version - + "' as a major.minor version code"); + + "' as a major.minor version code", e); } return major * VERSION_CODES_FULL.SDK_INT_MULTIPLIER + minor; } diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl index 56a089aff78a..4a14a8d0faf8 100644 --- a/core/java/android/os/IHintManager.aidl +++ b/core/java/android/os/IHintManager.aidl @@ -21,13 +21,11 @@ import android.os.CpuHeadroomParamsInternal; import android.os.GpuHeadroomParamsInternal; import android.os.IHintSession; import android.os.SessionCreationConfig; - -import android.hardware.power.ChannelConfig; import android.hardware.power.CpuHeadroomResult; +import android.hardware.power.ChannelConfig; import android.hardware.power.GpuHeadroomResult; import android.hardware.power.SessionConfig; import android.hardware.power.SessionTag; -import android.hardware.power.SupportInfo; /** {@hide} */ interface IHintManager { @@ -42,6 +40,11 @@ interface IHintManager { IHintSession createHintSessionWithConfig(in IBinder token, in SessionTag tag, in SessionCreationConfig creationConfig, out SessionConfig config); + /** + * Get preferred rate limit in nanoseconds. + */ + long getHintSessionPreferredRate(); + void setHintSessionThreads(in IHintSession hintSession, in int[] tids); int[] getHintSessionThreadIds(in IHintSession hintSession); @@ -58,28 +61,13 @@ interface IHintManager { long getGpuHeadroomMinIntervalMillis(); /** - * Used by the JNI to pass an interface to the SessionManager; - * for internal use only. + * Get Maximum number of graphics pipeline threads allowed per-app. */ - oneway void passSessionManagerBinder(in IBinder sessionManager); - - parcelable HintManagerClientData { - int powerHalVersion; - int maxGraphicsPipelineThreads; - long preferredRateNanos; - SupportInfo supportInfo; - } - - interface IHintManagerClient { - /** - * Returns FMQ channel information for the caller, which it associates to the callback binder lifespan. - */ - oneway void receiveChannelConfig(in ChannelConfig config); - } + int getMaxGraphicsPipelineThreadsCount(); /** - * Set up an ADPF client, receiving a remote client binder interface and - * passing back a bundle of support and configuration information. + * Used by the JNI to pass an interface to the SessionManager; + * for internal use only. */ - HintManagerClientData registerClient(in IHintManagerClient client); + oneway void passSessionManagerBinder(in IBinder sessionManager); } diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index af96ccfee787..07b9f5242b42 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -467,15 +467,6 @@ flag { } flag { - name: "cross_user_role_platform_api_enabled" - is_exported: true - is_fixed_read_only: true - namespace: "permissions" - description: "Enable cross-user roles platform API" - bug: "367732307" -} - -flag { name: "rate_limit_batched_note_op_async_callbacks_enabled" is_fixed_read_only: true is_exported: true diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4acb6312f90d..cf0e90fb43ce 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2473,7 +2473,7 @@ public final class Settings { * when a new SIM subscription has become available. * <p> * This Activity will only launch successfully if the newly active subscription ID is set as the - * value of {@link EXTRA_SUB_ID} and the value corresponds with an active SIM subscription. + * value of {@link #EXTRA_SUB_ID} and the value corresponds with an active SIM subscription. * <p> * Input: {@link #EXTRA_SUB_ID}: the subscription ID of the newly active SIM subscription. * <p> @@ -13710,6 +13710,14 @@ public final class Settings { "render_shadows_in_compositor"; /** + * Policy to be used for the display shade when connected to an external display. + * @hide + */ + @Readable + public static final String DEVELOPMENT_SHADE_DISPLAY_AWARENESS = + "shade_display_awareness"; + + /** * Path to the WindowManager display settings file. If unset, the default file path will * be used. * diff --git a/core/java/android/service/notification/ZenDeviceEffects.java b/core/java/android/service/notification/ZenDeviceEffects.java index 22b1be08e1db..06bd2555c2f8 100644 --- a/core/java/android/service/notification/ZenDeviceEffects.java +++ b/core/java/android/service/notification/ZenDeviceEffects.java @@ -42,21 +42,26 @@ public final class ZenDeviceEffects implements Parcelable { /** * Enum for the user-modifiable fields in this object. + * * @hide */ - @IntDef(flag = true, prefix = { "FIELD_" }, value = { - FIELD_GRAYSCALE, - FIELD_SUPPRESS_AMBIENT_DISPLAY, - FIELD_DIM_WALLPAPER, - FIELD_NIGHT_MODE, - FIELD_DISABLE_AUTO_BRIGHTNESS, - FIELD_DISABLE_TAP_TO_WAKE, - FIELD_DISABLE_TILT_TO_WAKE, - FIELD_DISABLE_TOUCH, - FIELD_MINIMIZE_RADIO_USAGE, - FIELD_MAXIMIZE_DOZE, - FIELD_EXTRA_EFFECTS - }) + @IntDef( + flag = true, + prefix = {"FIELD_"}, + value = { + FIELD_GRAYSCALE, + FIELD_SUPPRESS_AMBIENT_DISPLAY, + FIELD_DIM_WALLPAPER, + FIELD_NIGHT_MODE, + FIELD_DISABLE_AUTO_BRIGHTNESS, + FIELD_DISABLE_TAP_TO_WAKE, + FIELD_DISABLE_TILT_TO_WAKE, + FIELD_DISABLE_TOUCH, + FIELD_MINIMIZE_RADIO_USAGE, + FIELD_MAXIMIZE_DOZE, + FIELD_NIGHT_LIGHT, + FIELD_EXTRA_EFFECTS + }) @Retention(RetentionPolicy.SOURCE) public @interface ModifiableField {} @@ -105,6 +110,9 @@ public final class ZenDeviceEffects implements Parcelable { */ public static final int FIELD_EXTRA_EFFECTS = 1 << 10; + /** @hide */ + public static final int FIELD_NIGHT_LIGHT = 1 << 11; + private static final int MAX_EFFECTS_LENGTH = 2_000; // characters private final boolean mGrayscale; @@ -118,12 +126,22 @@ public final class ZenDeviceEffects implements Parcelable { private final boolean mDisableTouch; private final boolean mMinimizeRadioUsage; private final boolean mMaximizeDoze; + private final boolean mNightLight; private final Set<String> mExtraEffects; - private ZenDeviceEffects(boolean grayscale, boolean suppressAmbientDisplay, - boolean dimWallpaper, boolean nightMode, boolean disableAutoBrightness, - boolean disableTapToWake, boolean disableTiltToWake, boolean disableTouch, - boolean minimizeRadioUsage, boolean maximizeDoze, Set<String> extraEffects) { + private ZenDeviceEffects( + boolean grayscale, + boolean suppressAmbientDisplay, + boolean dimWallpaper, + boolean nightMode, + boolean disableAutoBrightness, + boolean disableTapToWake, + boolean disableTiltToWake, + boolean disableTouch, + boolean minimizeRadioUsage, + boolean maximizeDoze, + boolean nightLight, + Set<String> extraEffects) { mGrayscale = grayscale; mSuppressAmbientDisplay = suppressAmbientDisplay; mDimWallpaper = dimWallpaper; @@ -134,6 +152,7 @@ public final class ZenDeviceEffects implements Parcelable { mDisableTouch = disableTouch; mMinimizeRadioUsage = minimizeRadioUsage; mMaximizeDoze = maximizeDoze; + mNightLight = nightLight; mExtraEffects = Collections.unmodifiableSet(extraEffects); } @@ -166,14 +185,25 @@ public final class ZenDeviceEffects implements Parcelable { && this.mDisableTouch == that.mDisableTouch && this.mMinimizeRadioUsage == that.mMinimizeRadioUsage && this.mMaximizeDoze == that.mMaximizeDoze + && this.mNightLight == that.mNightLight && Objects.equals(this.mExtraEffects, that.mExtraEffects); } @Override public int hashCode() { - return Objects.hash(mGrayscale, mSuppressAmbientDisplay, mDimWallpaper, mNightMode, - mDisableAutoBrightness, mDisableTapToWake, mDisableTiltToWake, mDisableTouch, - mMinimizeRadioUsage, mMaximizeDoze, mExtraEffects); + return Objects.hash( + mGrayscale, + mSuppressAmbientDisplay, + mDimWallpaper, + mNightMode, + mDisableAutoBrightness, + mDisableTapToWake, + mDisableTiltToWake, + mDisableTouch, + mMinimizeRadioUsage, + mMaximizeDoze, + mNightLight, + mExtraEffects); } @Override @@ -189,6 +219,7 @@ public final class ZenDeviceEffects implements Parcelable { if (mDisableTouch) effects.add("disableTouch"); if (mMinimizeRadioUsage) effects.add("minimizeRadioUsage"); if (mMaximizeDoze) effects.add("maximizeDoze"); + if (mNightLight) effects.add("nightLight"); if (mExtraEffects.size() > 0) { effects.add("extraEffects=[" + String.join(",", mExtraEffects) + "]"); } @@ -228,6 +259,9 @@ public final class ZenDeviceEffects implements Parcelable { if ((bitmask & FIELD_MAXIMIZE_DOZE) != 0) { modified.add("FIELD_MAXIMIZE_DOZE"); } + if (((bitmask) & FIELD_NIGHT_LIGHT) != 0) { + modified.add("FIELD_NIGHT_LIGHT"); + } if ((bitmask & FIELD_EXTRA_EFFECTS) != 0) { modified.add("FIELD_EXTRA_EFFECTS"); } @@ -313,6 +347,15 @@ public final class ZenDeviceEffects implements Parcelable { } /** + * Whether the night display transformation should be activated while the rule is active. + * + * @hide + */ + public boolean shouldUseNightLight() { + return mNightLight; + } + + /** * (Immutable) set of extra effects to be applied while the rule is active. Extra effects are * not used in AOSP, but OEMs may add support for them by providing a custom * {@link DeviceEffectsApplier}. @@ -329,29 +372,46 @@ public final class ZenDeviceEffects implements Parcelable { * @hide */ public boolean hasEffects() { - return mGrayscale || mSuppressAmbientDisplay || mDimWallpaper || mNightMode - || mDisableAutoBrightness || mDisableTapToWake || mDisableTiltToWake - || mDisableTouch || mMinimizeRadioUsage || mMaximizeDoze + return mGrayscale + || mSuppressAmbientDisplay + || mDimWallpaper + || mNightMode + || mDisableAutoBrightness + || mDisableTapToWake + || mDisableTiltToWake + || mDisableTouch + || mMinimizeRadioUsage + || mMaximizeDoze + || mNightLight || mExtraEffects.size() > 0; } /** {@link Parcelable.Creator} that instantiates {@link ZenDeviceEffects} objects. */ @NonNull - public static final Creator<ZenDeviceEffects> CREATOR = new Creator<ZenDeviceEffects>() { - @Override - public ZenDeviceEffects createFromParcel(Parcel in) { - return new ZenDeviceEffects(in.readBoolean(), - in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readBoolean(), - in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readBoolean(), - in.readBoolean(), - Set.of(in.readArray(String.class.getClassLoader(), String.class))); - } - - @Override - public ZenDeviceEffects[] newArray(int size) { - return new ZenDeviceEffects[size]; - } - }; + public static final Creator<ZenDeviceEffects> CREATOR = + new Creator<ZenDeviceEffects>() { + @Override + public ZenDeviceEffects createFromParcel(Parcel in) { + return new ZenDeviceEffects( + in.readBoolean(), + in.readBoolean(), + in.readBoolean(), + in.readBoolean(), + in.readBoolean(), + in.readBoolean(), + in.readBoolean(), + in.readBoolean(), + in.readBoolean(), + in.readBoolean(), + in.readBoolean(), + Set.of(in.readArray(String.class.getClassLoader(), String.class))); + } + + @Override + public ZenDeviceEffects[] newArray(int size) { + return new ZenDeviceEffects[size]; + } + }; @Override public int describeContents() { @@ -370,6 +430,7 @@ public final class ZenDeviceEffects implements Parcelable { dest.writeBoolean(mDisableTouch); dest.writeBoolean(mMinimizeRadioUsage); dest.writeBoolean(mMaximizeDoze); + dest.writeBoolean(mNightLight); dest.writeArray(mExtraEffects.toArray(new String[0])); } @@ -387,6 +448,7 @@ public final class ZenDeviceEffects implements Parcelable { private boolean mDisableTouch; private boolean mMinimizeRadioUsage; private boolean mMaximizeDoze; + private boolean mNightLight; private final HashSet<String> mExtraEffects = new HashSet<>(); /** @@ -410,6 +472,7 @@ public final class ZenDeviceEffects implements Parcelable { mDisableTouch = zenDeviceEffects.shouldDisableTouch(); mMinimizeRadioUsage = zenDeviceEffects.shouldMinimizeRadioUsage(); mMaximizeDoze = zenDeviceEffects.shouldMaximizeDoze(); + mNightLight = zenDeviceEffects.shouldUseNightLight(); mExtraEffects.addAll(zenDeviceEffects.getExtraEffects()); } @@ -512,6 +575,18 @@ public final class ZenDeviceEffects implements Parcelable { } /** + * Sets whether the night display transformation should be activated while the rule is + * active. + * + * @hide + */ + @NonNull + public Builder setShouldUseNightLight(boolean nightLight) { + mNightLight = nightLight; + return this; + } + + /** * Sets the extra effects to be applied while the rule is active. Extra effects are not * used in AOSP, but OEMs may add support for them by providing a custom * {@link DeviceEffectsApplier}. @@ -577,6 +652,7 @@ public final class ZenDeviceEffects implements Parcelable { if (effects.shouldDisableTouch()) setShouldDisableTouch(true); if (effects.shouldMinimizeRadioUsage()) setShouldMinimizeRadioUsage(true); if (effects.shouldMaximizeDoze()) setShouldMaximizeDoze(true); + if (effects.shouldUseNightLight()) setShouldUseNightLight(true); addExtraEffects(effects.getExtraEffects()); return this; } @@ -584,10 +660,19 @@ public final class ZenDeviceEffects implements Parcelable { /** Builds a {@link ZenDeviceEffects} object based on the builder's state. */ @NonNull public ZenDeviceEffects build() { - return new ZenDeviceEffects(mGrayscale, - mSuppressAmbientDisplay, mDimWallpaper, mNightMode, mDisableAutoBrightness, - mDisableTapToWake, mDisableTiltToWake, mDisableTouch, mMinimizeRadioUsage, - mMaximizeDoze, mExtraEffects); + return new ZenDeviceEffects( + mGrayscale, + mSuppressAmbientDisplay, + mDimWallpaper, + mNightMode, + mDisableAutoBrightness, + mDisableTapToWake, + mDisableTiltToWake, + mDisableTouch, + mMinimizeRadioUsage, + mMaximizeDoze, + mNightLight, + mExtraEffects); } } } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 13887781f1ec..4f459aa9131a 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -325,6 +325,7 @@ public class ZenModeConfig implements Parcelable { private static final String DEVICE_EFFECT_DISABLE_TOUCH = "zdeDisableTouch"; private static final String DEVICE_EFFECT_MINIMIZE_RADIO_USAGE = "zdeMinimizeRadioUsage"; private static final String DEVICE_EFFECT_MAXIMIZE_DOZE = "zdeMaximizeDoze"; + private static final String DEVICE_EFFECT_USE_NIGHT_LIGHT = "zdeUseNightLight"; private static final String DEVICE_EFFECT_EXTRAS = "zdeExtraEffects"; private static final String DEVICE_EFFECT_USER_MODIFIED_FIELDS = "zdeUserModifiedFields"; @@ -1508,25 +1509,32 @@ public class ZenModeConfig implements Parcelable { @FlaggedApi(Flags.FLAG_MODES_API) @Nullable private static ZenDeviceEffects readZenDeviceEffectsXml(TypedXmlPullParser parser) { - ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder() - .setShouldDisplayGrayscale( - safeBoolean(parser, DEVICE_EFFECT_DISPLAY_GRAYSCALE, false)) - .setShouldSuppressAmbientDisplay( - safeBoolean(parser, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY, false)) - .setShouldDimWallpaper(safeBoolean(parser, DEVICE_EFFECT_DIM_WALLPAPER, false)) - .setShouldUseNightMode(safeBoolean(parser, DEVICE_EFFECT_USE_NIGHT_MODE, false)) - .setShouldDisableAutoBrightness( - safeBoolean(parser, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS, false)) - .setShouldDisableTapToWake( - safeBoolean(parser, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE, false)) - .setShouldDisableTiltToWake( - safeBoolean(parser, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE, false)) - .setShouldDisableTouch(safeBoolean(parser, DEVICE_EFFECT_DISABLE_TOUCH, false)) - .setShouldMinimizeRadioUsage( - safeBoolean(parser, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, false)) - .setShouldMaximizeDoze(safeBoolean(parser, DEVICE_EFFECT_MAXIMIZE_DOZE, false)) - .setExtraEffects(safeStringSet(parser, DEVICE_EFFECT_EXTRAS)) - .build(); + ZenDeviceEffects deviceEffects = + new ZenDeviceEffects.Builder() + .setShouldDisplayGrayscale( + safeBoolean(parser, DEVICE_EFFECT_DISPLAY_GRAYSCALE, false)) + .setShouldSuppressAmbientDisplay( + safeBoolean(parser, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY, false)) + .setShouldDimWallpaper( + safeBoolean(parser, DEVICE_EFFECT_DIM_WALLPAPER, false)) + .setShouldUseNightMode( + safeBoolean(parser, DEVICE_EFFECT_USE_NIGHT_MODE, false)) + .setShouldDisableAutoBrightness( + safeBoolean(parser, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS, false)) + .setShouldDisableTapToWake( + safeBoolean(parser, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE, false)) + .setShouldDisableTiltToWake( + safeBoolean(parser, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE, false)) + .setShouldDisableTouch( + safeBoolean(parser, DEVICE_EFFECT_DISABLE_TOUCH, false)) + .setShouldMinimizeRadioUsage( + safeBoolean(parser, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, false)) + .setShouldMaximizeDoze( + safeBoolean(parser, DEVICE_EFFECT_MAXIMIZE_DOZE, false)) + .setShouldUseNightLight( + safeBoolean(parser, DEVICE_EFFECT_USE_NIGHT_LIGHT, false)) + .setExtraEffects(safeStringSet(parser, DEVICE_EFFECT_EXTRAS)) + .build(); return deviceEffects.hasEffects() ? deviceEffects : null; } @@ -1550,6 +1558,7 @@ public class ZenModeConfig implements Parcelable { writeBooleanIfTrue(out, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, deviceEffects.shouldMinimizeRadioUsage()); writeBooleanIfTrue(out, DEVICE_EFFECT_MAXIMIZE_DOZE, deviceEffects.shouldMaximizeDoze()); + writeBooleanIfTrue(out, DEVICE_EFFECT_USE_NIGHT_LIGHT, deviceEffects.shouldUseNightLight()); writeStringSet(out, DEVICE_EFFECT_EXTRAS, deviceEffects.getExtraEffects()); } diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java index c9f464716e72..31acd248dcc0 100644 --- a/core/java/android/service/notification/ZenModeDiff.java +++ b/core/java/android/service/notification/ZenModeDiff.java @@ -714,6 +714,7 @@ public class ZenModeDiff { public static final String FIELD_DISABLE_TOUCH = "mDisableTouch"; public static final String FIELD_MINIMIZE_RADIO_USAGE = "mMinimizeRadioUsage"; public static final String FIELD_MAXIMIZE_DOZE = "mMaximizeDoze"; + public static final String FIELD_NIGHT_LIGHT = "mNightLight"; public static final String FIELD_EXTRA_EFFECTS = "mExtraEffects"; // NOTE: new field strings must match the variable names in ZenDeviceEffects @@ -781,6 +782,11 @@ public class ZenModeDiff { addField(FIELD_MAXIMIZE_DOZE, new FieldDiff<>(from.shouldMaximizeDoze(), to.shouldMaximizeDoze())); } + if (from.shouldUseNightLight() != to.shouldUseNightLight()) { + addField( + FIELD_NIGHT_LIGHT, + new FieldDiff<>(from.shouldUseNightLight(), to.shouldUseNightLight())); + } if (!Objects.equals(from.getExtraEffects(), to.getExtraEffects())) { addField(FIELD_EXTRA_EFFECTS, new FieldDiff<>(from.getExtraEffects(), to.getExtraEffects())); diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 8cb96ae1d611..089b5c256b6e 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -42,6 +42,7 @@ import android.view.animation.AnimationUtils; import java.io.PrintWriter; import java.util.Locale; +import java.util.concurrent.atomic.AtomicBoolean; /** * Coordinates the timing of animations, input and drawing. @@ -208,7 +209,7 @@ public final class Choreographer { private final FrameData mFrameData = new FrameData(); private volatile boolean mInDoFrameCallback = false; - private static class BufferStuffingData { + private static class BufferStuffingState { enum RecoveryAction { // No recovery NONE, @@ -218,21 +219,15 @@ public final class Choreographer { // back toward threshold. DELAY_FRAME } - // The maximum number of times frames will be delayed per buffer stuffing event. - // Since buffer stuffing can persist for several consecutive frames following the - // initial missed frame, we want to adjust the timeline with enough frame delays and - // offsets to return the queued buffer count back to threshold. - public static final int MAX_FRAME_DELAYS = 3; + // Indicates if recovery should begin. Is true whenever the client was blocked + // on dequeuing a buffer. When buffer stuffing recovery begins, this is reset + // since the scheduled frame delay reduces the number of queued buffers. + public AtomicBoolean isStuffed = new AtomicBoolean(false); // Whether buffer stuffing recovery has begun. Recovery can only end // when events are idle. public boolean isRecovering = false; - // The number of frames delayed so far during recovery. Used to compare with - // MAX_FRAME_DELAYS to safeguard against excessive frame delays during recovery. - // Also used as unique cookie for tracing. - public int numberFrameDelays = 0; - // The number of additional frame delays scheduled during recovery to wait for the next // vsync. These are scheduled when frame times appear to go backward or frames are // being skipped due to FPSDivisor. @@ -245,12 +240,20 @@ public final class Choreographer { */ public void reset() { isRecovering = false; - numberFrameDelays = 0; numberWaitsForNextVsync = 0; } } - private final BufferStuffingData mBufferStuffingData = new BufferStuffingData(); + private final BufferStuffingState mBufferStuffingState = new BufferStuffingState(); + + /** + * Set flag to indicate that client is blocked waiting for buffer release and + * buffer stuffing recovery should soon begin. + * @hide + */ + public void onWaitForBufferRelease() { + mBufferStuffingState.isStuffed.set(true); + } /** * Contains information about the current frame for jank-tracking, @@ -901,67 +904,56 @@ public final class Choreographer { // Conducts logic for beginning or ending buffer stuffing recovery. // Returns an enum for the recovery action that should be taken in doFrame(). - BufferStuffingData.RecoveryAction checkBufferStuffingRecovery(long frameTimeNanos, + BufferStuffingState.RecoveryAction updateBufferStuffingState(long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) { - // Canned animations can recover from buffer stuffing whenever more - // than 2 buffers are queued. - if (vsyncEventData.numberQueuedBuffers > 2) { - mBufferStuffingData.isRecovering = true; - // Intentional frame delay that can happen at most MAX_FRAME_DELAYS times per - // buffer stuffing event until the buffer count returns to threshold. The - // delayed frames are compensated for by the negative offsets added to the - // animation timestamps. - if (mBufferStuffingData.numberFrameDelays < mBufferStuffingData.MAX_FRAME_DELAYS) { - if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { - Trace.asyncTraceForTrackBegin( - Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", "Thread " - + android.os.Process.myTid() + ", recover frame #" - + mBufferStuffingData.numberFrameDelays, - mBufferStuffingData.numberFrameDelays); - } - mBufferStuffingData.numberFrameDelays++; - scheduleVsyncLocked(); - return BufferStuffingData.RecoveryAction.DELAY_FRAME; + if (!mBufferStuffingState.isRecovering) { + if (!mBufferStuffingState.isStuffed.getAndSet(false)) { + return BufferStuffingState.RecoveryAction.NONE; + } + // Canned animations can recover from buffer stuffing whenever the + // client is blocked on dequeueBuffer. Frame delay only occurs at + // the start of recovery to free a buffer. + mBufferStuffingState.isRecovering = true; + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.asyncTraceForTrackBegin( + Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", "Thread " + + android.os.Process.myTid() + ", recover frame", 0); } + return BufferStuffingState.RecoveryAction.DELAY_FRAME; } - if (mBufferStuffingData.isRecovering) { - // Includes an additional expected frame delay from the natural scheduling - // of the next vsync event. - int totalFrameDelays = mBufferStuffingData.numberFrameDelays - + mBufferStuffingData.numberWaitsForNextVsync + 1; - long vsyncsSinceLastCallback = mLastFrameIntervalNanos > 0 - ? (frameTimeNanos - mLastNoOffsetFrameTimeNanos) / mLastFrameIntervalNanos : 0; - - // Detected idle state due to a longer inactive period since the last vsync callback - // than the total expected number of vsync frame delays. End buffer stuffing recovery. - // There are no frames to animate and offsets no longer need to be added - // since the idle state gives the animation a chance to catch up. - if (vsyncsSinceLastCallback > totalFrameDelays) { - if (DEBUG_JANK) { - Log.d(TAG, "End buffer stuffing recovery"); - } - if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { - for (int i = 0; i < mBufferStuffingData.numberFrameDelays; i++) { - Trace.asyncTraceForTrackEnd( - Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", i); - } - } - mBufferStuffingData.reset(); - - } else { - if (DEBUG_JANK) { - Log.d(TAG, "Adjust animation timeline with a negative offset"); - } - if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { - Trace.instantForTrack( - Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", - "Negative offset added to animation"); - } - return BufferStuffingData.RecoveryAction.OFFSET; + // Total number of frame delays used to detect idle state. Includes an additional + // expected frame delay from the natural scheduling of the next vsync event and + // the intentional frame delay that was scheduled when stuffing was first detected. + int totalFrameDelays = mBufferStuffingState.numberWaitsForNextVsync + 2; + long vsyncsSinceLastCallback = mLastFrameIntervalNanos > 0 + ? (frameTimeNanos - mLastNoOffsetFrameTimeNanos) / mLastFrameIntervalNanos : 0; + + // Detected idle state due to a longer inactive period since the last vsync callback + // than the total expected number of vsync frame delays. End buffer stuffing recovery. + // There are no frames to animate and offsets no longer need to be added + // since the idle state gives the animation a chance to catch up. + if (vsyncsSinceLastCallback > totalFrameDelays) { + if (DEBUG_JANK) { + Log.d(TAG, "End buffer stuffing recovery"); } + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.asyncTraceForTrackEnd( + Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", 0); + } + mBufferStuffingState.reset(); + return BufferStuffingState.RecoveryAction.NONE; + } + + if (DEBUG_JANK) { + Log.d(TAG, "Adjust animation timeline with a negative offset"); + } + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.instantForTrack( + Trace.TRACE_TAG_VIEW, "Buffer stuffing recovery", + "Negative offset added to animation"); } - return BufferStuffingData.RecoveryAction.NONE; + return BufferStuffingState.RecoveryAction.OFFSET; } void doFrame(long frameTimeNanos, int frame, @@ -973,7 +965,7 @@ public final class Choreographer { // Evaluate if buffer stuffing recovery needs to start or end, and // what actions need to be taken for recovery. - switch (checkBufferStuffingRecovery(frameTimeNanos, vsyncEventData)) { + switch (updateBufferStuffingState(frameTimeNanos, vsyncEventData)) { case NONE: // Without buffer stuffing recovery, offsetFrameTimeNanos is // synonymous with frameTimeNanos. @@ -984,7 +976,8 @@ public final class Choreographer { offsetFrameTimeNanos = frameTimeNanos - frameIntervalNanos; break; case DELAY_FRAME: - // Intentional frame delay to help restore queued buffer count to threshold. + // Intentional frame delay to help reduce queued buffer count. + scheduleVsyncLocked(); return; default: break; @@ -1037,7 +1030,7 @@ public final class Choreographer { + " ms in the past."); } } - if (mBufferStuffingData.isRecovering) { + if (mBufferStuffingState.isRecovering) { frameTimeNanos -= frameIntervalNanos; if (DEBUG_JANK) { Log.d(TAG, "Adjusted animation timeline with a negative offset after" @@ -1055,8 +1048,8 @@ public final class Choreographer { + "previously skipped frame. Waiting for next vsync."); } traceMessage("Frame time goes backward"); - if (mBufferStuffingData.isRecovering) { - mBufferStuffingData.numberWaitsForNextVsync++; + if (mBufferStuffingState.isRecovering) { + mBufferStuffingState.numberWaitsForNextVsync++; } scheduleVsyncLocked(); return; @@ -1066,8 +1059,8 @@ public final class Choreographer { long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos; if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) { traceMessage("Frame skipped due to FPSDivisor"); - if (mBufferStuffingData.isRecovering) { - mBufferStuffingData.numberWaitsForNextVsync++; + if (mBufferStuffingState.isRecovering) { + mBufferStuffingState.numberWaitsForNextVsync++; } scheduleVsyncLocked(); return; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index c1b92ee3f74e..609f1ef06612 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2772,6 +2772,7 @@ public final class ViewRootImpl implements ViewParent, mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl, mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format); mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback); + mBlastBufferQueue.setWaitForBufferReleaseCallback(mChoreographer::onWaitForBufferRelease); // If we create and destroy BBQ without recreating the SurfaceControl, we can end up // queuing buffers on multiple apply tokens causing out of order buffer submissions. We // fix this by setting the same apply token on all BBQs created by this VRI. diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java index b535effd393a..f61eb30d3171 100644 --- a/core/java/android/window/BackProgressAnimator.java +++ b/core/java/android/window/BackProgressAnimator.java @@ -52,6 +52,7 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL */ private static final float SCALE_FACTOR = 100f; private static final float FLING_FRICTION = 8f; + private static final float BUTTON_SPRING_STIFFNESS = 100; private final SpringAnimation mSpring; private ProgressCallback mCallback; private float mProgress = 0; @@ -156,7 +157,7 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL /* frameTime */ System.nanoTime() / TimeUtils.NANOS_PER_MS); if (predictiveBackSwipeEdgeNoneApi()) { if (event.getSwipeEdge() == EDGE_NONE) { - mButtonSpringForce.setStiffness(SpringForce.STIFFNESS_LOW); + mButtonSpringForce.setStiffness(BUTTON_SPRING_STIFFNESS); mSpring.setSpring(mButtonSpringForce); mSpring.animateToFinalPosition(SCALE_FACTOR); } else { diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java index 169a9e8585b0..31d9770f6ac4 100644 --- a/core/java/com/android/internal/widget/MessagingGroup.java +++ b/core/java/com/android/internal/widget/MessagingGroup.java @@ -448,6 +448,17 @@ public class MessagingGroup extends NotificationOptimizedLinearLayout implements mSenderView.setVisibility(hidden ? GONE : VISIBLE); } + private void updateIconVisibility() { + if (Flags.notificationsRedesignTemplates() && !mIsInConversation) { + // We don't show any icon (other than the app icon) in the collapsed form. For + // conversations, keeping this container helps with aligning the message to the icon + // when collapsed, but the old messaging style already has this alignment built into + // the template like all other layouts. Conversations are special because we use the + // same base layout for both the collapsed and expanded views. + mMessagingIconContainer.setVisibility(mSingleLine ? GONE : VISIBLE); + } + } + @Override public boolean hasDifferentHeightWhenFirst() { return mCanHideSenderIfFirst && !mSingleLine && !TextUtils.isEmpty(mSenderName); @@ -703,6 +714,7 @@ public class MessagingGroup extends NotificationOptimizedLinearLayout implements updateMaxDisplayedLines(); updateClipRect(); updateSenderVisibility(); + updateIconVisibility(); } } @@ -716,13 +728,16 @@ public class MessagingGroup extends NotificationOptimizedLinearLayout implements * @param isInConversation is this in a conversation */ public void setIsInConversation(boolean isInConversation) { - if (Flags.notificationsRedesignTemplates()) { - // No alignment adjustments are necessary in the redesign, as the size of the icons - // in both conversations and old messaging notifications are the same. - return; - } if (mIsInConversation != isInConversation) { mIsInConversation = isInConversation; + + if (Flags.notificationsRedesignTemplates()) { + updateIconVisibility(); + // No other alignment adjustments are necessary in the redesign, as the size of the + // icons in both conversations and old messaging notifications are the same. + return; + } + MarginLayoutParams layoutParams = (MarginLayoutParams) mMessagingIconContainer.getLayoutParams(); layoutParams.width = mIsInConversation diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp index b9c3bf73f11c..7b61a5db0b41 100644 --- a/core/jni/android_graphics_BLASTBufferQueue.cpp +++ b/core/jni/android_graphics_BLASTBufferQueue.cpp @@ -87,6 +87,38 @@ private: jobject mTransactionHangObject; }; +struct { + jmethodID onWaitForBufferRelease; +} gWaitForBufferReleaseCallback; + +class WaitForBufferReleaseCallbackWrapper + : public LightRefBase<WaitForBufferReleaseCallbackWrapper> { +public: + explicit WaitForBufferReleaseCallbackWrapper(JNIEnv* env, jobject jobject) { + env->GetJavaVM(&mVm); + mWaitForBufferReleaseObject = env->NewGlobalRef(jobject); + LOG_ALWAYS_FATAL_IF(!mWaitForBufferReleaseObject, "Failed to make global ref"); + } + + ~WaitForBufferReleaseCallbackWrapper() { + if (mWaitForBufferReleaseObject != nullptr) { + getenv(mVm)->DeleteGlobalRef(mWaitForBufferReleaseObject); + mWaitForBufferReleaseObject = nullptr; + } + } + + void onWaitForBufferRelease() { + JNIEnv* env = getenv(mVm); + getenv(mVm)->CallVoidMethod(mWaitForBufferReleaseObject, + gWaitForBufferReleaseCallback.onWaitForBufferRelease); + DieIfException(env, "Uncaught exception in WaitForBufferReleaseCallback."); + } + +private: + JavaVM* mVm; + jobject mWaitForBufferReleaseObject; +}; + static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName, jboolean updateDestinationFrame) { ScopedUtfChars name(env, jName); @@ -215,6 +247,18 @@ static void nativeSetApplyToken(JNIEnv* env, jclass clazz, jlong ptr, jobject ap return queue->setApplyToken(std::move(token)); } +static void nativeSetWaitForBufferReleaseCallback(JNIEnv* env, jclass clazz, jlong ptr, + jobject waitForBufferReleaseCallback) { + sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr); + if (waitForBufferReleaseCallback == nullptr) { + queue->setWaitForBufferReleaseCallback(nullptr); + } else { + sp<WaitForBufferReleaseCallbackWrapper> wrapper = + new WaitForBufferReleaseCallbackWrapper{env, waitForBufferReleaseCallback}; + queue->setWaitForBufferReleaseCallback([wrapper]() { wrapper->onWaitForBufferRelease(); }); + } +} + static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ // clang-format off @@ -234,6 +278,9 @@ static const JNINativeMethod gMethods[] = { "(JLandroid/graphics/BLASTBufferQueue$TransactionHangCallback;)V", (void*)nativeSetTransactionHangCallback}, {"nativeSetApplyToken", "(JLandroid/os/IBinder;)V", (void*)nativeSetApplyToken}, + {"nativeSetWaitForBufferReleaseCallback", + "(JLandroid/graphics/BLASTBufferQueue$WaitForBufferReleaseCallback;)V", + (void*)nativeSetWaitForBufferReleaseCallback}, // clang-format on }; @@ -255,6 +302,10 @@ int register_android_graphics_BLASTBufferQueue(JNIEnv* env) { gTransactionHangCallback.onTransactionHang = GetMethodIDOrDie(env, transactionHangClass, "onTransactionHang", "(Ljava/lang/String;)V"); + jclass waitForBufferReleaseClass = + FindClassOrDie(env, "android/graphics/BLASTBufferQueue$WaitForBufferReleaseCallback"); + gWaitForBufferReleaseCallback.onWaitForBufferRelease = + GetMethodIDOrDie(env, waitForBufferReleaseClass, "onWaitForBufferRelease", "()V"); return 0; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 82cad8b3a477..4a948dd91fb0 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -8307,20 +8307,21 @@ android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager" android:protectionLevel="signature" /> - <!-- @SystemApi Allows a trusted application to perform actions on behalf of users inside of + <!-- Allows a trusted application to perform actions on behalf of users inside of applications with privacy guarantees from the system. <p>This permission is currently only granted to system packages in the {@link android.app.role.SYSTEM_UI_INTELLIGENCE} role which complies with privacy requirements outlined in the Android CDD section "9.8.6 Content Capture". <p>Apps are not able to opt-out from caller having this permission. <p>Protection level: internal|role + @SystemApi @hide - @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") --> + @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER) --> <permission android:name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED" android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager" android:protectionLevel="internal|role" /> - <!-- @SystemApi Allows an application to perform actions on behalf of users inside of + <!-- Allows an application to perform actions on behalf of users inside of applications. <p>This permission is currently only granted to preinstalled / system apps having the {@link android.app.role.ASSISTANT} role. @@ -8328,8 +8329,7 @@ limiting to only callers with {@link android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} instead. <p>Protection level: internal|role - @hide - @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") --> + @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER) --> <permission android:name="android.permission.EXECUTE_APP_FUNCTIONS" android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager" android:protectionLevel="internal|role" /> diff --git a/core/res/res/layout/notification_2025_template_header.xml b/core/res/res/layout/notification_2025_template_header.xml index 63872aff8dd0..fc727e1c72f5 100644 --- a/core/res/res/layout/notification_2025_template_header.xml +++ b/core/res/res/layout/notification_2025_template_header.xml @@ -20,7 +20,6 @@ android:id="@+id/notification_header" android:layout_width="match_parent" android:layout_height="@dimen/notification_2025_header_height" - android:layout_marginBottom="@dimen/notification_header_margin_bottom" android:clipChildren="false" android:gravity="center_vertical" android:orientation="horizontal" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 89184bcc3721..3d023c3e1d11 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2466,9 +2466,6 @@ <string name="config_systemCallStreaming" translatable="false"></string> <!-- The name of the package that will hold the default retail demo role. --> <string name="config_defaultRetailDemo" translatable="false"></string> - <!-- The name of the package that will hold the default reserved for testing profile group - exclusivity role. --> - <string name="config_defaultReservedForTestingProfileGroupExclusivity" translatable="false">android.app.rolemultiuser.cts.app</string> <!-- The component name of the wear service class that will be started by the system server. --> <string name="config_wearServiceComponent" translatable="false"></string> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 196da29127df..20ae29659783 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -471,6 +471,12 @@ <string name="satellite_access_config_file" translatable="false"></string> <java-symbol type="string" name="satellite_access_config_file" /> + <!-- A string defines the NIDD (Non-IP Data Delivery) APN to be used for satellite attachment. For more on NIDD, + see 3GPP TS 29.542. This config is used for an NTN-only subscription, which requires activation before being used. + --> + <string name="config_satellite_nidd_apn_name" translatable="false"></string> + <java-symbol type="string" name="config_satellite_nidd_apn_name" /> + <!-- Boolean indicating whether to enable MT SMS polling for NB IOT NTN. --> <bool name="config_enabled_mt_sms_polling">true</bool> <java-symbol type="bool" name="config_enabled_mt_sms_polling" /> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index 8259ce3c0aee..e82992b91783 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -151,9 +151,8 @@ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER) @hide @SystemApi --> <public name="config_systemDependencyInstaller" /> - <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_PLATFORM_API_ENABLED) - @hide @SystemApi --> - <public name="config_defaultReservedForTestingProfileGroupExclusivity" /> + <!-- @hide @SystemApi --> + <public name="removed_config_defaultReservedForTestingProfileGroupExclusivity" /> <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_VENDOR_INTELLIGENCE_ROLE_ENABLED) @hide @SystemApi --> <public name="config_systemVendorIntelligence" /> diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java index 2a67716aa215..0bbfb6566d00 100644 --- a/core/tests/coretests/src/android/os/BuildTest.java +++ b/core/tests/coretests/src/android/os/BuildTest.java @@ -178,6 +178,20 @@ public class BuildTest { } @Test + public void testParseFullVersionIncorrectInputMajorVersionTooLarge() throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + Build.parseFullVersion("40000.1"); + }); + } + + @Test + public void testParseFullVersionIncorrectInputMinorVersionTooLarge() throws Exception { + assertThrows(IllegalArgumentException.class, () -> { + Build.parseFullVersion("3.99999999"); + }); + } + + @Test public void testFullVersionToStringCorrectInput() throws Exception { assertEquals("0.0", Build.fullVersionToString(0)); assertEquals("1.0", Build.fullVersionToString(1 * 100000 + 0)); diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java index 90723b2f1493..906c71d9caca 100644 --- a/graphics/java/android/graphics/BLASTBufferQueue.java +++ b/graphics/java/android/graphics/BLASTBufferQueue.java @@ -49,11 +49,22 @@ public final class BLASTBufferQueue { private static native void nativeSetTransactionHangCallback(long ptr, TransactionHangCallback callback); private static native void nativeSetApplyToken(long ptr, IBinder applyToken); + private static native void nativeSetWaitForBufferReleaseCallback(long ptr, + WaitForBufferReleaseCallback callback); public interface TransactionHangCallback { void onTransactionHang(String reason); } + + public interface WaitForBufferReleaseCallback { + /** + * Indicates that the client is waiting on buffer release + * due to no free buffers being available to render into. + */ + void onWaitForBufferRelease(); + } + /** Create a new connection with the surface flinger. */ public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height, @PixelFormat.Format int format) { @@ -210,4 +221,11 @@ public final class BLASTBufferQueue { public void setApplyToken(IBinder applyToken) { nativeSetApplyToken(mNativeObject, applyToken); } + + /** + * Propagate callback about being blocked on buffer release. + */ + public void setWaitForBufferReleaseCallback(WaitForBufferReleaseCallback waitCallback) { + nativeSetWaitForBufferReleaseCallback(mNativeObject, waitCallback); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 56efdb885512..ddc107e0dbc4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -1106,11 +1106,13 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mCurrentTracker.updateStartLocation(); BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]); dispatchOnBackStarted(mActiveCallback, startEvent); - // TODO(b/373544911): onBackStarted is dispatched here so that - // WindowOnBackInvokedDispatcher knows about the back navigation and intercepts touch - // events while it's active. It would be cleaner and safer to disable multitouch - // altogether (same as in gesture-nav). - dispatchOnBackStarted(mBackNavigationInfo.getOnBackInvokedCallback(), startEvent); + if (startEvent.getSwipeEdge() == EDGE_NONE) { + // TODO(b/373544911): onBackStarted is dispatched here so that + // WindowOnBackInvokedDispatcher knows about the back navigation and intercepts + // touch events while it's active. It would be cleaner and safer to disable + // multitouch altogether (same as in gesture-nav). + dispatchOnBackStarted(mBackNavigationInfo.getOnBackInvokedCallback(), startEvent); + } } } 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 e48c887c625f..d0c21c9ec7c0 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 @@ -20,6 +20,7 @@ import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_A import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT; @@ -145,6 +146,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.shared.TransactionPool; import com.android.wm.shell.shared.TransitionUtil; +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.shared.split.SplitBounds; import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition; import com.android.wm.shell.shared.split.SplitScreenConstants.SplitIndex; @@ -2766,6 +2768,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final @WindowManager.TransitionType int type = request.getType(); final boolean isOpening = isOpeningType(type); final boolean inFullscreen = triggerTask.getWindowingMode() == WINDOWING_MODE_FULLSCREEN; + final boolean inDesktopMode = DesktopModeStatus.canEnterDesktopMode(mContext) + && triggerTask.getWindowingMode() == WINDOWING_MODE_FREEFORM; final StageTaskListener stage = getStageOfTask(triggerTask); if (isOpening && inFullscreen) { @@ -2820,6 +2824,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions.setDismissTransition(transition, stageType, EXIT_REASON_FULLSCREEN_REQUEST); } + } else if (isOpening && inDesktopMode) { + // If the app being opened is in Desktop mode, set it to full screen and dismiss + // split screen stage. + prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out); + out.setWindowingMode(triggerTask.token, WINDOWING_MODE_UNDEFINED) + .setBounds(triggerTask.token, null); } else if (isOpening && inFullscreen) { final int activityType = triggerTask.getActivityType(); if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) { diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 037b97a61b3f..bbb03e77c8c9 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -21,6 +21,7 @@ import static android.media.audio.Flags.FLAG_ENABLE_MULTICHANNEL_GROUP_DEVICE; import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER; import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES; +import static com.android.media.flags.Flags.FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME; import static com.android.media.flags.Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2; import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES; import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES; @@ -631,7 +632,7 @@ public final class MediaRoute2Info implements Parcelable { @ConnectionState private final int mConnectionState; private final String mClientPackageName; - private final String mPackageName; + private final String mProviderPackageName; @PlaybackVolume private final int mVolumeHandling; private final int mVolumeMax; private final int mVolume; @@ -655,7 +656,7 @@ public final class MediaRoute2Info implements Parcelable { mDescription = builder.mDescription; mConnectionState = builder.mConnectionState; mClientPackageName = builder.mClientPackageName; - mPackageName = builder.mPackageName; + mProviderPackageName = builder.mProviderPackageName; mVolumeHandling = builder.mVolumeHandling; mVolumeMax = builder.mVolumeMax; mVolume = builder.mVolume; @@ -681,7 +682,7 @@ public final class MediaRoute2Info implements Parcelable { mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mConnectionState = in.readInt(); mClientPackageName = in.readString(); - mPackageName = in.readString(); + mProviderPackageName = in.readString(); mVolumeHandling = in.readInt(); mVolumeMax = in.readInt(); mVolume = in.readInt(); @@ -801,14 +802,19 @@ public final class MediaRoute2Info implements Parcelable { } /** - * Gets the package name of the provider that published the route. - * <p> - * It is set by the system service. - * @hide + * Gets the package name of the {@link MediaRoute2ProviderService provider} that published the + * route, or null if it has not yet been populated. + * + * <p>The package name of the route provider is populated by the system as part of {@link + * MediaRoute2ProviderService#notifyRoutes(java.util.Collection)}. As a result, it's expectable + * that a {@link MediaRoute2Info} instance that hasn't yet been published will have a null + * provider package name. Otherwise, routes obtained via {@link MediaRouter2} should have a + * populated provider package name. */ + @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME) @Nullable - public String getPackageName() { - return mPackageName; + public String getProviderPackageName() { + return mProviderPackageName; } /** @@ -928,6 +934,15 @@ public final class MediaRoute2Info implements Parcelable { } /** + * Returns whether this route supports {@link #FLAG_ROUTING_TYPE_REMOTE remote routing}. + * + * @hide + */ + public boolean supportsRemoteRouting() { + return (mRoutingTypeFlags & MediaRoute2Info.FLAG_ROUTING_TYPE_REMOTE) != 0; + } + + /** * Returns true if the route info has all of the required field. * A route is valid if and only if it is obtained from * {@link com.android.server.media.MediaRouterService}. @@ -943,10 +958,13 @@ public final class MediaRoute2Info implements Parcelable { /** * Returns whether this route is visible to the package with the given name. + * * @hide */ + @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME) public boolean isVisibleTo(String packageName) { - return !mIsVisibilityRestricted || getPackageName().equals(packageName) + return !mIsVisibilityRestricted + || TextUtils.equals(getProviderPackageName(), packageName) || mAllowedPackages.contains(packageName); } @@ -1020,7 +1038,7 @@ public final class MediaRoute2Info implements Parcelable { pw.println(indent + "mDescription=" + mDescription); pw.println(indent + "mConnectionState=" + mConnectionState); pw.println(indent + "mClientPackageName=" + mClientPackageName); - pw.println(indent + "mPackageName=" + mPackageName); + pw.println(indent + "mProviderPackageName=" + mProviderPackageName); dumpVolume(pw, indent); @@ -1059,7 +1077,7 @@ public final class MediaRoute2Info implements Parcelable { && Objects.equals(mDescription, other.mDescription) && (mConnectionState == other.mConnectionState) && Objects.equals(mClientPackageName, other.mClientPackageName) - && Objects.equals(mPackageName, other.mPackageName) + && Objects.equals(mProviderPackageName, other.mProviderPackageName) && (mVolumeHandling == other.mVolumeHandling) && (mVolumeMax == other.mVolumeMax) && (mVolume == other.mVolume) @@ -1086,7 +1104,7 @@ public final class MediaRoute2Info implements Parcelable { mDescription, mConnectionState, mClientPackageName, - mPackageName, + mProviderPackageName, mVolumeHandling, mVolumeMax, mVolume, @@ -1162,7 +1180,7 @@ public final class MediaRoute2Info implements Parcelable { TextUtils.writeToParcel(mDescription, dest, flags); dest.writeInt(mConnectionState); dest.writeString(mClientPackageName); - dest.writeString(mPackageName); + dest.writeString(mProviderPackageName); dest.writeInt(mVolumeHandling); dest.writeInt(mVolumeMax); dest.writeInt(mVolume); @@ -1314,7 +1332,7 @@ public final class MediaRoute2Info implements Parcelable { @ConnectionState private int mConnectionState; private String mClientPackageName; - private String mPackageName; + private String mProviderPackageName; @PlaybackVolume private int mVolumeHandling = PLAYBACK_VOLUME_FIXED; private int mVolumeMax; private int mVolume; @@ -1387,7 +1405,7 @@ public final class MediaRoute2Info implements Parcelable { mDescription = routeInfo.mDescription; mConnectionState = routeInfo.mConnectionState; mClientPackageName = routeInfo.mClientPackageName; - mPackageName = routeInfo.mPackageName; + mProviderPackageName = routeInfo.mProviderPackageName; mVolumeHandling = routeInfo.mVolumeHandling; mVolumeMax = routeInfo.mVolumeMax; mVolume = routeInfo.mVolume; @@ -1534,11 +1552,13 @@ public final class MediaRoute2Info implements Parcelable { /** * Sets the package name of the route. + * * @hide */ + // It is set by the MediaRouterService. @NonNull - public Builder setPackageName(@NonNull String packageName) { - mPackageName = packageName; + public Builder setProviderPackageName(@NonNull String providerPackageName) { + mProviderPackageName = providerPackageName; return this; } diff --git a/media/java/android/media/MediaRoute2ProviderInfo.java b/media/java/android/media/MediaRoute2ProviderInfo.java index 809ee23a2b2e..bcc8cbbc2b91 100644 --- a/media/java/android/media/MediaRoute2ProviderInfo.java +++ b/media/java/android/media/MediaRoute2ProviderInfo.java @@ -152,11 +152,11 @@ public final class MediaRoute2ProviderInfo implements Parcelable { /** * Sets the package name and unique id of the provider info. - * <p> - * The unique id is automatically set by - * {@link com.android.server.media.MediaRouterService} and used to identify providers. - * The id set by {@link MediaRoute2ProviderService} will be ignored. - * </p> + * + * <p>The unique id is automatically set by {@link + * com.android.server.media.MediaRouterService} and used to identify providers. The id set + * by {@link MediaRoute2ProviderService} will be ignored. + * * @hide */ @NonNull @@ -168,10 +168,11 @@ public final class MediaRoute2ProviderInfo implements Parcelable { final ArrayMap<String, MediaRoute2Info> newRoutes = new ArrayMap<>(); for (Map.Entry<String, MediaRoute2Info> entry : mRoutes.entrySet()) { - MediaRoute2Info routeWithProviderId = new MediaRoute2Info.Builder(entry.getValue()) - .setPackageName(packageName) - .setProviderId(mUniqueId) - .build(); + MediaRoute2Info routeWithProviderId = + new MediaRoute2Info.Builder(entry.getValue()) + .setProviderPackageName(packageName) + .setProviderId(mUniqueId) + .build(); newRoutes.put(routeWithProviderId.getOriginalId(), routeWithProviderId); } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 3ac5de38f886..245360c925ad 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -19,6 +19,7 @@ package android.media; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES; import static com.android.media.flags.Flags.FLAG_ENABLE_GET_TRANSFERABLE_ROUTES; +import static com.android.media.flags.Flags.FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME; import static com.android.media.flags.Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL; import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2; import static com.android.media.flags.Flags.FLAG_ENABLE_SCREEN_OFF_SCANNING; @@ -1398,6 +1399,7 @@ public final class MediaRouter2 { requestCreateController(controller, route, managerRequestId); } + @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME) private List<MediaRoute2Info> getSortedRoutes( List<MediaRoute2Info> routes, List<String> packageOrder) { if (packageOrder.isEmpty()) { @@ -1412,11 +1414,13 @@ public final class MediaRouter2 { ArrayList<MediaRoute2Info> sortedRoutes = new ArrayList<>(routes); // take the negative for descending order sortedRoutes.sort( - Comparator.comparingInt(r -> -packagePriority.getOrDefault(r.getPackageName(), 0))); + Comparator.comparingInt( + r -> -packagePriority.getOrDefault(r.getProviderPackageName(), 0))); return sortedRoutes; } @GuardedBy("mLock") + @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME) private List<MediaRoute2Info> filterRoutesWithCompositePreferenceLocked( List<MediaRoute2Info> routes) { @@ -1429,10 +1433,10 @@ public final class MediaRouter2 { continue; } if (!mDiscoveryPreference.getAllowedPackages().isEmpty() - && (route.getPackageName() == null + && (route.getProviderPackageName() == null || !mDiscoveryPreference .getAllowedPackages() - .contains(route.getPackageName()))) { + .contains(route.getProviderPackageName()))) { continue; } if (mDiscoveryPreference.shouldRemoveDuplicates()) { @@ -3643,6 +3647,7 @@ public final class MediaRouter2 { } } + @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME) @Override public List<MediaRoute2Info> filterRoutesWithIndividualPreference( List<MediaRoute2Info> routes, RouteDiscoveryPreference discoveryPreference) { @@ -3652,10 +3657,10 @@ public final class MediaRouter2 { continue; } if (!discoveryPreference.getAllowedPackages().isEmpty() - && (route.getPackageName() == null + && (route.getProviderPackageName() == null || !discoveryPreference .getAllowedPackages() - .contains(route.getPackageName()))) { + .contains(route.getProviderPackageName()))) { continue; } filteredRoutes.add(route); diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 7e1dccf2d366..3854747f46e0 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -20,9 +20,11 @@ import static android.media.MediaRouter2.SCANNING_STATE_NOT_SCANNING; import static android.media.MediaRouter2.SCANNING_STATE_WHILE_INTERACTIVE; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; +import static com.android.media.flags.Flags.FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME; import android.Manifest; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -285,6 +287,7 @@ public final class MediaRouter2Manager { (route) -> sessionInfo.isSystemSession() ^ route.isSystemRoute()); } + @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME) private List<MediaRoute2Info> getSortedRoutes(RouteDiscoveryPreference preference) { if (!preference.shouldRemoveDuplicates()) { synchronized (mRoutesLock) { @@ -302,12 +305,15 @@ public final class MediaRouter2Manager { routes = new ArrayList<>(mRoutes.values()); } // take the negative for descending order - routes.sort(Comparator.comparingInt( - r -> -packagePriority.getOrDefault(r.getPackageName(), 0))); + routes.sort( + Comparator.comparingInt( + r -> -packagePriority.getOrDefault(r.getProviderPackageName(), 0))); return routes; } - private List<MediaRoute2Info> getFilteredRoutes(@NonNull RoutingSessionInfo sessionInfo, + @FlaggedApi(FLAG_ENABLE_MEDIA_ROUTE_2_INFO_PROVIDER_PACKAGE_NAME) + private List<MediaRoute2Info> getFilteredRoutes( + @NonNull RoutingSessionInfo sessionInfo, boolean includeSelectedRoutes, @Nullable Predicate<MediaRoute2Info> additionalFilter) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); @@ -336,9 +342,10 @@ public final class MediaRouter2Manager { continue; } if (!discoveryPreference.getAllowedPackages().isEmpty() - && (route.getPackageName() == null - || !discoveryPreference.getAllowedPackages() - .contains(route.getPackageName()))) { + && (route.getProviderPackageName() == null + || !discoveryPreference + .getAllowedPackages() + .contains(route.getProviderPackageName()))) { continue; } if (additionalFilter != null && !additionalFilter.test(route)) { diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig index bbe8e4ed7b34..4398b261377b 100644 --- a/media/java/android/media/flags/media_better_together.aconfig +++ b/media/java/android/media/flags/media_better_together.aconfig @@ -60,6 +60,13 @@ flag { } flag { + name: "enable_media_route_2_info_provider_package_name" + namespace: "media_better_together" + description: "Enables a new API to obtain the provider package name from MediaRoute2Info." + bug: "378788958" +} + +flag { name: "enable_mirroring_in_media_router_2" namespace: "media_better_together" description: "Enables support for mirroring routes in the MediaRouter2 framework, allowing Output Switcher to offer mirroring routes." diff --git a/native/android/dynamic_instrumentation_manager.cpp b/native/android/dynamic_instrumentation_manager.cpp index 074973188c66..ee2cd6f3fcde 100644 --- a/native/android/dynamic_instrumentation_manager.cpp +++ b/native/android/dynamic_instrumentation_manager.cpp @@ -73,6 +73,7 @@ ADynamicInstrumentationManager_TargetProcess* ADynamicInstrumentationManager_Tar void ADynamicInstrumentationManager_TargetProcess_destroy( const ADynamicInstrumentationManager_TargetProcess* instance) { + if (instance == nullptr) return; delete instance; } @@ -104,6 +105,7 @@ ADynamicInstrumentationManager_MethodDescriptor_create(const char* fullyQualifie void ADynamicInstrumentationManager_MethodDescriptor_destroy( const ADynamicInstrumentationManager_MethodDescriptor* instance) { + if (instance == nullptr) return; delete instance; } @@ -135,6 +137,7 @@ uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOff void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy( const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) { + if (instance == nullptr) return; delete instance; } diff --git a/native/android/include_platform/android/dynamic_instrumentation_manager.h b/native/android/include_platform/android/dynamic_instrumentation_manager.h index ab9f37034a22..7bb7615bc3a1 100644 --- a/native/android/include_platform/android/dynamic_instrumentation_manager.h +++ b/native/android/include_platform/android/dynamic_instrumentation_manager.h @@ -40,9 +40,12 @@ typedef struct ADynamicInstrumentationManager_ExecutableMethodFileOffsets * * @param uid of targeted process. * @param pid of targeted process. - * @param processName to disambiguate from corner cases that may arise from pid reuse. + * @param processName UTF-8 encoded string representing the same process as specified by `pid`. + * Supplied to disambiguate from corner cases that may arise from pid reuse. + * Referenced parameter must outlive the returned + * ADynamicInstrumentationManager_TargetProcess. */ -ADynamicInstrumentationManager_TargetProcess* _Nonnull +ADynamicInstrumentationManager_TargetProcess* _Nullable ADynamicInstrumentationManager_TargetProcess_create( uid_t uid, pid_t pid, const char* _Nonnull processName) __INTRODUCED_IN(36); /** @@ -51,22 +54,27 @@ ADynamicInstrumentationManager_TargetProcess* _Nonnull * @param instance returned from ADynamicInstrumentationManager_TargetProcess_create. */ void ADynamicInstrumentationManager_TargetProcess_destroy( - const ADynamicInstrumentationManager_TargetProcess* _Nonnull instance) __INTRODUCED_IN(36); + const ADynamicInstrumentationManager_TargetProcess* _Nullable instance) __INTRODUCED_IN(36); /** * Initializes an ADynamicInstrumentationManager_MethodDescriptor. Caller must clean up when they - * are done with ADynamicInstrumentationManager_MethodDescriptor_Destroy. + * are done with ADynamicInstrumentationManager_MethodDescriptor_destroy. * - * @param fullyQualifiedClassName fqcn of class containing the method. - * @param methodName - * @param fullyQualifiedParameters fqcn of parameters of the method's signature, or e.g. "int" for - * primitives. + * @param fullyQualifiedClassName UTF-8 encoded fqcn of class containing the method. Referenced + * parameter must outlive the returned + * ADynamicInstrumentationManager_MethodDescriptor. + * @param methodName UTF-8 encoded method name. Referenced parameter must outlive the returned + * ADynamicInstrumentationManager_MethodDescriptor. + * @param fullyQualifiedParameters UTF-8 encoded fqcn of parameters of the method's signature, + * or e.g. "int" for primitives. Referenced parameter should + * outlive the returned + * ADynamicInstrumentationManager_MethodDescriptor. * @param numParameters length of `fullyQualifiedParameters` array. */ -ADynamicInstrumentationManager_MethodDescriptor* _Nonnull +ADynamicInstrumentationManager_MethodDescriptor* _Nullable ADynamicInstrumentationManager_MethodDescriptor_create( const char* _Nonnull fullyQualifiedClassName, const char* _Nonnull methodName, - const char* _Nonnull fullyQualifiedParameters[_Nonnull], size_t numParameters) + const char* _Nonnull* _Nonnull fullyQualifiedParameters, size_t numParameters) __INTRODUCED_IN(36); /** * Clean up an ADynamicInstrumentationManager_MethodDescriptor. @@ -74,14 +82,16 @@ ADynamicInstrumentationManager_MethodDescriptor* _Nonnull * @param instance returned from ADynamicInstrumentationManager_MethodDescriptor_create. */ void ADynamicInstrumentationManager_MethodDescriptor_destroy( - const ADynamicInstrumentationManager_MethodDescriptor* _Nonnull instance) + const ADynamicInstrumentationManager_MethodDescriptor* _Nullable instance) __INTRODUCED_IN(36); /** * Get the containerPath calculated by * ADynamicInstrumentationManager_getExecutableMethodFileOffsets. * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets. - * @return The OS path of the containing file. + * @return The OS path of the containing file as a UTF-8 string, which has the same lifetime + * as the ADynamicInstrumentationManager_ExecutableMethodFileOffsets instance passed + * as a param. */ const char* _Nullable ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerPath( const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance) @@ -90,7 +100,8 @@ const char* _Nullable ADynamicInstrumentationManager_ExecutableMethodFileOffsets * Get the containerOffset calculated by * ADynamicInstrumentationManager_getExecutableMethodFileOffsets. * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets. - * @return The offset of the containing file within the process' memory. + * @return The absolute address of the containing file within remote the process' virtual memory + * space. */ uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset( const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance) @@ -98,7 +109,8 @@ uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainer /** * Get the methodOffset calculated by ADynamicInstrumentationManager_getExecutableMethodFileOffsets. * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets. - * @return The offset of the method within the containing file. + * @return The offset of the method within the container whose address is returned by + * ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset. */ uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOffset( const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance) @@ -109,7 +121,7 @@ uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOff * @param instance returned from ADynamicInstrumentationManager_getExecutableMethodFileOffsets. */ void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy( - const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance) + const ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nullable instance) __INTRODUCED_IN(36); /** * Provides ART metadata about the described java method within the target process. @@ -118,7 +130,9 @@ void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy( * @param methodDescriptor describes the targeted method. * @param out will be populated with the data if successful. A nullptr combined * with an OK status means that the program method is defined, but the offset - * info was unavailable because it is not AOT compiled. + * info was unavailable because it is not AOT compiled. Caller owns `out` and + * should clean it up with + * ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy. * @return status indicating success or failure. The values correspond to the `binder_exception_t` * enum values from <android/binder_status.h>. */ diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp index 9257901bcd1f..0db99ffd208a 100644 --- a/native/android/performance_hint.cpp +++ b/native/android/performance_hint.cpp @@ -22,7 +22,6 @@ #include <aidl/android/hardware/power/SessionHint.h> #include <aidl/android/hardware/power/SessionMode.h> #include <aidl/android/hardware/power/SessionTag.h> -#include <aidl/android/hardware/power/SupportInfo.h> #include <aidl/android/hardware/power/WorkDuration.h> #include <aidl/android/hardware/power/WorkDurationFixedV1.h> #include <aidl/android/os/IHintManager.h> @@ -149,36 +148,10 @@ private: std::future<bool> mChannelCreationFinished; }; -class SupportInfoWrapper { -public: - SupportInfoWrapper(hal::SupportInfo& info); - bool isSessionModeSupported(hal::SessionMode mode); - bool isSessionHintSupported(hal::SessionHint hint); - -private: - template <class T> - bool getEnumSupportFromBitfield(T& enumValue, int64_t& supportBitfield) { - // extract the bit corresponding to the enum by shifting the bitfield - // over that much and cutting off any extra values - return (supportBitfield >> static_cast<int>(enumValue)) % 2; - } - hal::SupportInfo mSupportInfo; -}; - -class HintManagerClient : public IHintManager::BnHintManagerClient { -public: - // Currently a no-op that exists for FMQ init to call in the future - ndk::ScopedAStatus receiveChannelConfig(const hal::ChannelConfig&) { - return ndk::ScopedAStatus::ok(); - } -}; - struct APerformanceHintManager { public: static APerformanceHintManager* getInstance(); - APerformanceHintManager(std::shared_ptr<IHintManager>& service, - IHintManager::HintManagerClientData&& clientData, - std::shared_ptr<HintManagerClient> callbackClient); + APerformanceHintManager(std::shared_ptr<IHintManager>& service, int64_t preferredRateNanos); APerformanceHintManager() = delete; ~APerformanceHintManager(); @@ -196,21 +169,29 @@ public: FMQWrapper& getFMQWrapper(); bool canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) REQUIRES(sHintMutex); void initJava(JNIEnv* _Nonnull env); + ndk::ScopedAIBinder_Weak x; template <class T> static void layersFromNativeSurfaces(ANativeWindow** windows, int numWindows, ASurfaceControl** controls, int numSurfaceControls, std::vector<T>& out); - ndk::SpAIBinder& getToken(); - SupportInfoWrapper& getSupportInfo(); private: + // Necessary to create an empty binder object + static void* tokenStubOnCreate(void*) { + return nullptr; + } + static void tokenStubOnDestroy(void*) {} + static binder_status_t tokenStubOnTransact(AIBinder*, transaction_code_t, const AParcel*, + AParcel*) { + return STATUS_OK; + } + static APerformanceHintManager* create(std::shared_ptr<IHintManager> iHintManager); std::shared_ptr<IHintManager> mHintManager; - std::shared_ptr<HintManagerClient> mCallbackClient; - IHintManager::HintManagerClientData mClientData; - SupportInfoWrapper mSupportInfoWrapper; ndk::SpAIBinder mToken; + const int64_t mPreferredRateNanos; + std::optional<int32_t> mMaxGraphicsPipelineThreadsCount; FMQWrapper mFMQWrapper; double mHintBudget = kMaxLoadHintsPerInterval; int64_t mLastBudgetReplenish = 0; @@ -292,27 +273,14 @@ static FMQWrapper& getFMQ() { return APerformanceHintManager::getInstance()->getFMQWrapper(); } -// ===================================== SupportInfoWrapper implementation - -SupportInfoWrapper::SupportInfoWrapper(hal::SupportInfo& info) : mSupportInfo(info) {} - -bool SupportInfoWrapper::isSessionHintSupported(hal::SessionHint hint) { - return getEnumSupportFromBitfield(hint, mSupportInfo.sessionHints); -} - -bool SupportInfoWrapper::isSessionModeSupported(hal::SessionMode mode) { - return getEnumSupportFromBitfield(mode, mSupportInfo.sessionModes); -} - // ===================================== APerformanceHintManager implementation APerformanceHintManager::APerformanceHintManager(std::shared_ptr<IHintManager>& manager, - IHintManager::HintManagerClientData&& clientData, - std::shared_ptr<HintManagerClient> callbackClient) - : mHintManager(std::move(manager)), - mCallbackClient(callbackClient), - mClientData(clientData), - mSupportInfoWrapper(clientData.supportInfo), - mToken(callbackClient->asBinder()) { + int64_t preferredRateNanos) + : mHintManager(std::move(manager)), mPreferredRateNanos(preferredRateNanos) { + static AIBinder_Class* tokenBinderClass = + AIBinder_Class_define("phm_token", tokenStubOnCreate, tokenStubOnDestroy, + tokenStubOnTransact); + mToken = ndk::SpAIBinder(AIBinder_new(tokenBinderClass, nullptr)); if (mFMQWrapper.isSupported()) { mFMQWrapper.setToken(mToken); mFMQWrapper.startChannel(mHintManager.get()); @@ -347,17 +315,16 @@ APerformanceHintManager* APerformanceHintManager::create(std::shared_ptr<IHintMa ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__); return nullptr; } - std::shared_ptr<HintManagerClient> client = ndk::SharedRefBase::make<HintManagerClient>(); - IHintManager::HintManagerClientData clientData; - ndk::ScopedAStatus ret = manager->registerClient(client, &clientData); + int64_t preferredRateNanos = -1L; + ndk::ScopedAStatus ret = manager->getHintSessionPreferredRate(&preferredRateNanos); if (!ret.isOk()) { - ALOGE("%s: PerformanceHint is not supported. %s", __FUNCTION__, ret.getMessage()); + ALOGE("%s: PerformanceHint cannot get preferred rate. %s", __FUNCTION__, ret.getMessage()); return nullptr; } - if (clientData.preferredRateNanos <= 0) { - clientData.preferredRateNanos = -1L; + if (preferredRateNanos <= 0) { + preferredRateNanos = -1L; } - return new APerformanceHintManager(manager, std::move(clientData), client); + return new APerformanceHintManager(manager, preferredRateNanos); } bool APerformanceHintManager::canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) { @@ -422,9 +389,7 @@ APerformanceHintSession* APerformanceHintManager::createSessionUsingConfig( ALOGE("%s: PerformanceHint cannot create session. %s", __FUNCTION__, ret.getMessage()); return nullptr; } - - auto out = new APerformanceHintSession(mHintManager, std::move(session), - mClientData.preferredRateNanos, + auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos, sessionCreationConfig->targetWorkDurationNanos, isJava, sessionConfig.id == -1 ? std::nullopt @@ -451,11 +416,24 @@ APerformanceHintSession* APerformanceHintManager::getSessionFromJava(JNIEnv* env } int64_t APerformanceHintManager::getPreferredRateNanos() const { - return mClientData.preferredRateNanos; + return mPreferredRateNanos; } int32_t APerformanceHintManager::getMaxGraphicsPipelineThreadsCount() { - return mClientData.maxGraphicsPipelineThreads; + if (!mMaxGraphicsPipelineThreadsCount.has_value()) { + int32_t threadsCount = -1; + ndk::ScopedAStatus ret = mHintManager->getMaxGraphicsPipelineThreadsCount(&threadsCount); + if (!ret.isOk()) { + ALOGE("%s: PerformanceHint cannot get max graphics pipeline threads count. %s", + __FUNCTION__, ret.getMessage()); + return -1; + } + if (threadsCount <= 0) { + threadsCount = -1; + } + mMaxGraphicsPipelineThreadsCount.emplace(threadsCount); + } + return mMaxGraphicsPipelineThreadsCount.value(); } FMQWrapper& APerformanceHintManager::getFMQWrapper() { @@ -472,14 +450,6 @@ void APerformanceHintManager::initJava(JNIEnv* _Nonnull env) { mJavaInitialized = true; } -ndk::SpAIBinder& APerformanceHintManager::getToken() { - return mToken; -} - -SupportInfoWrapper& APerformanceHintManager::getSupportInfo() { - return mSupportInfoWrapper; -} - // ===================================== APerformanceHintSession implementation constexpr int kNumEnums = enum_size<hal::SessionHint>(); diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp index e3c10f63abb4..c166e738ffb2 100644 --- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp +++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp @@ -56,6 +56,9 @@ public: const SessionCreationConfig& creationConfig, hal::SessionConfig* config, std::shared_ptr<IHintSession>* _aidl_return), (override)); + MOCK_METHOD(ScopedAStatus, getHintSessionPreferredRate, (int64_t * _aidl_return), (override)); + MOCK_METHOD(ScopedAStatus, getMaxGraphicsPipelineThreadsCount, (int32_t* _aidl_return), + (override)); MOCK_METHOD(ScopedAStatus, setHintSessionThreads, (const std::shared_ptr<IHintSession>& hintSession, const ::std::vector<int32_t>& tids), @@ -81,11 +84,6 @@ public: MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return), (override)); MOCK_METHOD(ScopedAStatus, passSessionManagerBinder, (const SpAIBinder& sessionManager)); - MOCK_METHOD(ScopedAStatus, registerClient, - (const std::shared_ptr<::aidl::android::os::IHintManager::IHintManagerClient>& - clientDataIn, - ::aidl::android::os::IHintManager::HintManagerClientData* _aidl_return), - (override)); MOCK_METHOD(SpAIBinder, asBinder, (), (override)); MOCK_METHOD(bool, isRemote, (), (override)); }; @@ -127,9 +125,10 @@ public: APerformanceHintManager* createManager() { APerformanceHint_setUseFMQForTesting(mUsingFMQ); - ON_CALL(*mMockIHintManager, registerClient(_, _)) - .WillByDefault( - DoAll(SetArgPointee<1>(mClientData), [] { return ScopedAStatus::ok(); })); + ON_CALL(*mMockIHintManager, getHintSessionPreferredRate(_)) + .WillByDefault(DoAll(SetArgPointee<0>(123L), [] { return ScopedAStatus::ok(); })); + ON_CALL(*mMockIHintManager, getMaxGraphicsPipelineThreadsCount(_)) + .WillByDefault(DoAll(SetArgPointee<0>(5), [] { return ScopedAStatus::ok(); })); return APerformanceHint_getManager(); } @@ -239,20 +238,6 @@ public: int kMockQueueSize = 20; bool mUsingFMQ = false; - IHintManager::HintManagerClientData mClientData{ - .powerHalVersion = 6, - .maxGraphicsPipelineThreads = 5, - .preferredRateNanos = 123L, - .supportInfo{ - .usesSessions = true, - .boosts = 0, - .modes = 0, - .sessionHints = -1, - .sessionModes = -1, - .sessionTags = -1, - }, - }; - int32_t mMaxLoadHintsPerInterval; int64_t mLoadHintInterval; @@ -271,6 +256,12 @@ bool equalsWithoutTimestamp(hal::WorkDuration lhs, hal::WorkDuration rhs) { lhs.gpuDurationNanos == rhs.gpuDurationNanos && lhs.durationNanos == rhs.durationNanos; } +TEST_F(PerformanceHintTest, TestGetPreferredUpdateRateNanos) { + APerformanceHintManager* manager = createManager(); + int64_t preferredUpdateRateNanos = APerformanceHint_getPreferredUpdateRateNanos(manager); + EXPECT_EQ(123L, preferredUpdateRateNanos); +} + TEST_F(PerformanceHintTest, TestSession) { APerformanceHintManager* manager = createManager(); APerformanceHintSession* session = createSession(manager); diff --git a/nfc/Android.bp b/nfc/Android.bp index b82dec88993d..c33665aef41d 100644 --- a/nfc/Android.bp +++ b/nfc/Android.bp @@ -56,7 +56,7 @@ java_sdk_library { ], defaults: ["framework-module-defaults"], sdk_version: "module_current", - min_sdk_version: "current", + min_sdk_version: "35", // Make it 36 once available. installable: true, optimize: { enabled: false, diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java index 63fe1b509751..e173c5e996df 100644 --- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java +++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java @@ -25,13 +25,15 @@ import android.widget.Spinner; import androidx.preference.Preference; import androidx.preference.Preference.OnPreferenceClickListener; import androidx.preference.PreferenceViewHolder; + import com.android.settingslib.widget.spinner.R; /** * This preference uses Spinner & SettingsSpinnerAdapter which provide default layouts for * both view and drop down view of the Spinner. */ -public class SettingsSpinnerPreference extends Preference implements OnPreferenceClickListener { +public class SettingsSpinnerPreference extends Preference + implements OnPreferenceClickListener, GroupSectionDividerMixin { private SettingsSpinnerAdapter mAdapter; private AdapterView.OnItemSelectedListener mListener; diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index 5a4d3ce5661b..63c8929ef652 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -667,4 +667,25 @@ <item>3</item> </string-array> + <!-- Options for showing shade on external display for developers --> + <string-array name="shade_display_awareness_entries" > + <item>Device display only (Default)</item> + <item>External display</item> + <item>Focus-based</item> + </string-array> + + <!-- Options for showing shade on external display for developers --> + <string-array name="shade_display_awareness_summaries" > + <item>Show shade on device display only </item> + <item>Show device on single external display</item> + <item>Show device on last focused display</item> + </string-array> + + <!-- Values for showing shade on external display for developers --> + <string-array name="shade_display_awareness_values" > + <item>device-display</item> + <item>external-display</item> + <item>focus-based</item> + </string-array> + </resources> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index eaf155df4785..e1929b725a58 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -990,6 +990,9 @@ <!-- UI debug setting: simulate secondary display devices using overlays [CHAR LIMIT=45] --> <string name="overlay_display_devices_title">Simulate secondary displays</string> + <!-- UI debug setting: shade display awareness title [CHAR LIMIT=45] --> + <string name="shade_display_awareness_title">Shade display position</string> + <!-- Preference category for application debugging development settings. [CHAR LIMIT=25] --> <string name="debug_applications_category">Apps</string> diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 9004488c2e12..c88a7fd834d6 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -182,6 +182,7 @@ public class SettingsBackupTest { Settings.Global.DEVELOPMENT_FORCE_RTL, Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, + Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS, Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH, Settings.Global.DEVICE_DEMO_MODE, Settings.Global.DEVICE_IDLE_CONSTANTS, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt index 85f549d43a11..55b42931b1fa 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt @@ -19,6 +19,7 @@ package com.android.systemui.bouncer.ui.composable import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.overscroll import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -101,6 +102,7 @@ private fun SceneScope.BouncerScene( viewModel, dialogFactory, Modifier.element(Bouncer.Elements.Content) + .overscroll(verticalOverscrollEffect) .sysuiResTag(Bouncer.TestTags.Root) .fillMaxSize(), ) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt index 364adcaffd77..5e9ade163ac2 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt @@ -82,20 +82,15 @@ constructor( Modifier.shortcutPadding() } else { Modifier - } + }, ) } } } @Composable - fun SceneScope.IndicationArea( - modifier: Modifier = Modifier, - ) { - Element( - key = IndicationAreaElementKey, - modifier = modifier.indicationAreaPadding(), - ) { + fun SceneScope.IndicationArea(modifier: Modifier = Modifier) { + Element(key = IndicationAreaElementKey, modifier = modifier.indicationAreaPadding()) { content { IndicationArea( indicationAreaViewModel = indicationAreaViewModel, @@ -138,24 +133,20 @@ constructor( ResourcesCompat.getDrawable( context.resources, R.drawable.keyguard_bottom_affordance_bg, - context.theme + context.theme, ) foreground = ResourcesCompat.getDrawable( context.resources, R.drawable.keyguard_bottom_affordance_selected_border, - context.theme + context.theme, ) visibility = View.INVISIBLE setPadding(padding, padding, padding, padding) } setBinding( - binder.bind( - view, - viewModel, - transitionAlpha, - ) { + binder.bind(view, viewModel, transitionAlpha) { indicationController.showTransientIndication(it) } ) @@ -164,10 +155,7 @@ constructor( }, onRelease = { binding?.destroy() }, modifier = - modifier.size( - width = shortcutSizeDp().width, - height = shortcutSizeDp().height, - ) + modifier.size(width = shortcutSizeDp().width, height = shortcutSizeDp().height), ) } @@ -182,6 +170,8 @@ constructor( AndroidView( factory = { context -> val view = KeyguardIndicationArea(context, null) + view.isFocusable = true + view.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES setDisposable( KeyguardIndicationAreaBinder.bind( view = view, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt index 48067ce3c4a0..ef8911dae566 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt @@ -29,7 +29,7 @@ import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.android.compose.animation.scene.SceneScope +import com.android.compose.animation.scene.ContentScope import com.android.compose.animation.scene.content.state.TransitionState import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shared.model.ShadeMode @@ -42,7 +42,7 @@ import kotlinx.coroutines.launch * transition. */ @Composable -fun SceneScope.NotificationLockscreenScrim( +fun ContentScope.NotificationLockscreenScrim( viewModel: NotificationLockscreenScrimViewModel, modifier: Modifier = Modifier, ) { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt index 94c18cdbef5a..cb87f0e7cf1c 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt @@ -62,7 +62,6 @@ fun NotificationScrimNestedScrollConnection( canStartPostScroll = { offsetAvailable, _, _ -> offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll()) }, - canStartPostFling = { false }, onStart = { firstScroll -> onStart(firstScroll) object : ScrollController { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt index d8abfd7a4b94..e1ee59ba0626 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt @@ -25,11 +25,13 @@ import androidx.compose.foundation.layout.offset import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastCoerceAtLeast import com.android.compose.nestedscroll.OnStopScope @@ -80,9 +82,29 @@ fun Modifier.stackVerticalOverscroll( } return this.then( - Modifier.nestedScroll(stackNestedScrollConnection).offset { - IntOffset(x = 0, y = overscrollOffset.value.roundToInt()) - } + Modifier.nestedScroll( + remember { + object : NestedScrollConnection { + override suspend fun onPostFling( + consumed: Velocity, + available: Velocity, + ): Velocity { + return if (available.y < 0f && !canScrollForward()) { + overscrollOffset.animateTo( + targetValue = 0f, + initialVelocity = available.y, + animationSpec = tween(), + ) + available + } else { + Velocity.Zero + } + } + } + } + ) + .nestedScroll(stackNestedScrollConnection) + .offset { IntOffset(x = 0, y = overscrollOffset.value.roundToInt()) } ) } @@ -100,7 +122,6 @@ fun NotificationStackNestedScrollConnection( canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ -> offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward() }, - canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() }, onStart = { firstScroll -> onStart(firstScroll) object : ScrollController { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt index ae273d8e2ad9..b54de784a202 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt @@ -45,6 +45,7 @@ import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.windowInsetsBottomHeight +import androidx.compose.foundation.overscroll import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme @@ -84,10 +85,10 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.util.lerp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.ContentKey +import com.android.compose.animation.scene.ContentScope import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.LowestZIndexContentPicker import com.android.compose.animation.scene.NestedScrollBehavior -import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.SceneTransitionLayoutState import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.modifiers.thenIf @@ -134,7 +135,7 @@ private val quickSettingsShadeContentKey: ContentKey * entire size of the scene. */ @Composable -fun SceneScope.HeadsUpNotificationSpace( +fun ContentScope.HeadsUpNotificationSpace( stackScrollView: NotificationScrollView, viewModel: NotificationsPlaceholderViewModel, useHunBounds: () -> Boolean = { true }, @@ -176,7 +177,7 @@ fun SceneScope.HeadsUpNotificationSpace( * the user. When swiped up, the heads up notification is snoozed. */ @Composable -fun SceneScope.SnoozeableHeadsUpNotificationSpace( +fun ContentScope.SnoozeableHeadsUpNotificationSpace( stackScrollView: NotificationScrollView, viewModel: NotificationsPlaceholderViewModel, ) { @@ -246,7 +247,7 @@ fun SceneScope.SnoozeableHeadsUpNotificationSpace( /** Adds the space where notification stack should appear in the scene. */ @Composable -fun SceneScope.ConstrainedNotificationStack( +fun ContentScope.ConstrainedNotificationStack( stackScrollView: NotificationScrollView, viewModel: NotificationsPlaceholderViewModel, modifier: Modifier = Modifier, @@ -281,7 +282,7 @@ fun SceneScope.ConstrainedNotificationStack( */ @OptIn(ExperimentalLayoutApi::class) @Composable -fun SceneScope.NotificationScrollingStack( +fun ContentScope.NotificationScrollingStack( shadeSession: SaveableSession, stackScrollView: NotificationScrollView, viewModel: NotificationsPlaceholderViewModel, @@ -480,6 +481,7 @@ fun SceneScope.NotificationScrollingStack( modifier = modifier .element(Notifications.Elements.NotificationScrim) + .overscroll(verticalOverscrollEffect) .offset { // if scrim is expanded while transitioning to Gone or QS scene, increase the // offset in step with the corresponding transition so that it is 0 when it @@ -622,7 +624,7 @@ fun SceneScope.NotificationScrollingStack( * the notification contents (stack, footer, shelf) should be drawn. */ @Composable -fun SceneScope.NotificationStackCutoffGuideline( +fun ContentScope.NotificationStackCutoffGuideline( stackScrollView: NotificationScrollView, viewModel: NotificationsPlaceholderViewModel, modifier: Modifier = Modifier, @@ -642,7 +644,7 @@ fun SceneScope.NotificationStackCutoffGuideline( } @Composable -private fun SceneScope.NotificationPlaceholder( +private fun ContentScope.NotificationPlaceholder( stackScrollView: NotificationScrollView, viewModel: NotificationsPlaceholderViewModel, useStackBounds: () -> Boolean, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt index 5fb9416cf35b..e4f4df386583 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt @@ -37,6 +37,7 @@ import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotific import com.android.systemui.qs.ui.composable.QuickSettings import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.Default +import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.viewmodel.GoneUserActionsViewModel import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView @@ -70,18 +71,22 @@ constructor( @Composable override fun SceneScope.Content(modifier: Modifier) { - val isIdle by remember { - derivedStateOf { layoutState.transitionState is TransitionState.Idle } + val isIdleAndNotOccluded by remember { + derivedStateOf { + layoutState.transitionState is TransitionState.Idle && + Overlays.NotificationsShade !in layoutState.transitionState.currentOverlays + } } - LaunchedEffect(isIdle) { + LaunchedEffect(isIdleAndNotOccluded) { // Wait for being Idle on this Scene, otherwise LaunchedEffect would fire too soon, // and another transition could override the NSSL stack bounds. - if (isIdle) { + if (isIdleAndNotOccluded) { // Reset the stack bounds to avoid caching these values from the previous Scenes, // and not to confuse the StackScrollAlgorithm when it displays a HUN over GONE. notificationStackScrolLView.get().apply { - setStackTop(0f) + // use -headsUpInset to allow HUN translation outside bounds for snoozing + setStackTop(-getHeadsUpInset().toFloat()) setStackCutoff(0f) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt index 9de7a5d659ae..55fafd5cfeca 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt @@ -33,7 +33,6 @@ import com.android.systemui.scene.ui.composable.transitions.notificationsShadeTo import com.android.systemui.scene.ui.composable.transitions.shadeToQuickSettingsTransition import com.android.systemui.scene.ui.composable.transitions.toNotificationsShadeTransition import com.android.systemui.scene.ui.composable.transitions.toQuickSettingsShadeTransition -import com.android.systemui.shade.ui.composable.OverlayShade import com.android.systemui.shade.ui.composable.Shade /** @@ -134,27 +133,11 @@ val SceneContainerTransitions = transitions { } // Scene overscroll - + // TODO(b/382477212) Remove STL Overscroll DSL overscrollDisabled(Scenes.Gone, Orientation.Vertical) overscrollDisabled(Scenes.Lockscreen, Orientation.Vertical) - overscroll(Scenes.Bouncer, Orientation.Vertical) { - translate(Bouncer.Elements.Content, y = { absoluteDistance }) - } - overscroll(Scenes.Shade, Orientation.Vertical) { - translate( - Notifications.Elements.NotificationScrim, - y = Shade.Dimensions.ScrimOverscrollLimit, - ) - translate(Shade.Elements.SplitShadeStartColumn, y = Shade.Dimensions.ScrimOverscrollLimit) - translate( - Notifications.Elements.NotificationStackPlaceholder, - y = Shade.Dimensions.ScrimOverscrollLimit, - ) - } - overscroll(Overlays.NotificationsShade, Orientation.Vertical) { - translate(OverlayShade.Elements.Panel, y = OverlayShade.Dimensions.OverscrollLimit) - } - overscroll(Overlays.QuickSettingsShade, Orientation.Vertical) { - translate(OverlayShade.Elements.Panel, y = OverlayShade.Dimensions.OverscrollLimit) - } + overscrollDisabled(Scenes.Bouncer, Orientation.Vertical) + overscrollDisabled(Scenes.Shade, Orientation.Vertical) + overscrollDisabled(Overlays.NotificationsShade, Orientation.Vertical) + overscrollDisabled(Overlays.QuickSettingsShade, Orientation.Vertical) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt index 46f5ecd99301..8a5c96da5ac6 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt @@ -35,6 +35,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsIgnoringVisibility import androidx.compose.foundation.layout.waterfall import androidx.compose.foundation.layout.width +import androidx.compose.foundation.overscroll import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass @@ -65,7 +66,10 @@ fun SceneScope.OverlayShade( Box(modifier = Modifier.fillMaxSize().panelPadding(), contentAlignment = Alignment.TopEnd) { Panel( - modifier = Modifier.element(OverlayShade.Elements.Panel).panelSize(), + modifier = + Modifier.element(OverlayShade.Elements.Panel) + .overscroll(verticalOverscrollEffect) + .panelSize(), content = content, ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt index 22b6dbcf41ec..79fd1d7ddd8f 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt @@ -39,6 +39,7 @@ import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.overscroll import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable @@ -527,6 +528,7 @@ private fun SceneScope.SplitShade( Box( modifier = Modifier.element(Shade.Elements.SplitShadeStartColumn) + .overscroll(verticalOverscrollEffect) .weight(1f) .graphicsLayer { translationX = unfoldTranslationXForStartSide } ) { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt index 2d589f37f3cb..bb61a131dac3 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt @@ -108,7 +108,7 @@ internal class DraggableHandlerImpl( swipes.updateSwipesResults(fromContent) val result = - swipes.findUserActionResult(overSlop) + (if (overSlop < 0f) swipes.upOrLeftResult else swipes.downOrRightResult) // As we were unable to locate a valid target scene, the initial SwipeAnimation // cannot be defined. Consequently, a simple NoOp Controller will be returned. ?: return NoOpDragController @@ -448,27 +448,6 @@ internal class Swipes(val upOrLeft: Swipe.Resolved, val downOrRight: Swipe.Resol this.upOrLeftResult = upOrLeftResult this.downOrRightResult = downOrRightResult } - - /** - * Returns the [UserActionResult] in the direction of [directionOffset]. - * - * @param directionOffset signed float that indicates the direction. Positive is down or right - * negative is up or left. - * @return null when there are no targets in either direction. If one direction is null and you - * drag into the null direction this function will return the opposite direction, assuming - * that the users intention is to start the drag into the other direction eventually. If - * [directionOffset] is 0f and both direction are available, it will default to - * [upOrLeftResult]. - */ - fun findUserActionResult(directionOffset: Float): UserActionResult? { - return when { - upOrLeftResult == null && downOrRightResult == null -> null - (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null -> - upOrLeftResult - - else -> downOrRightResult - } - } } internal class NestedScrollHandlerImpl( @@ -536,31 +515,6 @@ internal class NestedScrollHandlerImpl( } } }, - canStartPostFling = { velocityAvailable -> - val behavior: NestedScrollBehavior = - when { - velocityAvailable > 0f -> topOrLeftBehavior - velocityAvailable < 0f -> bottomOrRightBehavior - else -> return@PriorityNestedScrollConnection false - } - - // We could start an overscroll animation - canChangeScene = false - - val pointersDown: PointersInfo.PointersDown? = - when (val info = pointersInfoOwner.pointersInfo()) { - PointersInfo.MouseWheel -> { - // Do not support mouse wheel interactions - return@PriorityNestedScrollConnection false - } - - is PointersInfo.PointersDown -> info - null -> null - } - lastPointersDown = pointersDown - - behavior.canStartOnPostFling && shouldEnableSwipes() - }, onStart = { firstScroll -> scrollController( dragController = diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt index 955be603efaf..9622fc151bb7 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt @@ -32,7 +32,7 @@ import androidx.compose.ui.platform.InspectorInfo * not consumed by the [SceneTransitionLayout] unless specifically requested via * [nestedScrollToScene]. */ -enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) { +enum class NestedScrollBehavior { /** * Overscroll will only be used by the [SceneTransitionLayout] to move to the next scene if the * gesture begins at the edge of the scrollable component (so that a scroll in that direction @@ -42,7 +42,7 @@ enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) { * In addition, during scene transitions, scroll events are consumed by the * [SceneTransitionLayout] instead of the scrollable component. */ - EdgeNoPreview(canStartOnPostFling = false), + EdgeNoPreview, /** * Overscroll will only be used by the [SceneTransitionLayout] to move to the next scene if the @@ -52,7 +52,7 @@ enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) { * In addition, during scene transitions, scroll events are consumed by the * [SceneTransitionLayout] instead of the scrollable component. */ - EdgeWithPreview(canStartOnPostFling = true), + @Deprecated("This will be removed, see b/378470603") EdgeWithPreview, /** * Any overscroll will be used by the [SceneTransitionLayout] to move to the next scene. @@ -60,7 +60,7 @@ enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) { * In addition, during scene transitions, scroll events are consumed by the * [SceneTransitionLayout] instead of the scrollable component. */ - EdgeAlways(canStartOnPostFling = true); + EdgeAlways; companion object { val Default = EdgeNoPreview diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt index 48f08a7086d6..952668ab49ff 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt @@ -111,6 +111,9 @@ interface SceneTransitionsBuilder { * The overscroll animation always starts from a progress of 0f, and reaches 1f when moving the * [distance] down/right, -1f when moving in the opposite direction. */ + @Deprecated( + "Use verticalOverscrollEffect (or horizontalOverscrollEffect) directly from SceneScope." + ) fun overscroll( content: ContentKey, orientation: Orientation, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt index 98a00173f1d7..b26bf55c85ec 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt @@ -56,7 +56,6 @@ fun LargeTopAppBarNestedScrollConnection( canStartPostScroll = { offsetAvailable, _, _ -> offsetAvailable > 0 && height() < maxHeight() }, - canStartPostFling = { false }, onStart = { LargeTopAppBarScrollController( height = height, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt index 3f182363e20c..3d0f182fffee 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt @@ -24,7 +24,6 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.unit.Velocity import com.android.compose.ui.util.SpaceVectorConverter -import kotlin.math.sign import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope @@ -102,8 +101,8 @@ interface OnStopScope { * over the default nested scrolling logic. * * When started, this connection intercepts scroll events *before* they reach child composables. - * This "priority mode" is activated activated when either [canStartPreScroll], [canStartPostScroll] - * or [canStartPostFling] returns `true`. + * This "priority mode" is activated when either [canStartPreScroll] or [canStartPostScroll] returns + * `true`. * * Once started, the [onStart] lambda provides a [ScrollController] to manage the scrolling. This * controller allows you to directly manipulate the scroll state and define how scroll events are @@ -123,8 +122,6 @@ interface OnStopScope { * @param canStartPostScroll A lambda that returns `true` if the connection should enter priority * mode during the post-scroll phase. This is called after child connections have consumed the * scroll. - * @param canStartPostFling A lambda that returns `true` if the connection should enter priority - * mode during the post-fling phase. This is called after a fling gesture has been initiated. * @param onStart A lambda that is called when the connection enters priority mode. It should return * a [ScrollController] that will be used to control the scroll. * @sample LargeTopAppBarNestedScrollConnection @@ -136,7 +133,6 @@ class PriorityNestedScrollConnection( (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean, private val canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean, - private val canStartPostFling: (velocityAvailable: Float) -> Boolean, private val onStart: (firstScroll: Float) -> ScrollController, ) : NestedScrollConnection, SpaceVectorConverter by SpaceVectorConverter(orientation) { @@ -233,17 +229,6 @@ class PriorityNestedScrollConnection( return stop(velocity = availableFloat) } - // Check if post-fling condition is met, and start priority mode if necessary. - // TODO(b/291053278): Remove canStartPostFling() and instead make it possible to define the - // overscroll behavior on the Scene level. - if (canStartPostFling(availableFloat)) { - // The offset passed to onPriorityStart() must be != 0f, so we create a small offset of - // 1px given the available velocity. - val smallOffset = availableFloat.sign - start(availableOffset = smallOffset) - return stop(availableFloat) - } - // Reset offset tracking after the fling gesture is finished. resetOffsetTracker() return Velocity.Zero diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt index b20056d54de1..6985644579f6 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt @@ -19,7 +19,9 @@ package com.android.compose.animation.scene import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.overscroll import androidx.compose.material3.Text +import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.UserInput @@ -102,7 +104,7 @@ class DraggableHandlerTest { userActions = mapOf(Swipe.Up to SceneB, Swipe.Up(fromSource = Edge.Bottom) to SceneA), ) { - Text("SceneC") + Text("SceneC", Modifier.overscroll(verticalOverscrollEffect)) } overlay( key = OverlayA, @@ -434,35 +436,12 @@ class DraggableHandlerTest { } @Test - fun onDragIntoNoAction_startTransitionToOppositeDirection() = runGestureTest { + fun onDragIntoNoAction_stayIdle() = runGestureTest { navigateToSceneC() // We are on SceneC which has no action in Down direction - val dragController = onDragStarted(overSlop = 10f) - assertTransition( - currentScene = SceneC, - fromScene = SceneC, - toScene = SceneB, - progress = -0.1f, - ) - - // Reverse drag direction, it will consume the previous drag - dragController.onDragDelta(pixels = -10f) - assertTransition( - currentScene = SceneC, - fromScene = SceneC, - toScene = SceneB, - progress = 0.0f, - ) - - // Continue reverse drag direction, it should record progress to Scene B - dragController.onDragDelta(pixels = -10f) - assertTransition( - currentScene = SceneC, - fromScene = SceneC, - toScene = SceneB, - progress = 0.1f, - ) + onDragStarted(overSlop = 10f, expectedConsumedOverSlop = 0f) + assertIdle(currentScene = SceneC) } @Test @@ -942,30 +921,6 @@ class DraggableHandlerTest { } @Test - fun scrollFromIdleWithNoTargetScene_shouldUseOverscrollSpecIfAvailable() = runGestureTest { - layoutState.transitions = transitions { - overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) } - } - // Start at scene C. - navigateToSceneC() - - val scene = layoutState.transitionState.currentScene - // We should have overscroll spec for scene C - assertThat(layoutState.transitions.overscrollSpec(scene, Orientation.Vertical)).isNotNull() - assertThat(layoutState.currentTransition?.currentOverscrollSpec).isNull() - - val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) - nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.1f)) - - // We scrolled down, under scene C there is nothing, so we can use the overscroll spec - assertThat(layoutState.currentTransition?.currentOverscrollSpec).isNotNull() - assertThat(layoutState.currentTransition?.currentOverscrollSpec?.content).isEqualTo(SceneC) - val transition = layoutState.currentTransition - assertThat(transition).isNotNull() - assertThat(transition!!.progress).isEqualTo(-0.1f) - } - - @Test fun nestedScrollUseFromSourceInfo() = runGestureTest { // Start at scene C. navigateToSceneC() @@ -1229,72 +1184,6 @@ class DraggableHandlerTest { } @Test - fun overscroll_releaseAtNegativePercent_up() = runGestureTest { - // Make Scene A overscrollable. - layoutState.transitions = transitions { - from(SceneA, to = SceneB) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) } - overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) } - } - - mutableUserActionsA = mapOf(Swipe.Up to UserActionResult(SceneB)) - - val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) - val dragController = onDragStarted(pointersInfo = middle, overSlop = down(1f)) - val transition = assertThat(transitionState).isSceneTransition() - assertThat(transition).hasFromScene(SceneA) - assertThat(transition).hasToScene(SceneB) - assertThat(transition).hasProgress(-1f) - - // Release to A. - dragController.onDragStoppedAnimateNow( - velocity = 0f, - onAnimationStart = { - assertTransition(fromScene = SceneA, toScene = SceneB, progress = -1f) - }, - expectedConsumedVelocity = 0f, - ) - - // We kept the overscroll at 100% so that the placement logic didn't change at the end of - // the animation. - assertIdle(SceneA) - assertThat(transition).hasProgress(0f) - assertThat(transition).hasOverscrollSpec() - } - - @Test - fun overscroll_releaseAtNegativePercent_down() = runGestureTest { - // Make Scene A overscrollable. - layoutState.transitions = transitions { - from(SceneA, to = SceneC) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) } - overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) } - } - - mutableUserActionsA = mapOf(Swipe.Down to UserActionResult(SceneC)) - - val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) - val dragController = onDragStarted(pointersInfo = middle, overSlop = up(1f)) - val transition = assertThat(transitionState).isSceneTransition() - assertThat(transition).hasFromScene(SceneA) - assertThat(transition).hasToScene(SceneC) - assertThat(transition).hasProgress(-1f) - - // Release to A. - dragController.onDragStoppedAnimateNow( - velocity = 0f, - onAnimationStart = { - assertTransition(fromScene = SceneA, toScene = SceneC, progress = -1f) - }, - expectedConsumedVelocity = 0f, - ) - - // We kept the overscroll at 100% so that the placement logic didn't change at the end of - // the animation. - assertIdle(SceneA) - assertThat(transition).hasProgress(0f) - assertThat(transition).hasOverscrollSpec() - } - - @Test fun requireFullDistanceSwipe() = runGestureTest { mutableUserActionsA += Swipe.Up to UserActionResult(SceneB, requiresFullDistanceSwipe = true) diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt index 4410e157b526..1959f5914821 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt @@ -1006,77 +1006,74 @@ class ElementTest { @Test fun elementTransitionDuringNestedScrollOverscroll() { + lateinit var density: Density // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is // detected as a drag event. var touchSlop = 0f - val overscrollTranslateY = 10.dp val layoutWidth = 200.dp val layoutHeight = 400.dp val state = rule.runOnUiThread { MutableSceneTransitionLayoutState( - initialScene = SceneB, - transitions = - transitions { - overscroll(SceneB, Orientation.Vertical) { - progressConverter = ProgressConverter.linear() - translate(TestElements.Foo, y = overscrollTranslateY) - } - }, + initialScene = SceneA, + transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) }, ) - as MutableSceneTransitionLayoutStateImpl } rule.setContent { + density = LocalDensity.current touchSlop = LocalViewConfiguration.current.touchSlop SceneTransitionLayout( state = state, modifier = Modifier.size(layoutWidth, layoutHeight), ) { - scene(SceneA) { Spacer(Modifier.fillMaxSize()) } - scene(SceneB, userActions = mapOf(Swipe.Up to SceneA)) { + scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) { Box( Modifier // A scrollable that does not consume the scroll gesture .scrollable( - rememberScrollableState(consumeScrollDelta = { 0f }), - Orientation.Vertical, + state = rememberScrollableState(consumeScrollDelta = { 0f }), + orientation = Orientation.Vertical, ) .fillMaxSize() - ) { - Spacer(Modifier.element(TestElements.Foo).fillMaxSize()) - } + ) + } + scene(SceneB) { + Spacer( + Modifier.overscroll(verticalOverscrollEffect) + .element(TestElements.Foo) + .fillMaxSize() + ) } } } assertThat(state.transitionState).isIdle() - val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag) - fooElement.assertTopPositionInRootIsEqualTo(0.dp) + rule.onNodeWithTag(TestElements.Foo.testTag).assertDoesNotExist() // Swipe by half of verticalSwipeDistance. rule.onRoot().performTouchInput { val middleTop = Offset((layoutWidth / 2).toPx(), 0f) down(middleTop) - // Scroll 50% + // Scroll 50%. moveBy(Offset(0f, touchSlop + layoutHeight.toPx() * 0.5f), delayMillis = 1_000) } val transition = assertThat(state.transitionState).isSceneTransition() - assertThat(transition).hasOverscrollSpec() - assertThat(transition).hasProgress(-0.5f) - fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 0.5f) + assertThat(transition).hasProgress(0.5f) + rule.onNodeWithTag(TestElements.Foo.testTag).assertTopPositionInRootIsEqualTo(0.dp) rule.onRoot().performTouchInput { - // Scroll another 100% + // Scroll another 100%. moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000) } - // Scroll 150% (Scene B overscroll by 50%) - assertThat(transition).hasProgress(-1.5f) - assertThat(transition).hasOverscrollSpec() - fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 1.5f) + // Scroll 150% (Scene B overscroll by 50%). + assertThat(transition).hasProgress(1f) + rule + .onNodeWithTag(TestElements.Foo.testTag) + .assertTopPositionInRootIsEqualTo(expectedOffset(layoutHeight * 0.5f, density)) } @Test diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt index 28ea2d239b54..51483a894e1e 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt @@ -39,7 +39,6 @@ import org.junit.runner.RunWith class PriorityNestedScrollConnectionTest { private var canStartPreScroll = false private var canStartPostScroll = false - private var canStartPostFling = false private var canStopOnPreFling = true private var isStarted = false private var lastScroll: Float? = null @@ -63,7 +62,6 @@ class PriorityNestedScrollConnectionTest { orientation = Orientation.Vertical, canStartPreScroll = { _, _, _ -> canStartPreScroll }, canStartPostScroll = { _, _, _ -> canStartPostScroll }, - canStartPostFling = { canStartPostFling }, onStart = { _ -> isStarted = true object : ScrollController { @@ -239,36 +237,6 @@ class PriorityNestedScrollConnectionTest { } @Test - fun receive_onPostFling() = runTest { - canStartPostFling = true - - scrollConnection.onPostFling(consumed = Velocity(1f, 1f), available = Velocity(2f, 2f)) - - assertThat(lastStop).isEqualTo(2f) - } - - @Test - fun step1_priorityModeShouldStartOnlyOnPostFling() = runTest { - canStartPostFling = true - - scrollConnection.onPreScroll(available = Offset.Zero, source = UserInput) - assertThat(isStarted).isEqualTo(false) - - scrollConnection.onPostScroll( - consumed = Offset.Zero, - available = Offset.Zero, - source = UserInput, - ) - assertThat(isStarted).isEqualTo(false) - - scrollConnection.onPreFling(available = Velocity.Zero) - assertThat(isStarted).isEqualTo(false) - - scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero) - assertThat(isStarted).isEqualTo(true) - } - - @Test fun handleMultipleOnPreFlingCalls() = runTest { startPriorityModePostScroll() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt index e5f0d7c6bf37..68f4acde7609 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt @@ -42,6 +42,7 @@ import com.android.systemui.flags.andSceneContainer import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState @@ -494,6 +495,7 @@ class CommunalSceneStartableTest(flags: FlagsParameterization) : SysuiTestCase() // Start dreaming. updateDreaming(true) + advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS) // Hub times out immediately. assertThat(scene).isEqualTo(CommunalScenes.Blank) @@ -650,6 +652,7 @@ class CommunalSceneStartableTest(flags: FlagsParameterization) : SysuiTestCase() // Start dreaming. updateDreaming(true) + advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS) // Hub times out immediately. assertThat(scene).isEqualTo(Scenes.Dream) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index 79bb0c401e78..06dd046564df 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -74,6 +74,7 @@ import com.android.systemui.keyguard.data.repository.fakeTrustRepository import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.keyguard.dismissCallbackRegistry +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.dozeInteractor import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor import com.android.systemui.keyguard.domain.interactor.keyguardInteractor @@ -1270,8 +1271,11 @@ class SceneContainerStartableTest : SysuiTestCase() { authenticationMethod = AuthenticationMethodModel.None, isLockscreenEnabled = false, ) - assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) + powerInteractor.setAsleepForTest() underTest.start() + runCurrent() + assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) + powerInteractor.setAwakeForTest() runCurrent() @@ -2139,6 +2143,7 @@ class SceneContainerStartableTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.currentScene) val transitionStateFlow = prepareState() underTest.start() + runCurrent() emulateSceneTransition(transitionStateFlow, toScene = Scenes.Bouncer) assertThat(currentScene).isNotEqualTo(Scenes.Lockscreen) @@ -2153,6 +2158,7 @@ class SceneContainerStartableTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.currentScene) val transitionStateFlow = prepareState() underTest.start() + runCurrent() emulateSceneTransition(transitionStateFlow, toScene = Scenes.Bouncer) assertThat(currentScene).isEqualTo(Scenes.Bouncer) @@ -2269,6 +2275,7 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(Scenes.Gone) assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isTrue() underTest.start() + runCurrent() sceneInteractor.changeScene(Scenes.Shade, "") assertThat(currentScene).isEqualTo(Scenes.Shade) assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isTrue() @@ -2350,6 +2357,7 @@ class SceneContainerStartableTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.currentScene) prepareState() underTest.start() + runCurrent() // run all pending dismiss succeeded/cancelled calls from setup: kosmos.fakeExecutor.runAllReady() @@ -2461,13 +2469,18 @@ class SceneContainerStartableTest : SysuiTestCase() { @Test fun switchFromDreamToLockscreen_whenLockedAndDreamStopped() = testScope.runTest { - keyguardInteractor.setDreaming(true) val currentScene by collectLastValue(sceneInteractor.currentScene) prepareState(initialSceneKey = Scenes.Dream) - assertThat(currentScene).isEqualTo(Scenes.Dream) underTest.start() + advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS) + runCurrent() + keyguardInteractor.setDreaming(true) + advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS) + runCurrent() + assertThat(currentScene).isEqualTo(Scenes.Dream) keyguardInteractor.setDreaming(false) + advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS) runCurrent() assertThat(currentScene).isEqualTo(Scenes.Lockscreen) } @@ -2475,13 +2488,18 @@ class SceneContainerStartableTest : SysuiTestCase() { @Test fun switchFromDreamToGone_whenUnlockedAndDreamStopped() = testScope.runTest { - keyguardInteractor.setDreaming(true) val currentScene by collectLastValue(sceneInteractor.currentScene) prepareState(initialSceneKey = Scenes.Dream, isDeviceUnlocked = true) - assertThat(currentScene).isEqualTo(Scenes.Dream) underTest.start() + advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS) + runCurrent() + keyguardInteractor.setDreaming(true) + advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS) + runCurrent() + assertThat(currentScene).isEqualTo(Scenes.Dream) keyguardInteractor.setDreaming(false) + advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS) runCurrent() assertThat(currentScene).isEqualTo(Scenes.Gone) } @@ -2684,6 +2702,7 @@ class SceneContainerStartableTest : SysuiTestCase() { underTest.start() val currentScene by collectLastValue(sceneInteractor.currentScene) val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) + runCurrent() sceneInteractor.changeScene(Scenes.Shade, "reason") sceneInteractor.showOverlay(Overlays.NotificationsShade, "reason") assertThat(currentScene).isEqualTo(Scenes.Shade) @@ -2835,8 +2854,15 @@ class SceneContainerStartableTest : SysuiTestCase() { ) sceneInteractor.setTransitionState(transitionStateFlow) initialSceneKey?.let { + if (isDeviceUnlocked && initialSceneKey != Scenes.Gone) { + // Pass through the Gone scene to populate device entry state properly. + transitionStateFlow.value = ObservableTransitionState.Idle(Scenes.Gone) + sceneInteractor.changeScene(Scenes.Gone, "prepareState, passing through Gone scene") + runCurrent() + } + transitionStateFlow.value = ObservableTransitionState.Idle(it) - sceneInteractor.changeScene(it, "reason") + sceneInteractor.changeScene(it, "prepareState, initialSceneKey isn't null") } if (startsAwake) { powerInteractor.setAwakeForTest() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt index c90183df9847..1cc55bf87b1a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt @@ -127,12 +127,6 @@ class KeyguardBypassInteractorTest : SysuiTestCase() { kosmos.configureKeyguardBypass(isBypassAvailable = skipIsBypassAvailableCheck) underTest = kosmos.keyguardBypassInteractor - // bouncerShowing false, !onLockscreenScene false - // !onLockscreenScene false - setScene( - bouncerShowing = !skipBouncerShowingCheck, - onLockscreenScene = skipOnLockscreenSceneCheck, - ) // alternateBouncerShowing false setAlternateBouncerShowing(!skipAlternateBouncerShowingCheck) // launchingAffordance false @@ -141,6 +135,13 @@ class KeyguardBypassInteractorTest : SysuiTestCase() { setPulseExpanding(!skipPulseExpandingCheck) // qsExpanding false setQsExpanded(!skipQsExpandedCheck) + + // bouncerShowing false, !onLockscreenScene false + // !onLockscreenScene false + setScene( + bouncerShowing = !skipBouncerShowingCheck, + onLockscreenScene = skipOnLockscreenSceneCheck, + ) } private fun setAlternateBouncerShowing(alternateBouncerVisible: Boolean) { diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index 5644e6b3b9bf..34679b08cf20 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -183,7 +183,12 @@ constructor( this@CommunalSceneStartable.isDreaming = isDreaming if (scene.isCommunal() && isDreaming && timeoutJob == null) { // If dreaming starts after timeout has expired, ex. if dream restarts under - // the hub, just close the hub immediately. + // the hub, wait for IS_ABLE_TO_DREAM_DELAY_MS and then close the hub. The + // delay is necessary so the KeyguardInteractor.isAbleToDream flow passes + // through that same amount of delay and publishes a new value which is then + // picked up by the HomeSceneFamilyResolver such that the next call to + // SceneInteractor.changeScene(Home) will resolve "Home" to "Dream". + delay(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS) communalSceneInteractor.changeScene( CommunalScenes.Blank, "dream started after timeout", diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 26c286df01d7..0d9474e07ce7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -200,7 +200,10 @@ constructor( * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means * that doze mode is not running and DREAMING is ok to commence. * - * Allow a brief moment to prevent rapidly oscillating between true/false signals. + * Allow a brief moment to prevent rapidly oscillating between true/false signals. The amount of + * time is [IS_ABLE_TO_DREAM_DELAY_MS] - consumers should consider waiting for that long before + * examining the value of this flow, to let other consumers have enough time to also see that + * same new value. */ val isAbleToDream: Flow<Boolean> = dozeTransitionModel @@ -212,7 +215,7 @@ constructor( // do not immediately process any dreaming information when exiting AOD. It // should actually be quite strange to leave AOD and then go straight to // DREAMING so this should be fine. - delay(500L) + delay(IS_ABLE_TO_DREAM_DELAY_MS) isDreaming .sample(powerInteractor.isAwake) { isDreaming, isAwake -> isDreaming && isAwake @@ -550,5 +553,11 @@ constructor( companion object { private const val TAG = "KeyguardInteractor" + /** + * Amount of time that [KeyguardInteractor.isAbleToDream] is delayed; consumers of that flow + * should consider waiting this amount of time before check the value of this flow, to let + * other consumers have enough time to see the new value. + */ + const val IS_ABLE_TO_DREAM_DELAY_MS = 500L } } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt index 1204cde19c76..2a23620839e5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -74,7 +74,7 @@ constructor( context: Context, logger: MediaTttReceiverLogger, viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, - @Main mainExecutor: DelayableExecutor, + @Main private val mainExecutor: DelayableExecutor, accessibilityManager: AccessibilityManager, configurationController: ConfigurationController, dumpManager: DumpManager, @@ -285,6 +285,14 @@ constructor( } else { rippleController.collapseRipple(rippleView, onAnimationEnd) animateViewTranslationAndFade(iconContainerView, translationYBy, 0f) + mainExecutor.executeDelayed( + { + if (view.isAttachedToWindow) { + onAnimationEnd.run() + } + }, + ICON_TRANSLATION_ANIM_DURATION, + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index d83d74e4e538..0e6fc36fb96a 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -230,13 +230,7 @@ constructor( ) { val currentSceneKey = currentScene.value val resolvedScene = sceneFamilyResolvers.get()[toScene]?.resolvedScene?.value ?: toScene - if ( - !validateSceneChange( - from = currentSceneKey, - to = resolvedScene, - loggingReason = loggingReason, - ) - ) { + if (!validateSceneChange(to = resolvedScene, loggingReason = loggingReason)) { return } @@ -268,13 +262,7 @@ constructor( familyResolver.resolvedScene.value } } ?: toScene - if ( - !validateSceneChange( - from = currentSceneKey, - to = resolvedScene, - loggingReason = loggingReason, - ) - ) { + if (!validateSceneChange(to = resolvedScene, loggingReason = loggingReason)) { return } @@ -458,12 +446,11 @@ constructor( * Will throw a runtime exception for illegal states (for example, attempting to change to a * scene that's not part of the current scene framework configuration). * - * @param from The current scene being transitioned away from * @param to The desired destination scene to transition to * @param loggingReason The reason why the transition is requested, for logging purposes * @return `true` if the scene change is valid; `false` if it shouldn't happen */ - private fun validateSceneChange(from: SceneKey, to: SceneKey, loggingReason: String): Boolean { + private fun validateSceneChange(to: SceneKey, loggingReason: String): Boolean { if (to !in repository.allContentKeys) { return false } @@ -486,7 +473,7 @@ constructor( " Logging reason for scene change was: $loggingReason" } - return from != to + return true } /** diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt index b89eb5c762e0..2a0a22f32601 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt @@ -26,6 +26,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.util.kotlin.combine import dagger.Binds import dagger.Module import dagger.multibindings.IntoSet @@ -34,7 +35,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn /** @@ -59,6 +59,7 @@ constructor( deviceEntryInteractor.isDeviceEntered, deviceEntryInteractor.isUnlocked, keyguardInteractor.isDreamingWithOverlay, + keyguardInteractor.isAbleToDream, transform = ::homeScene, ) .stateIn( @@ -71,6 +72,7 @@ constructor( isDeviceEntered = deviceEntryInteractor.isDeviceEntered.value, isUnlocked = deviceEntryInteractor.isUnlocked.value, isDreamingWithOverlay = false, + isAbleToDream = false, ), ) @@ -82,10 +84,11 @@ constructor( isDeviceEntered: Boolean, isUnlocked: Boolean, isDreamingWithOverlay: Boolean, + isAbleToDream: Boolean, ): SceneKey = when { // Dream can run even if Keyguard is disabled, thus it has the highest priority here. - isDreamingWithOverlay -> Scenes.Dream + isDreamingWithOverlay && isAbleToDream -> Scenes.Dream !isKeyguardEnabled -> Scenes.Gone canSwipeToEnter == true -> Scenes.Lockscreen !isDeviceEntered -> Scenes.Lockscreen diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 072089981cc7..7c9d850eaf07 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -1284,7 +1284,11 @@ public class NotificationStackScrollLayout @Override public void setStackCutoff(float stackCutoff) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; - mAmbientState.setStackCutoff(stackCutoff); + if (mAmbientState.getStackCutoff() != stackCutoff) { + mAmbientState.setStackCutoff(stackCutoff); + updateStackEndHeightAndStackHeight(mAmbientState.getExpansionFraction()); + requestChildrenUpdate(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt index c5bef99f9307..ef68b4de5291 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt @@ -109,11 +109,6 @@ constructor( } } launch { - viewModel.shouldResetStackTop - .filter { it } - .collectTraced { view.setStackTop(-(view.getHeadsUpInset().toFloat())) } - } - launch { viewModel.shouldCloseGuts .filter { it } .collectTraced { view.closeGutsOnSceneTouch() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt index 56b335648138..1bb205cbcb61 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt @@ -192,12 +192,6 @@ constructor( /** Whether we should close any open notification guts. */ val shouldCloseGuts: Flow<Boolean> = stackAppearanceInteractor.shouldCloseGuts - val shouldResetStackTop: Flow<Boolean> = - sceneInteractor.transitionState - .mapNotNull { state -> state is Idle && state.currentScene == Scenes.Gone } - .distinctUntilChanged() - .dumpWhileCollecting("shouldResetStackTop") - /** Whether the Notification Stack is visibly on the lockscreen scene. */ val isShowingStackOnLockscreen: Flow<Boolean> = sceneInteractor.transitionState diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 99467cb11282..a91fb45c9c80 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -95,6 +95,7 @@ import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShade import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter; import com.android.systemui.statusbar.notification.footer.ui.view.FooterView; +import com.android.systemui.statusbar.notification.headsup.AvalancheController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; @@ -103,7 +104,6 @@ import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrim import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.notification.headsup.AvalancheController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor; @@ -356,6 +356,31 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test @EnableSceneContainer + public void updateStackCutoff_updatesStackEndHeight() { + // GIVEN shade is fully open + final float stackTop = 200f; + final float stackCutoff = 1000f; + final float stackHeight = stackCutoff - stackTop; + mAmbientState.setStackTop(stackTop); + mAmbientState.setStackCutoff(stackCutoff); + mAmbientState.setStatusBarState(StatusBarState.SHADE); + mStackScroller.setMaxDisplayedNotifications(-1); // no limit on the shade + mStackScroller.setExpandFraction(1f); // shade is fully expanded + assertThat(mAmbientState.getStackEndHeight()).isEqualTo(stackHeight); + assertThat(mAmbientState.getInterpolatedStackHeight()).isEqualTo(stackHeight); + + // WHEN stackCutoff changes + final float newStackCutoff = 800; + mStackScroller.setStackCutoff(newStackCutoff); + + // THEN stackEndHeight is updated + final float newStackHeight = newStackCutoff - stackTop; + assertThat(mAmbientState.getStackEndHeight()).isEqualTo(newStackHeight); + assertThat(mAmbientState.getInterpolatedStackHeight()).isEqualTo(newStackHeight); + } + + @Test + @EnableSceneContainer public void updateStackEndHeightAndStackHeight_maxNotificationsSet_withSceneContainer() { float stackHeight = 300f; when(mStackSizeCalculator.computeHeight(eq(mStackScroller), anyInt(), anyFloat())) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt index 6944e6c14096..ab193d294b8c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt @@ -261,7 +261,7 @@ class ShadeTestUtilSceneImpl( } private fun setIdleScene(scene: SceneKey) { - sceneInteractor.changeScene(scene, "test") + sceneInteractor.changeScene(scene, "ShadeTestUtil.setIdleScene") val transitionState = MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(scene)) sceneInteractor.setTransitionState(transitionState) @@ -274,7 +274,7 @@ class ShadeTestUtilSceneImpl( progress: Float, isInitiatedByUserInput: Boolean = true, ) { - sceneInteractor.changeScene(from, "test") + sceneInteractor.changeScene(from, "ShadeTestUtil.setTransitionProgress") val transitionState = MutableStateFlow<ObservableTransitionState>( ObservableTransitionState.Transition( diff --git a/services/art-wear-profile b/services/art-wear-profile index 1e3090f9bf00..f080715643ca 100644 --- a/services/art-wear-profile +++ b/services/art-wear-profile @@ -7419,7 +7419,7 @@ PLcom/android/server/app/GameManagerService;->sendUserMessage(IILjava/lang/Strin PLcom/android/server/app/GameManagerService;->updateConfigsForUser(IZ[Ljava/lang/String;)V PLcom/android/server/app/GameManagerService;->writeGameModeInterventionsToFile(I)V PLcom/android/server/app/GameManagerSettings;-><init>(Ljava/io/File;)V -HPLcom/android/server/app/GameManagerSettings;->getConfigOverride(Ljava/lang/String;)Lcom/android/server/app/GameManagerService$GamePackageConfiguration; +HPLcom/android/server/app/GameManagerSettings;->getConfigOverrideLocked(Ljava/lang/String;)Lcom/android/server/app/GameManagerService$GamePackageConfiguration; HPLcom/android/server/app/GameManagerSettings;->getGameModeLocked(Ljava/lang/String;)I PLcom/android/server/app/GameManagerSettings;->readPersistentDataLocked()Z PLcom/android/server/appbinding/AppBindingConstants;-><init>(Ljava/lang/String;)V diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index f42641ece09b..aadf6f61956c 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -2840,7 +2840,6 @@ public class OomAdjuster { return true; } } - capability |= PROCESS_CAPABILITY_CPU_TIME; } // Not doing bind OOM management, so treat // this guy more like a started service. @@ -3089,7 +3088,6 @@ public class OomAdjuster { return true; } } - capability |= PROCESS_CAPABILITY_CPU_TIME; } } if (cr.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) { @@ -4243,6 +4241,11 @@ public class OomAdjuster { != client.getSetCapability()) { // The connection might elevate the importance of the service's capabilities. needDryRun = true; + } else if (Flags.useCpuTimeCapability() + && (client.getSetCapability() & ~app.getSetCapability() + & PROCESS_CAPABILITY_CPU_TIME) != 0) { + // The connection might grant PROCESS_CAPABILITY_CPU_TIME to the service. + needDryRun = true; } else if (Flags.unfreezeBindPolicyFix() && cr.hasFlag(Context.BIND_WAIVE_PRIORITY | Context.BIND_ALLOW_OOM_MANAGEMENT)) { @@ -4290,6 +4293,10 @@ public class OomAdjuster { && client.mOptRecord.shouldNotFreeze()) { // Process has shouldNotFreeze and it could have gotten it from the client. return true; + } else if (Flags.useCpuTimeCapability() + && (client.getSetCapability() & app.getSetCapability() + & PROCESS_CAPABILITY_CPU_TIME) != 0) { + return true; } return false; } @@ -4309,6 +4316,11 @@ public class OomAdjuster { && client.mOptRecord.shouldNotFreeze() && !app.mOptRecord.shouldNotFreeze()) { needDryRun = true; + } else if (Flags.useCpuTimeCapability() + && (client.getSetCapability() & ~app.getSetCapability() + & PROCESS_CAPABILITY_CPU_TIME) != 0) { + // The connection might grant PROCESS_CAPABILITY_CPU_TIME to the provider. + needDryRun = true; } if (needDryRun) { @@ -4335,6 +4347,10 @@ public class OomAdjuster { && client.mOptRecord.shouldNotFreeze()) { // Process has shouldNotFreeze and it could have gotten it from the client. return true; + } else if (Flags.useCpuTimeCapability() + && (client.getSetCapability() & app.getSetCapability() + & PROCESS_CAPABILITY_CPU_TIME) != 0) { + return true; } return false; diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index 6f8dc105850d..c0a97db7275b 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -1423,10 +1423,10 @@ public final class GameManagerService extends IGameManagerService.Stub { } final GameManagerSettings settings = mSettings.get(userId); // look for the existing GamePackageConfiguration override - configOverride = settings.getConfigOverride(packageName); + configOverride = settings.getConfigOverrideLocked(packageName); if (configOverride == null) { configOverride = new GamePackageConfiguration(packageName); - settings.setConfigOverride(packageName, configOverride); + settings.setConfigOverrideLocked(packageName, configOverride); } } GamePackageConfiguration.GameModeConfiguration internalConfig = @@ -1759,10 +1759,10 @@ public final class GameManagerService extends IGameManagerService.Stub { } final GameManagerSettings settings = mSettings.get(userId); // look for the existing GamePackageConfiguration override - configOverride = settings.getConfigOverride(packageName); + configOverride = settings.getConfigOverrideLocked(packageName); if (configOverride == null) { configOverride = new GamePackageConfiguration(packageName); - settings.setConfigOverride(packageName, configOverride); + settings.setConfigOverrideLocked(packageName, configOverride); } } // modify GameModeConfiguration intervention settings @@ -1801,7 +1801,7 @@ public final class GameManagerService extends IGameManagerService.Stub { } final GameManagerSettings settings = mSettings.get(userId); if (gameModeToReset != -1) { - final GamePackageConfiguration configOverride = settings.getConfigOverride( + final GamePackageConfiguration configOverride = settings.getConfigOverrideLocked( packageName); if (configOverride == null) { return; @@ -1812,10 +1812,10 @@ public final class GameManagerService extends IGameManagerService.Stub { } configOverride.removeModeConfig(gameModeToReset); if (!configOverride.hasActiveGameModeConfig()) { - settings.removeConfigOverride(packageName); + settings.removeConfigOverrideLocked(packageName); } } else { - settings.removeConfigOverride(packageName); + settings.removeConfigOverrideLocked(packageName); } } @@ -2030,7 +2030,7 @@ public final class GameManagerService extends IGameManagerService.Stub { synchronized (mLock) { if (mSettings.containsKey(userId)) { - overrideConfig = mSettings.get(userId).getConfigOverride(packageName); + overrideConfig = mSettings.get(userId).getConfigOverrideLocked(packageName); } } if (overrideConfig == null || config == null) { @@ -2075,7 +2075,7 @@ public final class GameManagerService extends IGameManagerService.Stub { } synchronized (mLock) { if (mSettings.containsKey(userId)) { - mSettings.get(userId).removeGame(packageName); + mSettings.get(userId).removeGameLocked(packageName); } sendUserMessage(userId, WRITE_SETTINGS, Intent.ACTION_PACKAGE_REMOVED, WRITE_DELAY_MILLIS); diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java index b084cf3c3b12..c57a1f73d7d7 100644 --- a/services/core/java/com/android/server/app/GameManagerSettings.java +++ b/services/core/java/com/android/server/app/GameManagerSettings.java @@ -116,7 +116,7 @@ public class GameManagerSettings { * Removes all game settings of a given package. * This operation must be synced with an external lock. */ - void removeGame(String packageName) { + void removeGameLocked(String packageName) { mGameModes.remove(packageName); mConfigOverrides.remove(packageName); } @@ -125,7 +125,7 @@ public class GameManagerSettings { * Returns the game config override of a given package or null if absent. * This operation must be synced with an external lock. */ - GamePackageConfiguration getConfigOverride(String packageName) { + GamePackageConfiguration getConfigOverrideLocked(String packageName) { return mConfigOverrides.get(packageName); } @@ -133,7 +133,7 @@ public class GameManagerSettings { * Sets the game config override of a given package. * This operation must be synced with an external lock. */ - void setConfigOverride(String packageName, GamePackageConfiguration configOverride) { + void setConfigOverrideLocked(String packageName, GamePackageConfiguration configOverride) { mConfigOverrides.put(packageName, configOverride); } @@ -141,7 +141,7 @@ public class GameManagerSettings { * Removes the game mode config override of a given package. * This operation must be synced with an external lock. */ - void removeConfigOverride(String packageName) { + void removeConfigOverrideLocked(String packageName) { mConfigOverrides.remove(packageName); } diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java index 83b0801ce87f..50d650855b05 100644 --- a/services/core/java/com/android/server/display/BrightnessRangeController.java +++ b/services/core/java/com/android/server/display/BrightnessRangeController.java @@ -37,7 +37,6 @@ class BrightnessRangeController { private final HdrClamper mHdrClamper; private final Runnable mModeChangeCallback; - private final boolean mUseNbmController; private final boolean mUseHdrClamper; @@ -62,11 +61,8 @@ class BrightnessRangeController { mHdrClamper = hdrClamper; mNormalBrightnessModeController = normalBrightnessModeController; mUseHdrClamper = flags.isHdrClamperEnabled() && !flags.useNewHdrBrightnessModifier(); - mUseNbmController = flags.isNbmControllerEnabled(); - if (mUseNbmController) { - mNormalBrightnessModeController.resetNbmData( - displayDeviceConfig.getLuxThrottlingData()); - } + mNormalBrightnessModeController.resetNbmData( + displayDeviceConfig.getLuxThrottlingData()); if (flags.useNewHdrBrightnessModifier()) { // HDR boost is handled by HdrBrightnessModifier and should be disabled in HbmController mHbmController.disableHdrBoost(); @@ -76,7 +72,6 @@ class BrightnessRangeController { void dump(PrintWriter pw) { pw.println("BrightnessRangeController:"); - pw.println(" mUseNormalBrightnessController=" + mUseNbmController); pw.println(" mUseHdrClamper=" + mUseHdrClamper); mHbmController.dump(pw); mNormalBrightnessModeController.dump(pw); @@ -138,9 +133,7 @@ class BrightnessRangeController { float getCurrentBrightnessMax() { // nbmController might adjust maxBrightness only if device does not support HBM or // hbm is currently not allowed - if (mUseNbmController - && (!mHbmController.deviceSupportsHbm() - || !mHbmController.isHbmCurrentlyAllowed())) { + if (!mHbmController.deviceSupportsHbm() || !mHbmController.isHbmCurrentlyAllowed()) { return Math.min(mHbmController.getCurrentBrightnessMax(), mNormalBrightnessModeController.getCurrentBrightnessMax()); } @@ -173,16 +166,12 @@ class BrightnessRangeController { } private void applyChanges(BooleanSupplier nbmChangesFunc, Runnable hbmChangesFunc) { - if (mUseNbmController) { - boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean(); - hbmChangesFunc.run(); - // if nbm transition changed - trigger callback - // HighBrightnessModeController handles sending changes itself - if (nbmTransitionChanged) { - mModeChangeCallback.run(); - } - } else { - hbmChangesFunc.run(); + boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean(); + hbmChangesFunc.run(); + // if nbm transition changed - trigger callback + // HighBrightnessModeController handles sending changes itself + if (nbmTransitionChanged) { + mModeChangeCallback.run(); } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index f48fbea64f65..9387e9ede532 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1646,6 +1646,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_TEMPORARY) || mAutomaticBrightnessStrategy .isTemporaryAutoBrightnessAdjustmentApplied(); + float rampSpeed = 0; if (!mPendingScreenOff) { if (mSkipScreenOnBrightnessRamp) { if (state == Display.STATE_ON) { @@ -1747,7 +1748,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call customAnimationRate, /* ignoreAnimationLimits = */true); } else { boolean isIncreasing = animateValue > currentBrightness; - final float rampSpeed; final boolean idle = mAutomaticBrightnessController != null && mAutomaticBrightnessController.isInIdleMode(); if (isIncreasing && slowChange) { @@ -1832,6 +1832,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call .getDisplayBrightnessStrategyName()); mTempBrightnessEvent.setAutomaticBrightnessEnabled( displayBrightnessState.getShouldUseAutoBrightness()); + mTempBrightnessEvent.setSlowChange(slowChange); + mTempBrightnessEvent.setRampSpeed(rampSpeed); // Temporary is what we use during slider interactions. We avoid logging those so that // we don't spam logcat when the slider is being used. boolean tempToTempTransition = diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java index 9e9b899ffa7d..159c30ddf77f 100644 --- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java +++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java @@ -78,6 +78,8 @@ public final class BrightnessEvent { private String mDisplayBrightnessStrategyName; @AutomaticBrightnessController.AutomaticBrightnessMode private int mAutoBrightnessMode; + private boolean mSlowChange; + private float mRampSpeed; public BrightnessEvent(BrightnessEvent that) { copyFrom(that); @@ -126,6 +128,8 @@ public final class BrightnessEvent { mAutomaticBrightnessEnabled = that.isAutomaticBrightnessEnabled(); mDisplayBrightnessStrategyName = that.getDisplayBrightnessStrategyName(); mAutoBrightnessMode = that.mAutoBrightnessMode; + mSlowChange = that.mSlowChange; + mRampSpeed = that.mRampSpeed; } /** @@ -163,6 +167,8 @@ public final class BrightnessEvent { mAutomaticBrightnessEnabled = true; mDisplayBrightnessStrategyName = ""; mAutoBrightnessMode = AUTO_BRIGHTNESS_MODE_DEFAULT; + mSlowChange = false; + mRampSpeed = 0; } /** @@ -248,7 +254,9 @@ public final class BrightnessEvent { + ", powerFactor=" + mPowerFactor // Meta + ", physDisp=" + mPhysicalDisplayName + "(" + mPhysicalDisplayId + ")" - + ", logicalId=" + mDisplayId; + + ", logicalId=" + mDisplayId + + ", slowChange=" + mSlowChange + + ", rampSpeed=" + mRampSpeed; } @Override @@ -469,8 +477,8 @@ public final class BrightnessEvent { return mDisplayBrightnessStrategyName; } - public void setAutomaticBrightnessEnabled(boolean mAutomaticBrightnessEnabled) { - this.mAutomaticBrightnessEnabled = mAutomaticBrightnessEnabled; + public void setAutomaticBrightnessEnabled(boolean automaticBrightnessEnabled) { + mAutomaticBrightnessEnabled = automaticBrightnessEnabled; } @AutomaticBrightnessController.AutomaticBrightnessMode @@ -483,6 +491,14 @@ public final class BrightnessEvent { mAutoBrightnessMode = mode; } + public void setSlowChange(boolean slowChange) { + mSlowChange = slowChange; + } + + public void setRampSpeed(float rampSpeed) { + mRampSpeed = rampSpeed; + } + /** * A utility to stringify flags from a BrightnessEvent * @return Stringified flags from BrightnessEvent diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java index 7892639fc8ed..52e64905c984 100644 --- a/services/core/java/com/android/server/display/color/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -168,8 +168,7 @@ public final class ColorDisplayService extends SystemService { new NightDisplayTintController(); private final TintController mGlobalSaturationTintController = new GlobalSaturationTintController(); - private final ReduceBrightColorsTintController mReduceBrightColorsTintController = - new ReduceBrightColorsTintController(); + private final ReduceBrightColorsTintController mReduceBrightColorsTintController; @VisibleForTesting final Handler mHandler; @@ -201,7 +200,13 @@ public final class ColorDisplayService extends SystemService { private boolean mEvenDimmerActivated; public ColorDisplayService(Context context) { + this(context, new ReduceBrightColorsTintController()); + } + + @VisibleForTesting + public ColorDisplayService(Context context, ReduceBrightColorsTintController rbcController) { super(context); + mReduceBrightColorsTintController = rbcController; mHandler = new TintHandler(DisplayThread.get().getLooper()); mVisibleBackgroundUsersEnabled = isVisibleBackgroundUsersEnabled(); mUserManager = UserManagerService.getInstance(); @@ -571,27 +576,37 @@ public final class ColorDisplayService extends SystemService { return mColorModeCompositionColorSpaces.get(mode, Display.COLOR_MODE_INVALID); } - private void onDisplayColorModeChanged(int mode) { + @VisibleForTesting + void onDisplayColorModeChanged(int mode) { if (mode == NOT_SET) { return; } + mReduceBrightColorsTintController.cancelAnimator(); mNightDisplayTintController.cancelAnimator(); mDisplayWhiteBalanceTintController.cancelAnimator(); + final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); + if (mNightDisplayTintController.isAvailable(getContext())) { - final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); mNightDisplayTintController.setUp(getContext(), dtm.needsLinearColorMatrix(mode)); mNightDisplayTintController .setMatrix(mNightDisplayTintController.getColorTemperatureSetting()); } + if (mReduceBrightColorsTintController.isAvailable(getContext())) { + // Different color modes may require different coefficients to be loaded for RBC. + // Re-set up RBC so that it can recalculate its transform matrix with new values. + mReduceBrightColorsTintController.setUp(getContext(), dtm.needsLinearColorMatrix(mode)); + onReduceBrightColorsStrengthLevelChanged(); // Trigger matrix recalc + updates + } + // dtm.setColorMode() needs to be called before // updateDisplayWhiteBalanceStatus(), this is because the latter calls // DisplayTransformManager.needsLinearColorMatrix(), therefore it is dependent // on the state of DisplayTransformManager. - final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); dtm.setColorMode(mode, mNightDisplayTintController.getMatrix(), + mReduceBrightColorsTintController.getMatrix(), getCompositionColorSpace(mode)); if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) { diff --git a/services/core/java/com/android/server/display/color/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java index a76c427bec0e..cb7b1773e47e 100644 --- a/services/core/java/com/android/server/display/color/DisplayTransformManager.java +++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java @@ -265,7 +265,7 @@ public class DisplayTransformManager { /** * Sets color mode and updates night display transform values. */ - public boolean setColorMode(int colorMode, float[] nightDisplayMatrix, + public boolean setColorMode(int colorMode, float[] nightDisplayMatrix, float[] rbcMatrix, int compositionColorMode) { if (colorMode == ColorDisplayManager.COLOR_MODE_NATURAL) { applySaturation(COLOR_SATURATION_NATURAL); @@ -285,7 +285,11 @@ public class DisplayTransformManager { setDisplayColor(colorMode, compositionColorMode); } + // These are close to the setDisplayColor() call to reduce delay between + // setting these matrixes and updating the color mode. Without this proximity + // of calls, updates to color mode can result in flicker. setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, nightDisplayMatrix); + setColorMatrix(LEVEL_COLOR_MATRIX_REDUCE_BRIGHT_COLORS, rbcMatrix); updateConfiguration(); diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index 78bd41bd2e11..85b6bbb40b91 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -46,10 +46,6 @@ public class DisplayManagerFlags { Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT, Flags::enableConnectedDisplayManagement); - private final FlagState mNbmControllerFlagState = new FlagState( - Flags.FLAG_ENABLE_NBM_CONTROLLER, - Flags::enableNbmController); - private final FlagState mHdrClamperFlagState = new FlagState( Flags.FLAG_ENABLE_HDR_CLAMPER, Flags::enableHdrClamper); @@ -282,11 +278,6 @@ public class DisplayManagerFlags { return mConnectedDisplayManagementFlagState.isEnabled(); } - /** Returns whether NBM Controller is enabled or not. */ - public boolean isNbmControllerEnabled() { - return mNbmControllerFlagState.isEnabled(); - } - /** Returns whether hdr clamper is enabled on not. */ public boolean isHdrClamperEnabled() { return mHdrClamperFlagState.isEnabled(); @@ -595,7 +586,6 @@ public class DisplayManagerFlags { pw.println(" " + mExternalDisplayLimitModeState); pw.println(" " + mDisplayTopology); pw.println(" " + mHdrClamperFlagState); - pw.println(" " + mNbmControllerFlagState); pw.println(" " + mPowerThrottlingClamperFlagState); pw.println(" " + mEvenDimmerFlagState); pw.println(" " + mSmallAreaDetectionFlagState); diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index 123b7dfbf843..3358f723709c 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -37,14 +37,6 @@ flag { } flag { - name: "enable_nbm_controller" - namespace: "display_manager" - description: "Feature flag for Normal Brightness Mode Controller" - bug: "299527549" - is_fixed_read_only: true -} - -flag { name: "enable_hdr_clamper" namespace: "display_manager" description: "Feature flag for HDR Clamper" diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index d8c35358102d..f09be2c15ee0 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -289,7 +289,9 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider { // doesn't have any registered discovery preference, we should still be able to route their // system media. boolean bindDueToSystemMediaRoutingSupport = - mIsManagerScanning && mSupportsSystemMediaRouting; + mLastDiscoveryPreference != null + && mLastDiscoveryPreference.shouldPerformActiveScan() + && mSupportsSystemMediaRouting; if (!getSessionInfos().isEmpty() || bindDueToManagerScan || bindDueToSystemMediaRoutingSupport) { diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java index 69c460e0f19d..42303e042561 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java @@ -32,6 +32,7 @@ import android.content.pm.ServiceInfo; import android.media.MediaRoute2ProviderService; import android.os.Handler; import android.os.UserHandle; +import android.text.TextUtils; import android.util.Log; import android.util.Slog; @@ -162,8 +163,14 @@ final class MediaRoute2ProviderWatcher { mUserId); Slog.i( TAG, - "Enabling proxy for MediaRoute2ProviderService: " - + proxy.mComponentName); + TextUtils.formatSimple( + "Enabling proxy for MediaRoute2ProviderService: %s" + + " (isSelfScan=%b, supportsSystemMediaRouting=%b," + + " userId=%d)", + proxy.mComponentName, + isSelfScanOnlyProvider, + supportsSystemMediaRouting, + mUserId)); proxy.start(/* rebindIfDisconnected= */ false); mProxies.add(targetIndex++, proxy); mCallback.onAddProviderService(proxy); diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index e18ed410c045..58deffcbd4ba 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -848,7 +848,7 @@ class MediaRouter2ServiceImpl { UserRecord userRecord = getOrCreateUserRecordLocked(userId); List<RoutingSessionInfo> sessionInfos; if (hasSystemRoutingPermissions) { - if (setDeviceRouteSelected) { + if (setDeviceRouteSelected && !Flags.enableMirroringInMediaRouter2()) { // Return a fake system session that shows the device route as selected and // available bluetooth routes as transferable. return userRecord.mHandler.getSystemProvider() @@ -2733,6 +2733,15 @@ class MediaRouter2ServiceImpl { newRoutes = Collections.emptySet(); } + if (Flags.enableMirroringInMediaRouter2() + && provider instanceof MediaRoute2ProviderServiceProxy proxyProvider) { + // We notify the system provider of service updates, so that it can update the + // system routing session by adding them as transferable routes. And we remove those + // that don't support remote routing. + mSystemProvider.updateSystemMediaRoutesFromProxy(proxyProvider); + newRoutes.removeIf(it -> !it.supportsRemoteRouting()); + } + // Add new routes to the maps. ArrayList<MediaRoute2Info> addedRoutes = new ArrayList<>(); boolean hasAddedOrModifiedRoutes = false; diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 8dfba39dcfd8..b93846bf9ee7 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -73,6 +73,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { // For apps without MODIFYING_AUDIO_ROUTING permission. // This should be the currently selected route. MediaRoute2Info mDefaultRoute; + + @GuardedBy("mLock") + RoutingSessionInfo mSystemSessionInfo; + RoutingSessionInfo mDefaultSessionInfo; private final AudioManagerBroadcastReceiver mAudioReceiver = @@ -180,7 +184,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (TextUtils.equals(routeOriginalId, mSelectedRouteId)) { RoutingSessionInfo currentSessionInfo; synchronized (mLock) { - currentSessionInfo = mSessionInfos.get(0); + currentSessionInfo = + Flags.enableMirroringInMediaRouter2() + ? mSystemSessionInfo + : mSessionInfos.get(0); } mCallback.onSessionCreated(this, requestId, currentSessionInfo); return; @@ -354,7 +361,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { - RoutingSessionInfo oldSessionInfo = mSessionInfos.get(0); + var oldSessionInfo = + Flags.enableMirroringInMediaRouter2() + ? mSystemSessionInfo + : mSessionInfos.get(0); builder.setTransferReason(oldSessionInfo.getTransferReason()) .setTransferInitiator(oldSessionInfo.getTransferInitiatorUserHandle(), oldSessionInfo.getTransferInitiatorPackageName()); @@ -364,6 +374,31 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } } + /** + * Notifies the system provider of a {@link MediaRoute2ProviderServiceProxy} update. + * + * <p>To be overridden so as to generate system media routes for {@link + * MediaRoute2ProviderService} routes that {@link MediaRoute2Info#supportsSystemMediaRouting() + * support system media routing}). + * + * @param serviceProxy The proxy of the service that updated its state. + */ + public void updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy) { + // Do nothing. This implementation doesn't support MR2ProviderService system media routes. + // The subclass overrides this method to implement app-managed system media routing (aka + // mirroring). + } + + /** + * Called when the system provider state changes. + * + * <p>To be overridden by {@link SystemMediaRoute2Provider2}, so that app-provided system media + * routing routes are added before setting the provider state. + */ + public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) { + setProviderState(providerInfo); + } + protected void updateProviderState() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); @@ -373,7 +408,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { for (MediaRoute2Info route : deviceRoutes) { builder.addRoute(route); } - setProviderState(builder.build()); + if (!Flags.enableMirroringInMediaRouter2()) { + setProviderState(builder.build()); + } } else { builder.addRoute(mDeviceRouteController.getSelectedRoute()); } @@ -382,7 +419,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { builder.addRoute(route); } MediaRoute2ProviderInfo providerInfo = builder.build(); - setProviderState(providerInfo); + onSystemProviderRoutesChanged(providerInfo); if (DEBUG) { Slog.d(TAG, "Updating system provider info : " + providerInfo); } @@ -393,8 +430,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { */ boolean updateSessionInfosIfNeeded() { synchronized (mLock) { - RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get( - 0); + RoutingSessionInfo oldSessionInfo; + if (Flags.enableMirroringInMediaRouter2()) { + oldSessionInfo = mSystemSessionInfo; + } else { + oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0); + } RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( SYSTEM_SESSION_ID, "" /* clientPackageName */) @@ -483,8 +524,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (DEBUG) { Slog.d(TAG, "Updating system routing session info : " + newSessionInfo); } - mSessionInfos.clear(); - mSessionInfos.add(newSessionInfo); + mSystemSessionInfo = newSessionInfo; + onSystemSessionInfoUpdated(); mDefaultSessionInfo = new RoutingSessionInfo.Builder( SYSTEM_SESSION_ID, "" /* clientPackageName */) @@ -501,6 +542,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } } + @GuardedBy("mLock") + protected void onSystemSessionInfoUpdated() { + mSessionInfos.clear(); + mSessionInfos.add(mSystemSessionInfo); + } + @GuardedBy("mRequestLock") private void reportPendingSessionRequestResultLockedIfNeeded( RoutingSessionInfo newSessionInfo) { @@ -587,6 +634,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { RoutingSessionInfo sessionInfo; synchronized (mLock) { sessionInfo = mSessionInfos.get(0); + if (sessionInfo == null) { + return; + } } mCallback.onSessionUpdated(this, sessionInfo); diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java index 85b30ad8cadb..7dc30ab66fd2 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java @@ -16,11 +16,26 @@ package com.android.server.media; +import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO; + +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; +import android.media.MediaRoute2Info; +import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2ProviderService; +import android.media.RoutingSessionInfo; import android.os.Looper; import android.os.UserHandle; +import android.util.ArraySet; + +import com.android.internal.annotations.GuardedBy; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; /** * Extends {@link SystemMediaRoute2Provider} by adding system routes provided by {@link @@ -30,6 +45,15 @@ import android.os.UserHandle; */ /* package */ class SystemMediaRoute2Provider2 extends SystemMediaRoute2Provider { + private static final String ROUTE_ID_PREFIX_SYSTEM = "SYSTEM"; + private static final String ROUTE_ID_SYSTEM_SEPARATOR = "."; + + @GuardedBy("mLock") + private MediaRoute2ProviderInfo mLastSystemProviderInfo; + + @GuardedBy("mLock") + private final Map<String, ProviderProxyRecord> mProxyRecords = new HashMap<>(); + private static final ComponentName COMPONENT_NAME = new ComponentName( SystemMediaRoute2Provider2.class.getPackage().getName(), @@ -46,4 +70,117 @@ import android.os.UserHandle; private SystemMediaRoute2Provider2(Context context, UserHandle user, Looper looper) { super(context, COMPONENT_NAME, user, looper); } + + @Override + protected void onSystemSessionInfoUpdated() { + updateSessionInfo(); + } + + @Override + public void updateSystemMediaRoutesFromProxy(MediaRoute2ProviderServiceProxy serviceProxy) { + var proxyRecord = ProviderProxyRecord.createFor(serviceProxy); + synchronized (mLock) { + if (proxyRecord == null) { + mProxyRecords.remove(serviceProxy.mUniqueId); + } else { + mProxyRecords.put(serviceProxy.mUniqueId, proxyRecord); + } + setProviderState(buildProviderInfo()); + } + updateSessionInfo(); + notifyProviderState(); + notifySessionInfoUpdated(); + } + + @Override + public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) { + synchronized (mLock) { + mLastSystemProviderInfo = providerInfo; + setProviderState(buildProviderInfo()); + } + updateSessionInfo(); + notifySessionInfoUpdated(); + } + + /** + * Updates the {@link #mSessionInfos} by expanding the {@link SystemMediaRoute2Provider} session + * with information from the {@link MediaRoute2ProviderService provider services}. + */ + private void updateSessionInfo() { + synchronized (mLock) { + var systemSessionInfo = mSystemSessionInfo; + if (systemSessionInfo == null) { + // The system session info hasn't been initialized yet. Do nothing. + return; + } + var builder = new RoutingSessionInfo.Builder(systemSessionInfo); + mProxyRecords.values().stream() + .flatMap(ProviderProxyRecord::getRoutesStream) + .map(MediaRoute2Info::getId) + .forEach(builder::addTransferableRoute); + mSessionInfos.clear(); + mSessionInfos.add(builder.build()); + } + } + + /** + * Returns a new a provider info that includes all routes from the system provider {@link + * SystemMediaRoute2Provider}, along with system routes from {@link MediaRoute2ProviderService + * provider services}. + */ + @GuardedBy("mLock") + private MediaRoute2ProviderInfo buildProviderInfo() { + MediaRoute2ProviderInfo.Builder builder = + new MediaRoute2ProviderInfo.Builder(mLastSystemProviderInfo); + mProxyRecords.values().stream() + .flatMap(ProviderProxyRecord::getRoutesStream) + .forEach(builder::addRoute); + return builder.build(); + } + + /** + * Holds information about {@link MediaRoute2ProviderService provider services} registered in + * the system. + * + * @param mProxy The corresponding {@link MediaRoute2ProviderServiceProxy}. + * @param mSystemMediaRoutes The last snapshot of routes from the service that support system + * media routing, as defined by {@link MediaRoute2Info#supportsSystemMediaRouting()}. + */ + private record ProviderProxyRecord( + MediaRoute2ProviderServiceProxy mProxy, + Collection<MediaRoute2Info> mSystemMediaRoutes) { + + /** Returns a stream representation of the {@link #mSystemMediaRoutes}. */ + public Stream<MediaRoute2Info> getRoutesStream() { + return mSystemMediaRoutes.stream(); + } + + /** + * Returns a new instance, or null if the given {@code serviceProxy} doesn't have an + * associated {@link MediaRoute2ProviderInfo}. + */ + @Nullable + public static ProviderProxyRecord createFor(MediaRoute2ProviderServiceProxy serviceProxy) { + MediaRoute2ProviderInfo providerInfo = serviceProxy.getProviderInfo(); + if (providerInfo == null) { + return null; + } + ArraySet<MediaRoute2Info> routes = new ArraySet<>(); + providerInfo.getRoutes().stream() + .filter(MediaRoute2Info::supportsSystemMediaRouting) + .forEach( + route -> { + String id = + ROUTE_ID_PREFIX_SYSTEM + + route.getProviderId() + + ROUTE_ID_SYSTEM_SEPARATOR + + route.getOriginalId(); + routes.add( + new MediaRoute2Info.Builder(id, route.getName()) + .addFeature(FEATURE_LIVE_AUDIO) + .build()); + }); + return new ProviderProxyRecord(serviceProxy, Collections.unmodifiableSet(routes)); + } + } } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 95aff5652bb6..b571d62c0cba 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -1554,15 +1554,18 @@ public class ZenModeHelper { if (isFromApp) { // Don't allow apps to toggle hidden (non-public-API) effects. - newEffects = new ZenDeviceEffects.Builder(newEffects) - .setShouldDisableAutoBrightness(oldEffects.shouldDisableAutoBrightness()) - .setShouldDisableTapToWake(oldEffects.shouldDisableTapToWake()) - .setShouldDisableTiltToWake(oldEffects.shouldDisableTiltToWake()) - .setShouldDisableTouch(oldEffects.shouldDisableTouch()) - .setShouldMinimizeRadioUsage(oldEffects.shouldMinimizeRadioUsage()) - .setShouldMaximizeDoze(oldEffects.shouldMaximizeDoze()) - .setExtraEffects(oldEffects.getExtraEffects()) - .build(); + newEffects = + new ZenDeviceEffects.Builder(newEffects) + .setShouldDisableAutoBrightness( + oldEffects.shouldDisableAutoBrightness()) + .setShouldDisableTapToWake(oldEffects.shouldDisableTapToWake()) + .setShouldDisableTiltToWake(oldEffects.shouldDisableTiltToWake()) + .setShouldDisableTouch(oldEffects.shouldDisableTouch()) + .setShouldMinimizeRadioUsage(oldEffects.shouldMinimizeRadioUsage()) + .setShouldMaximizeDoze(oldEffects.shouldMaximizeDoze()) + .setShouldUseNightLight(oldEffects.shouldUseNightLight()) + .setExtraEffects(oldEffects.getExtraEffects()) + .build(); } zenRule.zenDeviceEffects = newEffects; @@ -1601,6 +1604,9 @@ public class ZenModeHelper { if (oldEffects.shouldMaximizeDoze() != newEffects.shouldMaximizeDoze()) { userModifiedFields |= ZenDeviceEffects.FIELD_MAXIMIZE_DOZE; } + if (oldEffects.shouldUseNightLight() != newEffects.shouldUseNightLight()) { + userModifiedFields |= ZenDeviceEffects.FIELD_NIGHT_LIGHT; + } if (!Objects.equals(oldEffects.getExtraEffects(), newEffects.getExtraEffects())) { userModifiedFields |= ZenDeviceEffects.FIELD_EXTRA_EFFECTS; } 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 137ea0617f21..7c7504dccf94 100644 --- a/services/core/java/com/android/server/power/hint/HintManagerService.java +++ b/services/core/java/com/android/server/power/hint/HintManagerService.java @@ -80,6 +80,7 @@ import com.android.server.utils.Slogf; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -296,11 +297,7 @@ public final class HintManagerService extends SystemService { mPowerHalVersion = 0; mUsesFmq = false; if (mPowerHal != null) { - try { - mSupportInfo = getSupportInfo(); - } catch (RemoteException e) { - throw new IllegalStateException("Could not contact PowerHAL!", e); - } + mSupportInfo = getSupportInfo(); } mDefaultCpuHeadroomCalculationWindowMillis = new CpuHeadroomParamsInternal().calculationWindowMillis; @@ -318,7 +315,7 @@ public final class HintManagerService extends SystemService { } } - SupportInfo getSupportInfo() throws RemoteException { + SupportInfo getSupportInfo() { try { mPowerHalVersion = mPowerHal.getInterfaceVersion(); if (mPowerHalVersion >= 6) { @@ -329,40 +326,9 @@ public final class HintManagerService extends SystemService { } SupportInfo supportInfo = new SupportInfo(); - supportInfo.usesSessions = isHintSessionSupported(); - // Global boosts & modes aren't currently relevant for HMS clients - supportInfo.boosts = 0; - supportInfo.modes = 0; - supportInfo.sessionHints = 0; - supportInfo.sessionModes = 0; - supportInfo.sessionTags = 0; - supportInfo.headroom = new SupportInfo.HeadroomSupportInfo(); supportInfo.headroom.isCpuSupported = false; supportInfo.headroom.isGpuSupported = false; - if (isHintSessionSupported()) { - if (mPowerHalVersion == 4) { - // Assume we support the V4 hints & modes unless specified - // otherwise; this is to avoid breaking backwards compat - // since we historically just assumed they were. - supportInfo.sessionHints = 31; // first 5 bits are ones - } - if (mPowerHalVersion == 5) { - // Assume we support the V5 hints & modes unless specified - // otherwise; this is to avoid breaking backwards compat - // since we historically just assumed they were. - - // Hal V5 has 8 modes, all of which it assumes are supported, - // so we represent that by having the first 8 bits set - supportInfo.sessionHints = 255; // first 8 bits are ones - // Hal V5 has 1 mode which it assumes is supported, so we - // represent that by having the first bit set - supportInfo.sessionModes = 1; - // Hal V5 has 5 tags, all of which it assumes are supported, - // so we represent that by having the first 5 bits set - supportInfo.sessionTags = 31; - } - } return supportInfo; } @@ -1263,7 +1229,7 @@ public final class HintManagerService extends SystemService { @SessionTag int tag, SessionCreationConfig creationConfig, SessionConfig config) { if (!isHintSessionSupported()) { - throw new UnsupportedOperationException("PowerHintSessions are not supported!"); + throw new UnsupportedOperationException("PowerHAL is not supported!"); } java.util.Objects.requireNonNull(token); @@ -1459,6 +1425,12 @@ public final class HintManagerService extends SystemService { removeChannelItem(callingTgid, callingUid); }; + @Override + public long getHintSessionPreferredRate() { + return mHintSessionPreferredRate; + } + + @Override public int getMaxGraphicsPipelineThreadsCount() { return MAX_GRAPHICS_PIPELINE_THREADS_COUNT; } @@ -1480,6 +1452,7 @@ public final class HintManagerService extends SystemService { if (!mSupportInfo.headroom.isCpuSupported) { throw new UnsupportedOperationException(); } + checkCpuHeadroomParams(params); final CpuHeadroomParams halParams = new CpuHeadroomParams(); halParams.tids = new int[]{Binder.getCallingPid()}; halParams.calculationType = params.calculationType; @@ -1487,10 +1460,6 @@ public final class HintManagerService extends SystemService { if (params.usesDeviceHeadroom) { halParams.tids = new int[]{}; } else if (params.tids != null && params.tids.length > 0) { - if (params.tids.length > 5) { - throw new IllegalArgumentException( - "More than 5 TIDs is requested: " + params.tids.length); - } if (SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_TID, true)) { final int tgid = Process.getThreadGroupLeader(Binder.getCallingPid()); for (int tid : params.tids) { @@ -1530,11 +1499,45 @@ public final class HintManagerService extends SystemService { } } + private void checkCpuHeadroomParams(CpuHeadroomParamsInternal params) { + boolean calculationTypeMatched = false; + try { + for (final Field field : + CpuHeadroomParams.CalculationType.class.getDeclaredFields()) { + if (field.getType() == byte.class) { + byte value = field.getByte(null); + if (value == params.calculationType) { + calculationTypeMatched = true; + break; + } + } + } + } catch (IllegalAccessException e) { + Slog.wtf(TAG, "Checking the calculation type was unexpectedly not allowed"); + } + if (!calculationTypeMatched) { + throw new IllegalArgumentException( + "Unknown CPU headroom calculation type " + (int) params.calculationType); + } + if (params.calculationWindowMillis < 50 || params.calculationWindowMillis > 10000) { + throw new IllegalArgumentException( + "Invalid CPU headroom calculation window, expected [50, 10000] but got " + + params.calculationWindowMillis); + } + if (!params.usesDeviceHeadroom) { + if (params.tids != null && params.tids.length > 5) { + throw new IllegalArgumentException( + "More than 5 TIDs requested: " + params.tids.length); + } + } + } + @Override public GpuHeadroomResult getGpuHeadroom(@NonNull GpuHeadroomParamsInternal params) { if (!mSupportInfo.headroom.isGpuSupported) { throw new UnsupportedOperationException(); } + checkGpuHeadroomParams(params); final GpuHeadroomParams halParams = new GpuHeadroomParams(); halParams.calculationType = params.calculationType; halParams.calculationWindowMillis = params.calculationWindowMillis; @@ -1565,6 +1568,33 @@ public final class HintManagerService extends SystemService { } } + private void checkGpuHeadroomParams(GpuHeadroomParamsInternal params) { + boolean calculationTypeMatched = false; + try { + for (final Field field : + GpuHeadroomParams.CalculationType.class.getDeclaredFields()) { + if (field.getType() == byte.class) { + byte value = field.getByte(null); + if (value == params.calculationType) { + calculationTypeMatched = true; + break; + } + } + } + } catch (IllegalAccessException e) { + Slog.wtf(TAG, "Checking the calculation type was unexpectedly not allowed"); + } + if (!calculationTypeMatched) { + throw new IllegalArgumentException( + "Unknown GPU headroom calculation type " + (int) params.calculationType); + } + if (params.calculationWindowMillis < 50 || params.calculationWindowMillis > 10000) { + throw new IllegalArgumentException( + "Invalid GPU headroom calculation window, expected [50, 10000] but got " + + params.calculationWindowMillis); + } + } + @Override public long getCpuHeadroomMinIntervalMillis() { if (!mSupportInfo.headroom.isCpuSupported) { @@ -1590,16 +1620,6 @@ public final class HintManagerService extends SystemService { mSessionManager = ISessionManager.Stub.asInterface(sessionManager); } - public IHintManager.HintManagerClientData - registerClient(@NonNull IHintManager.IHintManagerClient clientBinder) { - IHintManager.HintManagerClientData out = new IHintManager.HintManagerClientData(); - out.preferredRateNanos = mHintSessionPreferredRate; - out.maxGraphicsPipelineThreads = getMaxGraphicsPipelineThreadsCount(); - out.powerHalVersion = mPowerHalVersion; - out.supportInfo = mSupportInfo; - return out; - } - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) { @@ -1607,7 +1627,7 @@ public final class HintManagerService extends SystemService { } pw.println("HintSessionPreferredRate: " + mHintSessionPreferredRate); pw.println("MaxGraphicsPipelineThreadsCount: " + MAX_GRAPHICS_PIPELINE_THREADS_COUNT); - pw.println("Hint Session Support: " + isHintSessionSupported()); + pw.println("HAL Support: " + isHintSessionSupported()); pw.println("Active Sessions:"); synchronized (mLock) { for (int i = 0; i < mActiveSessions.size(); i++) { diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 24a6f118ad04..4bcba13448e9 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -258,16 +258,13 @@ class InsetsPolicy { * We also need to exclude certain types of insets source for client within specific windowing * modes. * - * @param attrs the LayoutParams of the target - * @param windowingMode the windowing mode of the target - * @param isAlwaysOnTop is the target always on top + * @param target the target on which the policy is applied * @param state the input inset state containing all the sources * @return The state stripped of the necessary information. */ - InsetsState enforceInsetsPolicyForTarget(WindowManager.LayoutParams attrs, - @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop, - InsetsState state) { + InsetsState enforceInsetsPolicyForTarget(WindowState target, InsetsState state) { final InsetsState originalState = state; + final WindowManager.LayoutParams attrs = target.getAttrs(); // The caller should not receive the visible insets provided by itself. if (attrs.type == TYPE_INPUT_METHOD) { @@ -316,12 +313,17 @@ class InsetsPolicy { } } + final @WindowConfiguration.WindowingMode int windowingMode = target.getWindowingMode(); if (WindowConfiguration.isFloating(windowingMode) - || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) { + || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && target.isAlwaysOnTop())) { // Keep frames, caption, and IME. int types = WindowInsets.Type.captionBar(); if (windowingMode != WINDOWING_MODE_PINNED) { - types |= WindowInsets.Type.ime(); + if (!Flags.refactorInsetsController() || (mDisplayContent != null + && target == mDisplayContent.getImeInputTarget() + && (WindowInsets.Type.ime() & target.getRequestedVisibleTypes()) != 0)) { + types |= WindowInsets.Type.ime(); + } } final InsetsState newState = new InsetsState(); newState.set(state, types); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index bcd12f253299..b4a22b0dd034 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -4150,7 +4150,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { .setSourceCrop(cropBounds) .setCaptureSecureLayers(true) .setAllowProtected(true) - .setHintForSeamlessTransition(isDisplayRotation) + // We always reroute this screenshot to the display, so this transition + // is ALWAYS seamless + .setHintForSeamlessTransition(true) .build(); ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = ScreenCapture.captureLayers(captureArgs); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 90bf053dfbef..68b4b6f0ae91 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1631,8 +1631,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } final InsetsState rawInsetsState = mFrozenInsetsState != null ? mFrozenInsetsState : getMergedInsetsState(); - final InsetsState insetsStateForWindow = insetsPolicy.enforceInsetsPolicyForTarget( - mAttrs, getWindowingMode(), isAlwaysOnTop(), rawInsetsState); + final InsetsState insetsStateForWindow = insetsPolicy.enforceInsetsPolicyForTarget(this, + rawInsetsState); return insetsPolicy.adjustInsetsForWindow(this, insetsStateForWindow, includeTransient); } @@ -3303,7 +3303,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // just kill it. And if it is a window of foreground activity, the activity can be // restarted automatically if needed. Slog.w(TAG, "Exception thrown during dispatchAppVisibility " + this, e); - if (android.os.Process.getUidForPid(mSession.mPid) == mSession.mUid) { + if (android.os.Process.getUidForPid(mSession.mPid) == mSession.mUid + && android.os.Process.getThreadGroupLeader(mSession.mPid) == mSession.mPid) { android.os.Process.killProcess(mSession.mPid); } } diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java index 0ccaa6043f5f..073ee31ddd60 100644 --- a/services/supervision/java/com/android/server/supervision/SupervisionService.java +++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java @@ -16,6 +16,11 @@ package com.android.server.supervision; +import static android.Manifest.permission.INTERACT_ACROSS_USERS; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import static com.android.internal.util.Preconditions.checkCallAuthorization; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -31,6 +36,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.os.Binder; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.ResultReceiver; @@ -78,6 +84,9 @@ public class SupervisionService extends ISupervisionManager.Stub { @Override public boolean isSupervisionEnabledForUser(@UserIdInt int userId) { + if (UserHandle.getUserId(Binder.getCallingUid()) != userId) { + enforcePermission(INTERACT_ACROSS_USERS); + } synchronized (getLockObject()) { return getUserDataLocked(userId).supervisionEnabled; } @@ -151,7 +160,8 @@ public class SupervisionService extends ISupervisionManager.Stub { /** Returns whether the supervision app has profile owner status. */ private boolean isProfileOwner(@UserIdInt int userId) { - ComponentName profileOwner = mDpmInternal.getProfileOwnerAsUser(userId); + ComponentName profileOwner = + mDpmInternal != null ? mDpmInternal.getProfileOwnerAsUser(userId) : null; return profileOwner != null && isSupervisionAppPackage(profileOwner.getPackageName()); } @@ -161,6 +171,12 @@ public class SupervisionService extends ISupervisionManager.Stub { mContext.getResources().getString(R.string.config_systemSupervision)); } + /** Enforces that the caller has the given permission. */ + private void enforcePermission(String permission) { + checkCallAuthorization( + mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED); + } + public static class Lifecycle extends SystemService { private final SupervisionService mSupervisionService; diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt index f589a2c9385c..7db6ea0bf86d 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt @@ -60,12 +60,6 @@ class BrightnessRangeControllerTest { } @Test - fun testMaxBrightness_HbmDisabledAndNotAllowed() { - val controller = createController(nbmEnabled = false, hbmAllowed = false) - assertThat(controller.currentBrightnessMax).isEqualTo(MAX_BRIGHTNESS) - } - - @Test fun testMaxBrightness_transitionPointLessThanCurrentNbmLimit() { val controller = createController( hbmAllowed = false, @@ -76,13 +70,11 @@ class BrightnessRangeControllerTest { } private fun createController( - nbmEnabled: Boolean = true, hbmSupported: Boolean = true, hbmAllowed: Boolean = true, hbmMaxBrightness: Float = MAX_BRIGHTNESS, nbmMaxBrightness: Float = NORMAL_BRIGHTNESS_LOW ): BrightnessRangeController { - whenever(mockFlags.isNbmControllerEnabled).thenReturn(nbmEnabled) whenever(mockHbmController.deviceSupportsHbm()).thenReturn(hbmSupported) whenever(mockHbmController.isHbmCurrentlyAllowed).thenReturn(hbmAllowed) whenever(mockHbmController.currentBrightnessMax).thenReturn(hbmMaxBrightness) diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index 8ca39194de3b..a4dfecb8ed96 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -2626,8 +2626,8 @@ public final class DisplayPowerControllerTest { mock(ScreenOffBrightnessSensorController.class); final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class); final HdrClamper hdrClamper = mock(HdrClamper.class); - final NormalBrightnessModeController normalBrightnessModeController = mock( - NormalBrightnessModeController.class); + final NormalBrightnessModeController normalBrightnessModeController = + new NormalBrightnessModeController(); BrightnessClamperController clamperController = mock(BrightnessClamperController.class); when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX); diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java index df09b046ddd2..6d1e56d1f479 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java @@ -68,6 +68,8 @@ public final class BrightnessEventTest { mBrightnessEvent.setAutomaticBrightnessEnabled(true); mBrightnessEvent.setDisplayBrightnessStrategyName(DISPLAY_BRIGHTNESS_STRATEGY_NAME); mBrightnessEvent.setAutoBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE); + mBrightnessEvent.setSlowChange(true); + mBrightnessEvent.setRampSpeed(0.3f); } @Test @@ -88,7 +90,7 @@ public final class BrightnessEventTest { + "preLux=150.0, wasShortTermModelActive=true, autoBrightness=true (idle), " + "unclampedBrt=0.65, hbmMax=0.62, hbmMode=off, thrmMax=0.65, " + "rbcStrength=-1, powerFactor=0.2, physDisp=display_name(987654321), " - + "logicalId=1"; + + "logicalId=1, slowChange=true, rampSpeed=0.3"; assertEquals(expectedString, actualString); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java index f391e409a717..4e81b3530b62 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java @@ -20,10 +20,13 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -89,6 +92,7 @@ public class ColorDisplayServiceTest { private ColorDisplayService.BinderService mBinderService; private Resources mResourcesSpy; + private ReduceBrightColorsTintController mRbcSpy; private static final int[] MINIMAL_COLOR_MODES = new int[] { ColorDisplayManager.COLOR_MODE_NATURAL, @@ -135,7 +139,8 @@ public class ColorDisplayServiceTest { mLocalServiceKeeperRule.overrideLocalService( DisplayManagerInternal.class, mDisplayManagerInternal); - mCds = new ColorDisplayService(mContext); + mRbcSpy = Mockito.spy(new ReduceBrightColorsTintController()); + mCds = new ColorDisplayService(mContext, mRbcSpy); mBinderService = mCds.new BinderService(); mLocalServiceKeeperRule.overrideLocalService( ColorDisplayService.ColorDisplayServiceInternal.class, @@ -1106,7 +1111,8 @@ public class ColorDisplayServiceTest { setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL); startService(); verify(mDisplayTransformManager).setColorMode( - eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID)); + eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), any(), + eq(Display.COLOR_MODE_INVALID)); } @Test @@ -1124,7 +1130,8 @@ public class ColorDisplayServiceTest { setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL); startService(); verify(mDisplayTransformManager).setColorMode( - eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID)); + eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), any(), + eq(Display.COLOR_MODE_INVALID)); } @Test @@ -1140,7 +1147,8 @@ public class ColorDisplayServiceTest { setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL); startService(); verify(mDisplayTransformManager).setColorMode( - eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_SRGB)); + eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), any(), + eq(Display.COLOR_MODE_SRGB)); } @Test @@ -1156,7 +1164,8 @@ public class ColorDisplayServiceTest { setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED); startService(); verify(mDisplayTransformManager).setColorMode( - eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(), eq(Display.COLOR_MODE_INVALID)); + eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(), any(), + eq(Display.COLOR_MODE_INVALID)); } @Test @@ -1164,10 +1173,27 @@ public class ColorDisplayServiceTest { when(mResourcesSpy.getIntArray(R.array.config_availableColorModes)) .thenReturn(new int[] {}); startService(); - verify(mDisplayTransformManager, never()).setColorMode(anyInt(), any(), anyInt()); + verify(mDisplayTransformManager, never()).setColorMode(anyInt(), any(), any(), anyInt()); assertThat(mBinderService.getColorMode()).isEqualTo(-1); } + @Test + public void ensureColorModeChangeTriggersRbcReload() { + // should set up RBC once at startup + startService(); + reset(mRbcSpy); + + // Make sure RBC is enabled and available for this test + doReturn(true).when(mRbcSpy).isAvailable(mContext); + + // When Color Mode changes, RBC needs to re-setup + // onDisplayColorModeChanged cancels animations, and should be called in handler thread + mCds.mHandler.runWithScissors( + () -> mCds.onDisplayColorModeChanged(ColorDisplayManager.COLOR_MODE_NATURAL), + 1000); + verify(mRbcSpy, times(1)).setUp(eq(mContext), anyBoolean()); + } + /** * Configures Night display to use a custom schedule. * diff --git a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java index 27f87aae35bb..a7ef5e0afc0e 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java @@ -19,6 +19,7 @@ package com.android.server.display.color; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY; +import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_REDUCE_BRIGHT_COLORS; import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE; import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_DISPLAY_COLOR; import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_SATURATION; @@ -51,12 +52,14 @@ public class DisplayTransformManagerTest { private MockitoSession mSession; private DisplayTransformManager mDtm; private float[] mNightDisplayMatrix; + private float[] mRbcMatrix; private HashMap<String, String> mSystemProperties; @Before public void setUp() { mDtm = new DisplayTransformManager(); mNightDisplayMatrix = mDtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY); + mRbcMatrix = mDtm.getColorMatrix(LEVEL_COLOR_MATRIX_REDUCE_BRIGHT_COLORS); mSession = ExtendedMockito.mockitoSession() .initMocks(this) @@ -81,7 +84,8 @@ public class DisplayTransformManagerTest { @Test public void setColorMode_natural() { - mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, -1); + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, mRbcMatrix, + Display.COLOR_MODE_INVALID); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo("0" /* managed */); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) @@ -90,7 +94,8 @@ public class DisplayTransformManagerTest { @Test public void setColorMode_boosted() { - mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED, mNightDisplayMatrix, -1); + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED, mNightDisplayMatrix, mRbcMatrix, + Display.COLOR_MODE_INVALID); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo("0" /* managed */); @@ -100,7 +105,8 @@ public class DisplayTransformManagerTest { @Test public void setColorMode_saturated() { - mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_SATURATED, mNightDisplayMatrix, -1); + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_SATURATED, mNightDisplayMatrix, mRbcMatrix, + Display.COLOR_MODE_INVALID); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo("1" /* unmanaged */); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) @@ -109,7 +115,8 @@ public class DisplayTransformManagerTest { @Test public void setColorMode_automatic() { - mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC, mNightDisplayMatrix, -1); + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC, mNightDisplayMatrix, mRbcMatrix, + Display.COLOR_MODE_INVALID); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo("2" /* enhanced */); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) @@ -118,7 +125,7 @@ public class DisplayTransformManagerTest { @Test public void setColorMode_vendor() { - mDtm.setColorMode(0x100, mNightDisplayMatrix, -1); + mDtm.setColorMode(0x100, mNightDisplayMatrix, mRbcMatrix, Display.COLOR_MODE_INVALID); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo(Integer.toString(0x100) /* pass-through */); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) @@ -127,7 +134,7 @@ public class DisplayTransformManagerTest { @Test public void setColorMode_outOfBounds() { - mDtm.setColorMode(0x50, mNightDisplayMatrix, -1); + mDtm.setColorMode(0x50, mNightDisplayMatrix, mRbcMatrix, Display.COLOR_MODE_INVALID); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo(null); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) @@ -141,7 +148,7 @@ public class DisplayTransformManagerTest { // Start with a known state, which we expect to remain unmodified SystemProperties.set(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE, magicPropertyValue); - mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, mRbcMatrix, Display.COLOR_MODE_INVALID); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE)) .isEqualTo(magicPropertyValue); @@ -155,7 +162,7 @@ public class DisplayTransformManagerTest { // Start with a known state, which we expect to get modified SystemProperties.set(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE, magicPropertyValue); - mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, mRbcMatrix, testPropertyValue); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE)) .isEqualTo(Integer.toString(testPropertyValue)); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index 1efe4707fc11..9e96800ca2e9 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -18,6 +18,7 @@ package com.android.server.am; import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; import static android.app.ActivityManager.PROCESS_CAPABILITY_BFSL; +import static android.app.ActivityManager.PROCESS_CAPABILITY_CPU_TIME; import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; @@ -40,6 +41,7 @@ import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING; import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ACTIVITY; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_SHORT_FGS_TIMEOUT; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -283,6 +285,15 @@ public class MockingOomAdjusterTests { } } + private static void assertNoCpuTime(ProcessRecord app) { + assertEquals(0, app.mState.getSetCapability() & PROCESS_CAPABILITY_CPU_TIME); + } + + private static void assertCpuTime(ProcessRecord app) { + assertEquals(PROCESS_CAPABILITY_CPU_TIME, + app.mState.getSetCapability() & PROCESS_CAPABILITY_CPU_TIME); + } + private static void assertBfsl(ProcessRecord app) { assertEquals(PROCESS_CAPABILITY_BFSL, app.mState.getSetCapability() & PROCESS_CAPABILITY_BFSL); @@ -661,6 +672,7 @@ public class MockingOomAdjusterTests { // SHORT_SERVICE, timed out already. s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); + mProcessStateController.setStartRequested(s, true); s.isForeground = true; s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; @@ -687,6 +699,51 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") @Test + @EnableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY) + public void testUpdateOomAdjFreezeState_bindingFromShortFgs() { + // Setting up a started short FGS within app1. + final ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService); + s.appInfo = new ApplicationInfo(); + mProcessStateController.setStartRequested(s, true); + s.isForeground = true; + s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; + mProcessStateController.setShortFgsInfo(s, SystemClock.uptimeMillis()); + + final ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); + mProcessStateController.setHostProcess(s, app); + mProcessStateController.setHasForegroundServices(app.mServices, true, + FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); + mProcessStateController.startService(app.mServices, s); + app.mState.setLastTopTime(SystemClock.uptimeMillis() + - mService.mConstants.TOP_TO_FGS_GRACE_DURATION); + + final ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + // App1 with short service binds to app2 + bindService(app2, app, null, null, 0, mock(IBinder.class)); + + setProcessesToLru(app, app2); + updateOomAdj(app); + + assertCpuTime(app); + assertCpuTime(app2); + + // Timeout the short FGS. + mProcessStateController.setShortFgsInfo(s, SystemClock.uptimeMillis() + - mService.mConstants.mShortFgsTimeoutDuration + - mService.mConstants.mShortFgsProcStateExtraWaitDuration); + mService.mServices.onShortFgsProcstateTimeout(s); + // mService is a mock, but this verifies that the timeout would trigger an update. + verify(mService).updateOomAdjLocked(app, OOM_ADJ_REASON_SHORT_FGS_TIMEOUT); + updateOomAdj(app); + + assertNoCpuTime(app); + assertNoCpuTime(app2); + } + + @SuppressWarnings("GuardedBy") + @Test public void testUpdateOomAdj_DoOne_OverlayUi() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); @@ -3142,11 +3199,19 @@ public class MockingOomAdjusterTests { assertEquals(true, app.getUidRecord().isSetAllowListed()); assertFreezeState(app, false); assertFreezeState(app2, false); + if (Flags.useCpuTimeCapability()) { + assertCpuTime(app); + assertCpuTime(app2); + } mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); assertEquals(false, app.getUidRecord().isSetAllowListed()); assertFreezeState(app, true); assertFreezeState(app2, true); + if (Flags.useCpuTimeCapability()) { + assertNoCpuTime(app); + assertNoCpuTime(app2); + } } @SuppressWarnings("GuardedBy") @@ -3171,6 +3236,11 @@ public class MockingOomAdjusterTests { assertFreezeState(app, false); assertFreezeState(app2, false); assertFreezeState(app3, false); + if (Flags.useCpuTimeCapability()) { + assertCpuTime(app); + assertCpuTime(app2); + assertCpuTime(app3); + } // Remove app1 from allowlist. mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); @@ -3179,6 +3249,11 @@ public class MockingOomAdjusterTests { assertFreezeState(app, true); assertFreezeState(app2, false); assertFreezeState(app3, false); + if (Flags.useCpuTimeCapability()) { + assertNoCpuTime(app); + assertCpuTime(app2); + assertCpuTime(app3); + } // Now remove app2 from allowlist. mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false); @@ -3187,6 +3262,11 @@ public class MockingOomAdjusterTests { assertFreezeState(app, true); assertFreezeState(app2, true); assertFreezeState(app3, true); + if (Flags.useCpuTimeCapability()) { + assertNoCpuTime(app); + assertNoCpuTime(app2); + assertNoCpuTime(app3); + } } @SuppressWarnings("GuardedBy") diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java index 2988c77703b7..7e052dcba3fd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.PROCESS_CAPABILITY_CPU_TIME; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY; @@ -64,6 +65,8 @@ import android.content.pm.ServiceInfo; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; @@ -326,6 +329,7 @@ public final class ServiceBindingOomAdjPolicyTest { @Test @RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX) + @DisableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY) public void testServiceDistinctBindingOomAdjShouldNotFreeze() throws Exception { // Enable the flags. mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); @@ -418,6 +422,7 @@ public final class ServiceBindingOomAdjPolicyTest { @Test @RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX) + @DisableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY) public void testServiceDistinctBindingOomAdjAllowOomManagement() throws Exception { // Enable the flags. mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); @@ -497,6 +502,7 @@ public final class ServiceBindingOomAdjPolicyTest { @Test @RequiresFlagsEnabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX) + @DisableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY) public void testServiceDistinctBindingOomAdjWaivePriority_propagateUnfreeze() throws Exception { // Enable the flags. mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); @@ -574,6 +580,50 @@ public final class ServiceBindingOomAdjPolicyTest { } @Test + @RequiresFlagsEnabled({ + Flags.FLAG_UNFREEZE_BIND_POLICY_FIX, + Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY + }) + @EnableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY) + public void testServiceDistinctBindingOomAdj_propagateCpuTimeCapability() throws Exception { + // Note that PROCESS_CAPABILITY_CPU_TIME is special and should be propagated even when + // BIND_INCLUDE_CAPABILITIES is not present. + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_CPU_TIME, TEST_APP1_NAME, + this::setHomeProcess, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE, + PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHasForegroundServices, + BIND_AUTO_CREATE, + atLeastOnce(), atLeastOnce()); + + // BIND_WAIVE_PRIORITY should not affect propagation of capability CPU_TIME + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_CPU_TIME, + TEST_APP1_NAME, + this::setHasForegroundServices, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME, HOME_APP_ADJ, + PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHomeProcess, + BIND_AUTO_CREATE | BIND_WAIVE_PRIORITY, + atLeastOnce(), atLeastOnce()); + + // If both process have the capability, the bind should not need an update but the unbind + // is not safe to skip. + // Note that this check can fail on future changes that are not related to + // PROCESS_CAPABILITY_CPU_TIME and trigger updates but this is important to ensure + // efficiency of OomAdjuster. + performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, + PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_CPU_TIME, TEST_APP1_NAME, + this::setHomeProcess, + TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME, HOME_APP_ADJ, + PROCESS_CAPABILITY_CPU_TIME, TEST_APP2_NAME, TEST_SERVICE2_NAME, + this::setHomeProcess, + BIND_AUTO_CREATE, + never(), atLeastOnce()); + } + + @Test @RequiresFlagsDisabled(com.android.server.am.Flags.FLAG_UNFREEZE_BIND_POLICY_FIX) public void testServiceDistinctBindingOomAdjWaivePriority() throws Exception { // Enable the flags. @@ -624,6 +674,9 @@ public final class ServiceBindingOomAdjPolicyTest { // Enable the flags. mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY); + // Note that some capabilities like PROCESS_CAPABILITY_CPU_TIME are special and propagated + // regardless of BIND_INCLUDE_CAPABILITIES. We don't test for them here. + // Verify that there should be 0 oom adj update // because we didn't specify the "BIND_INCLUDE_CAPABILITIES" performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID, 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 b166514ce0b9..5c73fd33f46f 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 @@ -64,7 +64,6 @@ import android.os.Binder; import android.os.CpuHeadroomParamsInternal; import android.os.GpuHeadroomParamsInternal; import android.os.IBinder; -import android.os.IHintManager; import android.os.IHintSession; import android.os.PerformanceHintManager; import android.os.Process; @@ -155,8 +154,6 @@ public class HintManagerServiceTest { private ActivityManagerInternal mAmInternalMock; @Mock private PackageManager mMockPackageManager; - @Mock - private IHintManager.IHintManagerClient mClientCallback; @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @@ -174,23 +171,6 @@ public class HintManagerServiceTest { }; } - private SupportInfo makeDefaultSupportInfo() { - mSupportInfo = new SupportInfo(); - mSupportInfo.usesSessions = true; - // By default, mark everything as fully supported - mSupportInfo.sessionHints = -1; - mSupportInfo.sessionModes = -1; - mSupportInfo.modes = -1; - mSupportInfo.boosts = -1; - mSupportInfo.sessionTags = -1; - mSupportInfo.headroom = new SupportInfo.HeadroomSupportInfo(); - mSupportInfo.headroom.isCpuSupported = true; - mSupportInfo.headroom.cpuMinIntervalMillis = 2000; - mSupportInfo.headroom.isGpuSupported = true; - mSupportInfo.headroom.gpuMinIntervalMillis = 2000; - return mSupportInfo; - } - @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -201,7 +181,12 @@ public class HintManagerServiceTest { mConfig.eventFlagDescriptor = new MQDescriptor<Byte, Byte>(); ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.category = ApplicationInfo.CATEGORY_GAME; - mSupportInfo = makeDefaultSupportInfo(); + mSupportInfo = new SupportInfo(); + mSupportInfo.headroom = new SupportInfo.HeadroomSupportInfo(); + mSupportInfo.headroom.isCpuSupported = true; + mSupportInfo.headroom.cpuMinIntervalMillis = 2000; + mSupportInfo.headroom.isGpuSupported = true; + mSupportInfo.headroom.gpuMinIntervalMillis = 2000; when(mContext.getPackageManager()).thenReturn(mMockPackageManager); when(mMockPackageManager.getNameForUid(anyInt())).thenReturn(TEST_APP_NAME); when(mMockPackageManager.getApplicationInfo(eq(TEST_APP_NAME), anyInt())) @@ -230,7 +215,6 @@ public class HintManagerServiceTest { when(mIPowerMock.getInterfaceVersion()).thenReturn(6); when(mIPowerMock.getSupportInfo()).thenReturn(mSupportInfo); when(mIPowerMock.getSessionChannel(anyInt(), anyInt())).thenReturn(mConfig); - when(mIPowerMock.getSupportInfo()).thenReturn(mSupportInfo); LocalServices.removeServiceForTest(ActivityManagerInternal.class); LocalServices.addService(ActivityManagerInternal.class, mAmInternalMock); } @@ -425,11 +409,8 @@ public class HintManagerServiceTest { HintManagerService service = createService(); IBinder token = new Binder(); - IHintManager.HintManagerClientData data = service.getBinderServiceInstance() - .registerClient(mClientCallback); - - final int threadCount = data.maxGraphicsPipelineThreads; - + final int threadCount = + service.getBinderServiceInstance().getMaxGraphicsPipelineThreadsCount(); long sessionPtr1 = 1111L; long sessionId1 = 11111L; CountDownLatch stopLatch1 = new CountDownLatch(1); @@ -1274,6 +1255,53 @@ public class HintManagerServiceTest { } @Test + public void testCpuHeadroomInvalidParams() { + HintManagerService service = createService(); + final CpuHeadroomParamsInternal param1 = new CpuHeadroomParamsInternal(); + param1.calculationType = 100; + assertThrows(IllegalArgumentException.class, () -> { + service.getBinderServiceInstance().getCpuHeadroom(param1); + }); + + final CpuHeadroomParamsInternal param2 = new CpuHeadroomParamsInternal(); + param2.calculationWindowMillis = 49; + assertThrows(IllegalArgumentException.class, () -> { + service.getBinderServiceInstance().getCpuHeadroom(param2); + }); + param2.calculationWindowMillis = 10001; + assertThrows(IllegalArgumentException.class, () -> { + service.getBinderServiceInstance().getCpuHeadroom(param2); + }); + + final CpuHeadroomParamsInternal param3 = new CpuHeadroomParamsInternal(); + param3.tids = new int[]{1, 2, 3, 4, 5, 6}; + assertThrows(IllegalArgumentException.class, () -> { + service.getBinderServiceInstance().getCpuHeadroom(param3); + }); + } + + @Test + public void testGpuHeadroomInvalidParams() { + HintManagerService service = createService(); + final GpuHeadroomParamsInternal param1 = new GpuHeadroomParamsInternal(); + param1.calculationType = 100; + assertThrows(IllegalArgumentException.class, () -> { + service.getBinderServiceInstance().getGpuHeadroom(param1); + }); + + final GpuHeadroomParamsInternal param2 = new GpuHeadroomParamsInternal(); + param2.calculationWindowMillis = 49; + assertThrows(IllegalArgumentException.class, () -> { + service.getBinderServiceInstance().getGpuHeadroom(param2); + }); + param2.calculationWindowMillis = 10001; + assertThrows(IllegalArgumentException.class, () -> { + service.getBinderServiceInstance().getGpuHeadroom(param2); + }); + } + + + @Test public void testCpuHeadroomCache() throws Exception { CpuHeadroomParamsInternal params1 = new CpuHeadroomParamsInternal(); CpuHeadroomParams halParams1 = new CpuHeadroomParams(); @@ -1419,67 +1447,4 @@ public class HintManagerServiceTest { verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1)); verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); } - - @Test - public void testRegisteringClient() throws Exception { - HintManagerService service = createService(); - IHintManager.HintManagerClientData data = service.getBinderServiceInstance() - .registerClient(mClientCallback); - assertNotNull(data); - assertEquals(data.supportInfo, mSupportInfo); - } - - @Test - public void testRegisteringClientOnV4() throws Exception { - when(mIPowerMock.getInterfaceVersion()).thenReturn(4); - HintManagerService service = createService(); - IHintManager.HintManagerClientData data = service.getBinderServiceInstance() - .registerClient(mClientCallback); - assertNotNull(data); - assertEquals(data.supportInfo.usesSessions, true); - assertEquals(data.supportInfo.boosts, 0); - assertEquals(data.supportInfo.modes, 0); - assertEquals(data.supportInfo.sessionHints, 31); - assertEquals(data.supportInfo.sessionModes, 0); - assertEquals(data.supportInfo.sessionTags, 0); - assertEquals(data.powerHalVersion, 4); - assertEquals(data.preferredRateNanos, DEFAULT_HINT_PREFERRED_RATE); - } - - @Test - public void testRegisteringClientOnV5() throws Exception { - when(mIPowerMock.getInterfaceVersion()).thenReturn(5); - HintManagerService service = createService(); - IHintManager.HintManagerClientData data = service.getBinderServiceInstance() - .registerClient(mClientCallback); - assertNotNull(data); - assertEquals(data.supportInfo.usesSessions, true); - assertEquals(data.supportInfo.boosts, 0); - assertEquals(data.supportInfo.modes, 0); - assertEquals(data.supportInfo.sessionHints, 255); - assertEquals(data.supportInfo.sessionModes, 1); - assertEquals(data.supportInfo.sessionTags, 31); - assertEquals(data.powerHalVersion, 5); - assertEquals(data.preferredRateNanos, DEFAULT_HINT_PREFERRED_RATE); - } - - @Test - public void testSettingUpOldClientWhenUnsupported() throws Exception { - when(mIPowerMock.getInterfaceVersion()).thenReturn(5); - // Mock unsupported to modify the default support behavior - when(mNativeWrapperMock.halGetHintSessionPreferredRate()) - .thenReturn(-1L); - HintManagerService service = createService(); - IHintManager.HintManagerClientData data = service.getBinderServiceInstance() - .registerClient(mClientCallback); - assertNotNull(data); - assertEquals(data.supportInfo.usesSessions, false); - assertEquals(data.supportInfo.boosts, 0); - assertEquals(data.supportInfo.modes, 0); - assertEquals(data.supportInfo.sessionHints, 0); - assertEquals(data.supportInfo.sessionModes, 0); - assertEquals(data.supportInfo.sessionTags, 0); - assertEquals(data.powerHalVersion, 5); - assertEquals(data.preferredRateNanos, -1); - } } diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java index fde3422b1ff3..17f5ebb3b02a 100644 --- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java @@ -130,9 +130,9 @@ public class GameManagerServiceSettingsTests { assertEquals(GameManager.GAME_MODE_STANDARD, settings.getGameModeLocked(PACKAGE_NAME_4)); // test game mode configs - assertNull(settings.getConfigOverride(PACKAGE_NAME_1)); - assertNull(settings.getConfigOverride(PACKAGE_NAME_3)); - GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2); + assertNull(settings.getConfigOverrideLocked(PACKAGE_NAME_1)); + assertNull(settings.getConfigOverrideLocked(PACKAGE_NAME_3)); + GamePackageConfiguration config = settings.getConfigOverrideLocked(PACKAGE_NAME_2); assertNotNull(config); assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_STANDARD)); @@ -152,7 +152,7 @@ public class GameManagerServiceSettingsTests { assertEquals(batteryConfig.getFpsStr(), GameModeConfiguration.DEFAULT_FPS); assertFalse(batteryConfig.getUseAngle()); - config = settings.getConfigOverride(PACKAGE_NAME_4); + config = settings.getConfigOverrideLocked(PACKAGE_NAME_4); assertNotNull(config); GameModeConfiguration customConfig = config.getGameModeConfiguration( GameManager.GAME_MODE_CUSTOM); @@ -177,7 +177,7 @@ public class GameManagerServiceSettingsTests { GameManagerSettings settings = new GameManagerSettings(context.getFilesDir()); assertTrue(settings.readPersistentDataLocked()); - final GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_1); + final GamePackageConfiguration config = settings.getConfigOverrideLocked(PACKAGE_NAME_1); assertNotNull(config); final GameModeConfiguration batteryConfig = config.getGameModeConfiguration( GameManager.GAME_MODE_BATTERY); @@ -218,7 +218,7 @@ public class GameManagerServiceSettingsTests { assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2)); assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_3)); - final GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2); + final GamePackageConfiguration config = settings.getConfigOverrideLocked(PACKAGE_NAME_2); assertNotNull(config); final GameModeConfiguration batteryConfig = config.getGameModeConfiguration( GameManager.GAME_MODE_BATTERY); @@ -248,7 +248,7 @@ public class GameManagerServiceSettingsTests { GameModeConfiguration batteryConfig = config.getOrAddDefaultGameModeConfiguration( GameManager.GAME_MODE_BATTERY); batteryConfig.setScaling(0.77f); - settings.setConfigOverride(PACKAGE_NAME_2, config); + settings.setConfigOverrideLocked(PACKAGE_NAME_2, config); // set config for app4 config = new GamePackageConfiguration(PACKAGE_NAME_4); @@ -256,15 +256,15 @@ public class GameManagerServiceSettingsTests { GameManager.GAME_MODE_CUSTOM); customConfig.setScaling(0.4f); customConfig.setFpsStr("30"); - settings.setConfigOverride(PACKAGE_NAME_4, config); + settings.setConfigOverrideLocked(PACKAGE_NAME_4, config); settings.writePersistentDataLocked(); // clear the settings in memory - settings.removeGame(PACKAGE_NAME_1); - settings.removeGame(PACKAGE_NAME_2); - settings.removeGame(PACKAGE_NAME_3); - settings.removeGame(PACKAGE_NAME_4); + settings.removeGameLocked(PACKAGE_NAME_1); + settings.removeGameLocked(PACKAGE_NAME_2); + settings.removeGameLocked(PACKAGE_NAME_3); + settings.removeGameLocked(PACKAGE_NAME_4); // read back in and verify assertTrue(settings.readPersistentDataLocked()); @@ -273,9 +273,9 @@ public class GameManagerServiceSettingsTests { assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_3)); assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_4)); - config = settings.getConfigOverride(PACKAGE_NAME_1); + config = settings.getConfigOverrideLocked(PACKAGE_NAME_1); assertNull(config); - config = settings.getConfigOverride(PACKAGE_NAME_2); + config = settings.getConfigOverrideLocked(PACKAGE_NAME_2); assertNotNull(config); batteryConfig = config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY); assertNotNull(batteryConfig); @@ -292,7 +292,7 @@ public class GameManagerServiceSettingsTests { assertEquals(performanceConfig.getFpsStr(), "60"); assertTrue(performanceConfig.getUseAngle()); - config = settings.getConfigOverride(PACKAGE_NAME_4); + config = settings.getConfigOverrideLocked(PACKAGE_NAME_4); assertNotNull(config); customConfig = config.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM); assertNotNull(customConfig); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java index 3ac78908d8ae..af911e811e5e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java @@ -49,18 +49,22 @@ public class ZenDeviceEffectsTest extends UiServiceTestCase { @Test public void builder() { - ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder() - .setShouldDimWallpaper(true) - .setShouldDisableTapToWake(true).setShouldDisableTapToWake(false) - .setShouldDisableTiltToWake(true) - .setShouldMaximizeDoze(true) - .setShouldUseNightMode(false) - .setShouldSuppressAmbientDisplay(false).setShouldSuppressAmbientDisplay(true) - .addExtraEffect("WILL BE GONE") - .setExtraEffects(ImmutableSet.of("1", "2")) - .addExtraEffects(ImmutableSet.of("3", "4")) - .addExtraEffect("5") - .build(); + ZenDeviceEffects deviceEffects = + new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .setShouldDisableTapToWake(true) + .setShouldDisableTapToWake(false) + .setShouldDisableTiltToWake(true) + .setShouldMaximizeDoze(true) + .setShouldUseNightMode(false) + .setShouldUseNightLight(true) + .setShouldSuppressAmbientDisplay(false) + .setShouldSuppressAmbientDisplay(true) + .addExtraEffect("WILL BE GONE") + .setExtraEffects(ImmutableSet.of("1", "2")) + .addExtraEffects(ImmutableSet.of("3", "4")) + .addExtraEffect("5") + .build(); assertThat(deviceEffects.shouldDimWallpaper()).isTrue(); assertThat(deviceEffects.shouldDisableAutoBrightness()).isFalse(); @@ -68,6 +72,7 @@ public class ZenDeviceEffectsTest extends UiServiceTestCase { assertThat(deviceEffects.shouldDisableTiltToWake()).isTrue(); assertThat(deviceEffects.shouldDisableTouch()).isFalse(); assertThat(deviceEffects.shouldDisplayGrayscale()).isFalse(); + assertThat(deviceEffects.shouldUseNightLight()).isTrue(); assertThat(deviceEffects.shouldMaximizeDoze()).isTrue(); assertThat(deviceEffects.shouldMinimizeRadioUsage()).isFalse(); assertThat(deviceEffects.shouldUseNightMode()).isFalse(); @@ -85,15 +90,18 @@ public class ZenDeviceEffectsTest extends UiServiceTestCase { .addExtraEffect("1") .build(); - ZenDeviceEffects modified = new ZenDeviceEffects.Builder(original) - .setShouldDisplayGrayscale(true) - .setShouldUseNightMode(false) - .addExtraEffect("2") - .build(); + ZenDeviceEffects modified = + new ZenDeviceEffects.Builder(original) + .setShouldDisplayGrayscale(true) + .setShouldUseNightMode(false) + .setShouldUseNightLight(true) + .addExtraEffect("2") + .build(); assertThat(modified.shouldDimWallpaper()).isTrue(); // from original assertThat(modified.shouldDisableTiltToWake()).isTrue(); // from original assertThat(modified.shouldDisplayGrayscale()).isTrue(); // updated + assertThat(modified.shouldUseNightLight()).isTrue(); // updated assertThat(modified.shouldUseNightMode()).isFalse(); // updated assertThat(modified.shouldSuppressAmbientDisplay()).isTrue(); // from original assertThat(modified.getExtraEffects()).containsExactly("1", "2"); // updated diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java index 3236f9501324..b42a6a5a7382 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -18,7 +18,6 @@ package com.android.server.notification; import static android.app.AutomaticZenRule.TYPE_BEDTIME; import static android.app.Flags.FLAG_BACKUP_RESTORE_LOGGING; -import static android.app.Flags.FLAG_MODES_API; import static android.app.Flags.FLAG_MODES_UI; import static android.app.Flags.modesUi; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; @@ -26,7 +25,6 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCRE import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.suppressedEffectsToString; -import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG; import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES; import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; import static android.provider.Settings.Global.ZEN_MODE_OFF; @@ -56,9 +54,7 @@ import static junit.framework.TestCase.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -71,7 +67,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Parcel; -import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.FlagsParameterization; @@ -100,6 +95,9 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.xmlpull.v1.XmlPullParserException; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; @@ -108,9 +106,6 @@ import java.io.IOException; import java.time.Instant; import java.util.List; -import platform.test.runner.parameterized.ParameterizedAndroidJunit4; -import platform.test.runner.parameterized.Parameters; - @SmallTest @RunWith(ParameterizedAndroidJunit4.class) public class ZenModeConfigTest extends UiServiceTestCase { @@ -731,19 +726,21 @@ public class ZenModeConfigTest extends UiServiceTestCase { rule.setConditionOverride(OVERRIDE_DEACTIVATE); rule.pkg = OWNER.getPackageName(); rule.zenPolicy = POLICY; - rule.zenDeviceEffects = new ZenDeviceEffects.Builder() - .setShouldDisplayGrayscale(false) - .setShouldSuppressAmbientDisplay(true) - .setShouldDimWallpaper(false) - .setShouldUseNightMode(true) - .setShouldDisableAutoBrightness(false) - .setShouldDisableTapToWake(true) - .setShouldDisableTiltToWake(false) - .setShouldDisableTouch(true) - .setShouldMinimizeRadioUsage(false) - .setShouldMaximizeDoze(true) - .setExtraEffects(ImmutableSet.of("one", "two")) - .build(); + rule.zenDeviceEffects = + new ZenDeviceEffects.Builder() + .setShouldDisplayGrayscale(false) + .setShouldSuppressAmbientDisplay(true) + .setShouldDimWallpaper(false) + .setShouldUseNightMode(true) + .setShouldDisableAutoBrightness(false) + .setShouldDisableTapToWake(true) + .setShouldDisableTiltToWake(false) + .setShouldDisableTouch(true) + .setShouldMinimizeRadioUsage(false) + .setShouldMaximizeDoze(true) + .setShouldUseNightLight(true) + .setExtraEffects(ImmutableSet.of("one", "two")) + .build(); rule.creationTime = CREATION_TIME; rule.allowManualInvocation = ALLOW_MANUAL; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java index c6cc941ba1cd..b138c72875a6 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java @@ -54,6 +54,9 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -67,10 +70,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import platform.test.runner.parameterized.ParameterizedAndroidJunit4; -import platform.test.runner.parameterized.Parameters; - - @SmallTest @RunWith(ParameterizedAndroidJunit4.class) @TestableLooper.RunWithLooper @@ -232,6 +231,7 @@ public class ZenModeDiffTest extends UiServiceTestCase { + "mDisableTouch:true->false, " + "mMinimizeRadioUsage:true->false, " + "mMaximizeDoze:true->false, " + + "mNightLight:true->false, " + "mExtraEffects:[effect1]->[effect2]}, " + "triggerDescription:string1->string2, " + "type:2->1, " @@ -358,18 +358,21 @@ public class ZenModeDiffTest extends UiServiceTestCase { generateFieldDiffs(effects1, effects2, fieldsForDiff, expectedFrom, expectedTo); d = new ZenModeDiff.DeviceEffectsDiff(effects1, effects2); - assertThat(d.toString()).isEqualTo("ZenDeviceEffectsDiff{" - + "mGrayscale:true->false, " - + "mSuppressAmbientDisplay:true->false, " - + "mDimWallpaper:true->false, " - + "mNightMode:true->false, " - + "mDisableAutoBrightness:true->false, " - + "mDisableTapToWake:true->false, " - + "mDisableTiltToWake:true->false, " - + "mDisableTouch:true->false, " - + "mMinimizeRadioUsage:true->false, " - + "mMaximizeDoze:true->false, " - + "mExtraEffects:[effect1]->[effect2]}"); + assertThat(d.toString()) + .isEqualTo( + "ZenDeviceEffectsDiff{" + + "mGrayscale:true->false, " + + "mSuppressAmbientDisplay:true->false, " + + "mDimWallpaper:true->false, " + + "mNightMode:true->false, " + + "mDisableAutoBrightness:true->false, " + + "mDisableTapToWake:true->false, " + + "mDisableTiltToWake:true->false, " + + "mDisableTouch:true->false, " + + "mMinimizeRadioUsage:true->false, " + + "mMaximizeDoze:true->false, " + + "mNightLight:true->false, " + + "mExtraEffects:[effect1]->[effect2]}"); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 09da0156eb82..1884bbd39bb9 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -202,6 +202,9 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.xmlpull.v1.XmlPullParserException; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; @@ -221,9 +224,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import platform.test.runner.parameterized.ParameterizedAndroidJunit4; -import platform.test.runner.parameterized.Parameters; - @SmallTest @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service. @RunWith(ParameterizedAndroidJunit4.class) @@ -2759,18 +2759,20 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Test @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_fromApp_ignoresHiddenEffects() { - ZenDeviceEffects zde = new ZenDeviceEffects.Builder() - .setShouldDisplayGrayscale(true) - .setShouldSuppressAmbientDisplay(true) - .setShouldDimWallpaper(true) - .setShouldUseNightMode(true) - .setShouldDisableAutoBrightness(true) - .setShouldDisableTapToWake(true) - .setShouldDisableTiltToWake(true) - .setShouldDisableTouch(true) - .setShouldMinimizeRadioUsage(true) - .setShouldMaximizeDoze(true) - .build(); + ZenDeviceEffects zde = + new ZenDeviceEffects.Builder() + .setShouldDisplayGrayscale(true) + .setShouldSuppressAmbientDisplay(true) + .setShouldDimWallpaper(true) + .setShouldUseNightMode(true) + .setShouldDisableAutoBrightness(true) + .setShouldDisableTapToWake(true) + .setShouldDisableTiltToWake(true) + .setShouldDisableTouch(true) + .setShouldMinimizeRadioUsage(true) + .setShouldMaximizeDoze(true) + .setShouldUseNightLight(true) + .build(); String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mContext.getPackageName(), |