Merge "Add flag for adjacent TF refactor" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 7977d73..3c2ae5a 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -56,7 +56,6 @@
"android.media.tv.flags-aconfig-java",
"android.multiuser.flags-aconfig-java",
"android.net.platform.flags-aconfig-java",
- "android.net.vcn.flags-aconfig-java-export",
"android.net.wifi.flags-aconfig-java",
"android.nfc.flags-aconfig-java",
"android.os.flags-aconfig-java",
@@ -1204,25 +1203,6 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
-// VCN
-// TODO:376339506 Move the VCN code, the flag declaration and
-// java_aconfig_library to framework-connectivity-b
-aconfig_declarations {
- name: "android.net.vcn.flags-aconfig",
- package: "android.net.vcn",
- container: "com.android.tethering",
- exportable: true,
- srcs: ["core/java/android/net/vcn/*.aconfig"],
-}
-
-java_aconfig_library {
- name: "android.net.vcn.flags-aconfig-java-export",
- aconfig_declarations: "android.net.vcn.flags-aconfig",
- mode: "exported",
- min_sdk_version: "35",
- defaults: ["framework-minus-apex-aconfig-java-defaults"],
-}
-
// DevicePolicy
aconfig_declarations {
name: "device_policy_aconfig_flags",
diff --git a/Android.bp b/Android.bp
index 42028e0..a525583b8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -87,6 +87,7 @@
":framework-wifi-non-updatable-sources",
":PacProcessor-aidl-sources",
":ProxyHandler-aidl-sources",
+ ":vcn-utils-platform-sources",
":net-utils-framework-common-srcs",
// AIDL from frameworks/base/native/
@@ -314,6 +315,7 @@
":framework-telephony-sources",
":framework-wifi-annotations",
":framework-wifi-non-updatable-sources",
+ ":vcn-utils-platform-sources",
":PacProcessor-aidl-sources",
":ProxyHandler-aidl-sources",
":net-utils-framework-common-srcs",
@@ -596,7 +598,7 @@
srcs: [
"core/java/com/android/internal/util/HexDump.java",
"core/java/com/android/internal/util/WakeupMessage.java",
- "core/java/android/net/vcn/util/PersistableBundleUtils.java",
+ "packages/Vcn/framework-b/src/android/net/vcn/util/PersistableBundleUtils.java",
"telephony/java/android/telephony/Annotation.java",
],
}
diff --git a/core/api/current.txt b/core/api/current.txt
index d9338a5..1b42977 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -20809,6 +20809,7 @@
method public int describeContents();
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @FloatRange(from=0.0f, to=1.0f) public float getDefaultBrightness();
method public int getDensityDpi();
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @FloatRange(from=0.0f, to=1.0f) public float getDimBrightness();
method @NonNull public java.util.Set<java.lang.String> getDisplayCategories();
method public int getFlags();
method public int getHeight();
@@ -20830,6 +20831,7 @@
method @NonNull public android.hardware.display.VirtualDisplayConfig build();
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setBrightnessListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.display.VirtualDisplayConfig.BrightnessListener);
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDefaultBrightness(@FloatRange(from=0.0f, to=1.0f) float);
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDimBrightness(@FloatRange(from=0.0f, to=1.0f) float);
method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCategories(@NonNull java.util.Set<java.lang.String>);
method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setFlags(int);
method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setRequestedRefreshRate(@FloatRange(from=0.0f) float);
@@ -30132,128 +30134,6 @@
}
-package android.net.vcn {
-
- public final class VcnCellUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate {
- method public int getCbs();
- method public int getDun();
- method public int getIms();
- method public int getInternet();
- method public int getMms();
- method @NonNull public java.util.Set<java.lang.String> getOperatorPlmnIds();
- method public int getOpportunistic();
- method public int getRcs();
- method public int getRoaming();
- method @NonNull public java.util.Set<java.lang.Integer> getSimSpecificCarrierIds();
- }
-
- public static final class VcnCellUnderlyingNetworkTemplate.Builder {
- ctor public VcnCellUnderlyingNetworkTemplate.Builder();
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate build();
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setCbs(int);
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setDun(int);
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setIms(int);
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setInternet(int);
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMetered(int);
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinDownstreamBandwidthKbps(int, int);
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMms(int);
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOperatorPlmnIds(@NonNull java.util.Set<java.lang.String>);
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOpportunistic(int);
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setRcs(int);
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setRoaming(int);
- method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setSimSpecificCarrierIds(@NonNull java.util.Set<java.lang.Integer>);
- }
-
- public final class VcnConfig implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public java.util.Set<android.net.vcn.VcnGatewayConnectionConfig> getGatewayConnectionConfigs();
- method @NonNull public java.util.Set<java.lang.Integer> getRestrictedUnderlyingNetworkTransports();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnConfig> CREATOR;
- }
-
- public static final class VcnConfig.Builder {
- ctor public VcnConfig.Builder(@NonNull android.content.Context);
- method @NonNull public android.net.vcn.VcnConfig.Builder addGatewayConnectionConfig(@NonNull android.net.vcn.VcnGatewayConnectionConfig);
- method @NonNull public android.net.vcn.VcnConfig build();
- method @NonNull public android.net.vcn.VcnConfig.Builder setRestrictedUnderlyingNetworkTransports(@NonNull java.util.Set<java.lang.Integer>);
- }
-
- public final class VcnGatewayConnectionConfig {
- method @NonNull public int[] getExposedCapabilities();
- method @NonNull public String getGatewayConnectionName();
- method @IntRange(from=0x500) public int getMaxMtu();
- method public int getMinUdpPort4500NatTimeoutSeconds();
- method @NonNull public long[] getRetryIntervalsMillis();
- method @NonNull public java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities();
- method public boolean hasGatewayOption(int);
- method @FlaggedApi("android.net.vcn.safe_mode_config") public boolean isSafeModeEnabled();
- field @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET = -1; // 0xffffffff
- field public static final int VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY = 0; // 0x0
- }
-
- public static final class VcnGatewayConnectionConfig.Builder {
- ctor public VcnGatewayConnectionConfig.Builder(@NonNull String, @NonNull android.net.ipsec.ike.IkeTunnelConnectionParams);
- method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int);
- method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addGatewayOption(int);
- method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
- method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
- method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeGatewayOption(int);
- method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int);
- method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMinUdpPort4500NatTimeoutSeconds(@IntRange(from=0x78) int);
- method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
- method @FlaggedApi("android.net.vcn.safe_mode_config") @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setSafeModeEnabled(boolean);
- method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setVcnUnderlyingNetworkPriorities(@NonNull java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate>);
- }
-
- public class VcnManager {
- method @RequiresPermission("carrier privileges") public void clearVcnConfig(@NonNull android.os.ParcelUuid) throws java.io.IOException;
- method @NonNull public java.util.List<android.os.ParcelUuid> getConfiguredSubscriptionGroups();
- method public void registerVcnStatusCallback(@NonNull android.os.ParcelUuid, @NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnStatusCallback);
- method @RequiresPermission("carrier privileges") public void setVcnConfig(@NonNull android.os.ParcelUuid, @NonNull android.net.vcn.VcnConfig) throws java.io.IOException;
- method public void unregisterVcnStatusCallback(@NonNull android.net.vcn.VcnManager.VcnStatusCallback);
- field public static final int VCN_ERROR_CODE_CONFIG_ERROR = 1; // 0x1
- field public static final int VCN_ERROR_CODE_INTERNAL_ERROR = 0; // 0x0
- field public static final int VCN_ERROR_CODE_NETWORK_ERROR = 2; // 0x2
- field public static final int VCN_STATUS_CODE_ACTIVE = 2; // 0x2
- field public static final int VCN_STATUS_CODE_INACTIVE = 1; // 0x1
- field public static final int VCN_STATUS_CODE_NOT_CONFIGURED = 0; // 0x0
- field public static final int VCN_STATUS_CODE_SAFE_MODE = 3; // 0x3
- }
-
- public abstract static class VcnManager.VcnStatusCallback {
- ctor public VcnManager.VcnStatusCallback();
- method public abstract void onGatewayConnectionError(@NonNull String, int, @Nullable Throwable);
- method public abstract void onStatusChanged(int);
- }
-
- public abstract class VcnUnderlyingNetworkTemplate {
- method public int getMetered();
- method public int getMinEntryDownstreamBandwidthKbps();
- method public int getMinEntryUpstreamBandwidthKbps();
- method public int getMinExitDownstreamBandwidthKbps();
- method public int getMinExitUpstreamBandwidthKbps();
- field public static final int MATCH_ANY = 0; // 0x0
- field public static final int MATCH_FORBIDDEN = 2; // 0x2
- field public static final int MATCH_REQUIRED = 1; // 0x1
- }
-
- public final class VcnWifiUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate {
- method @NonNull public java.util.Set<java.lang.String> getSsids();
- }
-
- public static final class VcnWifiUnderlyingNetworkTemplate.Builder {
- ctor public VcnWifiUnderlyingNetworkTemplate.Builder();
- method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate build();
- method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMetered(int);
- method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMinDownstreamBandwidthKbps(int, int);
- method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
- method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setSsids(@NonNull java.util.Set<java.lang.String>);
- }
-
-}
-
package android.opengl {
public class EGL14 {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 804210f..fe6e995 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -265,10 +265,6 @@
package android.net {
- @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public final class ConnectivityFrameworkInitializerBaklava {
- method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static void registerServiceWrappers();
- }
-
public class LocalSocket implements java.io.Closeable {
ctor public LocalSocket(@NonNull java.io.FileDescriptor);
}
@@ -328,25 +324,6 @@
}
-package android.net.vcn {
-
- @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public final class VcnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
- method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public int describeContents();
- method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public long getApplicableRedactions();
- method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public int getMinUdpPort4500NatTimeoutSeconds();
- method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.TransportInfo makeCopy(long);
- method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public void writeToParcel(@NonNull android.os.Parcel, int);
- field @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnTransportInfo> CREATOR;
- }
-
- @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static final class VcnTransportInfo.Builder {
- ctor @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public VcnTransportInfo.Builder();
- method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.vcn.VcnTransportInfo build();
- method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.vcn.VcnTransportInfo.Builder setMinUdpPort4500NatTimeoutSeconds(@IntRange(from=0x78) int);
- }
-
-}
-
package android.net.wifi {
public final class WifiMigration {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 612a48a..04aca54 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10614,28 +10614,6 @@
}
-package android.net.vcn {
-
- public class VcnManager {
- method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
- method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.vcn.VcnNetworkPolicyResult applyVcnNetworkPolicy(@NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties);
- method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void removeVcnNetworkPolicyChangeListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
- }
-
- public static interface VcnManager.VcnNetworkPolicyChangeListener {
- method public void onPolicyChanged();
- }
-
- public final class VcnNetworkPolicyResult implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
- method public boolean isTeardownRequested();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnNetworkPolicyResult> CREATOR;
- }
-
-}
-
package android.net.wifi {
public final class WifiKeystore {
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index eceaa8f..7257055 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -66,6 +66,7 @@
private final DisplayCutout mDisplayCutout;
private final boolean mIgnoreActivitySizeRestrictions;
private final float mDefaultBrightness;
+ private final float mDimBrightness;
private final IBrightnessListener mBrightnessListener;
private VirtualDisplayConfig(
@@ -84,6 +85,7 @@
@Nullable DisplayCutout displayCutout,
boolean ignoreActivitySizeRestrictions,
@FloatRange(from = 0.0f, to = 1.0f) float defaultBrightness,
+ @FloatRange(from = 0.0f, to = 1.0f) float dimBrightness,
IBrightnessListener brightnessListener) {
mName = name;
mWidth = width;
@@ -100,6 +102,7 @@
mDisplayCutout = displayCutout;
mIgnoreActivitySizeRestrictions = ignoreActivitySizeRestrictions;
mDefaultBrightness = defaultBrightness;
+ mDimBrightness = dimBrightness;
mBrightnessListener = brightnessListener;
}
@@ -180,6 +183,19 @@
}
/**
+ * Returns the dim brightness of the display.
+ *
+ * <p>Value of {@code 0.0} indicates the minimum supported brightness and value of {@code 1.0}
+ * indicates the maximum supported brightness.</p>
+ *
+ * @see Builder#setDimBrightness(float)
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ public @FloatRange(from = 0.0f, to = 1.0f) float getDimBrightness() {
+ return mDimBrightness;
+ }
+
+ /**
* Returns the listener to get notified about changes in the display brightness.
* @hide
*/
@@ -278,6 +294,7 @@
DisplayCutout.ParcelableWrapper.writeCutoutToParcel(mDisplayCutout, dest, flags);
dest.writeBoolean(mIgnoreActivitySizeRestrictions);
dest.writeFloat(mDefaultBrightness);
+ dest.writeFloat(mDimBrightness);
dest.writeStrongBinder(mBrightnessListener != null ? mBrightnessListener.asBinder() : null);
}
@@ -308,8 +325,8 @@
&& mIgnoreActivitySizeRestrictions == that.mIgnoreActivitySizeRestrictions
&& Objects.equals(mDisplayCutout, that.mDisplayCutout)
&& mDefaultBrightness == that.mDefaultBrightness
+ && mDimBrightness == that.mDimBrightness
&& Objects.equals(mBrightnessListener, that.mBrightnessListener);
-
}
@Override
@@ -318,7 +335,8 @@
mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
mRequestedRefreshRate, mIsHomeSupported, mDisplayCutout,
- mIgnoreActivitySizeRestrictions, mDefaultBrightness, mBrightnessListener);
+ mIgnoreActivitySizeRestrictions, mDefaultBrightness, mDimBrightness,
+ mBrightnessListener);
return hashCode;
}
@@ -341,6 +359,7 @@
+ " mDisplayCutout=" + mDisplayCutout
+ " mIgnoreActivitySizeRestrictions=" + mIgnoreActivitySizeRestrictions
+ " mDefaultBrightness=" + mDefaultBrightness
+ + " mDimBrightness=" + mDimBrightness
+ ")";
}
@@ -360,8 +379,8 @@
mDisplayCutout = DisplayCutout.ParcelableWrapper.readCutoutFromParcel(in);
mIgnoreActivitySizeRestrictions = in.readBoolean();
mDefaultBrightness = in.readFloat();
+ mDimBrightness = in.readFloat();
mBrightnessListener = IBrightnessListener.Stub.asInterface(in.readStrongBinder());
-
}
/**
@@ -432,6 +451,7 @@
private DisplayCutout mDisplayCutout = null;
private boolean mIgnoreActivitySizeRestrictions = false;
private float mDefaultBrightness = 0.0f;
+ private float mDimBrightness = PowerManager.BRIGHTNESS_INVALID;
private IBrightnessListener mBrightnessListener = null;
/**
@@ -635,14 +655,14 @@
*
* <p>If unset, defaults to {@code 0.0}</p>
*
+ * @throws IllegalArgumentException if the brightness is outside the valid range [0.0, 1.0]
* @see android.view.View#setKeepScreenOn(boolean)
* @see #setBrightnessListener(Executor, BrightnessListener)
*/
@FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
@NonNull
public Builder setDefaultBrightness(@FloatRange(from = 0.0f, to = 1.0f) float brightness) {
- if (brightness < PowerManager.BRIGHTNESS_MIN
- || brightness > PowerManager.BRIGHTNESS_MAX) {
+ if (!isValidBrightness(brightness)) {
throw new IllegalArgumentException(
"Virtual display default brightness must be in range [0.0, 1.0]");
}
@@ -651,6 +671,33 @@
}
/**
+ * Sets the dim brightness of the display.
+ *
+ * <p>The system will use this brightness value whenever the display should be dim, i.e.
+ * it is powered on and dimmed due to user activity or app activity.</p>
+ *
+ * <p>Value of {@code 0.0} indicates the minimum supported brightness and value of
+ * {@code 1.0} indicates the maximum supported brightness.</p>
+ *
+ * <p>If set, the default brightness must also be set to a value greater or equal to the
+ * dim brightness. If unset, defaults to the system default.</p>
+ *
+ * @throws IllegalArgumentException if the brightness is outside the valid range [0.0, 1.0]
+ * @see Builder#setDefaultBrightness(float)
+ * @see #setBrightnessListener(Executor, BrightnessListener)
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @NonNull
+ public Builder setDimBrightness(@FloatRange(from = 0.0f, to = 1.0f) float brightness) {
+ if (!isValidBrightness(brightness)) {
+ throw new IllegalArgumentException(
+ "Virtual display dim brightness must be in range [0.0, 1.0]");
+ }
+ mDimBrightness = brightness;
+ return this;
+ }
+
+ /**
* Sets the listener to get notified about changes in the display brightness.
*
* @param executor The executor where the callback is executed on.
@@ -666,11 +713,23 @@
return this;
}
+ private boolean isValidBrightness(float brightness) {
+ return !Float.isNaN(brightness) && PowerManager.BRIGHTNESS_MIN <= brightness
+ && brightness <= PowerManager.BRIGHTNESS_MAX;
+ }
+
/**
* Builds the {@link VirtualDisplayConfig} instance.
+ *
+ * @throws IllegalArgumentException if the dim brightness is set to a value greater than
+ * the default brightness.
*/
@NonNull
public VirtualDisplayConfig build() {
+ if (isValidBrightness(mDimBrightness) && mDimBrightness > mDefaultBrightness) {
+ throw new IllegalArgumentException(
+ "The dim brightness must not be greater than the default brightness");
+ }
return new VirtualDisplayConfig(
mName,
mWidth,
@@ -687,6 +746,7 @@
mDisplayCutout,
mIgnoreActivitySizeRestrictions,
mDefaultBrightness,
+ mDimBrightness,
mBrightnessListener);
}
}
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index e85e580..4cac4dee 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -55,7 +55,7 @@
void goToSleepWithDisplayId(int displayId, long time, int reason, int flags);
@UnsupportedAppUsage(maxTargetSdk = 28)
void nap(long time);
- float getBrightnessConstraint(int constraint);
+ float getBrightnessConstraint(int displayId, int constraint);
@UnsupportedAppUsage
boolean isInteractive();
boolean isDisplayInteractive(int displayId);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 9e7bf47..cd48f08 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1266,9 +1266,17 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public float getBrightnessConstraint(int constraint) {
+ public float getBrightnessConstraint(@BrightnessConstraint int constraint) {
+ return getBrightnessConstraint(Display.DEFAULT_DISPLAY, constraint);
+ }
+
+ /**
+ * Gets a float screen brightness setting for a specific display.
+ * @hide
+ */
+ public float getBrightnessConstraint(int displayId, @BrightnessConstraint int constraint) {
try {
- return mService.getBrightnessConstraint(constraint);
+ return mService.getBrightnessConstraint(displayId, constraint);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 8b6458a..4307884 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -361,6 +361,12 @@
public float brightnessDefault;
/**
+ * The current dim brightness of the display. Value between 0.0 and 1.0,
+ * derived from the configuration of the display device of this logical display.
+ */
+ public float brightnessDim;
+
+ /**
* The {@link RoundedCorners} if present, otherwise {@code null}.
*/
@Nullable
@@ -479,6 +485,7 @@
&& brightnessMinimum == other.brightnessMinimum
&& brightnessMaximum == other.brightnessMaximum
&& brightnessDefault == other.brightnessDefault
+ && brightnessDim == other.brightnessDim
&& Objects.equals(roundedCorners, other.roundedCorners)
&& installOrientation == other.installOrientation
&& Objects.equals(displayShape, other.displayShape)
@@ -546,6 +553,7 @@
brightnessMinimum = other.brightnessMinimum;
brightnessMaximum = other.brightnessMaximum;
brightnessDefault = other.brightnessDefault;
+ brightnessDim = other.brightnessDim;
roundedCorners = other.roundedCorners;
installOrientation = other.installOrientation;
displayShape = other.displayShape;
@@ -620,6 +628,7 @@
brightnessMinimum = source.readFloat();
brightnessMaximum = source.readFloat();
brightnessDefault = source.readFloat();
+ brightnessDim = source.readFloat();
roundedCorners = source.readTypedObject(RoundedCorners.CREATOR);
int numUserDisabledFormats = source.readInt();
userDisabledHdrTypes = new int[numUserDisabledFormats];
@@ -696,6 +705,7 @@
dest.writeFloat(brightnessMinimum);
dest.writeFloat(brightnessMaximum);
dest.writeFloat(brightnessDefault);
+ dest.writeFloat(brightnessDim);
dest.writeTypedObject(roundedCorners, flags);
dest.writeInt(userDisabledHdrTypes.length);
for (int i = 0; i < userDisabledHdrTypes.length; i++) {
@@ -994,6 +1004,8 @@
sb.append(brightnessMaximum);
sb.append(", brightnessDefault ");
sb.append(brightnessDefault);
+ sb.append(", brightnessDim ");
+ sb.append(brightnessDim);
sb.append(", installOrientation ");
sb.append(Surface.rotationToString(installOrientation));
sb.append(", layoutLimitedRefreshRate ");
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 02c7901..049189f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -28277,7 +28277,7 @@
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
- if (mParent != null) {
+ if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedBasicEnvelopeEffect.java b/core/java/com/android/internal/vibrator/persistence/SerializedBasicEnvelopeEffect.java
new file mode 100644
index 0000000..a090c7a
--- /dev/null
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedBasicEnvelopeEffect.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.vibrator.persistence;
+
+import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_DURATION_MS;
+import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_INITIAL_SHARPNESS;
+import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_INTENSITY;
+import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_SHARPNESS;
+import static com.android.internal.vibrator.persistence.XmlConstants.NAMESPACE;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_BASIC_ENVELOPE_EFFECT;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_CONTROL_POINT;
+
+import android.annotation.NonNull;
+import android.os.VibrationEffect;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Serialized representation of a basic envelope effect created via
+ * {@link VibrationEffect.BasicEnvelopeBuilder}.
+ *
+ * @hide
+ */
+final class SerializedBasicEnvelopeEffect implements SerializedComposedEffect.SerializedSegment {
+ private final BasicControlPoint[] mControlPoints;
+ private final float mInitialSharpness;
+
+ SerializedBasicEnvelopeEffect(BasicControlPoint[] controlPoints, float initialSharpness) {
+ mControlPoints = controlPoints;
+ mInitialSharpness = initialSharpness;
+ }
+
+ @Override
+ public void write(@NonNull TypedXmlSerializer serializer) throws IOException {
+ serializer.startTag(NAMESPACE, TAG_BASIC_ENVELOPE_EFFECT);
+
+ if (!Float.isNaN(mInitialSharpness)) {
+ serializer.attributeFloat(NAMESPACE, ATTRIBUTE_INITIAL_SHARPNESS, mInitialSharpness);
+ }
+
+ for (BasicControlPoint point : mControlPoints) {
+ serializer.startTag(NAMESPACE, TAG_CONTROL_POINT);
+ serializer.attributeFloat(NAMESPACE, ATTRIBUTE_INTENSITY, point.mIntensity);
+ serializer.attributeFloat(NAMESPACE, ATTRIBUTE_SHARPNESS, point.mSharpness);
+ serializer.attributeLong(NAMESPACE, ATTRIBUTE_DURATION_MS, point.mDurationMs);
+ serializer.endTag(NAMESPACE, TAG_CONTROL_POINT);
+ }
+
+ serializer.endTag(NAMESPACE, TAG_BASIC_ENVELOPE_EFFECT);
+ }
+
+ @Override
+ public void deserializeIntoComposition(@NonNull VibrationEffect.Composition composition) {
+ VibrationEffect.BasicEnvelopeBuilder builder = new VibrationEffect.BasicEnvelopeBuilder();
+
+ if (!Float.isNaN(mInitialSharpness)) {
+ builder.setInitialSharpness(mInitialSharpness);
+ }
+
+ for (BasicControlPoint point : mControlPoints) {
+ builder.addControlPoint(point.mIntensity, point.mSharpness, point.mDurationMs);
+ }
+ composition.addEffect(builder.build());
+ }
+
+ @Override
+ public String toString() {
+ return "SerializedBasicEnvelopeEffect{"
+ + "initialSharpness=" + (Float.isNaN(mInitialSharpness) ? "" : mInitialSharpness)
+ + ", controlPoints=" + Arrays.toString(mControlPoints)
+ + '}';
+ }
+
+ static final class Builder {
+ private final List<BasicControlPoint> mControlPoints;
+ private float mInitialSharpness = Float.NaN;
+
+ Builder() {
+ mControlPoints = new ArrayList<>();
+ }
+
+ void setInitialSharpness(float sharpness) {
+ mInitialSharpness = sharpness;
+ }
+
+ void addControlPoint(float intensity, float sharpness, long durationMs) {
+ mControlPoints.add(new BasicControlPoint(intensity, sharpness, durationMs));
+ }
+
+ SerializedBasicEnvelopeEffect build() {
+ return new SerializedBasicEnvelopeEffect(
+ mControlPoints.toArray(new BasicControlPoint[0]), mInitialSharpness);
+ }
+ }
+
+ /** Parser implementation for {@link SerializedBasicEnvelopeEffect}. */
+ static final class Parser {
+
+ @NonNull
+ static SerializedBasicEnvelopeEffect parseNext(@NonNull TypedXmlPullParser parser,
+ @XmlConstants.Flags int flags) throws XmlParserException, IOException {
+ XmlValidator.checkStartTag(parser, TAG_BASIC_ENVELOPE_EFFECT);
+ XmlValidator.checkTagHasNoUnexpectedAttributes(parser, ATTRIBUTE_INITIAL_SHARPNESS);
+
+ Builder builder = new Builder();
+ builder.setInitialSharpness(
+ XmlReader.readAttributeFloatInRange(parser, ATTRIBUTE_INITIAL_SHARPNESS, 0f, 1f,
+ Float.NaN));
+
+ int outerDepth = parser.getDepth();
+
+ // Read all nested tags
+ while (XmlReader.readNextTagWithin(parser, outerDepth)) {
+ parseControlPoint(parser, builder);
+ // Consume tag
+ XmlReader.readEndTag(parser);
+ }
+
+ // Check schema assertions about <basic-envelope-effect>
+ XmlValidator.checkParserCondition(!builder.mControlPoints.isEmpty(),
+ "Expected tag %s to have at least one control point",
+ TAG_BASIC_ENVELOPE_EFFECT);
+ XmlValidator.checkParserCondition(builder.mControlPoints.getLast().mIntensity == 0,
+ "Basic envelope effects must end at a zero intensity control point");
+
+ return builder.build();
+ }
+
+ private static void parseControlPoint(TypedXmlPullParser parser, Builder builder)
+ throws XmlParserException {
+ XmlValidator.checkStartTag(parser, TAG_CONTROL_POINT);
+ XmlValidator.checkTagHasNoUnexpectedAttributes(
+ parser, ATTRIBUTE_DURATION_MS, ATTRIBUTE_INTENSITY,
+ ATTRIBUTE_SHARPNESS);
+ float intensity = XmlReader.readAttributeFloatInRange(parser, ATTRIBUTE_INTENSITY, 0,
+ 1);
+ float sharpness = XmlReader.readAttributeFloatInRange(parser, ATTRIBUTE_SHARPNESS, 0,
+ 1);
+ long durationMs = XmlReader.readAttributePositiveLong(parser, ATTRIBUTE_DURATION_MS);
+
+ builder.addControlPoint(intensity, sharpness, durationMs);
+ }
+ }
+
+ private static final class BasicControlPoint {
+ private final float mIntensity;
+ private final float mSharpness;
+ private final long mDurationMs;
+
+ BasicControlPoint(float intensity, float sharpness, long durationMs) {
+ mIntensity = intensity;
+ mSharpness = sharpness;
+ mDurationMs = durationMs;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(Locale.ROOT, "(%.2f, %.2f, %dms)", mIntensity, mSharpness,
+ mDurationMs);
+ }
+ }
+}
+
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedWaveformEnvelopeEffect.java b/core/java/com/android/internal/vibrator/persistence/SerializedWaveformEnvelopeEffect.java
new file mode 100644
index 0000000..6a89343
--- /dev/null
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedWaveformEnvelopeEffect.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.vibrator.persistence;
+
+import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_AMPLITUDE;
+import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_DURATION_MS;
+import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_FREQUENCY_HZ;
+import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_INITIAL_FREQUENCY_HZ;
+import static com.android.internal.vibrator.persistence.XmlConstants.NAMESPACE;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_CONTROL_POINT;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_ENVELOPE_EFFECT;
+
+import android.annotation.NonNull;
+import android.os.VibrationEffect;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Serialized representation of a waveform envelope effect created via
+ * {@link VibrationEffect.WaveformEnvelopeBuilder}.
+ *
+ * @hide
+ */
+final class SerializedWaveformEnvelopeEffect implements SerializedComposedEffect.SerializedSegment {
+
+ private final WaveformControlPoint[] mControlPoints;
+ private final float mInitialFrequency;
+
+ SerializedWaveformEnvelopeEffect(WaveformControlPoint[] controlPoints, float initialFrequency) {
+ mControlPoints = controlPoints;
+ mInitialFrequency = initialFrequency;
+ }
+
+ @Override
+ public void write(@NonNull TypedXmlSerializer serializer) throws IOException {
+ serializer.startTag(NAMESPACE, TAG_WAVEFORM_ENVELOPE_EFFECT);
+
+ if (!Float.isNaN(mInitialFrequency)) {
+ serializer.attributeFloat(NAMESPACE, ATTRIBUTE_INITIAL_FREQUENCY_HZ, mInitialFrequency);
+ }
+
+ for (WaveformControlPoint point : mControlPoints) {
+ serializer.startTag(NAMESPACE, TAG_CONTROL_POINT);
+ serializer.attributeFloat(NAMESPACE, ATTRIBUTE_AMPLITUDE, point.mAmplitude);
+ serializer.attributeFloat(NAMESPACE, ATTRIBUTE_FREQUENCY_HZ, point.mFrequency);
+ serializer.attributeLong(NAMESPACE, ATTRIBUTE_DURATION_MS, point.mDurationMs);
+ serializer.endTag(NAMESPACE, TAG_CONTROL_POINT);
+ }
+
+ serializer.endTag(NAMESPACE, TAG_WAVEFORM_ENVELOPE_EFFECT);
+ }
+
+ @Override
+ public void deserializeIntoComposition(@NonNull VibrationEffect.Composition composition) {
+ VibrationEffect.WaveformEnvelopeBuilder builder =
+ new VibrationEffect.WaveformEnvelopeBuilder();
+
+ if (!Float.isNaN(mInitialFrequency)) {
+ builder.setInitialFrequencyHz(mInitialFrequency);
+ }
+
+ for (WaveformControlPoint point : mControlPoints) {
+ builder.addControlPoint(point.mAmplitude, point.mFrequency, point.mDurationMs);
+ }
+ composition.addEffect(builder.build());
+ }
+
+ @Override
+ public String toString() {
+ return "SerializedWaveformEnvelopeEffect{"
+ + "InitialFrequency=" + (Float.isNaN(mInitialFrequency) ? "" : mInitialFrequency)
+ + ", controlPoints=" + Arrays.toString(mControlPoints)
+ + '}';
+ }
+
+ static final class Builder {
+ private final List<WaveformControlPoint> mControlPoints;
+ private float mInitialFrequencyHz = Float.NaN;
+
+ Builder() {
+ mControlPoints = new ArrayList<>();
+ }
+
+ void setInitialFrequencyHz(float frequencyHz) {
+ mInitialFrequencyHz = frequencyHz;
+ }
+
+ void addControlPoint(float amplitude, float frequencyHz, long durationMs) {
+ mControlPoints.add(new WaveformControlPoint(amplitude, frequencyHz, durationMs));
+ }
+
+ SerializedWaveformEnvelopeEffect build() {
+ return new SerializedWaveformEnvelopeEffect(
+ mControlPoints.toArray(new WaveformControlPoint[0]), mInitialFrequencyHz);
+ }
+ }
+
+ /** Parser implementation for {@link SerializedWaveformEnvelopeEffect}. */
+ static final class Parser {
+
+ @NonNull
+ static SerializedWaveformEnvelopeEffect parseNext(@NonNull TypedXmlPullParser parser,
+ @XmlConstants.Flags int flags) throws XmlParserException, IOException {
+ XmlValidator.checkStartTag(parser, TAG_WAVEFORM_ENVELOPE_EFFECT);
+ XmlValidator.checkTagHasNoUnexpectedAttributes(parser, ATTRIBUTE_INITIAL_FREQUENCY_HZ);
+
+ Builder builder = new Builder();
+ builder.setInitialFrequencyHz(
+ XmlReader.readAttributePositiveFloat(parser, ATTRIBUTE_INITIAL_FREQUENCY_HZ,
+ Float.NaN));
+
+ int outerDepth = parser.getDepth();
+
+ while (XmlReader.readNextTagWithin(parser, outerDepth)) {
+ parseControlPoint(parser, builder);
+ // Consume tag
+ XmlReader.readEndTag(parser);
+ }
+
+ // Check schema assertions about <waveform-envelope-effect>
+ XmlValidator.checkParserCondition(!builder.mControlPoints.isEmpty(),
+ "Expected tag %s to have at least one control point",
+ TAG_WAVEFORM_ENVELOPE_EFFECT);
+
+ return builder.build();
+ }
+
+ private static void parseControlPoint(TypedXmlPullParser parser, Builder builder)
+ throws XmlParserException {
+ XmlValidator.checkStartTag(parser, TAG_CONTROL_POINT);
+ XmlValidator.checkTagHasNoUnexpectedAttributes(
+ parser, ATTRIBUTE_DURATION_MS, ATTRIBUTE_AMPLITUDE,
+ ATTRIBUTE_FREQUENCY_HZ);
+ float amplitude = XmlReader.readAttributeFloatInRange(parser, ATTRIBUTE_AMPLITUDE, 0,
+ 1);
+ float frequencyHz = XmlReader.readAttributePositiveFloat(parser,
+ ATTRIBUTE_FREQUENCY_HZ);
+ long durationMs = XmlReader.readAttributePositiveLong(parser, ATTRIBUTE_DURATION_MS);
+
+ builder.addControlPoint(amplitude, frequencyHz, durationMs);
+ }
+ }
+
+ private static final class WaveformControlPoint {
+ private final float mAmplitude;
+ private final float mFrequency;
+ private final long mDurationMs;
+
+ WaveformControlPoint(float amplitude, float frequency, long durationMs) {
+ mAmplitude = amplitude;
+ mFrequency = frequency;
+ mDurationMs = durationMs;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(Locale.ROOT, "(%.2f, %.2f, %dms)", mAmplitude, mFrequency,
+ mDurationMs);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java
index a9fbcaf..314bfe4 100644
--- a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java
+++ b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java
@@ -16,11 +16,13 @@
package com.android.internal.vibrator.persistence;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_BASIC_ENVELOPE_EFFECT;
import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PREDEFINED_EFFECT;
import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PRIMITIVE_EFFECT;
import static com.android.internal.vibrator.persistence.XmlConstants.TAG_VENDOR_EFFECT;
import static com.android.internal.vibrator.persistence.XmlConstants.TAG_VIBRATION_EFFECT;
import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_EFFECT;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_ENVELOPE_EFFECT;
import android.annotation.NonNull;
import android.os.VibrationEffect;
@@ -92,6 +94,32 @@
* }
* </pre>
*
+ * * Waveform Envelope effects
+ *
+ * <pre>
+ * {@code
+ * <vibration-effect>
+ * <waveform-envelope-effect initialFrequencyHz="20.0">
+ * <control-point amplitude="0.2" frequencyHz="80.0" durationMs="50" />
+ * <control-point amplitude="0.5" frequencyHz="150.0" durationMs="50" />
+ * </envelope-effect>
+ * </vibration-effect>
+ * }
+ * </pre>
+ *
+ * * Basic Envelope effects
+ *
+ * <pre>
+ * {@code
+ * <vibration-effect>
+ * <basic-envelope-effect initialSharpness="0.3">
+ * <control-point intensity="0.2" sharpness="0.5" durationMs="50" />
+ * <control-point intensity="0.0" sharpness="1.0" durationMs="50" />
+ * </envelope-effect>
+ * </vibration-effect>
+ * }
+ * </pre>
+ *
* @hide
*/
public class VibrationEffectXmlParser {
@@ -151,6 +179,18 @@
serializedVibration = new SerializedComposedEffect(
SerializedAmplitudeStepWaveform.Parser.parseNext(parser));
break;
+ case TAG_WAVEFORM_ENVELOPE_EFFECT:
+ if (Flags.normalizedPwleEffects()) {
+ serializedVibration = new SerializedComposedEffect(
+ SerializedWaveformEnvelopeEffect.Parser.parseNext(parser, flags));
+ break;
+ } // else fall through
+ case TAG_BASIC_ENVELOPE_EFFECT:
+ if (Flags.normalizedPwleEffects()) {
+ serializedVibration = new SerializedComposedEffect(
+ SerializedBasicEnvelopeEffect.Parser.parseNext(parser, flags));
+ break;
+ } // else fall through
default:
throw new XmlParserException("Unexpected tag " + parser.getName()
+ " in vibration tag " + vibrationTagName);
diff --git a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java
index cb834a5..ebe3434 100644
--- a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java
+++ b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java
@@ -19,9 +19,11 @@
import android.annotation.NonNull;
import android.os.PersistableBundle;
import android.os.VibrationEffect;
+import android.os.vibrator.BasicPwleSegment;
import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.PwleSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
@@ -45,6 +47,8 @@
* <li>A composition created exclusively via
* {@link VibrationEffect.Composition#addPrimitive(int, float, int)}
* <li>{@link VibrationEffect#createVendorEffect(PersistableBundle)}
+ * <li>{@link VibrationEffect.WaveformEnvelopeBuilder}
+ * <li>{@link VibrationEffect.BasicEnvelopeBuilder}
* </ul>
*
* @hide
@@ -77,6 +81,12 @@
if (firstSegment instanceof PrimitiveSegment) {
return serializePrimitiveEffect(composed);
}
+ if (Flags.normalizedPwleEffects() && firstSegment instanceof PwleSegment) {
+ return serializeWaveformEnvelopeEffect(composed);
+ }
+ if (Flags.normalizedPwleEffects() && firstSegment instanceof BasicPwleSegment) {
+ return serializeBasicEnvelopeEffect(composed);
+ }
return serializeWaveformEffect(composed);
}
@@ -110,6 +120,53 @@
return new SerializedComposedEffect(primitives);
}
+ private static SerializedComposedEffect serializeWaveformEnvelopeEffect(
+ VibrationEffect.Composed effect) throws XmlSerializerException {
+ SerializedWaveformEnvelopeEffect.Builder builder =
+ new SerializedWaveformEnvelopeEffect.Builder();
+ List<VibrationEffectSegment> segments = effect.getSegments();
+ XmlValidator.checkSerializerCondition(effect.getRepeatIndex() == -1,
+ "Unsupported repeating waveform envelope effect %s", effect);
+ for (int i = 0; i < segments.size(); i++) {
+ XmlValidator.checkSerializerCondition(segments.get(i) instanceof PwleSegment,
+ "Unsupported segment for waveform envelope effect %s", segments.get(i));
+ PwleSegment segment = (PwleSegment) segments.get(i);
+
+ if (i == 0 && segment.getStartFrequencyHz() != segment.getEndFrequencyHz()) {
+ // Initial frequency explicitly defined.
+ builder.setInitialFrequencyHz(segment.getStartFrequencyHz());
+ }
+
+ builder.addControlPoint(segment.getEndAmplitude(), segment.getEndFrequencyHz(),
+ segment.getDuration());
+ }
+
+ return new SerializedComposedEffect(builder.build());
+ }
+
+ private static SerializedComposedEffect serializeBasicEnvelopeEffect(
+ VibrationEffect.Composed effect) throws XmlSerializerException {
+ SerializedBasicEnvelopeEffect.Builder builder = new SerializedBasicEnvelopeEffect.Builder();
+ List<VibrationEffectSegment> segments = effect.getSegments();
+ XmlValidator.checkSerializerCondition(effect.getRepeatIndex() == -1,
+ "Unsupported repeating basic envelope effect %s", effect);
+ for (int i = 0; i < segments.size(); i++) {
+ XmlValidator.checkSerializerCondition(segments.get(i) instanceof BasicPwleSegment,
+ "Unsupported segment for basic envelope effect %s", segments.get(i));
+ BasicPwleSegment segment = (BasicPwleSegment) segments.get(i);
+
+ if (i == 0 && segment.getStartSharpness() != segment.getEndSharpness()) {
+ // Initial sharpness explicitly defined.
+ builder.setInitialSharpness(segment.getStartSharpness());
+ }
+
+ builder.addControlPoint(segment.getEndIntensity(), segment.getEndSharpness(),
+ segment.getDuration());
+ }
+
+ return new SerializedComposedEffect(builder.build());
+ }
+
private static SerializedComposedEffect serializeWaveformEffect(
VibrationEffect.Composed effect) throws XmlSerializerException {
SerializedAmplitudeStepWaveform.Builder serializedWaveformBuilder =
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlConstants.java b/core/java/com/android/internal/vibrator/persistence/XmlConstants.java
index 4122215..df262cf 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlConstants.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlConstants.java
@@ -42,14 +42,22 @@
public static final String TAG_PREDEFINED_EFFECT = "predefined-effect";
public static final String TAG_PRIMITIVE_EFFECT = "primitive-effect";
public static final String TAG_VENDOR_EFFECT = "vendor-effect";
+ public static final String TAG_WAVEFORM_ENVELOPE_EFFECT = "waveform-envelope-effect";
+ public static final String TAG_BASIC_ENVELOPE_EFFECT = "basic-envelope-effect";
public static final String TAG_WAVEFORM_EFFECT = "waveform-effect";
public static final String TAG_WAVEFORM_ENTRY = "waveform-entry";
public static final String TAG_REPEATING = "repeating";
+ public static final String TAG_CONTROL_POINT = "control-point";
public static final String ATTRIBUTE_NAME = "name";
public static final String ATTRIBUTE_FALLBACK = "fallback";
public static final String ATTRIBUTE_DURATION_MS = "durationMs";
public static final String ATTRIBUTE_AMPLITUDE = "amplitude";
+ public static final String ATTRIBUTE_FREQUENCY_HZ = "frequencyHz";
+ public static final String ATTRIBUTE_INITIAL_FREQUENCY_HZ = "initialFrequencyHz";
+ public static final String ATTRIBUTE_INTENSITY = "intensity";
+ public static final String ATTRIBUTE_SHARPNESS = "sharpness";
+ public static final String ATTRIBUTE_INITIAL_SHARPNESS = "initialSharpness";
public static final String ATTRIBUTE_SCALE = "scale";
public static final String ATTRIBUTE_DELAY_MS = "delayMs";
public static final String ATTRIBUTE_DELAY_TYPE = "delayType";
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlReader.java b/core/java/com/android/internal/vibrator/persistence/XmlReader.java
index 0ac6fef..1c4a783 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlReader.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlReader.java
@@ -221,12 +221,63 @@
if (parser.getAttributeIndex(NAMESPACE, attrName) < 0) {
return defaultValue;
}
+
+ return readAttributeFloatInRange(parser, attrName, lowerInclusive, upperInclusive);
+ }
+
+ /**
+ * Read attribute from current tag as a float within given inclusive range.
+ */
+ public static float readAttributeFloatInRange(
+ TypedXmlPullParser parser, String attrName, float lowerInclusive,
+ float upperInclusive) throws XmlParserException {
String tagName = parser.getName();
float value = readAttributeFloat(parser, attrName);
XmlValidator.checkParserCondition(value >= lowerInclusive && value <= upperInclusive,
- "Unexpected %s = %f in tag %s, expected %s in [%f, %f]",
- attrName, value, tagName, attrName, lowerInclusive, upperInclusive);
+ "Unexpected %s = %f in tag %s, expected %s in [%f, %f]", attrName, value, tagName,
+ attrName, lowerInclusive, upperInclusive);
+ return value;
+ }
+
+ /**
+ * Read attribute from current tag as a positive float, returning default value if attribute
+ * is missing.
+ */
+ public static float readAttributePositiveFloat(TypedXmlPullParser parser, String attrName,
+ float defaultValue) throws XmlParserException {
+ if (parser.getAttributeIndex(NAMESPACE, attrName) < 0) {
+ return defaultValue;
+ }
+
+ return readAttributePositiveFloat(parser, attrName);
+ }
+
+ /**
+ * Read attribute from current tag as a positive float.
+ */
+ public static float readAttributePositiveFloat(TypedXmlPullParser parser, String attrName)
+ throws XmlParserException {
+ String tagName = parser.getName();
+ float value = readAttributeFloat(parser, attrName);
+
+ XmlValidator.checkParserCondition(value > 0,
+ "Unexpected %s = %d in tag %s, expected %s > 0", attrName, value, tagName,
+ attrName);
+ return value;
+ }
+
+ /**
+ * Read attribute from current tag as a positive long.
+ */
+ public static long readAttributePositiveLong(TypedXmlPullParser parser, String attrName)
+ throws XmlParserException {
+ String tagName = parser.getName();
+ long value = readAttributeLong(parser, attrName);
+
+ XmlValidator.checkParserCondition(value > 0,
+ "Unexpected %s = %d in tag %s, expected %s > 0", attrName, value, tagName,
+ attrName);
return value;
}
@@ -251,4 +302,15 @@
throw XmlParserException.createFromPullParserException(tagName, attrName, rawValue, e);
}
}
+
+ private static long readAttributeLong(TypedXmlPullParser parser, String attrName)
+ throws XmlParserException {
+ String tagName = parser.getName();
+ try {
+ return parser.getAttributeLong(NAMESPACE, attrName);
+ } catch (XmlPullParserException e) {
+ String rawValue = parser.getAttributeValue(NAMESPACE, attrName);
+ throw XmlParserException.createFromPullParserException(tagName, attrName, rawValue, e);
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/view/ViewGroupTest.java b/core/tests/coretests/src/android/view/ViewGroupTest.java
index 43c404e..ae3ad36 100644
--- a/core/tests/coretests/src/android/view/ViewGroupTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupTest.java
@@ -213,35 +213,6 @@
assertTrue(autofillableViews.containsAll(Arrays.asList(viewA, viewC)));
}
- @Test
- public void testMeasureCache() {
- final int spec1 = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.AT_MOST);
- final int spec2 = View.MeasureSpec.makeMeasureSpec(50, View.MeasureSpec.AT_MOST);
- final Context context = getInstrumentation().getContext();
- final View child = new View(context);
- final TestView parent = new TestView(context, 0);
- parent.addView(child);
-
- child.setPadding(1, 2, 3, 4);
- parent.measure(spec1, spec1);
- assertEquals(4, parent.getMeasuredWidth());
- assertEquals(6, parent.getMeasuredHeight());
-
- child.setPadding(5, 6, 7, 8);
- parent.measure(spec2, spec2);
- assertEquals(12, parent.getMeasuredWidth());
- assertEquals(14, parent.getMeasuredHeight());
-
- // This ends the state of forceLayout.
- parent.layout(0, 0, 50, 50);
-
- // The cached values should be cleared after the new setPadding is called. And the measured
- // width and height should be up-to-date.
- parent.measure(spec1, spec1);
- assertEquals(12, parent.getMeasuredWidth());
- assertEquals(14, parent.getMeasuredHeight());
- }
-
private static void getUnobscuredTouchableRegion(Region outRegion, View view) {
outRegion.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
final ViewParent parent = view.getParent();
@@ -269,19 +240,6 @@
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// We don't layout this view.
}
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int measuredWidth = 0;
- int measuredHeight = 0;
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- measuredWidth += child.getPaddingLeft() + child.getPaddingRight();
- measuredHeight += child.getPaddingTop() + child.getPaddingBottom();
- }
- setMeasuredDimension(measuredWidth, measuredHeight);
- }
}
public static class AutofillableTestView extends TestView {
diff --git a/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
index 4071057..5f25e93 100644
--- a/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
@@ -439,6 +439,498 @@
}
@Test
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public void testWaveformEnvelopeEffect_allSucceed() throws Exception {
+ VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder()
+ .addControlPoint(0.2f, 80f, 10)
+ .addControlPoint(0.5f, 150f, 10)
+ .build();
+
+ String xml = """
+ <vibration-effect>
+ <waveform-envelope-effect>
+ <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" />
+ <control-point amplitude="0.5" frequencyHz="150.0" durationMs="10" />
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """;
+
+ assertPublicApisParserSucceeds(xml, effect);
+ assertPublicApisSerializerSucceeds(effect, xml);
+ assertPublicApisRoundTrip(effect);
+ assertHiddenApisParserSucceeds(xml, effect);
+ assertHiddenApisSerializerSucceeds(effect, xml);
+ assertHiddenApisRoundTrip(effect);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public void testWaveformEnvelopeEffectWithInitialFrequency_allSucceed() throws Exception {
+ VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder()
+ .setInitialFrequencyHz(20)
+ .addControlPoint(0.2f, 80f, 10)
+ .addControlPoint(0.5f, 150f, 10)
+ .build();
+
+ String xml = """
+ <vibration-effect>
+ <waveform-envelope-effect initialFrequencyHz="20.0">
+ <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" />
+ <control-point amplitude="0.5" frequencyHz="150.0" durationMs="10" />
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """;
+
+ assertPublicApisParserSucceeds(xml, effect);
+ assertPublicApisSerializerSucceeds(effect, xml);
+ assertPublicApisRoundTrip(effect);
+ assertHiddenApisParserSucceeds(xml, effect);
+ assertHiddenApisSerializerSucceeds(effect, xml);
+ assertHiddenApisRoundTrip(effect);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public void testWaveformEnvelopeEffect_badXml_throwsException() throws IOException {
+ // Incomplete XML
+ assertParseElementFails("""
+ <vibration-effect>
+ <waveform-envelope-effect>
+ <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" />
+ </vibration-effect>
+ """);
+ assertParseElementFails("""
+ <vibration-effect>
+ <waveform-envelope-effect>
+ <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10">
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """);
+ assertParseElementFails("""
+ <vibration-effect>
+ <waveform-envelope-effect>
+ <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" />
+ </waveform-envelope-effect>
+ """);
+
+ // Bad vibration XML
+ assertParseElementFails("""
+ <vibration-effect>
+ <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" />
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """);
+
+ // "waveform-envelope-effect" tag with invalid attributes
+ assertParseElementFails("""
+ <vibration-effect>
+ <waveform-envelope-effect init_freq="20.0">
+ <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" />
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public void testWaveformEnvelopeEffect_noControlPoints_allFail() throws IOException {
+ String xml = "<vibration-effect><waveform-envelope-effect/></vibration-effect>";
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = "<vibration-effect><waveform-envelope-effect> \n "
+ + "</waveform-envelope-effect></vibration-effect>";
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = "<vibration-effect><waveform-envelope-effect>invalid</waveform-envelope-effect"
+ + "></vibration-effect>";
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <waveform-envelope-effect>
+ <control-point />
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <waveform-envelope-effect initialFrequencyHz="20.0" />
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <waveform-envelope-effect initialFrequencyHz="20.0"> \n </waveform-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <waveform-envelope-effect initialFrequencyHz="20.0">
+ invalid
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <waveform-envelope-effect initialFrequencyHz="20.0">
+ <control-point />
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public void testWaveformEnvelopeEffect_badControlPointData_allFail() throws IOException {
+ String xml = """
+ <vibration-effect>
+ <waveform-envelope-effect>
+ <control-point amplitude="-1" frequencyHz="80.0" durationMs="100" />
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <waveform-envelope-effect>
+ <control-point amplitude="0.2" frequencyHz="0" durationMs="100" />
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <waveform-envelope-effect initialFrequencyHz="0">
+ <control-point amplitude="0.2" frequencyHz="30" durationMs="100" />
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <waveform-envelope-effect>
+ <control-point amplitude="0.2" frequencyHz="80.0" durationMs="0" />
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <waveform-envelope-effect>
+ <control-point amplitude="0.2" />
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public void testWaveformEnvelopeEffect_featureFlagDisabled_allFail() throws Exception {
+ VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder()
+ .setInitialFrequencyHz(20)
+ .addControlPoint(0.2f, 80f, 10)
+ .addControlPoint(0.5f, 150f, 10)
+ .build();
+
+ String xml = """
+ <vibration-effect>
+ <waveform-envelope-effect initialFrequencyHz="20.0">
+ <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" />
+ <control-point amplitude="0.5" frequencyHz="150.0" durationMs="10" />
+ </waveform-envelope-effect>
+ </vibration-effect>
+ """;
+
+ assertPublicApisParserFails(xml);
+ assertPublicApisSerializerFails(effect);
+ assertHiddenApisParserFails(xml);
+ assertHiddenApisSerializerFails(effect);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public void testBasicEnvelopeEffect_allSucceed() throws Exception {
+ VibrationEffect effect = new VibrationEffect.BasicEnvelopeBuilder()
+ .addControlPoint(0.2f, 0.5f, 10)
+ .addControlPoint(0.0f, 1f, 10)
+ .build();
+
+ String xml = """
+ <vibration-effect>
+ <basic-envelope-effect>
+ <control-point intensity="0.2" sharpness="0.5" durationMs="10" />
+ <control-point intensity="0.0" sharpness="1.0" durationMs="10" />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """;
+
+ assertPublicApisParserSucceeds(xml, effect);
+ assertPublicApisSerializerSucceeds(effect, xml);
+ assertPublicApisRoundTrip(effect);
+ assertHiddenApisParserSucceeds(xml, effect);
+ assertHiddenApisSerializerSucceeds(effect, xml);
+ assertHiddenApisRoundTrip(effect);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public void testBasicEnvelopeEffectWithInitialSharpness_allSucceed() throws Exception {
+ VibrationEffect effect = new VibrationEffect.BasicEnvelopeBuilder()
+ .setInitialSharpness(0.3f)
+ .addControlPoint(0.2f, 0.5f, 10)
+ .addControlPoint(0.0f, 1f, 10)
+ .build();
+
+ String xml = """
+ <vibration-effect>
+ <basic-envelope-effect initialSharpness="0.3">
+ <control-point intensity="0.2" sharpness="0.5" durationMs="10" />
+ <control-point intensity="0.0" sharpness="1.0" durationMs="10" />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """;
+
+ assertPublicApisParserSucceeds(xml, effect);
+ assertPublicApisSerializerSucceeds(effect, xml);
+ assertPublicApisRoundTrip(effect);
+ assertHiddenApisParserSucceeds(xml, effect);
+ assertHiddenApisSerializerSucceeds(effect, xml);
+ assertHiddenApisRoundTrip(effect);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public void testBasicEnvelopeEffect_badXml_throwsException() throws IOException {
+ // Incomplete XML
+ assertParseElementFails("""
+ <vibration-effect>
+ <basic-envelope-effect>
+ <control-point intensity="0.2" sharpness="0.8" durationMs="10" />
+ <control-point intensity="0.0" sharpness="1.0" durationMs="10" />
+ </vibration-effect>
+ """);
+ assertParseElementFails("""
+ <vibration-effect>
+ <basic-envelope-effect>
+ <control-point intensity="0.2" sharpness="0.8" durationMs="10">
+ <control-point intensity="0.0" sharpness="1.0" durationMs="10" />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """);
+ assertParseElementFails("""
+ <vibration-effect>
+ <basic-envelope-effect>
+ <control-point intensity="0.2" sharpness="0.8" durationMs="10" />
+ <control-point intensity="0.0" sharpness="1.0" durationMs="10" />
+ </basic-envelope-effect>
+ """);
+
+ // Bad vibration XML
+ assertParseElementFails("""
+ <vibration-effect>
+ <control-point intensity="0.2" sharpness="0.8" durationMs="10" />
+ <control-point intensity="0.0" sharpness="1.0" durationMs="10" />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """);
+
+ // "basic-envelope-effect" tag with invalid attributes
+ assertParseElementFails("""
+ <vibration-effect>
+ <basic-envelope-effect init_sharp="20.0">
+ <control-point intensity="0.2" sharpness="0.8" durationMs="10" />
+ <control-point intensity="0.0" sharpness="1.0" durationMs="10" />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public void testBasicEnvelopeEffect_noControlPoints_allFail() throws IOException {
+ String xml = "<vibration-effect><basic-envelope-effect/></vibration-effect>";
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = "<vibration-effect><basic-envelope-effect> \n "
+ + "</basic-envelope-effect></vibration-effect>";
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = "<vibration-effect><basic-envelope-effect>invalid</basic-envelope-effect"
+ + "></vibration-effect>";
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <basic-envelope-effect>
+ <control-point />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <basic-envelope-effect initialSharpness="0.2" />
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <basic-envelope-effect initialSharpness="0.2"> \n </basic-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <basic-envelope-effect initialSharpness="0.2">
+ invalid
+ </basic-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <basic-envelope-effect initialSharpness="0.2">
+ <control-point />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public void testBasicEnvelopeEffect_badControlPointData_allFail() throws IOException {
+ String xml = """
+ <vibration-effect>
+ <basic-envelope-effect>
+ <control-point intensity="-1" sharpness="0.8" durationMs="100" />
+ <control-point intensity="0.0" sharpness="1.0" durationMs="10" />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <basic-envelope-effect>
+ <control-point intensity="0.2" sharpness="-1" durationMs="100" />
+ <control-point intensity="0.0" sharpness="1.0" durationMs="10" />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <basic-envelope-effect initialSharpness="-1.0">
+ <control-point intensity="0.2" sharpness="0.8" durationMs="0" />
+ <control-point intensity="0.0" sharpness="1.0" durationMs="10" />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <basic-envelope-effect initialSharpness="2.0">
+ <control-point intensity="0.2" sharpness="0.8" durationMs="0" />
+ <control-point intensity="0.0" sharpness="1.0" durationMs="10" />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <basic-envelope-effect>
+ <control-point intensity="0.2" sharpness="0.8" durationMs="10" />
+ <control-point intensity="0.5" sharpness="0.8" durationMs="10" />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ xml = """
+ <vibration-effect>
+ <basic-envelope-effect>
+ <control-point intensity="0.2" />
+ <control-point intensity="0.0" sharpness="1.0" durationMs="10" />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """;
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ public void testBasicEnvelopeEffect_featureFlagDisabled_allFail() throws Exception {
+ VibrationEffect effect = new VibrationEffect.BasicEnvelopeBuilder()
+ .setInitialSharpness(0.3f)
+ .addControlPoint(0.2f, 0.5f, 10)
+ .addControlPoint(0.0f, 1f, 10)
+ .build();
+
+ String xml = """
+ <vibration-effect>
+ <basic-envelope-effect initialSharpness="0.3">
+ <control-point intensity="0.2" sharpness="0.5" durationMs="10" />
+ <control-point intensity="0.0" sharpness="1.0" durationMs="10" />
+ </basic-envelope-effect>
+ </vibration-effect>
+ """;
+
+ assertPublicApisParserFails(xml);
+ assertPublicApisSerializerFails(effect);
+
+ assertHiddenApisParserFails(xml);
+ assertHiddenApisSerializerFails(effect);
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
public void testVendorEffect_allSucceed() throws Exception {
PersistableBundle vendorData = new PersistableBundle();
diff --git a/core/xsd/vibrator/vibration/schema/current.txt b/core/xsd/vibrator/vibration/schema/current.txt
index b4148d6..29f8d19 100644
--- a/core/xsd/vibrator/vibration/schema/current.txt
+++ b/core/xsd/vibrator/vibration/schema/current.txt
@@ -1,6 +1,23 @@
// Signature format: 2.0
package com.android.internal.vibrator.persistence {
+ public class BasicControlPoint {
+ ctor public BasicControlPoint();
+ method public long getDurationMs();
+ method public float getIntensity();
+ method public float getSharpness();
+ method public void setDurationMs(long);
+ method public void setIntensity(float);
+ method public void setSharpness(float);
+ }
+
+ public class BasicEnvelopeEffect {
+ ctor public BasicEnvelopeEffect();
+ method public java.util.List<com.android.internal.vibrator.persistence.BasicControlPoint> getControlPoint();
+ method public float getInitialSharpness();
+ method public void setInitialSharpness(float);
+ }
+
public class PredefinedEffect {
ctor public PredefinedEffect();
method public com.android.internal.vibrator.persistence.PredefinedEffectName getName();
@@ -47,14 +64,18 @@
public class VibrationEffect {
ctor public VibrationEffect();
+ method public com.android.internal.vibrator.persistence.BasicEnvelopeEffect getBasicEnvelopeEffect_optional();
method public com.android.internal.vibrator.persistence.PredefinedEffect getPredefinedEffect_optional();
method public com.android.internal.vibrator.persistence.PrimitiveEffect getPrimitiveEffect_optional();
method public byte[] getVendorEffect_optional();
method public com.android.internal.vibrator.persistence.WaveformEffect getWaveformEffect_optional();
+ method public com.android.internal.vibrator.persistence.WaveformEnvelopeEffect getWaveformEnvelopeEffect_optional();
+ method public void setBasicEnvelopeEffect_optional(com.android.internal.vibrator.persistence.BasicEnvelopeEffect);
method public void setPredefinedEffect_optional(com.android.internal.vibrator.persistence.PredefinedEffect);
method public void setPrimitiveEffect_optional(com.android.internal.vibrator.persistence.PrimitiveEffect);
method public void setVendorEffect_optional(byte[]);
method public void setWaveformEffect_optional(com.android.internal.vibrator.persistence.WaveformEffect);
+ method public void setWaveformEnvelopeEffect_optional(com.android.internal.vibrator.persistence.WaveformEnvelopeEffect);
}
public class VibrationSelect {
@@ -67,6 +88,16 @@
enum_constant public static final com.android.internal.vibrator.persistence.WaveformAmplitudeDefault _default;
}
+ public class WaveformControlPoint {
+ ctor public WaveformControlPoint();
+ method public float getAmplitude();
+ method public long getDurationMs();
+ method public float getFrequencyHz();
+ method public void setAmplitude(float);
+ method public void setDurationMs(long);
+ method public void setFrequencyHz(float);
+ }
+
public class WaveformEffect {
ctor public WaveformEffect();
method public com.android.internal.vibrator.persistence.WaveformEffect.Repeating getRepeating();
@@ -87,6 +118,13 @@
method public void setDurationMs(java.math.BigInteger);
}
+ public class WaveformEnvelopeEffect {
+ ctor public WaveformEnvelopeEffect();
+ method public java.util.List<com.android.internal.vibrator.persistence.WaveformControlPoint> getControlPoint();
+ method public float getInitialFrequencyHz();
+ method public void setInitialFrequencyHz(float);
+ }
+
public class XmlParser {
ctor public XmlParser();
method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
diff --git a/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd b/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd
index 910a9b7..b4df2d1 100644
--- a/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd
+++ b/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd
@@ -54,6 +54,12 @@
<xs:element name="primitive-effect" type="PrimitiveEffect"/>
</xs:sequence>
+ <!-- Waveform envelope effect -->
+ <xs:element name="waveform-envelope-effect" type="WaveformEnvelopeEffect"/>
+
+ <!-- Basic envelope effect -->
+ <xs:element name="basic-envelope-effect" type="BasicEnvelopeEffect"/>
+
</xs:choice>
</xs:complexType>
@@ -180,4 +186,54 @@
</xs:restriction>
</xs:simpleType>
+ <!-- Definition of a waveform envelope effect -->
+ <xs:complexType name="WaveformEnvelopeEffect">
+ <xs:sequence>
+ <xs:element name="control-point" maxOccurs="unbounded" minOccurs="1"
+ type="WaveformControlPoint" />
+ </xs:sequence>
+ <xs:attribute name="initialFrequencyHz" type="ControlPointFrequency" />
+ </xs:complexType>
+
+ <!-- Definition of a basic envelope effect -->
+ <xs:complexType name="BasicEnvelopeEffect">
+ <xs:sequence>
+ <xs:element name="control-point" maxOccurs="unbounded" minOccurs="1"
+ type="BasicControlPoint" />
+ </xs:sequence>
+ <xs:attribute name="initialSharpness" type="NormalizedControlPointUnit" />
+ </xs:complexType>
+
+ <xs:complexType name="WaveformControlPoint">
+ <xs:attribute name="amplitude" type="NormalizedControlPointUnit" use="required"/>
+ <xs:attribute name="frequencyHz" type="ControlPointFrequency" use="required"/>
+ <xs:attribute name="durationMs" type="PositiveLong" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="BasicControlPoint">
+ <xs:attribute name="intensity" type="NormalizedControlPointUnit" use="required"/>
+ <xs:attribute name="sharpness" type="NormalizedControlPointUnit" use="required"/>
+ <xs:attribute name="durationMs" type="PositiveLong" use="required"/>
+ </xs:complexType>
+
+ <xs:simpleType name="ControlPointFrequency">
+ <xs:restriction base="xs:float">
+ <xs:minExclusive value="0"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="PositiveLong">
+ <xs:restriction base="xs:long">
+ <xs:minExclusive value="0"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- Normalized control point unit float in [0,1] -->
+ <xs:simpleType name="NormalizedControlPointUnit">
+ <xs:restriction base="xs:float">
+ <xs:minInclusive value="0"/>
+ <xs:maxInclusive value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+
</xs:schema>
diff --git a/core/xsd/vibrator/vibration/vibration.xsd b/core/xsd/vibrator/vibration/vibration.xsd
index 3c8e016..fba966f 100644
--- a/core/xsd/vibrator/vibration/vibration.xsd
+++ b/core/xsd/vibrator/vibration/vibration.xsd
@@ -52,6 +52,12 @@
<xs:element name="primitive-effect" type="PrimitiveEffect"/>
</xs:sequence>
+ <!-- Waveform envelope effect -->
+ <xs:element name="waveform-envelope-effect" type="WaveformEnvelopeEffect"/>
+
+ <!-- Basic envelope effect -->
+ <xs:element name="basic-envelope-effect" type="BasicEnvelopeEffect"/>
+
</xs:choice>
</xs:complexType>
@@ -157,4 +163,54 @@
</xs:restriction>
</xs:simpleType>
+ <!-- Definition of a waveform envelope effect -->
+ <xs:complexType name="WaveformEnvelopeEffect">
+ <xs:sequence>
+ <xs:element name="control-point" maxOccurs="unbounded" minOccurs="1"
+ type="WaveformControlPoint" />
+ </xs:sequence>
+ <xs:attribute name="initialFrequencyHz" type="ControlPointFrequency" />
+ </xs:complexType>
+
+ <!-- Definition of a basic envelope effect -->
+ <xs:complexType name="BasicEnvelopeEffect">
+ <xs:sequence>
+ <xs:element name="control-point" maxOccurs="unbounded" minOccurs="1"
+ type="BasicControlPoint" />
+ </xs:sequence>
+ <xs:attribute name="initialSharpness" type="NormalizedControlPointUnit" />
+ </xs:complexType>
+
+ <xs:complexType name="WaveformControlPoint">
+ <xs:attribute name="amplitude" type="NormalizedControlPointUnit" use="required"/>
+ <xs:attribute name="frequencyHz" type="ControlPointFrequency" use="required"/>
+ <xs:attribute name="durationMs" type="PositiveLong" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="BasicControlPoint">
+ <xs:attribute name="intensity" type="NormalizedControlPointUnit" use="required"/>
+ <xs:attribute name="sharpness" type="NormalizedControlPointUnit" use="required"/>
+ <xs:attribute name="durationMs" type="PositiveLong" use="required"/>
+ </xs:complexType>
+
+ <xs:simpleType name="ControlPointFrequency">
+ <xs:restriction base="xs:float">
+ <xs:minExclusive value="0"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="PositiveLong">
+ <xs:restriction base="xs:long">
+ <xs:minExclusive value="0"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- Normalized control point unit float in [0,1] -->
+ <xs:simpleType name="NormalizedControlPointUnit">
+ <xs:restriction base="xs:float">
+ <xs:minInclusive value="0"/>
+ <xs:maxInclusive value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+
</xs:schema>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategy.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategy.kt
new file mode 100644
index 0000000..9e3edf6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerStrategy.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import com.android.wm.shell.dagger.WMSingleton
+import javax.inject.Inject
+
+/**
+ * Encapsulate the logic related to the use of a single or multiple surfaces when
+ * implementing letterbox in shell.
+ */
+@WMSingleton
+class LetterboxControllerStrategy @Inject constructor() {
+
+ // Different letterbox implementation modes.
+ enum class LetterboxMode { SINGLE_SURFACE, MULTIPLE_SURFACES }
+
+ @Volatile
+ private var currentMode: LetterboxMode = LetterboxMode.SINGLE_SURFACE
+
+ fun configureLetterboxMode() {
+ // TODO(b/377875146): Define criteria for switching between [LetterboxMode]s.
+ currentMode = if (android.os.SystemProperties.getInt(
+ "multi_interface",
+ 0
+ ) == 0
+ ) {
+ LetterboxMode.SINGLE_SURFACE
+ } else {
+ LetterboxMode.MULTIPLE_SURFACES
+ }
+ }
+
+ /**
+ * @return The specific mode to use for implementing letterboxing for the given [request].
+ */
+ fun getLetterboxImplementationMode(): LetterboxMode = currentMode
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt
index a48dc8b..4718071 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt
@@ -35,7 +35,8 @@
shellInit: ShellInit,
private val transitions: Transitions,
private val letterboxController: LetterboxController,
- private val transitionStateHolder: TransitionStateHolder
+ private val transitionStateHolder: TransitionStateHolder,
+ private val letterboxModeStrategy: LetterboxControllerStrategy
) : Transitions.TransitionObserver {
companion object {
@@ -63,7 +64,6 @@
// We recognise the operation to execute and delegate to the LetterboxController
// the related operation.
// TODO(b/377875151): Identify Desktop Windowing Transactions.
- // TODO(b/377857898): Handling multiple surfaces
// TODO(b/371500295): Handle input events detection.
for (change in info.changes) {
change.taskInfo?.let { ti ->
@@ -83,6 +83,7 @@
} else {
val isTopActivityLetterboxed = ti.appCompatTaskInfo.isTopActivityLetterboxed
if (isTopActivityLetterboxed) {
+ letterboxModeStrategy.configureLetterboxMode()
createLetterboxSurface(
key,
startTransaction,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxUtils.kt
new file mode 100644
index 0000000..ef964f4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxUtils.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.graphics.Rect
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+
+/**
+ * Creates a [LetterboxController] which is the composition of other two [LetterboxController].
+ * It basically invokes the method on both of them.
+ */
+infix fun LetterboxController.append(other: LetterboxController) = object : LetterboxController {
+ override fun createLetterboxSurface(
+ key: LetterboxKey,
+ transaction: Transaction,
+ parentLeash: SurfaceControl
+ ) {
+ this@append.createLetterboxSurface(key, transaction, parentLeash)
+ other.createLetterboxSurface(key, transaction, parentLeash)
+ }
+
+ override fun destroyLetterboxSurface(
+ key: LetterboxKey,
+ transaction: Transaction
+ ) {
+ this@append.destroyLetterboxSurface(key, transaction)
+ other.destroyLetterboxSurface(key, transaction)
+ }
+
+ override fun updateLetterboxSurfaceVisibility(
+ key: LetterboxKey,
+ transaction: Transaction,
+ visible: Boolean
+ ) {
+ this@append.updateLetterboxSurfaceVisibility(key, transaction, visible)
+ other.updateLetterboxSurfaceVisibility(key, transaction, visible)
+ }
+
+ override fun updateLetterboxSurfaceBounds(
+ key: LetterboxKey,
+ transaction: Transaction,
+ taskBounds: Rect,
+ activityBounds: Rect
+ ) {
+ this@append.updateLetterboxSurfaceBounds(key, transaction, taskBounds, activityBounds)
+ other.updateLetterboxSurfaceBounds(key, transaction, taskBounds, activityBounds)
+ }
+
+ override fun dump() {
+ this@append.dump()
+ other.dump()
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MixedLetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MixedLetterboxController.kt
new file mode 100644
index 0000000..8d06570
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MixedLetterboxController.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy.LetterboxMode.MULTIPLE_SURFACES
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy.LetterboxMode.SINGLE_SURFACE
+import com.android.wm.shell.dagger.WMSingleton
+import javax.inject.Inject
+
+/**
+ * [LetterboxController] implementation working as coordinator of other [LetterboxController]
+ * implementations.
+ */
+@WMSingleton
+class MixedLetterboxController @Inject constructor(
+ private val singleSurfaceController: SingleSurfaceLetterboxController,
+ private val multipleSurfaceController: MultiSurfaceLetterboxController,
+ private val controllerStrategy: LetterboxControllerStrategy
+) : LetterboxController by singleSurfaceController append multipleSurfaceController {
+
+ override fun createLetterboxSurface(
+ key: LetterboxKey,
+ transaction: Transaction,
+ parentLeash: SurfaceControl
+ ) {
+ when (controllerStrategy.getLetterboxImplementationMode()) {
+ SINGLE_SURFACE -> {
+ multipleSurfaceController.destroyLetterboxSurface(key, transaction)
+ singleSurfaceController.createLetterboxSurface(key, transaction, parentLeash)
+ }
+
+ MULTIPLE_SURFACES -> {
+ singleSurfaceController.destroyLetterboxSurface(key, transaction)
+ multipleSurfaceController.createLetterboxSurface(key, transaction, parentLeash)
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index df2b849..806be8c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -74,8 +74,9 @@
import com.android.wm.shell.common.transition.TransitionStateHolder;
import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler;
import com.android.wm.shell.compatui.letterbox.LetterboxController;
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy;
import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver;
-import com.android.wm.shell.compatui.letterbox.SingleSurfaceLetterboxController;
+import com.android.wm.shell.compatui.letterbox.MixedLetterboxController;
import com.android.wm.shell.dagger.back.ShellBackAnimationModule;
import com.android.wm.shell.dagger.pip.PipModule;
import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler;
@@ -1338,14 +1339,15 @@
@NonNull ShellInit shellInit,
@NonNull Transitions transitions,
@NonNull LetterboxController letterboxController,
- @NonNull TransitionStateHolder transitionStateHolder
+ @NonNull TransitionStateHolder transitionStateHolder,
+ @NonNull LetterboxControllerStrategy letterboxControllerStrategy
) {
return new LetterboxTransitionObserver(shellInit, transitions, letterboxController,
- transitionStateHolder);
+ transitionStateHolder, letterboxControllerStrategy);
}
@WMSingleton
@Binds
abstract LetterboxController bindsLetterboxController(
- SingleSurfaceLetterboxController letterboxController);
+ MixedLetterboxController letterboxController);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerRobotTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerRobotTest.kt
index f9f01bc..95a0c82 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerRobotTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxControllerRobotTest.kt
@@ -98,6 +98,10 @@
activityBounds = activityBounds
)
+ fun invokeDump() {
+ letterboxController.dump()
+ }
+
fun checkSurfaceBuilderInvoked(times: Int = 1, name: String = "", callSite: String = "") {
verify(surfaceBuilder, times(times)).createSurface(
eq(transaction),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
index 8825bb4..78bb721 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
@@ -96,6 +96,7 @@
validateOutput {
r.creationEventDetected(expected = false)
+ r.configureStrategyInvoked(expected = false)
r.visibilityEventDetected(expected = false)
r.destroyEventDetected(expected = false)
r.updateSurfaceBoundsEventDetected(expected = false)
@@ -123,6 +124,7 @@
validateOutput {
r.creationEventDetected(expected = true)
+ r.configureStrategyInvoked(expected = true)
r.visibilityEventDetected(expected = true, visible = true)
r.destroyEventDetected(expected = false)
r.updateSurfaceBoundsEventDetected(
@@ -217,6 +219,7 @@
private val letterboxController: LetterboxController
private val letterboxObserver: LetterboxTransitionObserver
private val transitionStateHolder: TransitionStateHolder
+ private val letterboxStrategy: LetterboxControllerStrategy
val observerFactory: () -> LetterboxTransitionObserver
@@ -225,6 +228,7 @@
shellInit = ShellInit(executor)
transitions = mock<Transitions>()
letterboxController = mock<LetterboxController>()
+ letterboxStrategy = mock<LetterboxControllerStrategy>()
transitionStateHolder =
TransitionStateHolder(shellInit, mock<RecentsTransitionHandler>())
spyOn(transitionStateHolder)
@@ -233,7 +237,8 @@
shellInit,
transitions,
letterboxController,
- transitionStateHolder
+ transitionStateHolder,
+ letterboxStrategy
)
observerFactory = { letterboxObserver }
}
@@ -302,6 +307,9 @@
eq(activityBounds)
)
+ fun configureStrategyInvoked(expected: Boolean) =
+ verify(letterboxStrategy, expected.asMode()).configureLetterboxMode()
+
fun createTopActivityChange(
inputBuilder: TransitionObserverInputBuilder,
isLetterboxed: Boolean = true,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxUtilsTest.kt
new file mode 100644
index 0000000..06b8052
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxUtilsTest.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.content.Context
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import java.util.function.Consumer
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+/**
+ * Tests for [LetterboxUtils].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:LetterboxUtilsTest
+ */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class LetterboxUtilsTest : ShellTestCase() {
+
+ val firstLetterboxController = mock<LetterboxController>()
+ val secondLetterboxController = mock<LetterboxController>()
+ val thirdLetterboxController = mock<LetterboxController>()
+
+ private val letterboxControllerBuilder: (LetterboxSurfaceBuilder) -> LetterboxController =
+ { _ ->
+ firstLetterboxController.append(secondLetterboxController)
+ .append(thirdLetterboxController)
+ }
+
+ @Test
+ fun `Appended LetterboxController invoked creation on all the controllers`() {
+ runTestScenario { r ->
+ r.sendCreateSurfaceRequest()
+
+ r.verifyCreateSurfaceInvokedWithRequest(target = firstLetterboxController)
+ r.verifyCreateSurfaceInvokedWithRequest(target = secondLetterboxController)
+ r.verifyCreateSurfaceInvokedWithRequest(target = thirdLetterboxController)
+ }
+ }
+
+ @Test
+ fun `Appended LetterboxController invoked destroy on all the controllers`() {
+ runTestScenario { r ->
+ r.sendDestroySurfaceRequest()
+ r.verifyDestroySurfaceInvokedWithRequest(target = firstLetterboxController)
+ r.verifyDestroySurfaceInvokedWithRequest(target = secondLetterboxController)
+ r.verifyDestroySurfaceInvokedWithRequest(target = thirdLetterboxController)
+ }
+ }
+
+ @Test
+ fun `Appended LetterboxController invoked update visibility on all the controllers`() {
+ runTestScenario { r ->
+ r.sendUpdateSurfaceVisibilityRequest(visible = true)
+ r.verifyUpdateVisibilitySurfaceInvokedWithRequest(target = firstLetterboxController)
+ r.verifyUpdateVisibilitySurfaceInvokedWithRequest(target = secondLetterboxController)
+ r.verifyUpdateVisibilitySurfaceInvokedWithRequest(target = thirdLetterboxController)
+ }
+ }
+
+ @Test
+ fun `Appended LetterboxController invoked update bounds on all the controllers`() {
+ runTestScenario { r ->
+ r.sendUpdateSurfaceBoundsRequest(taskBounds = Rect(), activityBounds = Rect())
+ r.verifyUpdateSurfaceBoundsInvokedWithRequest(target = firstLetterboxController)
+ r.verifyUpdateSurfaceBoundsInvokedWithRequest(target = secondLetterboxController)
+ r.verifyUpdateSurfaceBoundsInvokedWithRequest(target = thirdLetterboxController)
+ }
+ }
+
+ @Test
+ fun `Appended LetterboxController invoked update dump on all the controllers`() {
+ runTestScenario { r ->
+ r.invokeDump()
+ r.verifyDumpInvoked(target = firstLetterboxController)
+ r.verifyDumpInvoked(target = secondLetterboxController)
+ r.verifyDumpInvoked(target = thirdLetterboxController)
+ }
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ fun runTestScenario(consumer: Consumer<AppendLetterboxControllerRobotTest>) {
+ val robot = AppendLetterboxControllerRobotTest(mContext, letterboxControllerBuilder)
+ consumer.accept(robot)
+ }
+
+ class AppendLetterboxControllerRobotTest(
+ ctx: Context,
+ builder: (LetterboxSurfaceBuilder) -> LetterboxController
+ ) : LetterboxControllerRobotTest(ctx, builder) {
+
+ fun verifyCreateSurfaceInvokedWithRequest(
+ target: LetterboxController,
+ times: Int = 1
+ ) {
+ verify(target, times(times)).createLetterboxSurface(any(), any(), any())
+ }
+
+ fun verifyDestroySurfaceInvokedWithRequest(
+ target: LetterboxController,
+ times: Int = 1
+ ) {
+ verify(target, times(times)).destroyLetterboxSurface(any(), any())
+ }
+
+ fun verifyUpdateVisibilitySurfaceInvokedWithRequest(
+ target: LetterboxController,
+ times: Int = 1
+ ) {
+ verify(target, times(times)).updateLetterboxSurfaceVisibility(any(), any(), any())
+ }
+
+ fun verifyUpdateSurfaceBoundsInvokedWithRequest(
+ target: LetterboxController,
+ times: Int = 1
+ ) {
+ verify(target, times(times)).updateLetterboxSurfaceBounds(any(), any(), any(), any())
+ }
+
+ fun verifyDumpInvoked(
+ target: LetterboxController,
+ times: Int = 1
+ ) {
+ verify(target, times(times)).dump()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/MixedLetterboxControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/MixedLetterboxControllerTest.kt
new file mode 100644
index 0000000..e6bff4c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/MixedLetterboxControllerTest.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.compatui.letterbox.LetterboxControllerStrategy.LetterboxMode
+import java.util.function.Consumer
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+/**
+ * Tests for [MixedLetterboxController].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:MixedLetterboxControllerTest
+ */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class MixedLetterboxControllerTest : ShellTestCase() {
+
+ @Test
+ fun `When strategy is SINGLE_SURFACE and a create request is sent multi are destroyed`() {
+ runTestScenario { r ->
+ r.configureStrategyFor(LetterboxMode.SINGLE_SURFACE)
+ r.sendCreateSurfaceRequest()
+ r.checkCreateInvokedOnSingleController()
+ r.checkDestroyInvokedOnMultiController()
+ }
+ }
+
+ @Test
+ fun `When strategy is MULTIPLE_SURFACES and a create request is sent single is destroyed`() {
+ runTestScenario { r ->
+ r.configureStrategyFor(LetterboxMode.MULTIPLE_SURFACES)
+ r.sendCreateSurfaceRequest()
+ r.checkDestroyInvokedOnSingleController()
+ r.checkCreateInvokedOnMultiController()
+ }
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ fun runTestScenario(consumer: Consumer<MixedLetterboxControllerRobotTest>) {
+ val robot = MixedLetterboxControllerRobotTest(mContext, ObjectToTestHolder())
+ consumer.accept(robot)
+ }
+
+ class MixedLetterboxControllerRobotTest(
+ ctx: Context,
+ private val objectToTestHolder: ObjectToTestHolder
+ ) : LetterboxControllerRobotTest(ctx, objectToTestHolder.controllerBuilder) {
+
+ fun configureStrategyFor(letterboxMode: LetterboxMode) {
+ doReturn(letterboxMode).`when`(objectToTestHolder.controllerStrategy)
+ .getLetterboxImplementationMode()
+ }
+
+ fun checkCreateInvokedOnSingleController(times: Int = 1) {
+ verify(
+ objectToTestHolder.singleLetterboxController,
+ times(times)
+ ).createLetterboxSurface(any(), any(), any())
+ }
+
+ fun checkCreateInvokedOnMultiController(times: Int = 1) {
+ verify(
+ objectToTestHolder.multipleLetterboxController,
+ times(times)
+ ).createLetterboxSurface(any(), any(), any())
+ }
+
+ fun checkDestroyInvokedOnSingleController(times: Int = 1) {
+ verify(
+ objectToTestHolder.singleLetterboxController,
+ times(times)
+ ).destroyLetterboxSurface(any(), any())
+ }
+
+ fun checkDestroyInvokedOnMultiController(times: Int = 1) {
+ verify(
+ objectToTestHolder.multipleLetterboxController,
+ times(times)
+ ).destroyLetterboxSurface(any(), any())
+ }
+ }
+
+ data class ObjectToTestHolder(
+ val singleLetterboxController: SingleSurfaceLetterboxController =
+ mock<SingleSurfaceLetterboxController>(),
+ val multipleLetterboxController: MultiSurfaceLetterboxController =
+ mock<MultiSurfaceLetterboxController>(),
+ val controllerStrategy: LetterboxControllerStrategy = mock<LetterboxControllerStrategy>()
+ ) {
+
+ private val mixedController =
+ MixedLetterboxController(
+ singleLetterboxController,
+ multipleLetterboxController,
+ controllerStrategy
+ )
+
+ val controllerBuilder: (LetterboxSurfaceBuilder) -> LetterboxController =
+ { _ -> mixedController }
+ }
+}
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index d993b87..28d96e3 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -28,12 +28,14 @@
#define INDENT " "
#define INDENT2 " "
+namespace android {
+
namespace {
+
// Time to spend fading out the pointer completely.
const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
-} // namespace
-namespace android {
+} // namespace
// --- MouseCursorController ---
@@ -64,17 +66,23 @@
mLocked.pointerSprite.clear();
}
-void MouseCursorController::move(float deltaX, float deltaY) {
+FloatPoint MouseCursorController::move(float deltaX, float deltaY) {
#if DEBUG_MOUSE_CURSOR_UPDATES
ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
#endif
if (deltaX == 0.0f && deltaY == 0.0f) {
- return;
+ return {0, 0};
}
+ // When transition occurs, the MouseCursorController object may or may not be deleted, depending
+ // if there's another display on the other side of the transition. At this point we still need
+ // to move the cursor to the boundary.
std::scoped_lock lock(mLock);
-
- setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
+ const FloatPoint position{mLocked.pointerX + deltaX, mLocked.pointerY + deltaY};
+ setPositionLocked(position.x, position.y);
+ // The amount of the delta that was not consumed as a result of the cursor
+ // hitting the edge of the display.
+ return {position.x - mLocked.pointerX, position.y - mLocked.pointerY};
}
void MouseCursorController::setPosition(float x, float y) {
@@ -85,19 +93,23 @@
setPositionLocked(x, y);
}
-void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) {
- const auto& v = mLocked.viewport;
- if (!v.isValid()) return;
-
+FloatRect MouseCursorController::getBoundsLocked() REQUIRES(mLock) {
// The valid bounds for a mouse cursor. Since the right and bottom edges are considered outside
// the display, clip the bounds by one pixel instead of letting the cursor get arbitrarily
// close to the outside edge.
- const FloatRect bounds{
+ return FloatRect{
static_cast<float>(mLocked.viewport.logicalLeft),
static_cast<float>(mLocked.viewport.logicalTop),
static_cast<float>(mLocked.viewport.logicalRight - 1),
static_cast<float>(mLocked.viewport.logicalBottom - 1),
};
+}
+
+void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) {
+ const auto& v = mLocked.viewport;
+ if (!v.isValid()) return;
+
+ const FloatRect bounds = getBoundsLocked();
mLocked.pointerX = std::max(bounds.left, std::min(bounds.right, x));
mLocked.pointerY = std::max(bounds.top, std::min(bounds.bottom, y));
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
index 12b31a8..e14a55d 100644
--- a/libs/input/MouseCursorController.h
+++ b/libs/input/MouseCursorController.h
@@ -20,9 +20,6 @@
#include <gui/DisplayEventReceiver.h>
#include <input/DisplayViewport.h>
#include <input/Input.h>
-#include <utils/BitSet.h>
-#include <utils/Looper.h>
-#include <utils/RefBase.h>
#include <functional>
#include <map>
@@ -43,7 +40,8 @@
MouseCursorController(PointerControllerContext& context);
~MouseCursorController();
- void move(float deltaX, float deltaY);
+ // Move the pointer and return unconsumed delta
+ FloatPoint move(float deltaX, float deltaY);
void setPosition(float x, float y);
FloatPoint getPosition() const;
ui::LogicalDisplayId getDisplayId() const;
@@ -113,6 +111,7 @@
bool doFadingAnimationLocked(nsecs_t timestamp);
void startAnimationLocked();
+ FloatRect getBoundsLocked();
};
} // namespace android
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 78d7d3a..a713f1d 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -138,15 +138,19 @@
return mDisplayInfoListener->mLock;
}
-void PointerController::move(float deltaX, float deltaY) {
+FloatPoint PointerController::move(float deltaX, float deltaY) {
const ui::LogicalDisplayId displayId = mCursorController.getDisplayId();
- vec2 transformed;
+ ui::Transform transform;
{
std::scoped_lock lock(getLock());
- const auto& transform = getTransformForDisplayLocked(displayId);
- transformed = transformWithoutTranslation(transform, {deltaX, deltaY});
+ transform = getTransformForDisplayLocked(displayId);
}
- mCursorController.move(transformed.x, transformed.y);
+
+ const vec2 transformed = transformWithoutTranslation(transform, {deltaX, deltaY});
+
+ const FloatPoint unconsumedDelta = mCursorController.move(transformed.x, transformed.y);
+ return FloatPoint(transformWithoutTranslation(transform.inverse(),
+ {unconsumedDelta.x, unconsumedDelta.y}));
}
void PointerController::setPosition(float x, float y) {
@@ -295,6 +299,11 @@
mCursorController.setSkipScreenshot(false);
}
+ui::Transform PointerController::getDisplayTransform() const {
+ std::scoped_lock lock(getLock());
+ return getTransformForDisplayLocked(mLocked.pointerDisplayId);
+}
+
void PointerController::doInactivityTimeout() {
fade(Transition::GRADUAL);
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index ee8d121..8b33190 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -51,7 +51,7 @@
~PointerController() override;
- void move(float deltaX, float deltaY) override;
+ FloatPoint move(float deltaX, float deltaY) override;
void setPosition(float x, float y) override;
FloatPoint getPosition() const override;
ui::LogicalDisplayId getDisplayId() const override;
@@ -67,6 +67,7 @@
void setCustomPointerIcon(const SpriteIcon& icon) override;
void setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) override;
void clearSkipScreenshotFlags() override;
+ ui::Transform getDisplayTransform() const override;
virtual void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void doInactivityTimeout();
@@ -165,7 +166,7 @@
~TouchPointerController() override;
- void move(float, float) override {
+ FloatPoint move(float, float) override {
LOG_ALWAYS_FATAL("Should not be called");
}
void setPosition(float, float) override {
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 5b00fca..80c934a 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -40,6 +40,8 @@
CURSOR_TYPE_CUSTOM = -1,
};
+static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
using ::testing::AllOf;
using ::testing::Field;
using ::testing::NiceMock;
@@ -399,6 +401,135 @@
testing::Values(PointerControllerInterface::ControllerType::MOUSE,
PointerControllerInterface::ControllerType::STYLUS));
+class MousePointerControllerTest : public PointerControllerTest {
+protected:
+ MousePointerControllerTest() {
+ sp<MockSprite> testPointerSprite(new NiceMock<MockSprite>);
+ EXPECT_CALL(*mSpriteController, createSprite).WillOnce(Return(testPointerSprite));
+
+ // create a mouse pointer controller
+ mPointerController =
+ PointerController::create(mPolicy, mLooper, *mSpriteController,
+ PointerControllerInterface::ControllerType::MOUSE);
+
+ // set display viewport
+ DisplayViewport viewport;
+ viewport.displayId = ui::LogicalDisplayId::DEFAULT;
+ viewport.logicalRight = 5;
+ viewport.logicalBottom = 5;
+ viewport.physicalRight = 5;
+ viewport.physicalBottom = 5;
+ viewport.deviceWidth = 5;
+ viewport.deviceHeight = 5;
+ mPointerController->setDisplayViewport(viewport);
+ }
+};
+
+struct MousePointerControllerTestParam {
+ vec2 startPosition;
+ vec2 delta;
+ vec2 endPosition;
+ vec2 unconsumedDelta;
+};
+
+class PointerControllerViewportTransitionTest
+ : public MousePointerControllerTest,
+ public testing::WithParamInterface<MousePointerControllerTestParam> {};
+
+TEST_P(PointerControllerViewportTransitionTest, testPointerViewportTransition) {
+ const auto& params = GetParam();
+
+ mPointerController->setPosition(params.startPosition.x, params.startPosition.y);
+ auto unconsumedDelta = mPointerController->move(params.delta.x, params.delta.y);
+
+ auto position = mPointerController->getPosition();
+ EXPECT_NEAR(position.x, params.endPosition.x, EPSILON);
+ EXPECT_NEAR(position.y, params.endPosition.y, EPSILON);
+ EXPECT_NEAR(unconsumedDelta.x, params.unconsumedDelta.x, EPSILON);
+ EXPECT_NEAR(unconsumedDelta.y, params.unconsumedDelta.y, EPSILON);
+}
+
+INSTANTIATE_TEST_SUITE_P(PointerControllerViewportTransitionTest,
+ PointerControllerViewportTransitionTest,
+ testing::Values(
+ // no transition
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {2.0f, 2.0f},
+ {4.0f, 4.0f},
+ {0.0f, 0.0f}},
+ // right boundary
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {3.0f, 0.0f},
+ {4.0f, 2.0f},
+ {1.0f, 0.0f}},
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {3.0f, -1.0f},
+ {4.0f, 1.0f},
+ {1.0f, 0.0f}},
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {3.0f, 1.0f},
+ {4.0f, 3.0f},
+ {1.0f, 0.0f}},
+ // left boundary
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {-3.0f, 0.0f},
+ {0.0f, 2.0f},
+ {-1.0f, 0.0f}},
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {-3.0f, -1.0f},
+ {0.0f, 1.0f},
+ {-1.0f, 0.0f}},
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {-3.0f, 1.0f},
+ {0.0f, 3.0f},
+ {-1.0f, 0.0f}},
+ // bottom boundary
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {0.0f, 3.0f},
+ {2.0f, 4.0f},
+ {0.0f, 1.0f}},
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {-1.0f, 3.0f},
+ {1.0f, 4.0f},
+ {0.0f, 1.0f}},
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {1.0f, 3.0f},
+ {3.0f, 4.0f},
+ {0.0f, 1.0f}},
+ // top boundary
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {0.0f, -3.0f},
+ {2.0f, 0.0f},
+ {0.0f, -1.0f}},
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {-1.0f, -3.0f},
+ {1.0f, 0.0f},
+ {0.0f, -1.0f}},
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {1.0f, -3.0f},
+ {3.0f, 0.0f},
+ {0.0f, -1.0f}},
+ // top-left corner
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {-3.0f, -3.0f},
+ {0.0f, 0.0f},
+ {-1.0f, -1.0f}},
+ // top-right corner
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {3.0f, -3.0f},
+ {4.0f, 0.0f},
+ {1.0f, -1.0f}},
+ // bottom-right corner
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {3.0f, 3.0f},
+ {4.0f, 4.0f},
+ {1.0f, 1.0f}},
+ // bottom-left corner
+ MousePointerControllerTestParam{{2.0f, 2.0f},
+ {-3.0f, 3.0f},
+ {0.0f, 4.0f},
+ {-1.0f, 1.0f}}));
+
class PointerControllerWindowInfoListenerTest : public Test {};
TEST_F(PointerControllerWindowInfoListenerTest,
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index 4f601f0..db1f6a2 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -107,7 +107,7 @@
* <intent-filter>
* <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
* </intent-filter>
- * <meta-data android:name="android.nfc.cardemulation.host_apdu_ervice" android:resource="@xml/apduservice"/>
+ * <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/apduservice"/>
* </service></pre>
*
* This meta-data tag points to an apduservice.xml file.
diff --git a/nfc/java/android/nfc/cardemulation/OffHostApduService.java b/nfc/java/android/nfc/cardemulation/OffHostApduService.java
index 2286e84..8d8a172 100644
--- a/nfc/java/android/nfc/cardemulation/OffHostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/OffHostApduService.java
@@ -96,7 +96,7 @@
* <intent-filter>
* <action android:name="android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"/>
* </intent-filter>
- * <meta-data android:name="android.nfc.cardemulation.off_host_apdu_ervice" android:resource="@xml/apduservice"/>
+ * <meta-data android:name="android.nfc.cardemulation.off_host_apdu_service" android:resource="@xml/apduservice"/>
* </service></pre>
*
* This meta-data tag points to an apduservice.xml file.
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
index 366d54b..7436ac1 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
@@ -123,7 +123,12 @@
*
* @return true if the result is handled
*/
- fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean = false
+ fun onActivityResult(
+ context: PreferenceLifecycleContext,
+ requestCode: Int,
+ resultCode: Int,
+ data: Intent?,
+ ): Boolean = false
}
/**
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index 153089e..03b225e 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -194,8 +194,8 @@
}
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- for (preference in lifecycleAwarePreferences) {
- if (preference.onActivityResult(requestCode, resultCode, data)) break
+ lifecycleAwarePreferences.firstOrNull {
+ it.onActivityResult(preferenceLifecycleContext, requestCode, resultCode, data)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorTest.kt
new file mode 100644
index 0000000..8acf538
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorTest.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.domain.interactor
+
+import android.app.ActivityManager
+import android.media.AudioManager
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.plugins.fakeVolumeDialogController
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.Events
+import com.android.systemui.volume.dialog.data.repository.volumeDialogVisibilityRepository
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+class VolumeDialogSafetyWarningInteractorTest : SysuiTestCase() {
+
+ private val kosmos: Kosmos = testKosmos()
+
+ private lateinit var underTest: VolumeDialogSafetyWarningInteractor
+
+ @Before
+ fun setup() {
+ kosmos.useUnconfinedTestDispatcher()
+ underTest = kosmos.volumeDialogSafetyWarningInteractor
+ }
+
+ @Test
+ fun dismiss_isShowingSafetyWarning_isFalse() =
+ with(kosmos) {
+ runTest {
+ val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+
+ underTest.onSafetyWarningDismissed()
+
+ assertThat(isShowingSafetyWarning).isFalse()
+ }
+ }
+
+ @Test
+ fun flagShowUi_isShowingSafetyWarning_isTrue() =
+ with(kosmos) {
+ runTest {
+ val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+
+ fakeVolumeDialogController.onShowSafetyWarning(AudioManager.FLAG_SHOW_UI)
+
+ assertThat(isShowingSafetyWarning).isTrue()
+ }
+ }
+
+ @Test
+ fun flagShowUiWarnings_isShowingSafetyWarning_isTrue() =
+ with(kosmos) {
+ runTest {
+ val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+
+ fakeVolumeDialogController.onShowSafetyWarning(AudioManager.FLAG_SHOW_UI_WARNINGS)
+
+ assertThat(isShowingSafetyWarning).isTrue()
+ }
+ }
+
+ @Test
+ fun invisibleAndNoFlags_isShowingSafetyWarning_isFalse() =
+ with(kosmos) {
+ runTest {
+ val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+ volumeDialogVisibilityRepository.updateVisibility {
+ VolumeDialogVisibilityModel.Invisible
+ }
+
+ fakeVolumeDialogController.onShowSafetyWarning(0)
+
+ assertThat(isShowingSafetyWarning).isFalse()
+ }
+ }
+
+ @Test
+ fun visibleAndNoFlags_isShowingSafetyWarning_isTrue() =
+ with(kosmos) {
+ runTest {
+ val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+ volumeDialogVisibilityRepository.updateVisibility {
+ VolumeDialogVisibilityModel.Visible(
+ Events.SHOW_REASON_VOLUME_CHANGED,
+ false,
+ ActivityManager.LOCK_TASK_MODE_LOCKED,
+ )
+ }
+
+ fakeVolumeDialogController.onShowSafetyWarning(0)
+
+ assertThat(isShowingSafetyWarning).isTrue()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt
index bba0050..a42ae03 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt
@@ -18,6 +18,7 @@
import android.content.res.Resources
import com.android.internal.R
+import com.android.systemui.common.ui.GlobalConfig
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -36,7 +37,7 @@
constructor(
@Application private val applicationScope: CoroutineScope,
@Main private val resources: Resources,
- configurationRepository: ConfigurationRepository,
+ @GlobalConfig configurationRepository: ConfigurationRepository,
) {
/**
* Whether to enable emergency services calls while the SIM card is locked. This is disabled in
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 61cd7c7..641400a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -42,6 +42,7 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -71,7 +72,7 @@
private val primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor,
private val falsingCollector: FalsingCollector,
private val dismissCallbackRegistry: DismissCallbackRegistry,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val trustRepository: TrustRepository,
@Application private val applicationScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
index 1aaf4fb..ec9ee91 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
@@ -37,6 +37,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.util.icuMessageFormat
import javax.inject.Inject
@@ -62,7 +63,7 @@
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val repository: SimBouncerRepository,
private val telephonyManager: TelephonyManager,
- @Main private val resources: Resources,
+ @ShadeDisplayAware private val resources: Resources,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val euiccManager: EuiccManager?,
// TODO(b/307977401): Replace this with `MobileConnectionsInteractor` when available.
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
index 38fc2a8..84a423e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
@@ -21,11 +21,12 @@
import android.view.WindowManager
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.Utils
+import com.android.systemui.common.ui.GlobalConfig
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.docking.domain.interactor.KeyboardDockingIndicationInteractor
-import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.surfaceeffects.glowboxeffect.GlowBoxConfig
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -37,9 +38,9 @@
@Inject
constructor(
private val windowManager: WindowManager,
- private val context: Context,
+ @Application private val context: Context,
keyboardDockingIndicationInteractor: KeyboardDockingIndicationInteractor,
- @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
+ @GlobalConfig configurationInteractor: ConfigurationInteractor,
@Background private val backgroundScope: CoroutineScope,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index eff5fc0..d8fc52b 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -216,20 +216,13 @@
"handleKeyGestureEvent: Received OPEN_NOTES gesture event from keycodes: " +
event.keycodes.contentToString()
}
- if (
- event.keycodes.contains(KEYCODE_N) &&
- event.hasModifiers(KeyEvent.META_CTRL_ON or KeyEvent.META_META_ON)
- ) {
- debugLog { "Note task triggered by keyboard shortcut" }
- backgroundExecutor.execute { controller.showNoteTask(KEYBOARD_SHORTCUT) }
- return true
- }
if (event.keycodes.size == 1 && event.keycodes[0] == KEYCODE_STYLUS_BUTTON_TAIL) {
debugLog { "Note task triggered by stylus tail button" }
backgroundExecutor.execute { controller.showNoteTask(TAIL_BUTTON) }
return true
}
- return false
+ backgroundExecutor.execute { controller.showNoteTask(KEYBOARD_SHORTCUT) }
+ return true
}
private fun isKeyGestureSupported(gestureType: Int): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
index 094ec39..203a157 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
@@ -16,19 +16,31 @@
package com.android.systemui.volume.dialog
+import android.content.Context
+import android.media.AudioManager
import com.android.app.tracing.coroutines.coroutineScopeTraced
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.VolumeDialog
+import com.android.systemui.volume.SafetyWarningDialog
import com.android.systemui.volume.dialog.dagger.VolumeDialogPluginComponent
+import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogPluginViewModel
import javax.inject.Inject
+import kotlin.coroutines.resume
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.suspendCancellableCoroutine
+@OptIn(ExperimentalCoroutinesApi::class)
class VolumeDialogPlugin
@Inject
constructor(
@Application private val applicationCoroutineScope: CoroutineScope,
+ private val context: Context,
+ private val audioManager: AudioManager,
private val volumeDialogPluginComponentFactory: VolumeDialogPluginComponent.Factory,
) : VolumeDialog {
@@ -41,14 +53,39 @@
coroutineScopeTraced("[Volume]plugin") {
pluginComponent =
volumeDialogPluginComponentFactory.create(this).also {
- it.viewModel().launchVolumeDialog()
+ bindPlugin(it.viewModel())
}
}
}
}
+ private fun CoroutineScope.bindPlugin(viewModel: VolumeDialogPluginViewModel) {
+ viewModel.launchVolumeDialog()
+
+ viewModel.isShowingSafetyWarning
+ .mapLatest { isShowingSafetyWarning ->
+ if (isShowingSafetyWarning) {
+ showSafetyWarningVisibility { viewModel.onSafetyWarningDismissed() }
+ }
+ }
+ .launchIn(this)
+ }
+
override fun destroy() {
job?.cancel()
pluginComponent = null
}
+
+ private suspend fun showSafetyWarningVisibility(onDismissed: () -> Unit) =
+ suspendCancellableCoroutine { continuation ->
+ val dialog =
+ object : SafetyWarningDialog(context, audioManager) {
+ override fun cleanUp() {
+ onDismissed()
+ continuation.resume(Unit)
+ }
+ }
+ dialog.show()
+ continuation.invokeOnCancellation { dialog.dismiss() }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractor.kt
new file mode 100644
index 0000000..f707d67
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractor.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.domain.interactor
+
+import android.media.AudioManager
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogSafetyWarningModel
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
+
+private const val VISIBLE_FLAGS = AudioManager.FLAG_SHOW_UI or AudioManager.FLAG_SHOW_UI_WARNINGS
+
+@VolumeDialogPluginScope
+class VolumeDialogSafetyWarningInteractor
+@Inject
+constructor(
+ private val stateInteractor: VolumeDialogStateInteractor,
+ visibilityInteractor: VolumeDialogVisibilityInteractor,
+) {
+
+ val isShowingSafetyWarning: Flow<Boolean> =
+ stateInteractor.volumeDialogState.map {
+ when (it.isShowingSafetyWarning) {
+ is VolumeDialogSafetyWarningModel.Visible ->
+ if (it.isShowingSafetyWarning.flags and VISIBLE_FLAGS == 0) {
+ visibilityInteractor.dialogVisibility.first() is
+ VolumeDialogVisibilityModel.Visible
+ } else {
+ true
+ }
+ is VolumeDialogSafetyWarningModel.Invisible -> false
+ }
+ }
+
+ fun onSafetyWarningDismissed() {
+ stateInteractor.setSafetyWarning(VolumeDialogSafetyWarningModel.Invisible)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
index 5c7289b..51e7924 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
@@ -23,6 +23,7 @@
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
import com.android.systemui.volume.dialog.data.repository.VolumeDialogStateRepository
import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogSafetyWarningModel
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import javax.inject.Inject
@@ -61,6 +62,9 @@
oldState.copy(shouldShowA11ySlider = event.showA11yStream)
}
}
+ is VolumeDialogEventModel.ShowSafetyWarning -> {
+ setSafetyWarning(VolumeDialogSafetyWarningModel.Visible(event.flags))
+ }
else -> {
// do nothing
}
@@ -72,6 +76,10 @@
val volumeDialogState: Flow<VolumeDialogStateModel> = volumeDialogStateRepository.state
+ fun setSafetyWarning(model: VolumeDialogSafetyWarningModel) {
+ volumeDialogStateRepository.updateState { it.copy(isShowingSafetyWarning = model) }
+ }
+
/** Returns a copy of [model] filled with the values from [VolumeDialogController.State]. */
private fun VolumeDialogController.State.copyIntoModel(
model: VolumeDialogStateModel
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogSafetyWarningModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogSafetyWarningModel.kt
new file mode 100644
index 0000000..39fc222
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogSafetyWarningModel.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.shared.model
+
+/** Models current Volume Safety Dialog state. */
+sealed interface VolumeDialogSafetyWarningModel {
+
+ /** Volume Safety Dialog is visible and has been shown with the [flags]. */
+ data class Visible(val flags: Int) : VolumeDialogSafetyWarningModel
+
+ /** Volume Safety Dialog is invisible. */
+ data object Invisible : VolumeDialogSafetyWarningModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt
index 1792b99..838006d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt
@@ -21,6 +21,8 @@
/** Models a state of the Volume Dialog. */
data class VolumeDialogStateModel(
val shouldShowA11ySlider: Boolean = false,
+ val isShowingSafetyWarning: VolumeDialogSafetyWarningModel =
+ VolumeDialogSafetyWarningModel.Invisible,
val streamModels: Map<Int, VolumeDialogStreamModel> = mapOf(),
val ringerModeInternal: Int = 0,
val ringerModeExternal: Int = 0,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
index ff525f46..9bab1b0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
@@ -18,14 +18,17 @@
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.VolumeDialog
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogSafetyWarningInteractor
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
import javax.inject.Inject
import javax.inject.Provider
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
@@ -34,29 +37,35 @@
class VolumeDialogPluginViewModel
@Inject
constructor(
+ @VolumeDialogPlugin private val coroutineScope: CoroutineScope,
private val dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
+ private val dialogSafetyWarningInteractor: VolumeDialogSafetyWarningInteractor,
private val volumeDialogProvider: Provider<VolumeDialog>,
private val logger: VolumeDialogLogger,
) {
- suspend fun launchVolumeDialog() {
- coroutineScope {
- dialogVisibilityInteractor.dialogVisibility
- .mapLatest { visibilityModel ->
- with(visibilityModel) {
- if (this is VolumeDialogVisibilityModel.Visible) {
- showDialog()
- Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
- logger.onShow(reason)
- }
- if (this is VolumeDialogVisibilityModel.Dismissed) {
- Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason)
- logger.onDismiss(reason)
- }
+ fun launchVolumeDialog() {
+ dialogVisibilityInteractor.dialogVisibility
+ .mapLatest { visibilityModel ->
+ with(visibilityModel) {
+ if (this is VolumeDialogVisibilityModel.Visible) {
+ showDialog()
+ Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
+ logger.onShow(reason)
+ }
+ if (this is VolumeDialogVisibilityModel.Dismissed) {
+ Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason)
+ logger.onDismiss(reason)
}
}
- .launchIn(this)
- }
+ }
+ .launchIn(coroutineScope)
+ }
+
+ val isShowingSafetyWarning: Flow<Boolean> = dialogSafetyWarningInteractor.isShowingSafetyWarning
+
+ fun onSafetyWarningDismissed() {
+ dialogSafetyWarningInteractor.onSafetyWarningDismissed()
}
private fun showDialog() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
index 7a6ede4..b20dffb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
@@ -44,9 +44,9 @@
@Inject
constructor(
private val context: Context,
- dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
+ private val dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor,
- volumeDialogStateInteractor: VolumeDialogStateInteractor,
+ private val volumeDialogStateInteractor: VolumeDialogStateInteractor,
devicePostureController: DevicePostureController,
configurationController: ConfigurationController,
) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index d88b758..266cb51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -181,11 +181,9 @@
@Test
@EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
- fun handlesShortcut_metaCtrlN() {
+ fun handlesShortcut_keyGestureTypeOpenNotes() {
val gestureEvent =
KeyGestureEvent.Builder()
- .setKeycodes(intArrayOf(KeyEvent.KEYCODE_N))
- .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON)
.setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES)
.setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
.build()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorKosmos.kt
new file mode 100644
index 0000000..ddd0014
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.volumeDialogSafetyWarningInteractor: VolumeDialogSafetyWarningInteractor by
+ Kosmos.Fixture {
+ VolumeDialogSafetyWarningInteractor(
+ volumeDialogStateInteractor,
+ volumeDialogVisibilityInteractor,
+ )
+ }
diff --git a/packages/Vcn/TEST_MAPPING b/packages/Vcn/TEST_MAPPING
index bde88fe..9722a83 100644
--- a/packages/Vcn/TEST_MAPPING
+++ b/packages/Vcn/TEST_MAPPING
@@ -1,4 +1,12 @@
{
+ "presubmit": [
+ {
+ "name": "FrameworksVcnTests"
+ },
+ {
+ "name": "CtsVcnTestCases"
+ }
+ ],
"postsubmit": [
{
"name": "FrameworksVcnTests"
diff --git a/packages/Vcn/flags/Android.bp b/packages/Vcn/flags/Android.bp
new file mode 100644
index 0000000..3943c6f
--- /dev/null
+++ b/packages/Vcn/flags/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_team: "trendy_team_enigma",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aconfig_declarations {
+ name: "android.net.vcn.flags-aconfig",
+ package: "android.net.vcn",
+ container: "com.android.tethering",
+ exportable: true,
+ srcs: [
+ "flags.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "android.net.vcn.flags-aconfig-java-export",
+ aconfig_declarations: "android.net.vcn.flags-aconfig",
+ mode: "exported",
+ min_sdk_version: "35",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/core/java/android/net/vcn/flags.aconfig b/packages/Vcn/flags/flags.aconfig
similarity index 100%
rename from core/java/android/net/vcn/flags.aconfig
rename to packages/Vcn/flags/flags.aconfig
diff --git a/packages/Vcn/framework-b/Android.bp b/packages/Vcn/framework-b/Android.bp
index 8b010c7..c312116 100644
--- a/packages/Vcn/framework-b/Android.bp
+++ b/packages/Vcn/framework-b/Android.bp
@@ -19,6 +19,18 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+filegroup {
+ name: "vcn-utils-platform-sources",
+ srcs: [
+ "src/android/net/vcn/persistablebundleutils/**/*.java",
+ "src/android/net/vcn/util/**/*.java",
+ ],
+ path: "src",
+ visibility: [
+ "//frameworks/base", // For VpnProfile.java and Vpn.java
+ ],
+}
+
java_defaults {
name: "framework-connectivity-b-defaults",
sdk_version: "module_current",
@@ -27,7 +39,25 @@
srcs: [
"src/**/*.java",
+ "src/**/*.aidl",
],
+
+ libs: [
+ "android.net.ipsec.ike.stubs.module_lib",
+ "app-compat-annotations",
+ "framework-wifi.stubs.module_lib",
+ "unsupportedappusage",
+ ],
+ static_libs: [
+ //TODO:375213246 Use a non-exported flag lib when VCN is in mainline
+ "android.net.vcn.flags-aconfig-java-export",
+ ],
+ aidl: {
+ include_dirs: [
+ // For connectivity-framework classes such as Network.aidl, NetworkCapabilities.aidl
+ "packages/modules/Connectivity/framework/aidl-export",
+ ],
+ },
}
java_sdk_library {
@@ -36,8 +66,35 @@
"framework-connectivity-b-defaults",
],
+ //TODO: b/375213246 Use "framework-connectivity-jarjar-rules" when VCN is
+ // in mainline
+ jarjar_rules: "framework-vcn-jarjar-rules.txt",
+
permitted_packages: [
+ "android.net",
"android.net.vcn",
+ "com.android.server.vcn.util",
+
+ ],
+ api_packages: [
+ "android.net",
+ "android.net.vcn",
+ ],
+
+ // Allow VCN APIs to reference APIs in IKE and Connectivity
+ stub_only_libs: [
+ "android.net.ipsec.ike.stubs.module_lib",
+ "framework-connectivity.stubs.module_lib",
+ ],
+
+ // To use non-jarjard names of utilities such as android.util.IndentingPrintWriter
+ impl_only_libs: [
+ "framework-connectivity-pre-jarjar",
+ ],
+
+ aconfig_declarations: [
+ //TODO:375213246 Use a non-exported flag lib when VCN is in mainline
+ "android.net.vcn.flags-aconfig-java-export",
],
impl_library_visibility: [
@@ -63,5 +120,18 @@
"//packages/modules/Wifi/service/tests/wifitests",
],
- // TODO: b/375213246 Expose this library to Tethering module
+ apex_available: [
+ // TODO: b/374174952 Remove it when VCN modularization is released
+ "//apex_available:platform",
+
+ "com.android.tethering",
+ ],
+}
+
+java_library {
+ name: "framework-connectivity-b-pre-jarjar",
+ defaults: ["framework-connectivity-b-defaults"],
+ libs: [
+ "framework-connectivity-pre-jarjar",
+ ],
}
diff --git a/packages/Vcn/framework-b/api/current.txt b/packages/Vcn/framework-b/api/current.txt
index d802177..831b741 100644
--- a/packages/Vcn/framework-b/api/current.txt
+++ b/packages/Vcn/framework-b/api/current.txt
@@ -1 +1,123 @@
// Signature format: 2.0
+package android.net.vcn {
+
+ public final class VcnCellUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate {
+ method public int getCbs();
+ method public int getDun();
+ method public int getIms();
+ method public int getInternet();
+ method public int getMms();
+ method @NonNull public java.util.Set<java.lang.String> getOperatorPlmnIds();
+ method public int getOpportunistic();
+ method public int getRcs();
+ method public int getRoaming();
+ method @NonNull public java.util.Set<java.lang.Integer> getSimSpecificCarrierIds();
+ }
+
+ public static final class VcnCellUnderlyingNetworkTemplate.Builder {
+ ctor public VcnCellUnderlyingNetworkTemplate.Builder();
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate build();
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setCbs(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setDun(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setIms(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setInternet(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMetered(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinDownstreamBandwidthKbps(int, int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMms(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOperatorPlmnIds(@NonNull java.util.Set<java.lang.String>);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOpportunistic(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setRcs(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setRoaming(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setSimSpecificCarrierIds(@NonNull java.util.Set<java.lang.Integer>);
+ }
+
+ public final class VcnConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.Set<android.net.vcn.VcnGatewayConnectionConfig> getGatewayConnectionConfigs();
+ method @NonNull public java.util.Set<java.lang.Integer> getRestrictedUnderlyingNetworkTransports();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnConfig> CREATOR;
+ }
+
+ public static final class VcnConfig.Builder {
+ ctor public VcnConfig.Builder(@NonNull android.content.Context);
+ method @NonNull public android.net.vcn.VcnConfig.Builder addGatewayConnectionConfig(@NonNull android.net.vcn.VcnGatewayConnectionConfig);
+ method @NonNull public android.net.vcn.VcnConfig build();
+ method @NonNull public android.net.vcn.VcnConfig.Builder setRestrictedUnderlyingNetworkTransports(@NonNull java.util.Set<java.lang.Integer>);
+ }
+
+ public final class VcnGatewayConnectionConfig {
+ method @NonNull public int[] getExposedCapabilities();
+ method @NonNull public String getGatewayConnectionName();
+ method @IntRange(from=0x500) public int getMaxMtu();
+ method public int getMinUdpPort4500NatTimeoutSeconds();
+ method @NonNull public long[] getRetryIntervalsMillis();
+ method @NonNull public java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities();
+ method public boolean hasGatewayOption(int);
+ method @FlaggedApi("android.net.vcn.safe_mode_config") public boolean isSafeModeEnabled();
+ field @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET = -1; // 0xffffffff
+ field public static final int VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY = 0; // 0x0
+ }
+
+ public static final class VcnGatewayConnectionConfig.Builder {
+ ctor public VcnGatewayConnectionConfig.Builder(@NonNull String, @NonNull android.net.ipsec.ike.IkeTunnelConnectionParams);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addGatewayOption(int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeGatewayOption(int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMinUdpPort4500NatTimeoutSeconds(@IntRange(from=0x78) int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
+ method @FlaggedApi("android.net.vcn.safe_mode_config") @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setSafeModeEnabled(boolean);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setVcnUnderlyingNetworkPriorities(@NonNull java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate>);
+ }
+
+ public class VcnManager {
+ method @RequiresPermission("carrier privileges") public void clearVcnConfig(@NonNull android.os.ParcelUuid) throws java.io.IOException;
+ method @NonNull public java.util.List<android.os.ParcelUuid> getConfiguredSubscriptionGroups();
+ method public void registerVcnStatusCallback(@NonNull android.os.ParcelUuid, @NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnStatusCallback);
+ method @RequiresPermission("carrier privileges") public void setVcnConfig(@NonNull android.os.ParcelUuid, @NonNull android.net.vcn.VcnConfig) throws java.io.IOException;
+ method public void unregisterVcnStatusCallback(@NonNull android.net.vcn.VcnManager.VcnStatusCallback);
+ field public static final int VCN_ERROR_CODE_CONFIG_ERROR = 1; // 0x1
+ field public static final int VCN_ERROR_CODE_INTERNAL_ERROR = 0; // 0x0
+ field public static final int VCN_ERROR_CODE_NETWORK_ERROR = 2; // 0x2
+ field public static final int VCN_STATUS_CODE_ACTIVE = 2; // 0x2
+ field public static final int VCN_STATUS_CODE_INACTIVE = 1; // 0x1
+ field public static final int VCN_STATUS_CODE_NOT_CONFIGURED = 0; // 0x0
+ field public static final int VCN_STATUS_CODE_SAFE_MODE = 3; // 0x3
+ }
+
+ public abstract static class VcnManager.VcnStatusCallback {
+ ctor public VcnManager.VcnStatusCallback();
+ method public abstract void onGatewayConnectionError(@NonNull String, int, @Nullable Throwable);
+ method public abstract void onStatusChanged(int);
+ }
+
+ public abstract class VcnUnderlyingNetworkTemplate {
+ method public int getMetered();
+ method public int getMinEntryDownstreamBandwidthKbps();
+ method public int getMinEntryUpstreamBandwidthKbps();
+ method public int getMinExitDownstreamBandwidthKbps();
+ method public int getMinExitUpstreamBandwidthKbps();
+ field public static final int MATCH_ANY = 0; // 0x0
+ field public static final int MATCH_FORBIDDEN = 2; // 0x2
+ field public static final int MATCH_REQUIRED = 1; // 0x1
+ }
+
+ public final class VcnWifiUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate {
+ method @NonNull public java.util.Set<java.lang.String> getSsids();
+ }
+
+ public static final class VcnWifiUnderlyingNetworkTemplate.Builder {
+ ctor public VcnWifiUnderlyingNetworkTemplate.Builder();
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate build();
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMetered(int);
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMinDownstreamBandwidthKbps(int, int);
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setSsids(@NonNull java.util.Set<java.lang.String>);
+ }
+
+}
+
diff --git a/packages/Vcn/framework-b/api/module-lib-current.txt b/packages/Vcn/framework-b/api/module-lib-current.txt
index d802177..8961b28 100644
--- a/packages/Vcn/framework-b/api/module-lib-current.txt
+++ b/packages/Vcn/framework-b/api/module-lib-current.txt
@@ -1 +1,28 @@
// Signature format: 2.0
+package android.net {
+
+ @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public final class ConnectivityFrameworkInitializerBaklava {
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static void registerServiceWrappers();
+ }
+
+}
+
+package android.net.vcn {
+
+ @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public final class VcnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public int describeContents();
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public long getApplicableRedactions();
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public int getMinUdpPort4500NatTimeoutSeconds();
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.TransportInfo makeCopy(long);
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnTransportInfo> CREATOR;
+ }
+
+ @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static final class VcnTransportInfo.Builder {
+ ctor @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public VcnTransportInfo.Builder();
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.vcn.VcnTransportInfo build();
+ method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.vcn.VcnTransportInfo.Builder setMinUdpPort4500NatTimeoutSeconds(@IntRange(from=0x78) int);
+ }
+
+}
+
diff --git a/packages/Vcn/framework-b/api/system-current.txt b/packages/Vcn/framework-b/api/system-current.txt
index d802177..9c5a677 100644
--- a/packages/Vcn/framework-b/api/system-current.txt
+++ b/packages/Vcn/framework-b/api/system-current.txt
@@ -1 +1,23 @@
// Signature format: 2.0
+package android.net.vcn {
+
+ public class VcnManager {
+ method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.vcn.VcnNetworkPolicyResult applyVcnNetworkPolicy(@NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void removeVcnNetworkPolicyChangeListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
+ }
+
+ public static interface VcnManager.VcnNetworkPolicyChangeListener {
+ method public void onPolicyChanged();
+ }
+
+ public final class VcnNetworkPolicyResult implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
+ method public boolean isTeardownRequested();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnNetworkPolicyResult> CREATOR;
+ }
+
+}
+
diff --git a/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt b/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
new file mode 100644
index 0000000..757043b
--- /dev/null
+++ b/packages/Vcn/framework-b/framework-vcn-jarjar-rules.txt
@@ -0,0 +1,2 @@
+rule android.net.vcn.persistablebundleutils.** android.net.vcn.module.repackaged.persistablebundleutils.@1
+rule android.net.vcn.util.** android.net.vcn.module.repackaged.util.@1
\ No newline at end of file
diff --git a/core/java/android/net/ConnectivityFrameworkInitializerBaklava.java b/packages/Vcn/framework-b/src/android/net/ConnectivityFrameworkInitializerBaklava.java
similarity index 100%
rename from core/java/android/net/ConnectivityFrameworkInitializerBaklava.java
rename to packages/Vcn/framework-b/src/android/net/ConnectivityFrameworkInitializerBaklava.java
diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/packages/Vcn/framework-b/src/android/net/vcn/IVcnManagementService.aidl
similarity index 100%
rename from core/java/android/net/vcn/IVcnManagementService.aidl
rename to packages/Vcn/framework-b/src/android/net/vcn/IVcnManagementService.aidl
diff --git a/core/java/android/net/vcn/IVcnStatusCallback.aidl b/packages/Vcn/framework-b/src/android/net/vcn/IVcnStatusCallback.aidl
similarity index 100%
rename from core/java/android/net/vcn/IVcnStatusCallback.aidl
rename to packages/Vcn/framework-b/src/android/net/vcn/IVcnStatusCallback.aidl
diff --git a/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl b/packages/Vcn/framework-b/src/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
similarity index 100%
rename from core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
rename to packages/Vcn/framework-b/src/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/packages/Vcn/framework-b/src/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
similarity index 100%
rename from core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
rename to packages/Vcn/framework-b/src/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
diff --git a/core/java/android/net/vcn/VcnConfig.aidl b/packages/Vcn/framework-b/src/android/net/vcn/VcnConfig.aidl
similarity index 100%
rename from core/java/android/net/vcn/VcnConfig.aidl
rename to packages/Vcn/framework-b/src/android/net/vcn/VcnConfig.aidl
diff --git a/core/java/android/net/vcn/VcnConfig.java b/packages/Vcn/framework-b/src/android/net/vcn/VcnConfig.java
similarity index 100%
rename from core/java/android/net/vcn/VcnConfig.java
rename to packages/Vcn/framework-b/src/android/net/vcn/VcnConfig.java
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/packages/Vcn/framework-b/src/android/net/vcn/VcnGatewayConnectionConfig.java
similarity index 100%
rename from core/java/android/net/vcn/VcnGatewayConnectionConfig.java
rename to packages/Vcn/framework-b/src/android/net/vcn/VcnGatewayConnectionConfig.java
diff --git a/core/java/android/net/vcn/VcnManager.java b/packages/Vcn/framework-b/src/android/net/vcn/VcnManager.java
similarity index 98%
rename from core/java/android/net/vcn/VcnManager.java
rename to packages/Vcn/framework-b/src/android/net/vcn/VcnManager.java
index f275714..594bbb8 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/packages/Vcn/framework-b/src/android/net/vcn/VcnManager.java
@@ -334,7 +334,7 @@
* @param executor the Executor that will be used for invoking all calls to the specified
* Listener
* @param listener the VcnUnderlyingNetworkPolicyListener to be added
- * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
+ * @throws SecurityException if the caller does not have the required permission
* @throws IllegalStateException if the specified VcnUnderlyingNetworkPolicyListener is already
* registered
* @hide
@@ -423,7 +423,7 @@
* @param executor the Executor that will be used for invoking all calls to the specified
* Listener
* @param listener the VcnNetworkPolicyChangeListener to be added
- * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
+ * @throws SecurityException if the caller does not have the required permission
* @throws IllegalStateException if the specified VcnNetworkPolicyChangeListener is already
* registered
* @hide
@@ -455,7 +455,7 @@
* <p>If the specified listener is not currently registered, this is a no-op.
*
* @param listener the VcnNetworkPolicyChangeListener that will be removed
- * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
+ * @throws SecurityException if the caller does not have the required permission
* @hide
*/
@SystemApi
@@ -489,7 +489,7 @@
* policy result for this Network.
* @param linkProperties the LinkProperties to be used in determining the Network policy result
* for this Network.
- * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
+ * @throws SecurityException if the caller does not have the required permission
* @return the {@link VcnNetworkPolicyResult} to be used for this Network.
* @hide
*/
diff --git a/core/java/android/net/vcn/VcnNetworkPolicyResult.aidl b/packages/Vcn/framework-b/src/android/net/vcn/VcnNetworkPolicyResult.aidl
similarity index 100%
rename from core/java/android/net/vcn/VcnNetworkPolicyResult.aidl
rename to packages/Vcn/framework-b/src/android/net/vcn/VcnNetworkPolicyResult.aidl
diff --git a/core/java/android/net/vcn/VcnNetworkPolicyResult.java b/packages/Vcn/framework-b/src/android/net/vcn/VcnNetworkPolicyResult.java
similarity index 100%
rename from core/java/android/net/vcn/VcnNetworkPolicyResult.java
rename to packages/Vcn/framework-b/src/android/net/vcn/VcnNetworkPolicyResult.java
diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/packages/Vcn/framework-b/src/android/net/vcn/VcnTransportInfo.java
similarity index 100%
rename from core/java/android/net/vcn/VcnTransportInfo.java
rename to packages/Vcn/framework-b/src/android/net/vcn/VcnTransportInfo.java
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl b/packages/Vcn/framework-b/src/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl
similarity index 100%
rename from core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl
rename to packages/Vcn/framework-b/src/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java b/packages/Vcn/framework-b/src/android/net/vcn/VcnUnderlyingNetworkPolicy.java
similarity index 100%
rename from core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java
rename to packages/Vcn/framework-b/src/android/net/vcn/VcnUnderlyingNetworkPolicy.java
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java b/packages/Vcn/framework-b/src/android/net/vcn/VcnUnderlyingNetworkSpecifier.java
similarity index 100%
rename from core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java
rename to packages/Vcn/framework-b/src/android/net/vcn/VcnUnderlyingNetworkSpecifier.java
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/packages/Vcn/framework-b/src/android/net/vcn/VcnUnderlyingNetworkTemplate.java
similarity index 100%
rename from core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
rename to packages/Vcn/framework-b/src/android/net/vcn/VcnUnderlyingNetworkTemplate.java
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/packages/Vcn/framework-b/src/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
similarity index 100%
rename from core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
rename to packages/Vcn/framework-b/src/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
diff --git a/core/java/android/net/vcn/persistablebundleutils/CertUtils.java b/packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/CertUtils.java
similarity index 100%
rename from core/java/android/net/vcn/persistablebundleutils/CertUtils.java
rename to packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/CertUtils.java
diff --git a/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java b/packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java
similarity index 100%
rename from core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java
rename to packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java
diff --git a/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java b/packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java
similarity index 100%
rename from core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java
rename to packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java b/packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java
similarity index 100%
rename from core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java
rename to packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java b/packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java
similarity index 100%
rename from core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java
rename to packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java b/packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
similarity index 100%
rename from core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
rename to packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtils.java b/packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtils.java
similarity index 100%
rename from core/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtils.java
rename to packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtils.java
diff --git a/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java b/packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java
similarity index 100%
rename from core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java
rename to packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java
diff --git a/core/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java b/packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java
similarity index 100%
rename from core/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java
rename to packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java
diff --git a/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java b/packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java
similarity index 100%
rename from core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java
rename to packages/Vcn/framework-b/src/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java
diff --git a/core/java/android/net/vcn/util/LogUtils.java b/packages/Vcn/framework-b/src/android/net/vcn/util/LogUtils.java
similarity index 95%
rename from core/java/android/net/vcn/util/LogUtils.java
rename to packages/Vcn/framework-b/src/android/net/vcn/util/LogUtils.java
index 7f7f852..742aa76 100644
--- a/core/java/android/net/vcn/util/LogUtils.java
+++ b/packages/Vcn/framework-b/src/android/net/vcn/util/LogUtils.java
@@ -19,7 +19,7 @@
import android.annotation.Nullable;
import android.os.ParcelUuid;
-import com.android.internal.util.HexDump;
+import com.android.net.module.util.HexDump;
/** @hide */
public class LogUtils {
diff --git a/core/java/android/net/vcn/util/MtuUtils.java b/packages/Vcn/framework-b/src/android/net/vcn/util/MtuUtils.java
similarity index 100%
rename from core/java/android/net/vcn/util/MtuUtils.java
rename to packages/Vcn/framework-b/src/android/net/vcn/util/MtuUtils.java
diff --git a/core/java/android/net/vcn/util/OneWayBoolean.java b/packages/Vcn/framework-b/src/android/net/vcn/util/OneWayBoolean.java
similarity index 100%
rename from core/java/android/net/vcn/util/OneWayBoolean.java
rename to packages/Vcn/framework-b/src/android/net/vcn/util/OneWayBoolean.java
diff --git a/core/java/android/net/vcn/util/PersistableBundleUtils.java b/packages/Vcn/framework-b/src/android/net/vcn/util/PersistableBundleUtils.java
similarity index 99%
rename from core/java/android/net/vcn/util/PersistableBundleUtils.java
rename to packages/Vcn/framework-b/src/android/net/vcn/util/PersistableBundleUtils.java
index 4dc42c7..8a687d8 100644
--- a/core/java/android/net/vcn/util/PersistableBundleUtils.java
+++ b/packages/Vcn/framework-b/src/android/net/vcn/util/PersistableBundleUtils.java
@@ -21,7 +21,7 @@
import android.os.ParcelUuid;
import android.os.PersistableBundle;
-import com.android.internal.util.HexDump;
+import com.android.net.module.util.HexDump;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
diff --git a/packages/Vcn/service-b/Android.bp b/packages/Vcn/service-b/Android.bp
index 03ef4e6..26d8397 100644
--- a/packages/Vcn/service-b/Android.bp
+++ b/packages/Vcn/service-b/Android.bp
@@ -32,6 +32,33 @@
visibility: ["//frameworks/base/services/core"],
}
+// Do not static include this lib in VCN because these files exist in
+// both service-connectivity.jar and framework.jar
+// TODO: b/374174952 After VCN moves to Connectivity/ and the modularization is done
+// this lib can be removed and "service-connectivity-b-pre-jarjar" can include
+// "service-connectivity-pre-jarjar"
+java_library {
+ name: "connectivity-utils-service-vcn-internal",
+ sdk_version: "module_current",
+ min_sdk_version: "30",
+ srcs: [
+ ":framework-connectivity-shared-srcs",
+ ],
+ libs: [
+ "framework-annotations-lib",
+ "unsupportedappusage",
+ ],
+ visibility: [
+ "//visibility:private",
+ ],
+ apex_available: [
+ // TODO: b/374174952 Remove it when VCN modularization is released
+ "//apex_available:platform",
+
+ "com.android.tethering",
+ ],
+}
+
java_library {
name: "service-connectivity-b-pre-jarjar",
sdk_version: "system_server_current",
@@ -42,8 +69,32 @@
"src/**/*.java",
],
- // TODO: b/375213246 Expose this library to Tethering module
+ libs: [
+ "android.net.ipsec.ike.stubs.module_lib",
+ "connectivity-utils-service-vcn-internal",
+ "framework-annotations-lib",
+ "framework-connectivity-pre-jarjar",
+ "framework-connectivity-t-pre-jarjar",
+ "framework-connectivity-b-pre-jarjar",
+ "framework-wifi.stubs.module_lib",
+ "modules-utils-statemachine",
+ "unsupportedappusage",
+ ],
+
+ // TODO: b/374174952 Dynamically include these libs when VCN
+ // modularization is released
+ static_libs: [
+ "net-utils-service-vcn",
+ "modules-utils-handlerexecutor",
+ ],
+
visibility: [
"//frameworks/base/services",
],
+ apex_available: [
+ // TODO: b/374174952 Remove it when VCN modularization is released
+ "//apex_available:platform",
+
+ "com.android.tethering",
+ ],
}
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
similarity index 97%
rename from services/core/java/com/android/server/VcnManagementService.java
rename to packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
index a45b715..26db6a9 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/packages/Vcn/service-b/src/com/android/server/VcnManagementService.java
@@ -86,6 +86,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.net.module.util.BinderUtils;
+import com.android.net.module.util.HandlerUtils;
import com.android.net.module.util.LocationPermissionChecker;
import com.android.net.module.util.PermissionUtils;
import com.android.server.vcn.TelephonySubscriptionTracker;
@@ -1332,41 +1333,46 @@
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "| ");
// Post to handler thread to prevent ConcurrentModificationExceptions, and avoid lock-hell.
- mHandler.runWithScissors(() -> {
- mNetworkProvider.dump(pw);
- pw.println();
+ HandlerUtils.runWithScissorsForDump(
+ mHandler,
+ () -> {
+ mNetworkProvider.dump(pw);
+ pw.println();
- mTrackingNetworkCallback.dump(pw);
- pw.println();
+ mTrackingNetworkCallback.dump(pw);
+ pw.println();
- synchronized (mLock) {
- mLastSnapshot.dump(pw);
- pw.println();
+ synchronized (mLock) {
+ mLastSnapshot.dump(pw);
+ pw.println();
- pw.println("mConfigs:");
- pw.increaseIndent();
- for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) {
- pw.println(entry.getKey() + ": "
- + entry.getValue().getProvisioningPackageName());
- }
- pw.decreaseIndent();
- pw.println();
+ pw.println("mConfigs:");
+ pw.increaseIndent();
+ for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) {
+ pw.println(
+ entry.getKey()
+ + ": "
+ + entry.getValue().getProvisioningPackageName());
+ }
+ pw.decreaseIndent();
+ pw.println();
- pw.println("mVcns:");
- pw.increaseIndent();
- for (Vcn vcn : mVcns.values()) {
- vcn.dump(pw);
- }
- pw.decreaseIndent();
- pw.println();
- }
+ pw.println("mVcns:");
+ pw.increaseIndent();
+ for (Vcn vcn : mVcns.values()) {
+ vcn.dump(pw);
+ }
+ pw.decreaseIndent();
+ pw.println();
+ }
- pw.println("Local log:");
- pw.increaseIndent();
- LOCAL_LOG.dump(pw);
- pw.decreaseIndent();
- pw.println();
- }, DUMP_TIMEOUT_MILLIS);
+ pw.println("Local log:");
+ pw.increaseIndent();
+ LOCAL_LOG.dump(pw);
+ pw.decreaseIndent();
+ pw.println();
+ },
+ DUMP_TIMEOUT_MILLIS);
}
// TODO(b/180452282): Make name more generic and implement directly with VcnManagementService
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
similarity index 99%
rename from services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
rename to packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
index 154897e..b448f75 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -29,7 +29,6 @@
import android.net.vcn.VcnManager;
import android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
@@ -46,6 +45,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.modules.utils.HandlerExecutor;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
similarity index 99%
rename from services/core/java/com/android/server/vcn/Vcn.java
rename to packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
index 95acb10..2524d0e 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/Vcn.java
@@ -40,7 +40,6 @@
import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.util.LogUtils;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.Message;
import android.os.ParcelUuid;
import android.provider.Settings;
@@ -53,6 +52,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.modules.utils.HandlerExecutor;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/packages/Vcn/service-b/src/com/android/server/vcn/VcnContext.java
similarity index 100%
rename from services/core/java/com/android/server/vcn/VcnContext.java
rename to packages/Vcn/service-b/src/com/android/server/vcn/VcnContext.java
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
similarity index 99%
rename from services/core/java/com/android/server/vcn/VcnGatewayConnection.java
rename to packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
index 9ccf040..f024b5f 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/VcnGatewayConnection.java
@@ -45,6 +45,7 @@
import android.net.IpPrefix;
import android.net.IpSecManager;
import android.net.IpSecManager.IpSecTunnelInterface;
+import android.net.IpSecManager.PolicyDirection;
import android.net.IpSecManager.ResourceUnavailableException;
import android.net.IpSecTransform;
import android.net.LinkAddress;
@@ -59,7 +60,6 @@
import android.net.RouteInfo;
import android.net.TelephonyNetworkSpecifier;
import android.net.Uri;
-import android.net.annotations.PolicyDirection;
import android.net.ipsec.ike.ChildSaProposal;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.ChildSessionConfiguration;
@@ -83,7 +83,6 @@
import android.net.vcn.util.OneWayBoolean;
import android.net.wifi.WifiInfo;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.Message;
import android.os.ParcelUuid;
import android.os.PowerManager;
@@ -101,6 +100,7 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.WakeupMessage;
+import com.android.modules.utils.HandlerExecutor;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.routeselection.UnderlyingNetworkController;
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
similarity index 99%
rename from services/core/java/com/android/server/vcn/VcnNetworkProvider.java
rename to packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
index 78ff432..4552f50 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/VcnNetworkProvider.java
@@ -33,7 +33,6 @@
import android.net.NetworkScore;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.Looper;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
@@ -41,6 +40,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.modules.utils.HandlerExecutor;
import java.util.Objects;
import java.util.Set;
diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
similarity index 99%
rename from services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
rename to packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index e6a1ff9..72de613 100644
--- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -32,12 +32,12 @@
import android.net.Network;
import android.net.vcn.VcnManager;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.OutcomeReceiver;
import android.os.PowerManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.modules.utils.HandlerExecutor;
import com.android.server.vcn.VcnContext;
import java.lang.annotation.ElementType;
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
similarity index 100%
rename from services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
rename to packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
similarity index 100%
rename from services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
rename to packages/Vcn/service-b/src/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
similarity index 99%
rename from services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
rename to packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index f7a564a..29a0762 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -41,7 +41,6 @@
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.net.vcn.util.LogUtils;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.ParcelUuid;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
@@ -52,6 +51,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.modules.utils.HandlerExecutor;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.VcnContext;
import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback;
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
similarity index 100%
rename from services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
rename to packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
similarity index 100%
rename from services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
rename to packages/Vcn/service-b/src/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
diff --git a/services/Android.bp b/services/Android.bp
index fc0bb33..225304f 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -294,6 +294,10 @@
"service-permission.stubs.system_server",
"service-rkp.stubs.system_server",
"service-sdksandbox.stubs.system_server",
+
+ // TODO: b/30242953 This is for accessing IVcnManagementService and
+ // can be removed VCN is in mainline
+ "framework-connectivity-b-pre-jarjar",
],
soong_config_variables: {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index c4e1036..e10bdaa 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -765,6 +765,7 @@
private float mBacklightMinimum = Float.NaN;
private float mBacklightMaximum = Float.NaN;
private float mBrightnessDefault = Float.NaN;
+ private float mBrightnessDim = Float.NaN;
private float mBrightnessRampFastDecrease = Float.NaN;
private float mBrightnessRampFastIncrease = Float.NaN;
private float mBrightnessRampSlowDecrease = Float.NaN;
@@ -1282,6 +1283,24 @@
}
/**
+ * Return the minimum brightness on a scale of 0.0f - 1.0f
+ *
+ * @return minimum brightness
+ */
+ public float getBrightnessMinimum() {
+ return getBrightnessFromBacklight(mBacklightMinimum);
+ }
+
+ /**
+ * Return the maximum brightness on a scale of 0.0f - 1.0f
+ *
+ * @return maximum brightness
+ */
+ public float getBrightnessMaximum() {
+ return getBrightnessFromBacklight(mBacklightMaximum);
+ }
+
+ /**
* Return the default brightness on a scale of 0.0f - 1.0f
*
* @return default brightness
@@ -1290,6 +1309,15 @@
return mBrightnessDefault;
}
+ /**
+ * Return the dim brightness on a scale of 0.0f - 1.0f
+ *
+ * @return dim brightness
+ */
+ public float getBrightnessDim() {
+ return mBrightnessDim;
+ }
+
public float getBrightnessRampFastDecrease() {
return mBrightnessRampFastDecrease;
}
@@ -1689,6 +1717,7 @@
+ ", mBacklightMinimum=" + mBacklightMinimum
+ ", mBacklightMaximum=" + mBacklightMaximum
+ ", mBrightnessDefault=" + mBrightnessDefault
+ + ", mBrightnessDim=" + mBrightnessDim
+ ", mQuirks=" + mQuirks
+ "\n"
+ "mLuxThrottlingData=" + mLuxThrottlingData
@@ -1906,6 +1935,7 @@
mBacklightMinimum = PowerManager.BRIGHTNESS_MIN;
mBacklightMaximum = PowerManager.BRIGHTNESS_MAX;
mBrightnessDefault = BRIGHTNESS_DEFAULT;
+ mBrightnessDim = PowerManager.BRIGHTNESS_INVALID;
mBrightnessRampFastDecrease = PowerManager.BRIGHTNESS_MAX;
mBrightnessRampFastIncrease = PowerManager.BRIGHTNESS_MAX;
mBrightnessRampSlowDecrease = PowerManager.BRIGHTNESS_MAX;
@@ -2003,6 +2033,15 @@
mBacklightMinimum = min;
mBacklightMaximum = max;
}
+ final float dim = mContext.getResources().getFloat(com.android.internal.R.dimen
+ .config_screenBrightnessDimFloat);
+ if (dim == INVALID_BRIGHTNESS_IN_CONFIG) {
+ mBrightnessDim = BrightnessSynchronizer.brightnessIntToFloat(
+ mContext.getResources().getInteger(com.android.internal.R.integer
+ .config_screenBrightnessDim));
+ } else {
+ mBrightnessDim = dim;
+ }
}
private void loadBrightnessMap(DisplayConfiguration config) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 1c1bdad..3aaf4f6 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -477,6 +477,7 @@
public float brightnessMinimum;
public float brightnessMaximum;
public float brightnessDefault;
+ public float brightnessDim;
// NaN means unsupported
public float hdrSdrRatio = Float.NaN;
@@ -561,8 +562,8 @@
|| !Objects.equals(ownerPackageName, other.ownerPackageName)
|| !BrightnessSynchronizer.floatEquals(brightnessMinimum, other.brightnessMinimum)
|| !BrightnessSynchronizer.floatEquals(brightnessMaximum, other.brightnessMaximum)
- || !BrightnessSynchronizer.floatEquals(brightnessDefault,
- other.brightnessDefault)
+ || !BrightnessSynchronizer.floatEquals(brightnessDefault, other.brightnessDefault)
+ || !BrightnessSynchronizer.floatEquals(brightnessDim, other.brightnessDim)
|| !Objects.equals(roundedCorners, other.roundedCorners)
|| installOrientation != other.installOrientation
|| !Objects.equals(displayShape, other.displayShape)
@@ -618,6 +619,7 @@
brightnessMinimum = other.brightnessMinimum;
brightnessMaximum = other.brightnessMaximum;
brightnessDefault = other.brightnessDefault;
+ brightnessDim = other.brightnessDim;
hdrSdrRatio = other.hdrSdrRatio;
roundedCorners = other.roundedCorners;
installOrientation = other.installOrientation;
@@ -672,6 +674,7 @@
sb.append(", brightnessMinimum ").append(brightnessMinimum);
sb.append(", brightnessMaximum ").append(brightnessMaximum);
sb.append(", brightnessDefault ").append(brightnessDefault);
+ sb.append(", brightnessDim ").append(brightnessDim);
sb.append(", hdrSdrRatio ").append(hdrSdrRatio);
if (roundedCorners != null) {
sb.append(", roundedCorners ").append(roundedCorners);
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 0b8f7d5..d37dd30 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -812,9 +812,10 @@
// The display is trusted since it is created by system.
mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
- mInfo.brightnessMinimum = PowerManager.BRIGHTNESS_MIN;
- mInfo.brightnessMaximum = PowerManager.BRIGHTNESS_MAX;
+ mInfo.brightnessMinimum = getDisplayDeviceConfig().getBrightnessMinimum();
+ mInfo.brightnessMaximum = getDisplayDeviceConfig().getBrightnessMaximum();
mInfo.brightnessDefault = getDisplayDeviceConfig().getBrightnessDefault();
+ mInfo.brightnessDim = getDisplayDeviceConfig().getBrightnessDim();
mInfo.hdrSdrRatio = mCurrentHdrSdrRatio;
}
return mInfo;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 8546598..1de9c95 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -548,6 +548,7 @@
mBaseDisplayInfo.brightnessMinimum = deviceInfo.brightnessMinimum;
mBaseDisplayInfo.brightnessMaximum = deviceInfo.brightnessMaximum;
mBaseDisplayInfo.brightnessDefault = deviceInfo.brightnessDefault;
+ mBaseDisplayInfo.brightnessDim = deviceInfo.brightnessDim;
mBaseDisplayInfo.hdrSdrRatio = deviceInfo.hdrSdrRatio;
mBaseDisplayInfo.roundedCorners = deviceInfo.roundedCorners;
mBaseDisplayInfo.installOrientation = deviceInfo.installOrientation;
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 6ae58c4..836f4ed 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -340,6 +340,7 @@
private boolean mIsWindowManagerMirroring;
private final DisplayCutout mDisplayCutout;
private final float mDefaultBrightness;
+ private final float mDimBrightness;
private float mCurrentBrightness;
private final IBrightnessListener mBrightnessListener;
@@ -359,6 +360,7 @@
mRequestedRefreshRate = virtualDisplayConfig.getRequestedRefreshRate();
mDisplayCutout = virtualDisplayConfig.getDisplayCutout();
mDefaultBrightness = virtualDisplayConfig.getDefaultBrightness();
+ mDimBrightness = virtualDisplayConfig.getDimBrightness();
mCurrentBrightness = PowerManager.BRIGHTNESS_INVALID;
mBrightnessListener = virtualDisplayConfig.getBrightnessListener();
mMode = createMode(mWidth, mHeight, getRefreshRate());
@@ -645,6 +647,7 @@
mInfo.brightnessMinimum = PowerManager.BRIGHTNESS_MIN;
mInfo.brightnessMaximum = PowerManager.BRIGHTNESS_MAX;
mInfo.brightnessDefault = mDefaultBrightness;
+ mInfo.brightnessDim = mDimBrightness;
mInfo.ownerUid = mOwnerUid;
mInfo.ownerPackageName = mOwnerPackageName;
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 7ccab05..ceade37 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -267,7 +267,7 @@
}
}
- modifiers.add(new DisplayDimModifier(context));
+ modifiers.add(new DisplayDimModifier(data.mDisplayId, context));
modifiers.add(new BrightnessLowPowerModeModifier());
if (flags.isEvenDimmerEnabled() && data.mDisplayDeviceConfig.isEvenDimmerAvailable()) {
modifiers.add(new BrightnessLowLuxModifier(handler, listener, context,
diff --git a/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java
index ab880bf..0237af3 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java
@@ -38,12 +38,12 @@
// mScreenBrightnessDimConfig.
private final float mScreenBrightnessMinimumDimAmount;
- DisplayDimModifier(Context context) {
+ DisplayDimModifier(int displayId, Context context) {
PowerManager pm = Objects.requireNonNull(context.getSystemService(PowerManager.class));
Resources resources = context.getResources();
mScreenBrightnessDimConfig = BrightnessUtils.clampAbsoluteBrightness(
- pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM));
+ pm.getBrightnessConstraint(displayId, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM));
mScreenBrightnessMinimumDimAmount = resources.getFloat(
R.dimen.config_screenBrightnessMinimumDimAmountFloat);
}
diff --git a/services/core/java/com/android/server/pm/InstallDependencyHelper.java b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
index 13aab11..673a102 100644
--- a/services/core/java/com/android/server/pm/InstallDependencyHelper.java
+++ b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
@@ -32,11 +32,13 @@
import android.content.pm.dependencyinstaller.IDependencyInstallerCallback;
import android.content.pm.dependencyinstaller.IDependencyInstallerService;
import android.content.pm.parsing.PackageLite;
+import android.os.Binder;
import android.os.Handler;
import android.os.OutcomeReceiver;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -63,12 +65,11 @@
private final Context mContext;
private final SharedLibrariesImpl mSharedLibraries;
private final PackageInstallerService mPackageInstallerService;
- private final Object mRemoteServiceLock = new Object();
@GuardedBy("mTrackers")
private final List<DependencyInstallTracker> mTrackers = new ArrayList<>();
-
- @GuardedBy("mRemoteServiceLock")
- private ServiceConnector<IDependencyInstallerService> mRemoteService = null;
+ @GuardedBy("mRemoteServices")
+ private final ArrayMap<Integer, ServiceConnector<IDependencyInstallerService>> mRemoteServices =
+ new ArrayMap<>();
InstallDependencyHelper(Context context, SharedLibrariesImpl sharedLibraries,
PackageInstallerService packageInstallerService) {
@@ -97,13 +98,18 @@
if (missing.isEmpty()) {
if (DEBUG) {
- Slog.d(TAG, "No missing dependency for " + pkg);
+ Slog.d(TAG, "No missing dependency for " + pkg.getPackageName());
}
// No need for dependency resolution. Move to installation directly.
callback.onResult(null);
return;
}
+ if (DEBUG) {
+ Slog.d(TAG, "Missing dependencies found for pkg: " + pkg.getPackageName()
+ + " user: " + userId);
+ }
+
if (!bindToDependencyInstallerIfNeeded(userId, handler, snapshot)) {
onError(callback, "Dependency Installer Service not found");
return;
@@ -112,8 +118,8 @@
IDependencyInstallerCallback serviceCallback =
new DependencyInstallerCallbackCallOnce(handler, callback, userId);
boolean scheduleSuccess;
- synchronized (mRemoteServiceLock) {
- scheduleSuccess = mRemoteService.run(service -> {
+ synchronized (mRemoteServices) {
+ scheduleSuccess = mRemoteServices.get(userId).run(service -> {
service.onDependenciesRequired(missing,
new DependencyInstallerCallback(serviceCallback.asBinder()));
});
@@ -149,15 +155,17 @@
private boolean bindToDependencyInstallerIfNeeded(int userId, Handler handler,
Computer snapshot) {
- synchronized (mRemoteServiceLock) {
- if (mRemoteService != null) {
+ synchronized (mRemoteServices) {
+ if (mRemoteServices.containsKey(userId)) {
if (DEBUG) {
- Slog.i(TAG, "DependencyInstallerService already bound");
+ Slog.i(TAG, "DependencyInstallerService for user " + userId + " already bound");
}
return true;
}
}
+ Slog.i(TAG, "Attempting to bind to Dependency Installer Service for user " + userId);
+
RoleManager roleManager = mContext.getSystemService(RoleManager.class);
if (roleManager == null) {
Slog.w(TAG, "Cannot find RoleManager system service");
@@ -166,7 +174,7 @@
List<String> holders = roleManager.getRoleHoldersAsUser(
ROLE_SYSTEM_DEPENDENCY_INSTALLER, UserHandle.of(userId));
if (holders.isEmpty()) {
- Slog.w(TAG, "No holders of ROLE_SYSTEM_DEPENDENCY_INSTALLER found");
+ Slog.w(TAG, "No holders of ROLE_SYSTEM_DEPENDENCY_INSTALLER found for user " + userId);
return false;
}
@@ -178,6 +186,8 @@
/*includeInstantApps*/ false, /*resolveForStart*/ false);
if (resolvedIntents.isEmpty()) {
+ Slog.w(TAG, "No package holding ROLE_SYSTEM_DEPENDENCY_INSTALLER found for user "
+ + userId);
return false;
}
@@ -206,13 +216,14 @@
};
- synchronized (mRemoteServiceLock) {
+ synchronized (mRemoteServices) {
// Some other thread managed to connect to the service first
- if (mRemoteService != null) {
+ if (mRemoteServices.containsKey(userId)) {
return true;
}
- mRemoteService = serviceConnector;
- mRemoteService.setServiceLifecycleCallbacks(
+ mRemoteServices.put(userId, serviceConnector);
+ // Block the lock until we connect to the service
+ serviceConnector.setServiceLifecycleCallbacks(
new ServiceConnector.ServiceLifecycleCallbacks<>() {
@Override
public void onDisconnected(@NonNull IDependencyInstallerService service) {
@@ -228,17 +239,18 @@
}
private void destroy() {
- synchronized (mRemoteServiceLock) {
- if (mRemoteService != null) {
- mRemoteService.unbind();
- mRemoteService = null;
+ synchronized (mRemoteServices) {
+ if (mRemoteServices.containsKey(userId)) {
+ mRemoteServices.get(userId).unbind();
+ mRemoteServices.remove(userId);
}
}
}
});
- AndroidFuture<IDependencyInstallerService> unusedFuture = mRemoteService.connect();
+ AndroidFuture<IDependencyInstallerService> unusedFuture = serviceConnector.connect();
}
+ Slog.i(TAG, "Successfully bound to Dependency Installer Service for user " + userId);
return true;
}
@@ -318,30 +330,40 @@
Slog.d(TAG, "onAllDependenciesResolved started");
}
- // Before creating any tracker, validate the arguments
- ArraySet<Integer> validSessionIds = validateSessionIds(sessionIds);
+ try {
+ // Before creating any tracker, validate the arguments
+ ArraySet<Integer> validSessionIds = validateSessionIds(sessionIds);
- if (validSessionIds.isEmpty()) {
- mCallback.onResult(null);
- return;
- }
-
- // Create a tracker now if there are any pending sessions remaining.
- DependencyInstallTracker tracker = new DependencyInstallTracker(
- mCallback, validSessionIds);
- synchronized (mTrackers) {
- mTrackers.add(tracker);
- }
-
- // By the time the tracker was created, some of the sessions in validSessionIds
- // could have finished. Avoid waiting for them indefinitely.
- for (int sessionId : validSessionIds) {
- SessionInfo sessionInfo = mPackageInstallerService.getSessionInfo(sessionId);
-
- // Don't wait for sessions that finished already
- if (sessionInfo == null) {
- notifySessionComplete(sessionId, /*success=*/ true);
+ if (validSessionIds.isEmpty()) {
+ mCallback.onResult(null);
+ return;
}
+
+ // Create a tracker now if there are any pending sessions remaining.
+ DependencyInstallTracker tracker = new DependencyInstallTracker(
+ mCallback, validSessionIds);
+ synchronized (mTrackers) {
+ mTrackers.add(tracker);
+ }
+
+ // By the time the tracker was created, some of the sessions in validSessionIds
+ // could have finished. Avoid waiting for them indefinitely.
+ for (int sessionId : validSessionIds) {
+ SessionInfo sessionInfo = mPackageInstallerService.getSessionInfo(sessionId);
+
+ // Don't wait for sessions that finished already
+ if (sessionInfo == null) {
+ Binder.withCleanCallingIdentity(() -> {
+ notifySessionComplete(sessionId, /*success=*/ true);
+ });
+ }
+ }
+ } catch (Exception e) {
+ // Allow calling the callback again
+ synchronized (this) {
+ mDependencyInstallerCallbackInvoked = false;
+ }
+ throw e;
}
}
@@ -354,7 +376,10 @@
}
mDependencyInstallerCallbackInvoked = true;
}
- onError(mCallback, "Failed to resolve all dependencies automatically");
+
+ Binder.withCleanCallingIdentity(() -> {
+ onError(mCallback, "Failed to resolve all dependencies automatically");
+ });
}
private ArraySet<Integer> validateSessionIds(int[] sessionIds) {
@@ -369,7 +394,8 @@
// Continue waiting if session exists and hasn't passed or failed yet.
if (sessionInfo != null) {
if (sessionInfo.isSessionFailed) {
- throwValidationError("Session already finished: " + sessionId);
+ throw new IllegalArgumentException("Session already finished: "
+ + sessionId);
}
// Wait for session to finish install if it's not already successful.
@@ -398,25 +424,17 @@
s -> s.sessionId == sessionId).findFirst().orElse(null);
if (sessionInfo == null) {
- throwValidationError("Failed to find session: " + sessionId);
+ throw new IllegalArgumentException("Failed to find session: " + sessionId);
}
// Historical session must have been successful, otherwise throw IAE.
if (!sessionInfo.isSessionApplied) {
- throwValidationError("Session already finished: " + sessionId);
+ throw new IllegalArgumentException("Session already finished: " + sessionId);
}
}
return validSessionIds;
}
-
- private void throwValidationError(String msg) {
- // Allow client to invoke callback again.
- synchronized (this) {
- mDependencyInstallerCallbackInvoked = false;
- }
- throw new IllegalArgumentException(msg);
- }
}
/**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 8e73023..7c4d425 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4426,9 +4426,9 @@
int screenDisplayId = displayId < 0 ? DEFAULT_DISPLAY : displayId;
float minLinearBrightness = mPowerManager.getBrightnessConstraint(
- PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
+ screenDisplayId, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
float maxLinearBrightness = mPowerManager.getBrightnessConstraint(
- PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
+ screenDisplayId, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
float linearBrightness = mDisplayManager.getBrightness(screenDisplayId);
float gammaBrightness = BrightnessUtils.convertLinearToGamma(linearBrightness);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index c573293..36bc0b9 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -6099,16 +6099,28 @@
}
}
- public float getBrightnessConstraint(int constraint) {
+ @Override
+ public float getBrightnessConstraint(
+ int displayId, @PowerManager.BrightnessConstraint int constraint) {
+ DisplayInfo info = null;
+ if (android.companion.virtualdevice.flags.Flags.displayPowerManagerApis()
+ && mDisplayManagerInternal != null) {
+ info = mDisplayManagerInternal.getDisplayInfo(displayId);
+ }
switch (constraint) {
case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM:
- return mScreenBrightnessMinimum;
+ return info != null && isValidBrightnessValue(info.brightnessMinimum)
+ ? info.brightnessMinimum : mScreenBrightnessMinimum;
case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM:
- return mScreenBrightnessMaximum;
+ return info != null && isValidBrightnessValue(info.brightnessMaximum)
+ ? info.brightnessMaximum : mScreenBrightnessMaximum;
case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT:
- return mScreenBrightnessDefault;
+ return info != null && isValidBrightnessValue(info.brightnessDefault)
+ ? info.brightnessDefault : mScreenBrightnessDefault;
case PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM:
- return mScreenBrightnessDim;
+ return android.companion.virtualdevice.flags.Flags.deviceAwareDisplayPower()
+ && info != null && isValidBrightnessValue(info.brightnessDim)
+ ? info.brightnessDim : mScreenBrightnessDim;
default:
return PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
diff --git a/services/core/java/com/android/server/vcn/TEST_MAPPING b/services/core/java/com/android/server/vcn/TEST_MAPPING
deleted file mode 100644
index 5b04d88..0000000
--- a/services/core/java/com/android/server/vcn/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "presubmit": [
- {
- "name": "FrameworksVcnTests"
- },
- {
- "name": "CtsVcnTestCases"
- }
- ]
-}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index 4e79e37..37e8f62 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -424,6 +424,7 @@
|| first.brightnessMinimum != second.brightnessMinimum
|| first.brightnessMaximum != second.brightnessMaximum
|| first.brightnessDefault != second.brightnessDefault
+ || first.brightnessDim != second.brightnessDim
|| first.installOrientation != second.installOrientation
|| first.isForceSdr != second.isForceSdr
|| !Objects.equals(first.layoutLimitedRefreshRate, second.layoutLimitedRefreshRate)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
index b002a1f..241dc10 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -327,6 +329,23 @@
}
@Test
+ public void testBrightnessConfigurationFromDisplayDevice() {
+ mDisplayDeviceInfo.brightnessMinimum = 0.12f;
+ mDisplayDeviceInfo.brightnessDim = 0.34f;
+ mDisplayDeviceInfo.brightnessDefault = 0.56f;
+ mDisplayDeviceInfo.brightnessMaximum = 0.78f;
+
+ mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice);
+ mLogicalDisplay.updateLocked(mDeviceRepo, mSyntheticModeManager);
+
+ DisplayInfo info = mLogicalDisplay.getDisplayInfoLocked();
+ assertThat(info.brightnessMinimum).isEqualTo(0.12f);
+ assertThat(info.brightnessDim).isEqualTo(0.34f);
+ assertThat(info.brightnessDefault).isEqualTo(0.56f);
+ assertThat(info.brightnessMaximum).isEqualTo(0.78f);
+ }
+
+ @Test
public void testGetDisplayPosition() {
Point expectedPosition = new Point(0, 0);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index 3ac7fb0..dbd5c65 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -25,10 +27,12 @@
import android.hardware.display.VirtualDisplayConfig;
import android.media.projection.IMediaProjection;
import android.os.IBinder;
+import android.os.PowerManager;
import android.os.Process;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.TestableContext;
+import android.view.Display;
import android.view.Surface;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -56,6 +60,9 @@
private static final int MAX_DEVICES = 3;
private static final int MAX_DEVICES_PER_PACKAGE = 2;
+ private static final float DEFAULT_BRIGHTNESS = 0.34f;
+ private static final float DIM_BRIGHTNESS = 0.12f;
+
@Rule
public final TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getContext());
@@ -123,6 +130,64 @@
}
@Test
+ public void testCreateVirtualDisplay_createDisplayDeviceInfoFromDefaults() {
+ VirtualDisplayConfig config = new VirtualDisplayConfig.Builder(
+ "testDisplayName", /* width= */ 640, /* height= */ 480, /* densityDpi= */ 240)
+ .build();
+
+ final String packageName = "testpackage";
+ final String displayUniqueId = VirtualDisplayAdapter.generateDisplayUniqueId(
+ packageName, Process.myUid(), config);
+
+ DisplayDevice displayDevice = mAdapter.createVirtualDisplayLocked(
+ mMockCallback, /* projection= */ null, /* ownerUid= */ 10,
+ packageName, displayUniqueId, /* surface= */ null, /* flags= */ 0, config);
+
+ assertNotNull(displayDevice);
+ DisplayDeviceInfo info = displayDevice.getDisplayDeviceInfoLocked();
+ assertNotNull(info);
+
+ assertThat(info.width).isEqualTo(640);
+ assertThat(info.height).isEqualTo(480);
+ assertThat(info.densityDpi).isEqualTo(240);
+ assertThat(info.xDpi).isEqualTo(240);
+ assertThat(info.yDpi).isEqualTo(240);
+ assertThat(info.name).isEqualTo("testDisplayName");
+ assertThat(info.uniqueId).isEqualTo(displayUniqueId);
+ assertThat(info.ownerPackageName).isEqualTo(packageName);
+ assertThat(info.ownerUid).isEqualTo(10);
+ assertThat(info.type).isEqualTo(Display.TYPE_VIRTUAL);
+ assertThat(info.brightnessMinimum).isEqualTo(PowerManager.BRIGHTNESS_MIN);
+ assertThat(info.brightnessMaximum).isEqualTo(PowerManager.BRIGHTNESS_MAX);
+ assertThat(info.brightnessDefault).isEqualTo(PowerManager.BRIGHTNESS_MIN);
+ assertThat(info.brightnessDim).isEqualTo(PowerManager.BRIGHTNESS_INVALID);
+ }
+
+ @Test
+ public void testCreateVirtualDisplay_createDisplayDeviceInfoFromVirtualDisplayConfig() {
+ VirtualDisplayConfig config = new VirtualDisplayConfig.Builder(
+ "testDisplayName", /* width= */ 640, /* height= */ 480, /* densityDpi= */ 240)
+ .setDefaultBrightness(DEFAULT_BRIGHTNESS)
+ .setDimBrightness(DIM_BRIGHTNESS)
+ .build();
+
+ final String packageName = "testpackage";
+ final String displayUniqueId = VirtualDisplayAdapter.generateDisplayUniqueId(
+ packageName, Process.myUid(), config);
+
+ DisplayDevice displayDevice = mAdapter.createVirtualDisplayLocked(
+ mMockCallback, /* projection= */ null, /* ownerUid= */ 10,
+ packageName, displayUniqueId, /* surface= */ null, /* flags= */ 0, config);
+
+ assertNotNull(displayDevice);
+ DisplayDeviceInfo info = displayDevice.getDisplayDeviceInfoLocked();
+ assertNotNull(info);
+
+ assertThat(info.brightnessDefault).isEqualTo(DEFAULT_BRIGHTNESS);
+ assertThat(info.brightnessDim).isEqualTo(DIM_BRIGHTNESS);
+ }
+
+ @Test
public void testCreatesVirtualDisplay_checkGeneratedDisplayUniqueIdPrefix() {
VirtualDisplayConfig config = new VirtualDisplayConfig.Builder("test", /* width= */ 1,
/* height= */ 1, /* densityDpi= */ 1).build();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/DisplayDimModifierTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/DisplayDimModifierTest.java
index be4e7c7..7e4042e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/DisplayDimModifierTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/DisplayDimModifierTest.java
@@ -44,6 +44,8 @@
private static final float MIN_DIM_AMOUNT = 0.05f;
private static final float DIM_CONFIG = 0.4f;
+ private static final int DISPLAY_ID = 3;
+
@Mock
private Context mMockContext;
@@ -66,9 +68,9 @@
R.dimen.config_screenBrightnessMinimumDimAmountFloat)).thenReturn(MIN_DIM_AMOUNT);
when(mMockContext.getSystemService(PowerManager.class)).thenReturn(mMockPowerManager);
when(mMockPowerManager.getBrightnessConstraint(
- PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM)).thenReturn(DIM_CONFIG);
+ DISPLAY_ID, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM)).thenReturn(DIM_CONFIG);
- mModifier = new DisplayDimModifier(mMockContext);
+ mModifier = new DisplayDimModifier(DISPLAY_ID, mMockContext);
mRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM;
}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index b48c2d7..376091e 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -1257,6 +1257,36 @@
.isEqualTo(WAKEFULNESS_DOZING);
}
+ @EnableFlags({
+ android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER,
+ android.companion.virtualdevice.flags.Flags.FLAG_DISPLAY_POWER_MANAGER_APIS})
+ @Test
+ public void getBrightnessConstraint_valuesMatchDisplayInfo() {
+ final int displayId = 7;
+ final DisplayInfo info = new DisplayInfo();
+ info.brightnessMinimum = 0.12f;
+ info.brightnessDim = 0.34f;
+ info.brightnessDefault = 0.56f;
+ info.brightnessMaximum = 0.78f;
+ when(mDisplayManagerInternalMock.getDisplayInfo(displayId)).thenReturn(info);
+
+ createService();
+ startSystem();
+
+ assertThat(mService.getBinderServiceInstance().getBrightnessConstraint(
+ displayId, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM))
+ .isEqualTo(info.brightnessMinimum);
+ assertThat(mService.getBinderServiceInstance().getBrightnessConstraint(
+ displayId, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM))
+ .isEqualTo(info.brightnessMaximum);
+ assertThat(mService.getBinderServiceInstance().getBrightnessConstraint(
+ displayId, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT))
+ .isEqualTo(info.brightnessDefault);
+ assertThat(mService.getBinderServiceInstance().getBrightnessConstraint(
+ displayId, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM))
+ .isEqualTo(info.brightnessDim);
+ }
+
@SuppressWarnings("GuardedBy")
@Test
public void testAmbientSuppression_disablesDreamingAndWakesDevice() {
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index bc03c23..9db76d4 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -523,12 +523,12 @@
}
void prepareBrightnessDecrease(float currentBrightness) {
- doReturn(0.0f).when(mPowerManager)
- .getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
- doReturn(1.0f).when(mPowerManager)
- .getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
+ doReturn(0.0f).when(mPowerManager).getBrightnessConstraint(
+ DEFAULT_DISPLAY, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
+ doReturn(1.0f).when(mPowerManager).getBrightnessConstraint(
+ DEFAULT_DISPLAY, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
doReturn(currentBrightness).when(mDisplayManager)
- .getBrightness(0);
+ .getBrightness(DEFAULT_DISPLAY);
}
void verifyNewBrightness(float newBrightness) {
diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp
index b16ba15..51a300b 100644
--- a/tests/vcn/Android.bp
+++ b/tests/vcn/Android.bp
@@ -14,21 +14,24 @@
android_test {
name: "FrameworksVcnTests",
+ // For access hidden connectivity methods in tests
+ defaults: ["framework-connectivity-test-defaults"],
srcs: [
"java/**/*.java",
"java/**/*.kt",
],
platform_apis: true,
- defaults: ["framework-connectivity-test-defaults"],
test_suites: ["device-tests"],
certificate: "platform",
static_libs: [
+ "android.net.vcn.flags-aconfig-java-export",
"androidx.test.rules",
"frameworks-base-testutils",
"framework-protos",
"mockito-target-minus-junit4",
"net-tests-utils",
"platform-test-annotations",
+ "service-connectivity-b-pre-jarjar",
"services.core",
"service-connectivity-tiramisu-pre-jarjar",
"flag-junit",
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index b999475..77f82f0 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -55,7 +55,6 @@
import android.content.IntentFilter;
import android.net.vcn.VcnManager;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.test.TestLooper;
@@ -72,6 +71,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.modules.utils.HandlerExecutor;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;