Merge "media: polish EncoderProfiles" into sc-dev
diff --git a/StubLibraries.bp b/StubLibraries.bp
index bc3f131..b995f95 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -404,7 +404,6 @@
java_defaults {
name: "android_defaults_stubs_current",
- static_libs: ["art-notices-for-framework-stubs-jar"], // License notices from art module
sdk_version: "none",
system_modules: "none",
java_version: "1.8",
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
index 5a45961..e83c64c 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
@@ -44,7 +44,7 @@
@RunWith(AndroidJUnit4.class)
public class TypefaceCreatePerfTest {
// A font file name in asset directory.
- private static final String TEST_FONT_NAME = "DancingScript.ttf";
+ private static final String TEST_FONT_NAME = "DancingScript-Regular.ttf";
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/cmds/locksettings/TEST_MAPPING b/cmds/locksettings/TEST_MAPPING
index c1cba5f..7a449ef 100644
--- a/cmds/locksettings/TEST_MAPPING
+++ b/cmds/locksettings/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-large": [
{
"name": "CtsDevicePolicyManagerTestCases",
"options": [
diff --git a/core/api/current.txt b/core/api/current.txt
index 2843a2c..499eb9b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17705,6 +17705,15 @@
method public void onDynamicSensorDisconnected(android.hardware.Sensor);
}
+ public final class SensorPrivacyManager {
+ method public boolean supportsSensorToggle(int);
+ }
+
+ public static class SensorPrivacyManager.Sensors {
+ field public static final int CAMERA = 2; // 0x2
+ field public static final int MICROPHONE = 1; // 0x1
+ }
+
public final class TriggerEvent {
field public android.hardware.Sensor sensor;
field public long timestamp;
@@ -20358,6 +20367,7 @@
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener);
method public int abandonAudioFocusRequest(@NonNull android.media.AudioFocusRequest);
method public void addOnCommunicationDeviceChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnCommunicationDeviceChangedListener);
+ method public void addOnModeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnModeChangedListener);
method public void adjustStreamVolume(int, int, int);
method public void adjustSuggestedStreamVolume(int, int, int);
method public void adjustVolume(int, int);
@@ -20408,6 +20418,7 @@
method @Deprecated public void registerRemoteControlClient(android.media.RemoteControlClient);
method @Deprecated public boolean registerRemoteController(android.media.RemoteController);
method public void removeOnCommunicationDeviceChangedListener(@NonNull android.media.AudioManager.OnCommunicationDeviceChangedListener);
+ method public void removeOnModeChangedListener(@NonNull android.media.AudioManager.OnModeChangedListener);
method @Deprecated public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
method public int requestAudioFocus(@NonNull android.media.AudioFocusRequest);
method public void setAllowedCapturePolicy(int);
@@ -20563,6 +20574,10 @@
method public void onCommunicationDeviceChanged(@Nullable android.media.AudioDeviceInfo);
}
+ public static interface AudioManager.OnModeChangedListener {
+ method public void onModeChanged(int);
+ }
+
public final class AudioMetadata {
method @NonNull public static android.media.AudioMetadataMap createMap();
}
@@ -26526,6 +26541,9 @@
field public static final int UNSUPPORTED = -1; // 0xffffffff
}
+ public interface TunnelConnectionParams {
+ }
+
public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable {
method public abstract android.net.Uri.Builder buildUpon();
method public int compareTo(android.net.Uri);
@@ -27086,15 +27104,6 @@
method @NonNull public android.net.vcn.VcnConfig build();
}
- public abstract class VcnControlPlaneConfig {
- }
-
- public final class VcnControlPlaneIkeConfig extends android.net.vcn.VcnControlPlaneConfig {
- ctor public VcnControlPlaneIkeConfig(@NonNull android.net.ipsec.ike.IkeSessionParams, @NonNull android.net.ipsec.ike.TunnelModeChildSessionParams);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams getChildSessionParams();
- method @NonNull public android.net.ipsec.ike.IkeSessionParams getIkeSessionParams();
- }
-
public final class VcnGatewayConnectionConfig {
method @NonNull public int[] getExposedCapabilities();
method @NonNull public String getGatewayConnectionName();
@@ -27103,7 +27112,7 @@
}
public static final class VcnGatewayConnectionConfig.Builder {
- ctor public VcnGatewayConnectionConfig.Builder(@NonNull String, @NonNull android.net.vcn.VcnControlPlaneConfig);
+ ctor public VcnGatewayConnectionConfig.Builder(@NonNull String, @NonNull android.net.TunnelConnectionParams);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
@@ -32081,6 +32090,7 @@
method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
method public int getId();
+ method @NonNull public int[] getPrimitiveDurations(@NonNull int...);
method public abstract boolean hasAmplitudeControl();
method public abstract boolean hasVibrator();
method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d0326bb..d1cc864 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2735,10 +2735,8 @@
field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
- field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
- field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg";
@@ -3119,12 +3117,7 @@
}
public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
- method public void onSensorPrivacyChanged(boolean);
- }
-
- public static class SensorPrivacyManager.Sensors {
- field public static final int CAMERA = 2; // 0x2
- field public static final int MICROPHONE = 1; // 0x1
+ method public void onSensorPrivacyChanged(int, boolean);
}
}
@@ -8631,6 +8624,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isCloneProfile();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isGuestUser();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isMediaSharedWithParent();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isPrimaryUser();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isProfile();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile();
@@ -8642,7 +8636,6 @@
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean sharesMediaWithParent();
field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
field @Deprecated public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
@@ -12817,10 +12810,11 @@
}
public interface DelegateStateCallback {
+ method public void onConfigurationChanged(@NonNull android.telephony.ims.SipDelegateConfiguration);
method public void onCreated(@NonNull android.telephony.ims.stub.SipDelegate, @Nullable java.util.Set<android.telephony.ims.FeatureTagState>);
method public void onDestroyed(int);
method public void onFeatureTagRegistrationChanged(@NonNull android.telephony.ims.DelegateRegistrationState);
- method public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+ method @Deprecated public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
}
public final class FeatureTagState implements android.os.Parcelable {
@@ -13564,6 +13558,76 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RtpHeaderExtensionType> CREATOR;
}
+ public final class SipDelegateConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getHomeDomain();
+ method @Nullable public String getImei();
+ method @Nullable public android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration getIpSecConfiguration();
+ method @NonNull public java.net.InetSocketAddress getLocalAddress();
+ method public int getMaxUdpPayloadSizeBytes();
+ method @Nullable public java.net.InetSocketAddress getNatSocketAddress();
+ method @Nullable public String getPrivateUserIdentifier();
+ method @Nullable public android.net.Uri getPublicGruuUri();
+ method @Nullable public String getPublicUserIdentifier();
+ method @Nullable public String getSipAssociatedUriHeader();
+ method @Nullable public String getSipAuthenticationHeader();
+ method @Nullable public String getSipAuthenticationNonce();
+ method @Nullable public String getSipCniHeader();
+ method @Nullable public String getSipContactUserParameter();
+ method @Nullable public String getSipPaniHeader();
+ method @Nullable public String getSipPathHeader();
+ method @Nullable public String getSipPlaniHeader();
+ method @NonNull public java.net.InetSocketAddress getSipServerAddress();
+ method @Nullable public String getSipServiceRouteHeader();
+ method @Nullable public String getSipUserAgentHeader();
+ method public int getTransportType();
+ method @IntRange(from=0) public long getVersion();
+ method public boolean isSipCompactFormEnabled();
+ method public boolean isSipKeepaliveEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateConfiguration> CREATOR;
+ field public static final int SIP_TRANSPORT_TCP = 1; // 0x1
+ field public static final int SIP_TRANSPORT_UDP = 0; // 0x0
+ field public static final int UDP_PAYLOAD_SIZE_UNDEFINED = -1; // 0xffffffff
+ }
+
+ public static final class SipDelegateConfiguration.Builder {
+ ctor public SipDelegateConfiguration.Builder(@IntRange(from=0) long, int, @NonNull java.net.InetSocketAddress, @NonNull java.net.InetSocketAddress);
+ ctor public SipDelegateConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateConfiguration);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration build();
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setHomeDomain(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setImei(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setIpSecConfiguration(@Nullable android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setMaxUdpPayloadSizeBytes(@IntRange(from=1) int);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setNatSocketAddress(@Nullable java.net.InetSocketAddress);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setPrivateUserIdentifier(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setPublicGruuUri(@Nullable android.net.Uri);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setPublicUserIdentifier(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipAssociatedUriHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipAuthenticationHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipAuthenticationNonce(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipCniHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipCompactFormEnabled(boolean);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipContactUserParameter(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipKeepaliveEnabled(boolean);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipPaniHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipPathHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipPlaniHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipServiceRouteHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipUserAgentHeader(@Nullable String);
+ }
+
+ public static final class SipDelegateConfiguration.IpSecConfiguration {
+ ctor public SipDelegateConfiguration.IpSecConfiguration(int, int, int, int, int, int, @NonNull String);
+ method public int getLastLocalTxPort();
+ method public int getLastRemoteTxPort();
+ method public int getLocalRxPort();
+ method public int getLocalTxPort();
+ method public int getRemoteRxPort();
+ method public int getRemoteTxPort();
+ method @NonNull public String getSipSecurityVerifyHeader();
+ }
+
public interface SipDelegateConnection {
method public default void cleanupSession(@NonNull String);
method @Deprecated public default void closeDialog(@NonNull String);
@@ -13572,65 +13636,65 @@
method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long);
}
- public final class SipDelegateImsConfiguration implements android.os.Parcelable {
- method public boolean containsKey(@NonNull String);
- method @NonNull public android.os.PersistableBundle copyBundle();
- method public int describeContents();
- method public boolean getBoolean(@NonNull String, boolean);
- method public int getInt(@NonNull String, int);
- method @Nullable public String getString(@NonNull String);
- method public long getVersion();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
- field public static final String IPTYPE_IPV4 = "IPV4";
- field public static final String IPTYPE_IPV6 = "IPV6";
- field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
- field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
- field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
- field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
- field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
- field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
- field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
- field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
- field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
- field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
- field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
- field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
- field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
- field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
- field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
- field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
- field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
- field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
- field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
- field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
- field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
- field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
- field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
- field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
- field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
- field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
- field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
- field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
- field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
- field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
- field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
- field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
- field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
- field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
- field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
- field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
- field public static final String SIP_TRANSPORT_TCP = "TCP";
- field public static final String SIP_TRANSPORT_UDP = "UDP";
+ @Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable {
+ method @Deprecated public boolean containsKey(@NonNull String);
+ method @Deprecated @NonNull public android.os.PersistableBundle copyBundle();
+ method @Deprecated public int describeContents();
+ method @Deprecated public boolean getBoolean(@NonNull String, boolean);
+ method @Deprecated public int getInt(@NonNull String, int);
+ method @Deprecated @Nullable public String getString(@NonNull String);
+ method @Deprecated public long getVersion();
+ method @Deprecated public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
+ field @Deprecated public static final String IPTYPE_IPV4 = "IPV4";
+ field @Deprecated public static final String IPTYPE_IPV6 = "IPV6";
+ field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
+ field @Deprecated public static final String SIP_TRANSPORT_TCP = "TCP";
+ field @Deprecated public static final String SIP_TRANSPORT_UDP = "UDP";
}
- public static final class SipDelegateImsConfiguration.Builder {
- ctor public SipDelegateImsConfiguration.Builder(int);
- ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
- method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
- method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
- method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
- method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
+ @Deprecated public static final class SipDelegateImsConfiguration.Builder {
+ ctor @Deprecated public SipDelegateImsConfiguration.Builder(int);
+ ctor @Deprecated public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+ method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
+ method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
+ method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
+ method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
}
public class SipDelegateManager {
@@ -13800,10 +13864,11 @@
}
public interface DelegateConnectionStateCallback {
+ method public default void onConfigurationChanged(@NonNull android.telephony.ims.SipDelegateConfiguration);
method public void onCreated(@NonNull android.telephony.ims.SipDelegateConnection);
method public void onDestroyed(int);
method public void onFeatureTagStatusChanged(@NonNull android.telephony.ims.DelegateRegistrationState, @NonNull java.util.Set<android.telephony.ims.FeatureTagState>);
- method public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+ method @Deprecated public default void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
}
public class ImsCallSessionImplBase implements java.lang.AutoCloseable {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 0e6e8a5..d8c1387 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -835,10 +835,8 @@
method public void holdLock(android.os.IBinder, int);
method @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES) public void setKeepUninstalledPackages(@NonNull java.util.List<java.lang.String>);
field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
- field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
- field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
@@ -1063,23 +1061,10 @@
package android.hardware {
public final class SensorPrivacyManager {
- method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
- method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
- method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
- method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacyForProfileGroup(int, boolean);
}
- public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
- method public void onSensorPrivacyChanged(boolean);
- }
-
- public static class SensorPrivacyManager.Sensors {
- field public static final int CAMERA = 2; // 0x2
- field public static final int MICROPHONE = 1; // 0x1
- }
-
}
package android.hardware.biometrics {
@@ -1240,6 +1225,14 @@
}
+package android.hardware.lights {
+
+ public abstract class LightsManager {
+ method @NonNull public abstract android.hardware.lights.LightsManager.LightsSession openSession(int);
+ }
+
+}
+
package android.hardware.soundtrigger {
public class KeyphraseEnrollmentInfo {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4dc0442..c93a88f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6432,7 +6432,7 @@
* broadcast when access to a key is granted.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
- * {@code null} if calling from a delegated certificate installer.
+ * {@code null} if calling from a delegated certificate chooser.
* @param alias The alias of the key to grant access to.
* @param packageName The name of the (already installed) package to grant access to.
* @return {@code true} if the grant was set successfully, {@code false} otherwise.
@@ -6498,7 +6498,7 @@
* broadcast when access to a key is revoked.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
- * {@code null} if calling from a delegated certificate installer.
+ * {@code null} if calling from a delegated certificate chooser.
* @param alias The alias of the key to revoke access from.
* @param packageName The name of the (already installed) package to revoke access from.
* @return {@code true} if the grant was revoked successfully, {@code false} otherwise.
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 0841910..b1b59b0 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -16,6 +16,7 @@
package android.app.backup;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.IBackupAgent;
import android.app.QueuedWork;
@@ -48,6 +49,8 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
@@ -188,6 +191,15 @@
*/
public static final int FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED = 1 << 31;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED,
+ FLAG_DEVICE_TO_DEVICE_TRANSFER,
+ FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED
+ })
+ public @interface BackupTransportFlags {}
+
Handler mHandler = null;
@Nullable private UserHandle mUser;
diff --git a/core/java/android/app/backup/RestoreSet.java b/core/java/android/app/backup/RestoreSet.java
index 51430c0..24b6910 100644
--- a/core/java/android/app/backup/RestoreSet.java
+++ b/core/java/android/app/backup/RestoreSet.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.backup.BackupAgent.BackupTransportFlags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -54,6 +55,7 @@
* Properties of the {@link BackupTransport} transport that was used to obtain the data in
* this restore set.
*/
+ @BackupTransportFlags
public final int backupTransportFlags;
/**
@@ -87,7 +89,7 @@
* flag values.
*/
public RestoreSet(@Nullable String name, @Nullable String device, long token,
- int backupTransportFlags) {
+ @BackupTransportFlags int backupTransportFlags) {
this.name = name;
this.device = device;
this.token = token;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 71cd6f1..39933a9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2569,6 +2569,32 @@
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
/**
+ * Broadcast Action: An existing application package has been removed from
+ * the device. The data contains the name of the package and the visibility
+ * allow list. The package that is being removed does <em>not</em> receive
+ * this Intent.
+ * <ul>
+ * <li> {@link #EXTRA_UID} containing the integer uid previously assigned
+ * to the package.
+ * <li> {@link #EXTRA_DATA_REMOVED} is set to true if the entire
+ * application -- data and code -- is being removed.
+ * <li> {@link #EXTRA_REPLACING} is set to true if this will be followed
+ * by an {@link #ACTION_PACKAGE_ADDED} broadcast for the same package.
+ * <li> {@link #EXTRA_USER_INITIATED} containing boolean field to signal
+ * that the application was removed with the user-initiated action.
+ * <li> {@link #EXTRA_VISIBILITY_ALLOW_LIST} containing an int array to
+ * indicate the visibility allow list.
+ * </ul>
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system.
+ *
+ * @hide This broadcast is used internally by the system.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PACKAGE_REMOVED_INTERNAL =
+ "android.intent.action.PACKAGE_REMOVED_INTERNAL";
+ /**
* Broadcast Action: An existing application package has been completely
* removed from the device. The data contains the name of the package.
* This is like {@link #ACTION_PACKAGE_REMOVED}, but only set when
@@ -6122,6 +6148,16 @@
*/
public static final String EXTRA_LOCUS_ID = "android.intent.extra.LOCUS_ID";
+ /**
+ * Used as an int array extra field in
+ * {@link android.content.Intent#ACTION_PACKAGE_REMOVED_INTERNAL}
+ * intents to indicate that visibility allow list of this removed package.
+ *
+ * @hide
+ */
+ public static final String EXTRA_VISIBILITY_ALLOW_LIST =
+ "android.intent.extra.VISIBILITY_ALLOW_LIST";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 67ecb7c..90105d3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3654,32 +3654,6 @@
public static final String FEATURE_TUNER = "android.hardware.tv.tuner";
/**
- * Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for
- * microphone. When sensory privacy for the microphone is enabled no microphone data is sent to
- * clients, e.g. all audio data is silent.
- *
- * @hide
- */
- @SystemApi
- @TestApi
- @SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
-
- /**
- * Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for
- * camera. When sensory privacy for the camera is enabled no camera data is sent to clients,
- * e.g. the view finder in a camera app would appear blank.
- *
- * @hide
- */
- @SystemApi
- @TestApi
- @SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
-
- /**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
* the necessary changes to support app enumeration.
*
diff --git a/core/java/android/hardware/ISensorPrivacyManager.aidl b/core/java/android/hardware/ISensorPrivacyManager.aidl
index dbcd79d..a71bb09 100644
--- a/core/java/android/hardware/ISensorPrivacyManager.aidl
+++ b/core/java/android/hardware/ISensorPrivacyManager.aidl
@@ -24,6 +24,8 @@
// the ones in
// frameworks/native/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
// =============== Beginning of transactions used on native side as well ======================
+ boolean supportsSensorToggle(int sensor);
+
void addSensorPrivacyListener(in ISensorPrivacyListener listener);
void addIndividualSensorPrivacyListener(int userId, int sensor,
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 85e7d77..fc0204a 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -16,6 +16,7 @@
package android.hardware;
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -30,6 +31,8 @@
import android.os.ServiceManager;
import android.service.SensorPrivacyIndividualEnabledSensorProto;
import android.util.ArrayMap;
+import android.util.Pair;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -43,24 +46,11 @@
* current state of sensor privacy as well as to register / unregister for notification when
* the sensor privacy state changes.
*
- * @hide
*/
-@SystemApi
-@TestApi
@SystemService(Context.SENSOR_PRIVACY_SERVICE)
public final class SensorPrivacyManager {
/**
- * @hide
- */
- public static final boolean USE_MICROPHONE_TOGGLE = true;
-
- /**
- * @hide
- */
- public static final boolean USE_CAMERA_TOGGLE = true;
-
- /**
* Unique Id of this manager to identify to the service
* @hide
*/
@@ -80,25 +70,23 @@
public static final String EXTRA_ALL_SENSORS = SensorPrivacyManager.class.getName()
+ ".extra.all_sensors";
+ private final SparseArray<Boolean> mToggleSupportCache = new SparseArray<>();
+
/**
* Individual sensors not listed in {@link Sensors}
- * @hide
*/
- @SystemApi
- @TestApi
public static class Sensors {
private Sensors() {}
- /** Microphone
- * @hide */
- @SystemApi
- @TestApi
+ /**
+ * Constant for the microphone
+ */
public static final int MICROPHONE = SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
- /** Camera
- * @hide */
- @SystemApi
- @TestApi
+
+ /**
+ * Constant for the camera
+ */
public static final int CAMERA = SensorPrivacyIndividualEnabledSensorProto.CAMERA;
/**
@@ -122,14 +110,14 @@
* @hide
*/
@SystemApi
- @TestApi
public interface OnSensorPrivacyChangedListener {
/**
* Callback invoked when the sensor privacy state changes.
*
+ * @param sensor the sensor whose state is changing
* @param enabled true if sensor privacy is enabled, false otherwise.
*/
- void onSensorPrivacyChanged(boolean enabled);
+ void onSensorPrivacyChanged(int sensor, boolean enabled);
}
private static final Object sInstanceLock = new Object();
@@ -144,7 +132,11 @@
private final ISensorPrivacyManager mService;
@NonNull
- private final ArrayMap<OnSensorPrivacyChangedListener, ISensorPrivacyListener> mListeners;
+ private final ArrayMap<OnAllSensorPrivacyChangedListener, ISensorPrivacyListener> mListeners;
+
+ @NonNull
+ private final ArrayMap<Pair<OnSensorPrivacyChangedListener, Integer>, ISensorPrivacyListener>
+ mIndividualListeners;
/**
* Private constructor to ensure only a single instance is created.
@@ -153,6 +145,7 @@
mContext = context;
mService = service;
mListeners = new ArrayMap<>();
+ mIndividualListeners = new ArrayMap<>();
}
/**
@@ -176,16 +169,18 @@
}
/**
- * Sets sensor privacy to the specified state.
- *
- * @param enable the state to which sensor privacy should be set.
- *
- * @hide
+ * Checks if the given toggle is supported on this device
+ * @param sensor The sensor to check
+ * @return whether the toggle for the sensor is supported on this device.
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
- public void setSensorPrivacy(boolean enable) {
+ public boolean supportsSensorToggle(@Sensors.Sensor int sensor) {
try {
- mService.setSensorPrivacy(enable);
+ Boolean val = mToggleSupportCache.get(sensor);
+ if (val == null) {
+ val = mService.supportsSensorToggle(sensor);
+ mToggleSupportCache.put(sensor, val);
+ }
+ return val;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -195,37 +190,6 @@
* Registers a new listener to receive notification when the state of sensor privacy
* changes.
*
- * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
- * privacy changes.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
- public void addSensorPrivacyListener(@NonNull final OnSensorPrivacyChangedListener listener) {
- synchronized (mListeners) {
- ISensorPrivacyListener iListener = mListeners.get(listener);
- if (iListener == null) {
- iListener = new ISensorPrivacyListener.Stub() {
- @Override
- public void onSensorPrivacyChanged(boolean enabled) {
- listener.onSensorPrivacyChanged(enabled);
- }
- };
- mListeners.put(listener, iListener);
- }
-
- try {
- mService.addSensorPrivacyListener(iListener);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- /**
- * Registers a new listener to receive notification when the state of sensor privacy
- * changes.
- *
* @param sensor the sensor to listen to changes to
* @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
* privacy changes.
@@ -233,8 +197,7 @@
* @hide
*/
@SystemApi
- @TestApi
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor,
@NonNull OnSensorPrivacyChangedListener listener) {
addSensorPrivacyListener(sensor, mContext.getUserId(), mContext.getMainExecutor(),
@@ -252,7 +215,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @UserIdInt int userId,
@NonNull OnSensorPrivacyChangedListener listener) {
addSensorPrivacyListener(sensor, userId, mContext.getMainExecutor(), listener);
@@ -270,8 +233,7 @@
* @hide
*/
@SystemApi
- @TestApi
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor,
@NonNull OnSensorPrivacyChangedListener listener) {
addSensorPrivacyListener(sensor, mContext.getUserId(), executor, listener);
@@ -289,19 +251,19 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @UserIdInt int userId,
@NonNull Executor executor, @NonNull OnSensorPrivacyChangedListener listener) {
- synchronized (mListeners) {
- ISensorPrivacyListener iListener = mListeners.get(listener);
+ synchronized (mIndividualListeners) {
+ ISensorPrivacyListener iListener = mIndividualListeners.get(listener);
if (iListener == null) {
iListener = new ISensorPrivacyListener.Stub() {
@Override
public void onSensorPrivacyChanged(boolean enabled) {
- executor.execute(() -> listener.onSensorPrivacyChanged(enabled));
+ executor.execute(() -> listener.onSensorPrivacyChanged(sensor, enabled));
}
};
- mListeners.put(listener, iListener);
+ mIndividualListeners.put(new Pair<>(listener, sensor), iListener);
}
try {
@@ -314,7 +276,7 @@
}
/**
- * Unregisters the specified listener from receiving notifications when the state of sensor
+ * Unregisters the specified listener from receiving notifications when the state of any sensor
* privacy changes.
*
* @param listener the OnSensorPrivacyChangedListener to be unregistered from notifications when
@@ -323,39 +285,24 @@
* @hide
*/
@SystemApi
- @TestApi
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void removeSensorPrivacyListener(@NonNull OnSensorPrivacyChangedListener listener) {
synchronized (mListeners) {
- ISensorPrivacyListener iListener = mListeners.get(listener);
- if (iListener != null) {
- mListeners.remove(iListener);
- try {
- mService.removeSensorPrivacyListener(iListener);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ for (int i = 0; i < mIndividualListeners.size(); i++) {
+ Pair<OnSensorPrivacyChangedListener, Integer> pair = mIndividualListeners.keyAt(i);
+ if (pair.first.equals(listener)) {
+ try {
+ mService.removeSensorPrivacyListener(mIndividualListeners.valueAt(i));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mIndividualListeners.removeAt(i--);
}
}
}
}
/**
- * Returns whether sensor privacy is currently enabled.
- *
- * @return true if sensor privacy is currently enabled, false otherwise.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
- public boolean isSensorPrivacyEnabled() {
- try {
- return mService.isSensorPrivacyEnabled();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Returns whether sensor privacy is currently enabled for a specific sensor.
*
* @return true if sensor privacy is currently enabled, false otherwise.
@@ -363,8 +310,7 @@
* @hide
*/
@SystemApi
- @TestApi
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
return isSensorPrivacyEnabled(sensor, mContext.getUserId());
}
@@ -376,7 +322,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor, @UserIdInt int userId) {
try {
return mService.isIndividualSensorPrivacyEnabled(userId, sensor);
@@ -394,7 +340,7 @@
* @hide
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacy(@Sensors.Sensor int sensor, boolean enable) {
setSensorPrivacy(sensor, enable, mContext.getUserId());
}
@@ -408,7 +354,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacy(@Sensors.Sensor int sensor, boolean enable,
@UserIdInt int userId) {
try {
@@ -428,7 +374,7 @@
* @hide
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacyForProfileGroup(@Sensors.Sensor int sensor,
boolean enable) {
setSensorPrivacyForProfileGroup(sensor, enable, mContext.getUserId());
@@ -444,7 +390,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacyForProfileGroup(@Sensors.Sensor int sensor,
boolean enable, @UserIdInt int userId) {
try {
@@ -463,7 +409,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void suppressSensorPrivacyReminders(@NonNull String packageName,
boolean suppress) {
suppressSensorPrivacyReminders(packageName, suppress, mContext.getUserId());
@@ -478,7 +424,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void suppressSensorPrivacyReminders(@NonNull String packageName,
boolean suppress, @UserIdInt int userId) {
try {
@@ -488,4 +434,109 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * A class implementing this interface can register with the {@link
+ * android.hardware.SensorPrivacyManager} to receive notification when the all-sensor privacy
+ * state changes.
+ *
+ * @hide
+ */
+ public interface OnAllSensorPrivacyChangedListener {
+ /**
+ * Callback invoked when the sensor privacy state changes.
+ *
+ * @param enabled true if sensor privacy is enabled, false otherwise.
+ */
+ void onAllSensorPrivacyChanged(boolean enabled);
+ }
+
+ /**
+ * Sets all-sensor privacy to the specified state.
+ *
+ * @param enable the state to which sensor privacy should be set.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ public void setAllSensorPrivacy(boolean enable) {
+ try {
+ mService.setSensorPrivacy(enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers a new listener to receive notification when the state of all-sensor privacy
+ * changes.
+ *
+ * @param listener the OnSensorPrivacyChangedListener to be notified when the state of
+ * all-sensor privacy changes.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public void addAllSensorPrivacyListener(
+ @NonNull final OnAllSensorPrivacyChangedListener listener) {
+ synchronized (mListeners) {
+ ISensorPrivacyListener iListener = mListeners.get(listener);
+ if (iListener == null) {
+ iListener = new ISensorPrivacyListener.Stub() {
+ @Override
+ public void onSensorPrivacyChanged(boolean enabled) {
+ listener.onAllSensorPrivacyChanged(enabled);
+ }
+ };
+ mListeners.put(listener, iListener);
+ }
+
+ try {
+ mService.addSensorPrivacyListener(iListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters the specified listener from receiving notifications when the state of all-sensor
+ * privacy changes.
+ *
+ * @param listener the OnAllSensorPrivacyChangedListener to be unregistered from notifications
+ * when all-sensor privacy changes.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public void removeAllSensorPrivacyListener(
+ @NonNull OnAllSensorPrivacyChangedListener listener) {
+ synchronized (mListeners) {
+ ISensorPrivacyListener iListener = mListeners.get(listener);
+ if (iListener != null) {
+ mListeners.remove(iListener);
+ try {
+ mService.removeSensorPrivacyListener(iListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns whether all-sensor privacy is currently enabled.
+ *
+ * @return true if all-sensor privacy is currently enabled, false otherwise.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public boolean isAllSensorPrivacyEnabled() {
+ try {
+ return mService.isSensorPrivacyEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/hardware/input/InputDeviceLightsManager.java b/core/java/android/hardware/input/InputDeviceLightsManager.java
index a3b91a9..885df7b 100644
--- a/core/java/android/hardware/input/InputDeviceLightsManager.java
+++ b/core/java/android/hardware/input/InputDeviceLightsManager.java
@@ -81,6 +81,11 @@
return session;
}
+ @Override
+ public @NonNull LightsSession openSession(int priority) {
+ throw new UnsupportedOperationException();
+ }
+
/**
* Encapsulates a session that can be used to control device lights and represents the lifetime
* of the requests.
diff --git a/core/java/android/hardware/lights/ILightsManager.aidl b/core/java/android/hardware/lights/ILightsManager.aidl
index 6ea24b7..077797f 100644
--- a/core/java/android/hardware/lights/ILightsManager.aidl
+++ b/core/java/android/hardware/lights/ILightsManager.aidl
@@ -27,7 +27,7 @@
interface ILightsManager {
List<Light> getLights();
LightState getLightState(int lightId);
- void openSession(in IBinder sessionToken);
+ void openSession(in IBinder sessionToken, in int priority);
void closeSession(in IBinder sessionToken);
void setLightStates(in IBinder sessionToken, in int[] lightIds, in LightState[] states);
}
diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java
index 8fd56db..8bc86da 100644
--- a/core/java/android/hardware/lights/LightsManager.java
+++ b/core/java/android/hardware/lights/LightsManager.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -84,11 +85,24 @@
public abstract @NonNull LightsSession openSession();
/**
+ *
+ * Creates a new {@link LightsSession}
+ *
+ * @param priority the larger this number, the higher the priority of this session when multiple
+ * light state requests arrive simultaneously.
+ *
+ * @hide
+ */
+ @TestApi
+ public abstract @NonNull LightsSession openSession(int priority);
+
+ /**
* Encapsulates a session that can be used to control device lights and represents the lifetime
* of the requests.
*/
public abstract static class LightsSession implements AutoCloseable {
private final IBinder mToken = new Binder();
+
/**
* Sends a request to modify the states of multiple lights.
*
diff --git a/core/java/android/hardware/lights/SystemLightsManager.java b/core/java/android/hardware/lights/SystemLightsManager.java
index 726a613..da034ee 100644
--- a/core/java/android/hardware/lights/SystemLightsManager.java
+++ b/core/java/android/hardware/lights/SystemLightsManager.java
@@ -102,7 +102,28 @@
public @NonNull LightsSession openSession() {
try {
final LightsSession session = new SystemLightsSession();
- mService.openSession(session.getToken());
+ mService.openSession(session.getToken(), 0);
+ return session;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ *
+ * Creates a new {@link LightsSession}
+ *
+ * @param priority the larger this number, the higher the priority of this session when multiple
+ * light state requests arrive simultaneously.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ @Override
+ public @NonNull LightsSession openSession(int priority) {
+ try {
+ final LightsSession session = new SystemLightsSession();
+ mService.openSession(session.getToken(), priority);
return session;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/net/TunnelConnectionParams.java b/core/java/android/net/TunnelConnectionParams.java
new file mode 100644
index 0000000..f5b3539
--- /dev/null
+++ b/core/java/android/net/TunnelConnectionParams.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+/**
+ * TunnelConnectionParams represents a configuration to set up a tunnel connection.
+ *
+ * <p>Concrete implementations for a control plane protocol should implement this interface.
+ * Subclasses should be immutable data classes containing connection, authentication and
+ * authorization parameters required to establish a tunnel connection.
+ *
+ * @see android.net.ipsec.ike.IkeTunnelConnectionParams
+ */
+// TODO:b/186071626 Remove TunnelConnectionParams when non-updatable API stub can resolve
+// IkeTunnelConnectionParams
+public interface TunnelConnectionParams {}
diff --git a/core/java/android/net/vcn/VcnControlPlaneConfig.java b/core/java/android/net/vcn/VcnControlPlaneConfig.java
deleted file mode 100644
index 92f6c44..0000000
--- a/core/java/android/net/vcn/VcnControlPlaneConfig.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net.vcn;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.PersistableBundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * This class represents a control plane configuration for a Virtual Carrier Network connection.
- *
- * <p>Each {@link VcnGatewayConnectionConfig} must have a {@link VcnControlPlaneConfig}, containing
- * all connection, authentication and authorization parameters required to establish a Gateway
- * Connection with a remote endpoint.
- *
- * <p>A {@link VcnControlPlaneConfig} object can be shared by multiple {@link
- * VcnGatewayConnectionConfig}(s) if they will used for connecting with the same remote endpoint.
- *
- * @see VcnManager
- * @see VcnGatewayConnectionConfig
- */
-public abstract class VcnControlPlaneConfig {
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({CONFIG_TYPE_IKE})
- public @interface ConfigType {}
-
- /** @hide */
- public static final int CONFIG_TYPE_IKE = 1;
-
- private static final String CONFIG_TYPE_KEY = "mConfigType";
- @ConfigType private final int mConfigType;
-
- /**
- * Package private constructor.
- *
- * @hide
- */
- VcnControlPlaneConfig(int configType) {
- mConfigType = configType;
- }
-
- /**
- * Constructs a VcnControlPlaneConfig object by deserializing a PersistableBundle.
- *
- * @param in the {@link PersistableBundle} containing an {@link VcnControlPlaneConfig} object
- * @hide
- */
- public static VcnControlPlaneConfig fromPersistableBundle(@NonNull PersistableBundle in) {
- Objects.requireNonNull(in, "PersistableBundle was null");
-
- int configType = in.getInt(CONFIG_TYPE_KEY);
- switch (configType) {
- case CONFIG_TYPE_IKE:
- return new VcnControlPlaneIkeConfig(in);
- default:
- throw new IllegalStateException("Unrecognized configType: " + configType);
- }
- }
-
- /**
- * Converts this VcnControlPlaneConfig to a PersistableBundle.
- *
- * @hide
- */
- @NonNull
- public PersistableBundle toPersistableBundle() {
- final PersistableBundle result = new PersistableBundle();
- result.putInt(CONFIG_TYPE_KEY, mConfigType);
- return result;
- }
-
- /** @hide */
- @Override
- public int hashCode() {
- return Objects.hash(mConfigType);
- }
-
- /** @hide */
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof VcnControlPlaneConfig)) {
- return false;
- }
-
- return mConfigType == ((VcnControlPlaneConfig) o).mConfigType;
- }
-
- /**
- * Returns a deep copy of this object.
- *
- * @hide
- */
- public abstract VcnControlPlaneConfig copy();
-}
diff --git a/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java b/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java
deleted file mode 100644
index 22d7faf..0000000
--- a/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.vcn;
-
-import static android.net.vcn.VcnControlPlaneConfig.CONFIG_TYPE_IKE;
-
-import android.annotation.NonNull;
-import android.net.ipsec.ike.IkeSessionParams;
-import android.net.ipsec.ike.TunnelModeChildSessionParams;
-import android.net.vcn.persistablebundleutils.IkeSessionParamsUtils;
-import android.net.vcn.persistablebundleutils.TunnelModeChildSessionParamsUtils;
-import android.os.PersistableBundle;
-import android.util.ArraySet;
-import android.util.Log;
-
-import java.util.Objects;
-
-/**
- * This class is an IKEv2 control plane configuration for a Virtual Carrier Network connection.
- *
- * <p>This class is an extension of the {@link VcnControlPlaneConfig}, containing IKEv2-specific
- * configuration, authentication and authorization parameters.
- *
- * @see VcnControlPlaneConfig
- */
-public final class VcnControlPlaneIkeConfig extends VcnControlPlaneConfig {
- private static final String TAG = VcnControlPlaneIkeConfig.class.getSimpleName();
-
- private static final String IKE_PARAMS_KEY = "mIkeParams";
- @NonNull private final IkeSessionParams mIkeParams;
-
- private static final String CHILD_PARAMS_KEY = "mChildParams";
- @NonNull private final TunnelModeChildSessionParams mChildParams;
-
- private static final ArraySet<String> BUNDLE_KEY_SET = new ArraySet<>();
-
- {
- BUNDLE_KEY_SET.add(IKE_PARAMS_KEY);
- BUNDLE_KEY_SET.add(CHILD_PARAMS_KEY);
- }
-
- /**
- * Constructs a VcnControlPlaneIkeConfig object.
- *
- * @param ikeParams the IKE Session negotiation parameters
- * @param childParams the tunnel mode Child Session negotiation parameters
- */
- public VcnControlPlaneIkeConfig(
- @NonNull IkeSessionParams ikeParams,
- @NonNull TunnelModeChildSessionParams childParams) {
- super(CONFIG_TYPE_IKE);
- mIkeParams = ikeParams;
- mChildParams = childParams;
- validate();
- }
-
- /**
- * Constructs a VcnControlPlaneIkeConfig object by deserializing a PersistableBundle.
- *
- * @param in the {@link PersistableBundle} containing an {@link VcnControlPlaneIkeConfig} object
- * @hide
- */
- public VcnControlPlaneIkeConfig(@NonNull PersistableBundle in) {
- super(CONFIG_TYPE_IKE);
- final PersistableBundle ikeParamsBundle = in.getPersistableBundle(IKE_PARAMS_KEY);
- final PersistableBundle childParamsBundle = in.getPersistableBundle(CHILD_PARAMS_KEY);
-
- Objects.requireNonNull(ikeParamsBundle, "IKE Session Params was null");
- Objects.requireNonNull(childParamsBundle, "Child Session Params was null");
-
- mIkeParams = IkeSessionParamsUtils.fromPersistableBundle(ikeParamsBundle);
- mChildParams = TunnelModeChildSessionParamsUtils.fromPersistableBundle(childParamsBundle);
-
- for (String key : in.keySet()) {
- if (!BUNDLE_KEY_SET.contains(key)) {
- Log.w(TAG, "Found an unexpected key in the PersistableBundle: " + key);
- }
- }
-
- validate();
- }
-
- private void validate() {
- Objects.requireNonNull(mIkeParams, "mIkeParams was null");
- Objects.requireNonNull(mChildParams, "mChildParams was null");
- }
-
- /**
- * Converts this VcnControlPlaneConfig to a PersistableBundle.
- *
- * @hide
- */
- @Override
- @NonNull
- public PersistableBundle toPersistableBundle() {
- final PersistableBundle result = super.toPersistableBundle();
- result.putPersistableBundle(
- IKE_PARAMS_KEY, IkeSessionParamsUtils.toPersistableBundle(mIkeParams));
- result.putPersistableBundle(
- CHILD_PARAMS_KEY,
- TunnelModeChildSessionParamsUtils.toPersistableBundle(mChildParams));
- return result;
- }
-
- /** Retrieves the IKE Session configuration. */
- @NonNull
- public IkeSessionParams getIkeSessionParams() {
- return mIkeParams;
- }
-
- /** Retrieves the tunnel mode Child Session configuration. */
- @NonNull
- public TunnelModeChildSessionParams getChildSessionParams() {
- return mChildParams;
- }
-
- /** @hide */
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), mIkeParams, mChildParams);
- }
-
- /** @hide */
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof VcnControlPlaneIkeConfig)) {
- return false;
- }
-
- VcnControlPlaneIkeConfig other = (VcnControlPlaneIkeConfig) o;
-
- return super.equals(o)
- && Objects.equals(mIkeParams, other.mIkeParams)
- && Objects.equals(mChildParams, other.mChildParams);
- }
-
- /** @hide */
- @Override
- public VcnControlPlaneConfig copy() {
- return new VcnControlPlaneIkeConfig(
- new IkeSessionParams.Builder(mIkeParams).build(),
- new TunnelModeChildSessionParams.Builder(mChildParams).build());
- }
-}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 4ff1ccd..3c02cf0 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -24,6 +24,8 @@
import android.annotation.SuppressLint;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.TunnelConnectionParams;
+import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtils;
import android.os.PersistableBundle;
import android.util.ArraySet;
@@ -156,8 +158,8 @@
private static final String GATEWAY_CONNECTION_NAME_KEY = "mGatewayConnectionName";
@NonNull private final String mGatewayConnectionName;
- private static final String CTRL_PLANE_CONFIG_KEY = "mCtrlPlaneConfig";
- @NonNull private VcnControlPlaneConfig mCtrlPlaneConfig;
+ private static final String TUNNEL_CONNECTION_PARAMS_KEY = "mTunnelConnectionParams";
+ @NonNull private TunnelConnectionParams mTunnelConnectionParams;
private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities";
@NonNull private final SortedSet<Integer> mExposedCapabilities;
@@ -174,13 +176,13 @@
/** Builds a VcnGatewayConnectionConfig with the specified parameters. */
private VcnGatewayConnectionConfig(
@NonNull String gatewayConnectionName,
- @NonNull VcnControlPlaneConfig ctrlPlaneConfig,
+ @NonNull TunnelConnectionParams tunnelConnectionParams,
@NonNull Set<Integer> exposedCapabilities,
@NonNull Set<Integer> underlyingCapabilities,
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu) {
mGatewayConnectionName = gatewayConnectionName;
- mCtrlPlaneConfig = ctrlPlaneConfig;
+ mTunnelConnectionParams = tunnelConnectionParams;
mExposedCapabilities = new TreeSet(exposedCapabilities);
mUnderlyingCapabilities = new TreeSet(underlyingCapabilities);
mRetryIntervalsMs = retryIntervalsMs;
@@ -192,9 +194,10 @@
/** @hide */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public VcnGatewayConnectionConfig(@NonNull PersistableBundle in) {
- final PersistableBundle ctrlPlaneConfigBundle =
- in.getPersistableBundle(CTRL_PLANE_CONFIG_KEY);
- Objects.requireNonNull(ctrlPlaneConfigBundle, "ctrlPlaneConfigBundle was null");
+ final PersistableBundle tunnelConnectionParamsBundle =
+ in.getPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY);
+ Objects.requireNonNull(
+ tunnelConnectionParamsBundle, "tunnelConnectionParamsBundle was null");
final PersistableBundle exposedCapsBundle =
in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY);
@@ -202,7 +205,8 @@
in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY);
mGatewayConnectionName = in.getString(GATEWAY_CONNECTION_NAME_KEY);
- mCtrlPlaneConfig = VcnControlPlaneConfig.fromPersistableBundle(ctrlPlaneConfigBundle);
+ mTunnelConnectionParams =
+ TunnelConnectionParamsUtils.fromPersistableBundle(tunnelConnectionParamsBundle);
mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
mUnderlyingCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
@@ -215,7 +219,7 @@
private void validate() {
Objects.requireNonNull(mGatewayConnectionName, "gatewayConnectionName was null");
- Objects.requireNonNull(mCtrlPlaneConfig, "control plane config was null");
+ Objects.requireNonNull(mTunnelConnectionParams, "tunnel connection parameter was null");
Preconditions.checkArgument(
mExposedCapabilities != null && !mExposedCapabilities.isEmpty(),
@@ -267,13 +271,13 @@
}
/**
- * Returns control plane configuration.
+ * Returns tunnel connection parameters.
*
* @hide
*/
@NonNull
- public VcnControlPlaneConfig getControlPlaneConfig() {
- return mCtrlPlaneConfig.copy();
+ public TunnelConnectionParams getTunnelConnectionParams() {
+ return mTunnelConnectionParams;
}
/**
@@ -365,7 +369,8 @@
public PersistableBundle toPersistableBundle() {
final PersistableBundle result = new PersistableBundle();
- final PersistableBundle ctrlPlaneConfigBundle = mCtrlPlaneConfig.toPersistableBundle();
+ final PersistableBundle tunnelConnectionParamsBundle =
+ TunnelConnectionParamsUtils.toPersistableBundle(mTunnelConnectionParams);
final PersistableBundle exposedCapsBundle =
PersistableBundleUtils.fromList(
new ArrayList<>(mExposedCapabilities),
@@ -376,7 +381,7 @@
PersistableBundleUtils.INTEGER_SERIALIZER);
result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName);
- result.putPersistableBundle(CTRL_PLANE_CONFIG_KEY, ctrlPlaneConfigBundle);
+ result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle);
result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle);
result.putPersistableBundle(UNDERLYING_CAPABILITIES_KEY, underlyingCapsBundle);
result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
@@ -414,7 +419,7 @@
*/
public static final class Builder {
@NonNull private final String mGatewayConnectionName;
- @NonNull private final VcnControlPlaneConfig mCtrlPlaneConfig;
+ @NonNull private final TunnelConnectionParams mTunnelConnectionParams;
@NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
@NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet();
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
@@ -432,18 +437,18 @@
* VcnConfig} must be given a unique name. This name is used by the caller to
* distinguish between VcnGatewayConnectionConfigs configured on a single {@link
* VcnConfig}. This will be used as the identifier in VcnStatusCallback invocations.
- * @param ctrlPlaneConfig the control plane configuration
- * @see VcnControlPlaneConfig
+ * @param tunnelConnectionParams the tunnel connection configuration
+ * @see TunnelConnectionParams
* @see VcnManager.VcnStatusCallback#onGatewayConnectionError
*/
public Builder(
@NonNull String gatewayConnectionName,
- @NonNull VcnControlPlaneConfig ctrlPlaneConfig) {
+ @NonNull TunnelConnectionParams tunnelConnectionParams) {
Objects.requireNonNull(gatewayConnectionName, "gatewayConnectionName was null");
- Objects.requireNonNull(ctrlPlaneConfig, "ctrlPlaneConfig was null");
+ Objects.requireNonNull(tunnelConnectionParams, "tunnelConnectionParams was null");
mGatewayConnectionName = gatewayConnectionName;
- mCtrlPlaneConfig = ctrlPlaneConfig;
+ mTunnelConnectionParams = tunnelConnectionParams;
}
/**
@@ -588,7 +593,7 @@
public VcnGatewayConnectionConfig build() {
return new VcnGatewayConnectionConfig(
mGatewayConnectionName,
- mCtrlPlaneConfig,
+ mTunnelConnectionParams,
mExposedCapabilities,
mUnderlyingCapabilities,
mRetryIntervalsMs,
diff --git a/core/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java
new file mode 100644
index 0000000..690e4e7
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn.persistablebundleutils;
+
+import android.annotation.NonNull;
+import android.net.TunnelConnectionParams;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.os.PersistableBundle;
+
+import java.util.Objects;
+
+/**
+ * Utility class to convert TunnelConnectionParams to/from PersistableBundle
+ *
+ * @hide
+ */
+public final class TunnelConnectionParamsUtils {
+ private static final int EXPECTED_BUNDLE_KEY_CNT = 1;
+
+ private static final String PARAMS_TYPE_IKE = "IKE";
+
+ /** Serializes an TunnelConnectionParams to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull TunnelConnectionParams params) {
+ final PersistableBundle result = new PersistableBundle();
+
+ if (params instanceof IkeTunnelConnectionParams) {
+ result.putPersistableBundle(
+ PARAMS_TYPE_IKE,
+ IkeTunnelConnectionParamsUtils.serializeIkeParams(
+ (IkeTunnelConnectionParams) params));
+ return result;
+ } else {
+ throw new UnsupportedOperationException("Invalid TunnelConnectionParams type");
+ }
+ }
+
+ /** Constructs an TunnelConnectionParams by deserializing a PersistableBundle. */
+ @NonNull
+ public static TunnelConnectionParams fromPersistableBundle(@NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ if (in.keySet().size() != EXPECTED_BUNDLE_KEY_CNT) {
+ throw new IllegalArgumentException(
+ "Expect PersistableBundle to have one element but found: " + in.keySet());
+ }
+
+ if (in.get(PARAMS_TYPE_IKE) != null) {
+ return IkeTunnelConnectionParamsUtils.deserializeIkeParams(
+ in.getPersistableBundle(PARAMS_TYPE_IKE));
+ }
+
+ throw new IllegalArgumentException(
+ "Invalid TunnelConnectionParams type " + in.keySet().iterator().next());
+ }
+
+ private static final class IkeTunnelConnectionParamsUtils {
+ private static final String IKE_PARAMS_KEY = "IKE_PARAMS_KEY";
+ private static final String CHILD_PARAMS_KEY = "CHILD_PARAMS_KEY";
+
+ @NonNull
+ public static PersistableBundle serializeIkeParams(
+ @NonNull IkeTunnelConnectionParams ikeParams) {
+ final PersistableBundle result = new PersistableBundle();
+
+ result.putPersistableBundle(
+ IKE_PARAMS_KEY,
+ IkeSessionParamsUtils.toPersistableBundle(ikeParams.getIkeSessionParams()));
+ result.putPersistableBundle(
+ CHILD_PARAMS_KEY,
+ TunnelModeChildSessionParamsUtils.toPersistableBundle(
+ ikeParams.getTunnelModeChildSessionParams()));
+ return result;
+ }
+
+ @NonNull
+ public static IkeTunnelConnectionParams deserializeIkeParams(
+ @NonNull PersistableBundle in) {
+ final PersistableBundle ikeBundle = in.getPersistableBundle(IKE_PARAMS_KEY);
+ final PersistableBundle childBundle = in.getPersistableBundle(CHILD_PARAMS_KEY);
+ Objects.requireNonNull(ikeBundle, "IkeSessionParams was null");
+ Objects.requireNonNull(ikeBundle, "TunnelModeChildSessionParams was null");
+
+ final IkeSessionParams ikeParams =
+ IkeSessionParamsUtils.fromPersistableBundle(ikeBundle);
+ final TunnelModeChildSessionParams childParams =
+ TunnelModeChildSessionParamsUtils.fromPersistableBundle(childBundle);
+ return new IkeTunnelConnectionParams(ikeParams, childParams);
+ }
+ }
+}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 34f2c103f..9a81942 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -100,7 +100,7 @@
boolean isProfile(int userId);
boolean isManagedProfile(int userId);
boolean isCloneProfile(int userId);
- boolean sharesMediaWithParent(int userId);
+ boolean isMediaSharedWithParent(int userId);
boolean isDemoUser(int userId);
boolean isPreCreated(int userId);
UserInfo createProfileForUserEvenWhenDisallowedWithThrow(in String name, in String userType, int flags,
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index d4de4fa..b8ad068 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4124,9 +4124,9 @@
Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
@UserHandleAware
@SuppressAutoDoc
- public boolean sharesMediaWithParent() {
+ public boolean isMediaSharedWithParent() {
try {
- return mService.sharesMediaWithParent(mUserId);
+ return mService.isMediaSharedWithParent(mUserId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 2876775..b3502f3 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -552,6 +552,27 @@
}
/**
+ * Query the estimated durations of the given primitives.
+ *
+ * The returned array will be the same length as the query array and the value at a given index
+ * will contain the duration in milliseconds of the effect at the same index in the querying
+ * array.
+ *
+ * @param primitiveIds Which primitives to query for.
+ * @return The duration of each primitive, with zeroes for primitives that are not supported.
+ */
+ @NonNull
+ public int[] getPrimitiveDurations(
+ @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
+ VibratorInfo info = getInfo();
+ int[] durations = new int[primitiveIds.length];
+ for (int i = 0; i < primitiveIds.length; i++) {
+ durations[i] = info.getPrimitiveDuration(primitiveIds[i]);
+ }
+ return durations;
+ }
+
+ /**
* Turn the vibrator off.
*/
@RequiresPermission(android.Manifest.permission.VIBRATE)
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index c7d66f0..d73469c 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -25,6 +25,7 @@
import android.util.MathUtils;
import android.util.Range;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import java.util.ArrayList;
import java.util.Arrays;
@@ -51,7 +52,7 @@
@Nullable
private final SparseBooleanArray mSupportedBraking;
@Nullable
- private final SparseBooleanArray mSupportedPrimitives;
+ private final SparseIntArray mSupportedPrimitives;
private final float mQFactor;
private final FrequencyMapping mFrequencyMapping;
@@ -60,19 +61,37 @@
mCapabilities = in.readLong();
mSupportedEffects = in.readSparseBooleanArray();
mSupportedBraking = in.readSparseBooleanArray();
- mSupportedPrimitives = in.readSparseBooleanArray();
+ mSupportedPrimitives = in.readSparseIntArray();
mQFactor = in.readFloat();
mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader());
}
- /** @hide */
+ /**
+ * Default constructor.
+ *
+ * @param id The vibrator id.
+ * @param capabilities All capability flags of the vibrator, defined in IVibrator.CAP_*.
+ * @param supportedEffects All supported predefined effects, enum values from {@link
+ * android.hardware.vibrator.Effect}.
+ * @param supportedBraking All supported braking types, enum values from {@link Braking}.
+ * @param supportedPrimitives All supported primitive effects, enum values from {@link
+ * android.hardware.vibrator.CompositePrimitive}.
+ * @param primitiveDurations A mapping of primitive durations, where indexes are enum values
+ * from {@link android.hardware.vibrator.CompositePrimitive} and the
+ * values are estimated durations in milliseconds.
+ * @param qFactor The vibrator quality factor.
+ * @param frequencyMapping The description of the vibrator supported frequencies and max
+ * amplitude mappings.
+ * @hide
+ */
public VibratorInfo(int id, long capabilities, int[] supportedEffects, int[] supportedBraking,
- int[] supportedPrimitives, float qFactor, @NonNull FrequencyMapping frequencyMapping) {
+ int[] supportedPrimitives, int[] primitiveDurations, float qFactor,
+ @NonNull FrequencyMapping frequencyMapping) {
mId = id;
mCapabilities = capabilities;
mSupportedEffects = toSparseBooleanArray(supportedEffects);
mSupportedBraking = toSparseBooleanArray(supportedBraking);
- mSupportedPrimitives = toSparseBooleanArray(supportedPrimitives);
+ mSupportedPrimitives = toSparseIntArray(supportedPrimitives, primitiveDurations);
mQFactor = qFactor;
mFrequencyMapping = frequencyMapping;
}
@@ -100,7 +119,7 @@
dest.writeLong(mCapabilities);
dest.writeSparseBooleanArray(mSupportedEffects);
dest.writeSparseBooleanArray(mSupportedBraking);
- dest.writeSparseBooleanArray(mSupportedPrimitives);
+ dest.writeSparseIntArray(mSupportedPrimitives);
dest.writeFloat(mQFactor);
dest.writeParcelable(mFrequencyMapping, flags);
}
@@ -119,18 +138,41 @@
return false;
}
VibratorInfo that = (VibratorInfo) o;
+ if (mSupportedPrimitives == null || that.mSupportedPrimitives == null) {
+ if (mSupportedPrimitives != that.mSupportedPrimitives) {
+ return false;
+ }
+ } else {
+ if (mSupportedPrimitives.size() != that.mSupportedPrimitives.size()) {
+ return false;
+ }
+ for (int i = 0; i < mSupportedPrimitives.size(); i++) {
+ if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
+ return false;
+ }
+ if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
+ return false;
+ }
+ }
+ }
return mId == that.mId && mCapabilities == that.mCapabilities
&& Objects.equals(mSupportedEffects, that.mSupportedEffects)
&& Objects.equals(mSupportedBraking, that.mSupportedBraking)
- && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives)
&& Objects.equals(mQFactor, that.mQFactor)
&& Objects.equals(mFrequencyMapping, that.mFrequencyMapping);
}
@Override
public int hashCode() {
- return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
- mSupportedPrimitives, mQFactor, mFrequencyMapping);
+ int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
+ mQFactor, mFrequencyMapping);
+ if (mSupportedPrimitives != null) {
+ for (int i = 0; i < mSupportedPrimitives.size(); i++) {
+ hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
+ hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
+ }
+ }
+ return hashCode;
}
@Override
@@ -206,7 +248,19 @@
public boolean isPrimitiveSupported(
@VibrationEffect.Composition.PrimitiveType int primitiveId) {
return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) && mSupportedPrimitives != null
- && mSupportedPrimitives.get(primitiveId);
+ && (mSupportedPrimitives.indexOfKey(primitiveId) >= 0);
+ }
+
+ /**
+ * Query the estimated duration of given primitive.
+ *
+ * @param primitiveId Which primitives to query for.
+ * @return The duration in milliseconds estimated for the primitive, or zero if primitive not
+ * supported.
+ */
+ public int getPrimitiveDuration(
+ @VibrationEffect.Composition.PrimitiveType int primitiveId) {
+ return mSupportedPrimitives.get(primitiveId);
}
/**
@@ -364,14 +418,37 @@
return names;
}
+ /**
+ * Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is mapped
+ * to {@code true}.
+ */
@Nullable
- private static SparseBooleanArray toSparseBooleanArray(int[] values) {
- if (values == null) {
+ private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
+ if (supportedKeys == null) {
return null;
}
SparseBooleanArray array = new SparseBooleanArray();
- for (int value : values) {
- array.put(value, true);
+ for (int key : supportedKeys) {
+ array.put(key, true);
+ }
+ return array;
+ }
+
+ /**
+ * Create a {@link SparseIntArray} from given {@code supportedKeys} where each key is mapped
+ * to the value indexed by it.
+ *
+ * <p>If {@code values} is null or does not contain a given key as a index, then zero is stored
+ * to the sparse array so it can still be used to query the supported keys.
+ */
+ @Nullable
+ private static SparseIntArray toSparseIntArray(int[] supportedKeys, int[] values) {
+ if (supportedKeys == null) {
+ return null;
+ }
+ SparseIntArray array = new SparseIntArray();
+ for (int key : supportedKeys) {
+ array.put(key, (values == null || key >= values.length) ? 0 : values[key]);
}
return array;
}
@@ -419,7 +496,20 @@
in.createFloatArray());
}
- /** @hide */
+ /**
+ * Default constructor.
+ *
+ * @param minFrequencyHz Minimum supported frequency, in hertz.
+ * @param resonantFrequencyHz The vibrator resonant frequency, in hertz.
+ * @param frequencyResolutionHz The frequency resolution, in hertz, used by the max
+ * amplitudes mapping.
+ * @param suggestedSafeRangeHz The suggested range, in hertz, for the safe relative
+ * frequency range represented by [-1, 1].
+ * @param maxAmplitudes The max amplitude supported by each supported frequency,
+ * starting at minimum frequency with jumps of frequency
+ * resolution.
+ * @hide
+ */
public FrequencyMapping(float minFrequencyHz, float resonantFrequencyHz,
float frequencyResolutionHz, float suggestedSafeRangeHz, float[] maxAmplitudes) {
mMinFrequencyHz = minFrequencyHz;
@@ -547,8 +637,10 @@
@Override
public int hashCode() {
- return Objects.hash(mMinFrequencyHz, mFrequencyResolutionHz, mFrequencyResolutionHz,
- mSuggestedSafeRangeHz, mMaxAmplitudes);
+ int hashCode = Objects.hash(mMinFrequencyHz, mFrequencyResolutionHz,
+ mFrequencyResolutionHz, mSuggestedSafeRangeHz);
+ hashCode = 31 * hashCode + Arrays.hashCode(mMaxAmplitudes);
+ return hashCode;
}
@Override
@@ -587,6 +679,7 @@
private int[] mSupportedEffects = null;
private int[] mSupportedBraking = null;
private int[] mSupportedPrimitives = null;
+ private int[] mPrimitiveDurations = new int[0];
private float mQFactor = Float.NaN;
private FrequencyMapping mFrequencyMapping =
new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
@@ -627,6 +720,16 @@
return this;
}
+ /** Configure the duration of a {@link android.hardware.vibrator.CompositePrimitive}. */
+ @NonNull
+ public Builder setPrimitiveDuration(int primitiveId, int duration) {
+ if (mPrimitiveDurations.length <= primitiveId) {
+ mPrimitiveDurations = Arrays.copyOf(mPrimitiveDurations, primitiveId + 1);
+ }
+ mPrimitiveDurations[primitiveId] = duration;
+ return this;
+ }
+
/** Configure the vibrator quality factor. */
@NonNull
public Builder setQFactor(float qFactor) {
@@ -645,7 +748,7 @@
@NonNull
public VibratorInfo build() {
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
- mSupportedPrimitives, mQFactor, mFrequencyMapping);
+ mSupportedPrimitives, mPrimitiveDurations, mQFactor, mFrequencyMapping);
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a4f9ddc..f8991ce 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4658,9 +4658,12 @@
public static final String AUTO_TIME_ZONE = Global.AUTO_TIME_ZONE;
/**
- * Display times as 12 or 24 hours
- * 12
- * 24
+ * Display the user's times, e.g. in the status bar, as 12 or 24 hours.
+ * <ul>
+ * <li>24 = 24 hour</li>
+ * <li>12 = 12 hour</li>
+ * <li>[unset] = use the device locale's default</li>
+ * </ul>
*/
@Readable
public static final String TIME_12_24 = "time_12_24";
@@ -10243,15 +10246,16 @@
public static final int WIFI_SLEEP_POLICY_NEVER = 2;
/**
- * Value to specify if the user prefers the date, time and time zone
- * to be automatically fetched from the network (NITZ). 1=yes, 0=no
+ * Value to specify if the device's UTC system clock should be set automatically, e.g. using
+ * telephony signals like NITZ, or other sources like GNSS or NTP. 1=yes, 0=no (manual)
*/
@Readable
public static final String AUTO_TIME = "auto_time";
/**
- * Value to specify if the user prefers the time zone
- * to be automatically fetched from the network (NITZ). 1=yes, 0=no
+ * Value to specify if the device's time zone system property should be set automatically,
+ * e.g. using telephony signals like MCC and NITZ, or other mechanisms like the location.
+ * 1=yes, 0=no (manual).
*/
@Readable
public static final String AUTO_TIME_ZONE = "auto_time_zone";
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index c2c8d63..ee33541 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -184,7 +184,9 @@
if (oldLeash == null || newLeash == null || !oldLeash.isSameSurface(newLeash)) {
applyHiddenToControl();
}
- if (!requestedVisible && !mIsAnimationPending) {
+
+ // Remove the surface that owned by last control when it lost.
+ if (!requestedVisible && !mIsAnimationPending && lastControl == null) {
removeSurface();
}
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 01cb0a6..538b888 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1459,8 +1459,8 @@
PropertyInfo<ExportedProperty, ?>[] properties = sExportProperties.get(klass);
if (properties == null) {
- properties = convertToPropertyInfos(klass.getDeclaredMethodsUnchecked(false),
- klass.getDeclaredFieldsUnchecked(false), ExportedProperty.class);
+ properties = convertToPropertyInfos(klass.getDeclaredMethods(),
+ klass.getDeclaredFields(), ExportedProperty.class);
map.put(klass, properties);
}
return properties;
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 50773b1..5ca9d13 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -46,6 +46,7 @@
import android.util.Pair;
import android.util.Printer;
import android.view.Gravity;
+import android.view.RemotableViewMethod;
import android.view.View;
import android.view.ViewGroup;
import android.view.inspector.InspectableProperty;
@@ -412,6 +413,7 @@
*
* @attr ref android.R.styleable#GridLayout_rowCount
*/
+ @RemotableViewMethod
public void setRowCount(int rowCount) {
mVerticalAxis.setCount(rowCount);
invalidateStructure();
@@ -446,6 +448,7 @@
*
* @attr ref android.R.styleable#GridLayout_columnCount
*/
+ @RemotableViewMethod
public void setColumnCount(int columnCount) {
mHorizontalAxis.setCount(columnCount);
invalidateStructure();
@@ -534,6 +537,7 @@
*
* @attr ref android.R.styleable#GridLayout_alignmentMode
*/
+ @RemotableViewMethod
public void setAlignmentMode(@AlignmentMode int alignmentMode) {
this.mAlignmentMode = alignmentMode;
requestLayout();
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 196d68b..647f69d 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -30,6 +30,7 @@
import android.util.MathUtils;
import android.view.Gravity;
import android.view.KeyEvent;
+import android.view.RemotableViewMethod;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewDebug;
@@ -2050,6 +2051,7 @@
*
* @attr ref android.R.styleable#GridView_gravity
*/
+ @RemotableViewMethod
public void setGravity(int gravity) {
if (mGravity != gravity) {
mGravity = gravity;
@@ -2078,6 +2080,7 @@
*
* @attr ref android.R.styleable#GridView_horizontalSpacing
*/
+ @RemotableViewMethod
public void setHorizontalSpacing(int horizontalSpacing) {
if (horizontalSpacing != mRequestedHorizontalSpacing) {
mRequestedHorizontalSpacing = horizontalSpacing;
@@ -2136,6 +2139,7 @@
*
* @attr ref android.R.styleable#GridView_verticalSpacing
*/
+ @RemotableViewMethod
public void setVerticalSpacing(int verticalSpacing) {
if (verticalSpacing != mVerticalSpacing) {
mVerticalSpacing = verticalSpacing;
@@ -2165,6 +2169,7 @@
*
* @attr ref android.R.styleable#GridView_stretchMode
*/
+ @RemotableViewMethod
public void setStretchMode(@StretchMode int stretchMode) {
if (stretchMode != mStretchMode) {
mStretchMode = stretchMode;
@@ -2191,6 +2196,7 @@
*
* @attr ref android.R.styleable#GridView_columnWidth
*/
+ @RemotableViewMethod
public void setColumnWidth(int columnWidth) {
if (columnWidth != mRequestedColumnWidth) {
mRequestedColumnWidth = columnWidth;
@@ -2239,6 +2245,7 @@
*
* @attr ref android.R.styleable#GridView_numColumns
*/
+ @RemotableViewMethod
public void setNumColumns(int numColumns) {
if (numColumns != mRequestedNumColumns) {
mRequestedNumColumns = numColumns;
diff --git a/core/java/android/widget/inline/TEST_MAPPING b/core/java/android/widget/inline/TEST_MAPPING
index 0baad5c..26a5569 100644
--- a/core/java/android/widget/inline/TEST_MAPPING
+++ b/core/java/android/widget/inline/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-large": [
{
"name": "CtsAutoFillServiceTestCases",
"options": [
diff --git a/core/java/android/window/IRemoteTransition.aidl b/core/java/android/window/IRemoteTransition.aidl
index e71de70..2efb68a 100644
--- a/core/java/android/window/IRemoteTransition.aidl
+++ b/core/java/android/window/IRemoteTransition.aidl
@@ -40,7 +40,23 @@
/**
* Starts a transition animation. Once complete, the implementation should call
* `finishCallback`.
+ *
+ * @param token An identifier for the transition that should be animated.
*/
- void startAnimation(in TransitionInfo info, in SurfaceControl.Transaction t,
+ void startAnimation(in IBinder token, in TransitionInfo info, in SurfaceControl.Transaction t,
+ in IRemoteTransitionFinishedCallback finishCallback);
+
+ /**
+ * Attempts to merge a transition animation into the animation that is currently
+ * being played by this remote. If merge is not possible/supported, this should be a no-op.
+ * If it *is* merged, the implementation should call `finishCallback` immediately.
+ *
+ * @param transition An identifier for the transition that wants to be merged.
+ * @param mergeTarget The transition that is currently being animated by this remote.
+ * If it can be merged, call `finishCallback`; otherwise, do
+ * nothing.
+ */
+ void mergeAnimation(in IBinder transition, in TransitionInfo info,
+ in SurfaceControl.Transaction t, in IBinder mergeTarget,
in IRemoteTransitionFinishedCallback finishCallback);
}
diff --git a/core/java/android/window/ITransitionPlayer.aidl b/core/java/android/window/ITransitionPlayer.aidl
index af37fbc..3eca803 100644
--- a/core/java/android/window/ITransitionPlayer.aidl
+++ b/core/java/android/window/ITransitionPlayer.aidl
@@ -47,9 +47,12 @@
* @param transitionToken An identifying token for the transition that is now ready to animate.
* @param info A collection of all the changes encapsulated by this transition.
* @param t A surface transaction containing the surface state prior to animating.
+ * @param finishT A surface transaction that will reset parenting/layering and generally put
+ * surfaces into their final (post-transition) state. Apply this after playing
+ * the animation but before calling finish.
*/
void onTransitionReady(in IBinder transitionToken, in TransitionInfo info,
- in SurfaceControl.Transaction t);
+ in SurfaceControl.Transaction t, in SurfaceControl.Transaction finishT);
/**
* Called when something in WMCore requires a transition to play -- for example when an Activity
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index db536d3..a9b47aa 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -24,7 +24,6 @@
import android.content.ComponentName;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
-import android.hardware.SensorPrivacyManager;
import android.os.Build;
import android.os.CarrierAssociatedAppEntry;
import android.os.Environment;
@@ -1245,14 +1244,6 @@
addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0);
}
- if (SensorPrivacyManager.USE_MICROPHONE_TOGGLE) {
- addFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE, 0);
- }
-
- if (SensorPrivacyManager.USE_CAMERA_TOGGLE) {
- addFeature(PackageManager.FEATURE_CAMERA_TOGGLE, 0);
- }
-
for (String featureName : mUnavailableFeatures) {
removeFeature(featureName);
}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 7c4c970..5eb1e00 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1930,7 +1930,11 @@
audio_port_handle_t handle;
status_t status = AudioSystem::startAudioSource(&nAudioPortConfig, paa.get(), &handle);
ALOGV("AudioSystem::startAudioSource() returned %d handle %d", status, handle);
- return handle > 0 ? handle : nativeToJavaStatus(status);
+ if (status != NO_ERROR) {
+ return nativeToJavaStatus(status);
+ }
+ ALOG_ASSERT(handle > 0, "%s: invalid handle reported on successful call", __func__);
+ return handle;
}
static jint
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index ad1d252..8ee0e39 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -89,14 +89,6 @@
optional SettingProto assisted_gps_enabled = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto audio_safe_volume_state = 15 [ (android.privacy).dest = DEST_AUTOMATIC ];
- message Auto {
- option (android.msg_privacy).dest = DEST_EXPLICIT;
-
- optional SettingProto time = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto time_zone = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
- }
- optional Auto auto = 16;
-
reserved 17; // Used to be autofill_compat_mode_allowed_packages
message Autofill {
@@ -253,6 +245,14 @@
}
optional Database database = 36;
+ message DateTime {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto auto_time = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto auto_time_zone = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional DateTime date_time = 16;
+
message Debug {
option (android.msg_privacy).dest = DEST_EXPLICIT;
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 530cb44..ae6dcfd 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -217,6 +217,13 @@
optional SettingProto display_density_forced = 19 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto double_tap_to_wake = 20 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ message DateTime {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto location_time_zone_detection_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional DateTime date_time = 90;
+
message Doze {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -656,5 +663,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 90;
+ // Next tag = 91;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0e1f6de..bf98a2e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -40,6 +40,7 @@
<protected-broadcast android:name="android.intent.action.PACKAGE_REPLACED" />
<protected-broadcast android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED_INTERNAL" />
<protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_CHANGED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_LOADED" />
@@ -4104,7 +4105,7 @@
@hide
-->
<permission android:name="com.android.permission.USE_INSTALLER_V2"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|privileged" />
<!-- @TestApi Allows a testOnly application to get installed.
<p>Not for use by third-party applications.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c2a7e07..529aa66 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4868,4 +4868,9 @@
<string name="config_systemVisualIntelligence" translatable="false"></string>
<!-- On-device package for providing companion device associations. -->
<string name="config_systemCompanionDeviceProvider" translatable="false"></string>
+
+ <!-- Whether this device is supporting the microphone toggle -->
+ <bool name="config_supportsMicToggle">false</bool>
+ <!-- Whether this device is supporting the camera toggle -->
+ <bool name="config_supportsCamToggle">false</bool>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0cb80f3..cc347f9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5007,10 +5007,10 @@
<string name="confirm_battery_saver">OK</string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, with a "learn more" link. -->
- <string name="battery_saver_description_with_learn_more">Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, and features like \u201cHey Google\u201d\n\n<annotation id="url">Learn more</annotation></string>
+ <string name="battery_saver_description_with_learn_more">Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, and certain features.\n\n<annotation id="url">Learn more</annotation></string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, without a "learn more" link. -->
- <string name="battery_saver_description">Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, and features like \u201cHey Google\u201d.</string>
+ <string name="battery_saver_description">Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, and certain features.</string>
<!-- [CHAR_LIMIT=NONE] Data saver: Feature description -->
<string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 402ea60..78a794a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4357,4 +4357,7 @@
<java-symbol type="string" name="view_and_control_notification_content" />
<java-symbol type="layout" name="notification_expand_button"/>
+
+ <java-symbol type="bool" name="config_supportsMicToggle" />
+ <java-symbol type="bool" name="config_supportsCamToggle" />
</resources>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
index d49ab57..52a77a7 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
@@ -25,8 +25,7 @@
<application
android:theme="@style/Theme"
android:label="Battery Stats Viewer">
- <activity android:name=".BatteryStatsViewerActivity"
- android:icon="@mipmap/ic_launcher"
+ <activity android:name=".BatteryConsumerPickerActivity"
android:label="Battery Stats"
android:launchMode="singleTop"
android:exported="true">
@@ -36,7 +35,9 @@
</intent-filter>
</activity>
- <activity android:name=".BatteryConsumerPickerActivity"
- android:label="Select a battery consumer"/>
+ <activity android:name=".BatteryStatsViewerActivity"
+ android:icon="@mipmap/ic_launcher"
+ android:label="Battery Stats"
+ android:parentActivityName=".BatteryConsumerPickerActivity"/>
</application>
</manifest>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java
index 2db848b..63a15d6 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java
@@ -20,9 +20,7 @@
import android.content.Intent;
import android.os.Bundle;
-import androidx.activity.result.contract.ActivityResultContract;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentStatePagerAdapter;
@@ -32,31 +30,14 @@
/**
* Picker, showing a sorted lists of applications and other types of entities consuming power.
- * Returns the selected entity ID or null.
+ * Opens BatteryStatsViewerActivity upon item selection.
*/
public class BatteryConsumerPickerActivity extends FragmentActivity {
-
- public static final ActivityResultContract<Void, String> CONTRACT =
- new ActivityResultContract<Void, String>() {
- @NonNull
- @Override
- public Intent createIntent(@NonNull Context context, Void aVoid) {
- return new Intent(context, BatteryConsumerPickerActivity.class);
- }
-
- @Override
- public String parseResult(int resultCode, @Nullable Intent intent) {
- if (resultCode != RESULT_OK || intent == null) {
- return null;
- }
- return intent.getStringExtra(Intent.EXTRA_RETURN_RESULT);
- }
- };
+ private static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId";
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
- getActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.battery_consumer_picker_activity_layout);
@@ -99,18 +80,25 @@
viewPager.setAdapter(adapter);
TabLayout tabLayout = findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(viewPager);
+ if (icicle == null) {
+ final String batteryConsumerId = getPreferences(Context.MODE_PRIVATE)
+ .getString(PREF_SELECTED_BATTERY_CONSUMER, null);
+ if (batteryConsumerId != null) {
+ startBatteryStatsActivity(batteryConsumerId);
+ }
+ }
}
public void setSelectedBatteryConsumer(String batteryConsumerId) {
- Intent intent = new Intent();
- intent.putExtra(Intent.EXTRA_RETURN_RESULT, batteryConsumerId);
- setResult(RESULT_OK, intent);
- finish();
+ getPreferences(Context.MODE_PRIVATE).edit()
+ .putString(PREF_SELECTED_BATTERY_CONSUMER, batteryConsumerId)
+ .apply();
+ startBatteryStatsActivity(batteryConsumerId);
}
- @Override
- public boolean onNavigateUp() {
- onBackPressed();
- return true;
+ private void startBatteryStatsActivity(String batteryConsumerId) {
+ final Intent intent = new Intent(this, BatteryStatsViewerActivity.class)
+ .putExtra(BatteryStatsViewerActivity.EXTRA_BATTERY_CONSUMER, batteryConsumerId);
+ startActivity(intent);
}
}
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
index 74d3fb3..03dde04 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
@@ -17,7 +17,6 @@
package com.android.frameworks.core.batterystatsviewer;
import android.content.Context;
-import android.content.SharedPreferences;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
@@ -27,9 +26,9 @@
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
+import android.widget.Toast;
import androidx.activity.ComponentActivity;
-import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.loader.app.LoaderManager;
@@ -45,14 +44,14 @@
import java.util.Locale;
public class BatteryStatsViewerActivity extends ComponentActivity {
+ public static final String EXTRA_BATTERY_CONSUMER = "batteryConsumerId";
+
private static final int BATTERY_STATS_REFRESH_RATE_MILLIS = 60 * 1000;
private static final int MILLIS_IN_MINUTE = 60000;
- private static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId";
private static final int LOADER_BATTERY_USAGE_STATS = 1;
private BatteryStatsDataAdapter mBatteryStatsDataAdapter;
private final Runnable mBatteryStatsRefresh = this::periodicBatteryStatsRefresh;
- private SharedPreferences mSharedPref;
private String mBatteryConsumerId;
private TextView mTitleView;
private TextView mDetailsView;
@@ -62,21 +61,16 @@
private RecyclerView mBatteryConsumerDataView;
private View mLoadingView;
private View mEmptyView;
- private final ActivityResultLauncher<Void> mStartAppPicker = registerForActivityResult(
- BatteryConsumerPickerActivity.CONTRACT, this::onApplicationSelected);
private List<BatteryUsageStats> mBatteryUsageStats;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mSharedPref = getPreferences(Context.MODE_PRIVATE);
+ mBatteryConsumerId = getIntent().getStringExtra(EXTRA_BATTERY_CONSUMER);
setContentView(R.layout.battery_stats_viewer_layout);
- View appCard = findViewById(R.id.app_card);
- appCard.setOnClickListener((e) -> startAppPicker());
-
mTitleView = findViewById(android.R.id.title);
mDetailsView = findViewById(R.id.details);
mIconView = findViewById(android.R.id.icon);
@@ -91,11 +85,7 @@
mLoadingView = findViewById(R.id.loading_view);
mEmptyView = findViewById(R.id.empty_view);
- mBatteryConsumerId = mSharedPref.getString(PREF_SELECTED_BATTERY_CONSUMER, null);
loadBatteryStats();
- if (mBatteryConsumerId == null) {
- startAppPicker();
- }
}
@Override
@@ -110,25 +100,6 @@
getMainThreadHandler().removeCallbacks(mBatteryStatsRefresh);
}
- private void startAppPicker() {
- mStartAppPicker.launch(null);
- }
-
- private void onApplicationSelected(String batteryConsumerId) {
- if (batteryConsumerId == null) {
- if (mBatteryConsumerId == null) {
- finish();
- }
- } else {
- mBatteryConsumerId = batteryConsumerId;
- mSharedPref.edit()
- .putString(PREF_SELECTED_BATTERY_CONSUMER, mBatteryConsumerId)
- .apply();
- mLoadingView.setVisibility(View.VISIBLE);
- loadBatteryStats();
- }
- }
-
private void periodicBatteryStatsRefresh() {
loadBatteryStats();
getMainThreadHandler().postDelayed(mBatteryStatsRefresh, BATTERY_STATS_REFRESH_RATE_MILLIS);
@@ -200,9 +171,10 @@
BatteryConsumerInfoHelper.BatteryConsumerInfo
batteryConsumerInfo = batteryConsumerData.getBatteryConsumerInfo();
if (batteryConsumerInfo == null) {
- mTitleView.setText("Battery consumer not found");
- mPackagesView.setVisibility(View.GONE);
- mHeadingsView.setVisibility(View.GONE);
+ Toast.makeText(this, "Battery consumer not found: " + mBatteryConsumerId,
+ Toast.LENGTH_SHORT).show();
+ finish();
+ return;
} else {
mTitleView.setText(batteryConsumerInfo.label);
if (batteryConsumerInfo.details != null) {
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index 3a80464..9880f8c 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -100,6 +100,17 @@
}
@Test
+ public void testGetPrimitiveDuration() {
+ VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .build();
+ assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
+ assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_TICK));
+ }
+
+ @Test
public void testGetDefaultBraking_returnsFirstSupportedBraking() {
assertEquals(Braking.NONE, new VibratorInfo.Builder(
TEST_VIBRATOR_ID).build().getDefaultBraking());
@@ -251,12 +262,14 @@
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
.setQFactor(2f)
.setFrequencyMapping(TEST_FREQUENCY_MAPPING);
VibratorInfo complete = completeBuilder.build();
assertEquals(complete, complete);
assertEquals(complete, completeBuilder.build());
+ assertEquals(complete.hashCode(), completeBuilder.build().hashCode());
VibratorInfo completeWithComposeControl = completeBuilder
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
@@ -279,6 +292,11 @@
.build();
assertNotEquals(complete, completeWithUnknownPrimitives);
+ VibratorInfo completeWithDifferentPrimitiveDuration = completeBuilder
+ .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+ assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
+
VibratorInfo completeWithDifferentFrequencyMapping = completeBuilder
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(TEST_MIN_FREQUENCY + 10,
TEST_RESONANT_FREQUENCY + 20, TEST_FREQUENCY_RESOLUTION + 5,
@@ -314,7 +332,8 @@
VibratorInfo original = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
- .setSupportedPrimitives(null)
+ .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
.setQFactor(Float.NaN)
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
.build();
@@ -324,5 +343,6 @@
parcel.setDataPosition(0);
VibratorInfo restored = VibratorInfo.CREATOR.createFromParcel(parcel);
assertEquals(original, restored);
+ assertEquals(original.hashCode(), restored.hashCode());
}
}
diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/coretests/src/android/os/VibratorTest.java
index 6213285..8f9168b 100644
--- a/core/tests/coretests/src/android/os/VibratorTest.java
+++ b/core/tests/coretests/src/android/os/VibratorTest.java
@@ -80,6 +80,16 @@
}
@Test
+ public void getPrimitivesDurations_returnsArrayOfSameSize() {
+ assertEquals(0, mVibratorSpy.getPrimitiveDurations(new int[0]).length);
+ assertEquals(1, mVibratorSpy.getPrimitiveDurations(
+ new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}).length);
+ assertEquals(2, mVibratorSpy.getPrimitiveDurations(
+ new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE}).length);
+ }
+
+ @Test
public void vibrate_withAudioAttributes_createsVibrationAttributesWithSameUsage() {
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 091f579..6d3d84c 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -66,7 +66,8 @@
boolean isCredentialManagementApp(String packageName);
// APIs used by KeyChainActivity
- void setGrant(int uid, String alias, boolean value);
+ // setGrant may fail with value=false when ungrant operation fails in KeyStore.
+ boolean setGrant(int uid, String alias, boolean value);
boolean hasGrant(int uid, String alias);
// API used by Wifi
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
index 0006b92..850c551 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
@@ -40,6 +40,7 @@
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
+import java.util.Random;
/**
* Assorted utility methods for implementing crypto operations on top of KeyStore.
@@ -49,6 +50,7 @@
abstract class KeyStoreCryptoOperationUtils {
private static volatile SecureRandom sRng;
+ private static final Random sRandom = new Random();
private KeyStoreCryptoOperationUtils() {}
@@ -211,7 +213,7 @@
} else {
// Keystore won't give us an operation challenge if the operation doesn't
// need user authorization. So we make our own.
- return Math.randomLongInternal();
+ return sRandom.nextLong();
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 71fd917..4da6664 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -82,18 +82,39 @@
if (mRemote.asBinder() != null) {
mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
- mRemote.startAnimation(info, t, cb);
+ mRemote.startAnimation(transition, info, t, cb);
} catch (RemoteException e) {
+ Log.e(Transitions.TAG, "Error running remote transition.", e);
if (mRemote.asBinder() != null) {
mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
- Log.e(Transitions.TAG, "Error running remote transition.", e);
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
}
return true;
}
@Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
+ + " transition %s for %s.", mRemote, transition);
+
+ IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
+ @Override
+ public void onTransitionFinished(WindowContainerTransaction wct) {
+ mMainExecutor.execute(
+ () -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
+ }
+ };
+ try {
+ mRemote.mergeAnimation(transition, info, t, mergeTarget, cb);
+ } catch (RemoteException e) {
+ Log.e(Transitions.TAG, "Error merging remote transition.", e);
+ }
+ }
+
+ @Override
@Nullable
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@Nullable TransitionRequestInfo request) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 8876e5f..9bfb261 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.transition;
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
@@ -52,7 +50,7 @@
private final ShellExecutor mMainExecutor;
/** Includes remotes explicitly requested by, eg, ActivityOptions */
- private final ArrayMap<IBinder, IRemoteTransition> mPendingRemotes = new ArrayMap<>();
+ private final ArrayMap<IBinder, IRemoteTransition> mRequestedRemotes = new ArrayMap<>();
/** Ordered by specificity. Last filters will be checked first */
private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters =
@@ -63,9 +61,7 @@
@Override
@BinderThread
public void binderDied() {
- mMainExecutor.execute(() -> {
- mFilters.clear();
- });
+ mMainExecutor.execute(() -> mFilters.clear());
}
};
@@ -97,10 +93,15 @@
}
@Override
+ public void onTransitionMerged(@NonNull IBinder transition) {
+ mRequestedRemotes.remove(transition);
+ }
+
+ @Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- IRemoteTransition pendingRemote = mPendingRemotes.remove(transition);
+ IRemoteTransition pendingRemote = mRequestedRemotes.get(transition);
if (pendingRemote == null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s doesn't have "
+ "explicit remote, search filters for match for %s", transition, info);
@@ -110,6 +111,8 @@
mFilters.get(i));
if (mFilters.get(i).first.matches(info)) {
pendingRemote = mFilters.get(i).second;
+ // Add to requested list so that it can be found for merge requests.
+ mRequestedRemotes.put(transition, pendingRemote);
break;
}
}
@@ -122,8 +125,10 @@
final IRemoteTransition remote = pendingRemote;
final IBinder.DeathRecipient remoteDied = () -> {
Log.e(Transitions.TAG, "Remote transition died, finishing");
- mMainExecutor.execute(
- () -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */));
+ mMainExecutor.execute(() -> {
+ mRequestedRemotes.remove(transition);
+ finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ });
};
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
@@ -131,20 +136,23 @@
if (remote.asBinder() != null) {
remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
- mMainExecutor.execute(
- () -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
+ mMainExecutor.execute(() -> {
+ mRequestedRemotes.remove(transition);
+ finishCallback.onTransitionFinished(wct, null /* wctCB */);
+ });
}
};
try {
if (remote.asBinder() != null) {
remote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
- remote.startAnimation(info, t, cb);
+ remote.startAnimation(transition, info, t, cb);
} catch (RemoteException e) {
+ Log.e(Transitions.TAG, "Error running remote transition.", e);
if (remote.asBinder() != null) {
remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
- Log.e(Transitions.TAG, "Error running remote transition.", e);
+ mRequestedRemotes.remove(transition);
mMainExecutor.execute(
() -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */));
}
@@ -152,12 +160,42 @@
}
@Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ final IRemoteTransition remote = mRequestedRemotes.get(mergeTarget);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt merge %s into %s",
+ transition, remote);
+ if (remote == null) return;
+
+ IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
+ @Override
+ public void onTransitionFinished(WindowContainerTransaction wct) {
+ mMainExecutor.execute(() -> {
+ if (!mRequestedRemotes.containsKey(mergeTarget)) {
+ Log.e(TAG, "Merged transition finished after it's mergeTarget (the "
+ + "transition it was supposed to merge into). This usually means "
+ + "that the mergeTarget's RemoteTransition impl erroneously "
+ + "accepted/ran the merge request after finishing the mergeTarget");
+ }
+ finishCallback.onTransitionFinished(wct, null /* wctCB */);
+ });
+ }
+ };
+ try {
+ remote.mergeAnimation(transition, info, t, mergeTarget, cb);
+ } catch (RemoteException e) {
+ Log.e(Transitions.TAG, "Error attempting to merge remote transition.", e);
+ }
+ }
+
+ @Override
@Nullable
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@Nullable TransitionRequestInfo request) {
IRemoteTransition remote = request.getRemoteTransition();
if (remote == null) return null;
- mPendingRemotes.put(transition, remote);
+ mRequestedRemotes.put(transition, remote);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "RemoteTransition directly requested"
+ " for %s: %s", transition, remote);
return new WindowContainerTransaction();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 64dc47f..3251abc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -35,7 +35,6 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.util.ArrayMap;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -91,11 +90,16 @@
private float mTransitionAnimationScaleSetting = 1.0f;
private static final class ActiveTransition {
- TransitionHandler mFirstHandler = null;
+ IBinder mToken = null;
+ TransitionHandler mHandler = null;
+ boolean mMerged = false;
+ TransitionInfo mInfo = null;
+ SurfaceControl.Transaction mStartT = null;
+ SurfaceControl.Transaction mFinishT = null;
}
- /** Keeps track of currently tracked transitions and all the animations associated with each */
- private final ArrayMap<IBinder, ActiveTransition> mActiveTransitions = new ArrayMap<>();
+ /** Keeps track of currently playing transitions in the order of receipt. */
+ private final ArrayList<ActiveTransition> mActiveTransitions = new ArrayList<>();
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
@NonNull Context context, @NonNull ShellExecutor mainExecutor,
@@ -226,7 +230,7 @@
* type, their transit mode, and their destination z-order.
*/
private static void setupStartState(@NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t) {
+ @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
boolean isOpening = isOpeningType(info.getType());
if (info.getRootLeash().isValid()) {
t.show(info.getRootLeash());
@@ -270,6 +274,8 @@
t.setAlpha(leash, 1.f);
} else {
t.setAlpha(leash, 0.f);
+ // fix alpha in finish transaction in case the animator itself no-ops.
+ finishT.setAlpha(leash, 1.f);
}
} else {
// put on bottom and leave it visible
@@ -290,16 +296,24 @@
}
}
+ private int findActiveTransition(IBinder token) {
+ for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
+ if (mActiveTransitions.get(i).mToken == token) return i;
+ }
+ return -1;
+ }
+
@VisibleForTesting
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t) {
+ @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s",
transitionToken, info);
- final ActiveTransition active = mActiveTransitions.get(transitionToken);
- if (active == null) {
+ final int activeIdx = findActiveTransition(transitionToken);
+ if (activeIdx < 0) {
throw new IllegalStateException("Got transitionReady for non-active transition "
+ transitionToken + ". expecting one of "
- + Arrays.toString(mActiveTransitions.keySet().toArray()));
+ + Arrays.toString(mActiveTransitions.stream().map(
+ activeTransition -> activeTransition.mToken).toArray()));
}
if (!info.getRootLeash().isValid()) {
// Invalid root-leash implies that the transition is empty/no-op, so just do
@@ -307,30 +321,62 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Invalid root leash (%s): %s",
transitionToken, info);
t.apply();
- onFinish(transitionToken, null /* wct */, null /* wctCB */);
+ onAbort(transitionToken);
return;
}
- setupStartState(info, t);
+ final ActiveTransition active = mActiveTransitions.get(activeIdx);
+ active.mInfo = info;
+ active.mStartT = t;
+ active.mFinishT = finishT;
+ if (activeIdx > 0) {
+ // This is now playing at the same time as an existing animation, so try merging it.
+ attemptMergeTransition(mActiveTransitions.get(0), active);
+ return;
+ }
+ // The normal case, just play it.
+ playTransition(active);
+ }
- final TransitionFinishCallback finishCb = (wct, cb) -> onFinish(transitionToken, wct, cb);
- // If a handler chose to uniquely run this animation, try delegating to it.
- if (active.mFirstHandler != null) {
+ /**
+ * Attempt to merge by delegating the transition start to the handler of the currently
+ * playing transition.
+ */
+ void attemptMergeTransition(@NonNull ActiveTransition playing,
+ @NonNull ActiveTransition merging) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
+ + " another transition %s is still animating. Notify the animating transition"
+ + " in case they can be merged", merging.mToken, playing.mToken);
+ playing.mHandler.mergeAnimation(merging.mToken, merging.mInfo, merging.mStartT,
+ playing.mToken, (wct, cb) -> onFinish(merging.mToken, wct, cb));
+ }
+
+ boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) {
+ return handler.startAnimation(active.mToken, active.mInfo, active.mStartT,
+ (wct, cb) -> onFinish(active.mToken, wct, cb));
+ }
+
+ void playTransition(@NonNull ActiveTransition active) {
+ setupStartState(active.mInfo, active.mStartT, active.mFinishT);
+
+ // If a handler already chose to run this animation, try delegating to it first.
+ if (active.mHandler != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
- active.mFirstHandler);
- if (active.mFirstHandler.startAnimation(transitionToken, info, t, finishCb)) {
+ active.mHandler);
+ if (startAnimation(active, active.mHandler)) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
return;
}
}
// Otherwise give every other handler a chance (in order)
for (int i = mHandlers.size() - 1; i >= 0; --i) {
- if (mHandlers.get(i) == active.mFirstHandler) continue;
+ if (mHandlers.get(i) == active.mHandler) continue;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
mHandlers.get(i));
- if (mHandlers.get(i).startAnimation(transitionToken, info, t, finishCb)) {
+ if (startAnimation(active, mHandlers.get(i))) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
mHandlers.get(i));
+ active.mHandler = mHandlers.get(i);
return;
}
}
@@ -338,23 +384,107 @@
"This shouldn't happen, maybe the default handler is broken.");
}
- private void onFinish(IBinder transition, @Nullable WindowContainerTransaction wct,
+ /** Special version of finish just for dealing with no-op/invalid transitions. */
+ private void onAbort(IBinder transition) {
+ final int activeIdx = findActiveTransition(transition);
+ if (activeIdx < 0) return;
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Transition animation aborted due to no-op, notifying core %s", transition);
+ mActiveTransitions.remove(activeIdx);
+ mOrganizer.finishTransition(transition, null /* wct */, null /* wctCB */);
+ }
+
+ private void onFinish(IBinder transition,
+ @Nullable WindowContainerTransaction wct,
@Nullable WindowContainerTransactionCallback wctCB) {
- if (!mActiveTransitions.containsKey(transition)) {
- Log.e(TAG, "Trying to finish a non-running transition. Maybe remote crashed?");
+ int activeIdx = findActiveTransition(transition);
+ if (activeIdx < 0) {
+ Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
+ + " a handler didn't properly deal with a merge.", new RuntimeException());
+ return;
+ } else if (activeIdx > 0) {
+ // This transition was merged.
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s",
+ transition);
+ final ActiveTransition active = mActiveTransitions.get(activeIdx);
+ active.mMerged = true;
+ if (active.mHandler != null) {
+ active.mHandler.onTransitionMerged(active.mToken);
+ }
return;
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Transition animations finished, notifying core %s", transition);
- mActiveTransitions.remove(transition);
+ "Transition animation finished, notifying core %s", transition);
+ // Merge all relevant transactions together
+ SurfaceControl.Transaction fullFinish = mActiveTransitions.get(activeIdx).mFinishT;
+ for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) {
+ final ActiveTransition toMerge = mActiveTransitions.get(iA);
+ if (!toMerge.mMerged) break;
+ // Include start. It will be a no-op if it was already applied. Otherwise, we need it
+ // to maintain consistent state.
+ fullFinish.merge(mActiveTransitions.get(iA).mStartT);
+ fullFinish.merge(mActiveTransitions.get(iA).mFinishT);
+ }
+ fullFinish.apply();
+ // Now perform all the finishes.
+ mActiveTransitions.remove(activeIdx);
mOrganizer.finishTransition(transition, wct, wctCB);
+ while (activeIdx < mActiveTransitions.size()) {
+ if (!mActiveTransitions.get(activeIdx).mMerged) break;
+ ActiveTransition merged = mActiveTransitions.remove(activeIdx);
+ mOrganizer.finishTransition(merged.mToken, null /* wct */, null /* wctCB */);
+ }
+ if (mActiveTransitions.size() <= activeIdx) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
+ + "finished");
+ return;
+ }
+ // Start animating the next active transition
+ final ActiveTransition next = mActiveTransitions.get(activeIdx);
+ if (next.mInfo == null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Pending transition after one"
+ + " finished, but it isn't ready yet.");
+ return;
+ }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Pending transitions after one"
+ + " finished, so start the next one.");
+ playTransition(next);
+ // Now try to merge the rest of the transitions (re-acquire activeIdx since next may have
+ // finished immediately)
+ activeIdx = findActiveTransition(next.mToken);
+ if (activeIdx < 0) {
+ // This means 'next' finished immediately and thus re-entered this function. Since
+ // that is the case, just return here since all relevant logic has already run in the
+ // re-entered call.
+ return;
+ }
+
+ // This logic is also convoluted because 'next' may finish immediately in response to any of
+ // the merge requests (eg. if it decided to "cancel" itself).
+ int mergeIdx = activeIdx + 1;
+ while (mergeIdx < mActiveTransitions.size()) {
+ ActiveTransition mergeCandidate = mActiveTransitions.get(mergeIdx);
+ if (mergeCandidate.mMerged) {
+ throw new IllegalStateException("Can't merge a transition after not-merging"
+ + " a preceding one.");
+ }
+ attemptMergeTransition(next, mergeCandidate);
+ mergeIdx = findActiveTransition(mergeCandidate.mToken);
+ if (mergeIdx < 0) {
+ // This means 'next' finished immediately and thus re-entered this function. Since
+ // that is the case, just return here since all relevant logic has already run in
+ // the re-entered call.
+ return;
+ }
+ ++mergeIdx;
+ }
}
void requestStartTransition(@NonNull IBinder transitionToken,
@Nullable TransitionRequestInfo request) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: %s %s",
transitionToken, request);
- if (mActiveTransitions.containsKey(transitionToken)) {
+ if (findActiveTransition(transitionToken) >= 0) {
throw new RuntimeException("Transition already started " + transitionToken);
}
final ActiveTransition active = new ActiveTransition();
@@ -362,23 +492,23 @@
for (int i = mHandlers.size() - 1; i >= 0; --i) {
wct = mHandlers.get(i).handleRequest(transitionToken, request);
if (wct != null) {
- active.mFirstHandler = mHandlers.get(i);
+ active.mHandler = mHandlers.get(i);
break;
}
}
- IBinder transition = mOrganizer.startTransition(
+ active.mToken = mOrganizer.startTransition(
request.getType(), transitionToken, wct);
- mActiveTransitions.put(transition, active);
+ mActiveTransitions.add(active);
}
/** Start a new transition directly. */
public IBinder startTransition(@WindowManager.TransitionType int type,
@NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
final ActiveTransition active = new ActiveTransition();
- active.mFirstHandler = handler;
- IBinder transition = mOrganizer.startTransition(type, null /* token */, wct);
- mActiveTransitions.put(transition, active);
- return transition;
+ active.mHandler = handler;
+ active.mToken = mOrganizer.startTransition(type, null /* token */, wct);
+ mActiveTransitions.add(active);
+ return active.mToken;
}
/**
@@ -415,6 +545,32 @@
@NonNull TransitionFinishCallback finishCallback);
/**
+ * Attempts to merge a different transition's animation into an animation that this handler
+ * is currently playing. If a merge is not possible/supported, this should be a no-op.
+ *
+ * This gets called if another transition becomes ready while this handler is still playing
+ * an animation. This is called regardless of whether this handler claims to support that
+ * particular transition or not.
+ *
+ * When this happens, there are 2 options:
+ * 1. Do nothing. This effectively rejects the merge request. This is the "safest" option.
+ * 2. Merge the incoming transition into this one. The implementation is up to this
+ * handler. To indicate that this handler has "consumed" the merge transition, it
+ * must call the finishCallback immediately, or at-least before the original
+ * transition's finishCallback is called.
+ *
+ * @param transition This is the transition that wants to be merged.
+ * @param info Information about what is changing in the transition.
+ * @param t Contains surface changes that resulted from the transition.
+ * @param mergeTarget This is the transition that we are attempting to merge with (ie. the
+ * one this handler is currently already animating).
+ * @param finishCallback Call this if merged. This MUST be called on main thread.
+ */
+ default void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull TransitionFinishCallback finishCallback) { }
+
+ /**
* Potentially handles a startTransition request.
*
* @param transition The transition whose start is being requested.
@@ -427,6 +583,12 @@
@NonNull TransitionRequestInfo request);
/**
+ * Called when a transition which was already "claimed" by this handler has been merged
+ * into another animation. Gives this handler a chance to clean-up any expectations.
+ */
+ default void onTransitionMerged(@NonNull IBinder transition) { }
+
+ /**
* Sets transition animation scale settings value to handler.
*
* @param scale The setting value of transition animation scale.
@@ -438,10 +600,10 @@
private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
@Override
public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
- SurfaceControl.Transaction transaction) throws RemoteException {
- mMainExecutor.execute(() -> {
- Transitions.this.onTransitionReady(iBinder, transitionInfo, transaction);
- });
+ SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
+ throws RemoteException {
+ mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
+ iBinder, transitionInfo, t, finishT));
}
@Override
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 18642fc..08ac2a6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -332,11 +332,18 @@
final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction();
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
- IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
+ throws RemoteException {
mCalled = true;
finishCallback.onTransitionFinished(mRemoteFinishWCT);
}
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index c1733de..2d2ab2c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -107,7 +107,8 @@
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
- transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class));
+ transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
assertEquals(1, mDefaultHandler.activeCount());
mDefaultHandler.finishAll();
mMainExecutor.flushAll();
@@ -155,7 +156,8 @@
transitions.requestStartTransition(transitToken,
new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), isNull());
- transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class));
+ transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
assertEquals(1, mDefaultHandler.activeCount());
assertEquals(0, testHandler.activeCount());
mDefaultHandler.finishAll();
@@ -168,7 +170,8 @@
new TransitionRequestInfo(TRANSIT_OPEN, mwTaskInfo, null /* remote */));
verify(mOrganizer, times(1)).startTransition(
eq(TRANSIT_OPEN), eq(transitToken), eq(handlerWCT));
- transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class));
+ transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
assertEquals(1, mDefaultHandler.activeCount());
assertEquals(0, testHandler.activeCount());
mDefaultHandler.finishAll();
@@ -185,7 +188,8 @@
eq(TRANSIT_CHANGE), eq(transitToken), eq(handlerWCT));
TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE)
.addChange(TRANSIT_CHANGE).build();
- transitions.onTransitionReady(transitToken, change, mock(SurfaceControl.Transaction.class));
+ transitions.onTransitionReady(transitToken, change, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
assertEquals(0, mDefaultHandler.activeCount());
assertEquals(1, testHandler.activeCount());
assertEquals(0, topHandler.activeCount());
@@ -203,11 +207,18 @@
final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction();
IRemoteTransition testRemote = new IRemoteTransition.Stub() {
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ public void startAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
finishCallback.onTransitionFinished(remoteFinishWCT);
}
+
+ @Override
+ public void mergeAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ }
};
IBinder transitToken = new Binder();
transitions.requestStartTransition(transitToken,
@@ -215,7 +226,8 @@
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
- transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class));
+ transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
assertEquals(0, mDefaultHandler.activeCount());
assertTrue(remoteCalled[0]);
mDefaultHandler.finishAll();
@@ -269,11 +281,18 @@
final boolean[] remoteCalled = new boolean[]{false};
IRemoteTransition testRemote = new IRemoteTransition.Stub() {
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ public void startAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
finishCallback.onTransitionFinished(null /* wct */);
}
+
+ @Override
+ public void mergeAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ }
};
TransitionFilter filter = new TransitionFilter();
@@ -290,7 +309,8 @@
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
- transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class));
+ transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
assertEquals(0, mDefaultHandler.activeCount());
assertTrue(remoteCalled[0]);
mDefaultHandler.finishAll();
@@ -308,11 +328,18 @@
final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction();
IRemoteTransition testRemote = new IRemoteTransition.Stub() {
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ public void startAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
finishCallback.onTransitionFinished(remoteFinishWCT);
}
+
+ @Override
+ public void mergeAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ }
};
final int transitType = TRANSIT_FIRST_CUSTOM + 1;
@@ -336,6 +363,86 @@
mock(SurfaceControl.Transaction.class), testFinish));
}
+ @Test
+ public void testTransitionQueueing() {
+ Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
+ mMainExecutor, mAnimExecutor);
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+ IBinder transitToken1 = new Binder();
+ transitions.requestStartTransition(transitToken1,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ transitions.onTransitionReady(transitToken1, info1, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+ assertEquals(1, mDefaultHandler.activeCount());
+
+ IBinder transitToken2 = new Binder();
+ transitions.requestStartTransition(transitToken2,
+ new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */));
+ TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ transitions.onTransitionReady(transitToken2, info2, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+ // default handler doesn't merge by default, so it shouldn't increment active count.
+ assertEquals(1, mDefaultHandler.activeCount());
+ assertEquals(0, mDefaultHandler.mergeCount());
+ verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any(), any());
+ verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any());
+
+ mDefaultHandler.finishAll();
+ mMainExecutor.flushAll();
+ // first transition finished
+ verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any(), any());
+ verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any());
+ // But now the "queued" transition is running
+ assertEquals(1, mDefaultHandler.activeCount());
+
+ mDefaultHandler.finishAll();
+ mMainExecutor.flushAll();
+ verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any(), any());
+ }
+
+ @Test
+ public void testTransitionMerging() {
+ Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
+ mMainExecutor, mAnimExecutor);
+ mDefaultHandler.setSimulateMerge(true);
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+ IBinder transitToken1 = new Binder();
+ transitions.requestStartTransition(transitToken1,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ transitions.onTransitionReady(transitToken1, info1, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+ assertEquals(1, mDefaultHandler.activeCount());
+
+ IBinder transitToken2 = new Binder();
+ transitions.requestStartTransition(transitToken2,
+ new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */));
+ TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ transitions.onTransitionReady(transitToken2, info2, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+ // it should still only have 1 active, but then show 1 merged
+ assertEquals(1, mDefaultHandler.activeCount());
+ assertEquals(1, mDefaultHandler.mergeCount());
+ verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any(), any());
+ // We don't tell organizer it is finished yet (since we still want to maintain ordering)
+ verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any());
+
+ mDefaultHandler.finishAll();
+ mMainExecutor.flushAll();
+ // transition + merged all finished.
+ verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any(), any());
+ verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any(), any());
+ // Make sure nothing was queued
+ assertEquals(0, mDefaultHandler.activeCount());
+ }
+
class TransitionInfoBuilder {
final TransitionInfo mInfo;
@@ -364,7 +471,9 @@
}
class TestTransitionHandler implements Transitions.TransitionHandler {
- final ArrayList<Transitions.TransitionFinishCallback> mFinishes = new ArrayList<>();
+ ArrayList<Transitions.TransitionFinishCallback> mFinishes = new ArrayList<>();
+ final ArrayList<IBinder> mMerged = new ArrayList<>();
+ boolean mSimulateMerge = false;
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@@ -374,6 +483,15 @@
return true;
}
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (!mSimulateMerge) return;
+ mMerged.add(transition);
+ finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ }
+
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@@ -381,16 +499,25 @@
return null;
}
+ void setSimulateMerge(boolean sim) {
+ mSimulateMerge = sim;
+ }
+
void finishAll() {
- for (int i = mFinishes.size() - 1; i >= 0; --i) {
- mFinishes.get(i).onTransitionFinished(null /* wct */, null /* wctCB */);
+ final ArrayList<Transitions.TransitionFinishCallback> finishes = mFinishes;
+ mFinishes = new ArrayList<>();
+ for (int i = finishes.size() - 1; i >= 0; --i) {
+ finishes.get(i).onTransitionFinished(null /* wct */, null /* wctCB */);
}
- mFinishes.clear();
}
int activeCount() {
return mFinishes.size();
}
+
+ int mergeCount() {
+ return mMerged.size();
+ }
}
private static SurfaceControl createMockSurface(boolean valid) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 4dc1cca..f2f9a26 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2846,6 +2846,159 @@
}
/**
+ * Interface definition of a callback that is notified when the audio mode changes
+ */
+ public interface OnModeChangedListener {
+ /**
+ * Called on the listener to indicate that the audio mode has changed
+ *
+ * @param mode The current audio mode
+ */
+ void onModeChanged(@AudioMode int mode);
+ }
+
+ private final Object mModeListenerLock = new Object();
+ /**
+ * List of listeners for audio mode and their associated Executor.
+ * List is lazy-initialized on first registration
+ */
+ @GuardedBy("mModeListenerLock")
+ private @Nullable ArrayList<ModeListenerInfo> mModeListeners;
+
+ @GuardedBy("mModeListenerLock")
+ private ModeDispatcherStub mModeDispatcherStub;
+
+ private final class ModeDispatcherStub
+ extends IAudioModeDispatcher.Stub {
+
+ @Override
+ public void dispatchAudioModeChanged(int mode) {
+ // make a shallow copy of listeners so callback is not executed under lock
+ final ArrayList<ModeListenerInfo> modeListeners;
+ synchronized (mModeListenerLock) {
+ if (mModeListeners == null || mModeListeners.size() == 0) {
+ return;
+ }
+ modeListeners = (ArrayList<ModeListenerInfo>) mModeListeners.clone();
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ for (ModeListenerInfo info : modeListeners) {
+ info.mExecutor.execute(() ->
+ info.mListener.onModeChanged(mode));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ private static class ModeListenerInfo {
+ final @NonNull OnModeChangedListener mListener;
+ final @NonNull Executor mExecutor;
+
+ ModeListenerInfo(OnModeChangedListener listener, Executor exe) {
+ mListener = listener;
+ mExecutor = exe;
+ }
+ }
+
+ @GuardedBy("mModeListenerLock")
+ private boolean hasModeListener(OnModeChangedListener listener) {
+ return getModeListenerInfo(listener) != null;
+ }
+
+ @GuardedBy("mModeListenerLock")
+ private @Nullable ModeListenerInfo getModeListenerInfo(
+ OnModeChangedListener listener) {
+ if (mModeListeners == null) {
+ return null;
+ }
+ for (ModeListenerInfo info : mModeListeners) {
+ if (info.mListener == listener) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+
+ @GuardedBy("mModeListenerLock")
+ /**
+ * @return true if the listener was removed from the list
+ */
+ private boolean removeModeListener(OnModeChangedListener listener) {
+ final ModeListenerInfo infoToRemove = getModeListenerInfo(listener);
+ if (infoToRemove != null) {
+ mModeListeners.remove(infoToRemove);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Adds a listener to be notified of changes to the audio mode.
+ * See {@link #getMode()}
+ * @param executor
+ * @param listener
+ */
+ public void addOnModeChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnModeChangedListener listener) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
+ synchronized (mModeListenerLock) {
+ if (hasModeListener(listener)) {
+ throw new IllegalArgumentException("attempt to call addOnModeChangedListener() "
+ + "on a previously registered listener");
+ }
+ // lazy initialization of the list of strategy-preferred device listener
+ if (mModeListeners == null) {
+ mModeListeners = new ArrayList<>();
+ }
+ final int oldCbCount = mModeListeners.size();
+ mModeListeners.add(new ModeListenerInfo(listener, executor));
+ if (oldCbCount == 0) {
+ // register binder for callbacks
+ if (mModeDispatcherStub == null) {
+ mModeDispatcherStub = new ModeDispatcherStub();
+ }
+ try {
+ getService().registerModeDispatcher(mModeDispatcherStub);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes a previously added listener for changes to audio mode.
+ * See {@link #getMode()}
+ * @param listener
+ */
+ public void removeOnModeChangedListener(@NonNull OnModeChangedListener listener) {
+ Objects.requireNonNull(listener);
+ synchronized (mModeListenerLock) {
+ if (!removeModeListener(listener)) {
+ throw new IllegalArgumentException("attempt to call removeOnModeChangedListener() "
+ + "on an unregistered listener");
+ }
+ if (mModeListeners.size() == 0) {
+ // unregister binder for callbacks
+ try {
+ getService().unregisterModeDispatcher(mModeDispatcherStub);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ mModeDispatcherStub = null;
+ mModeListeners = null;
+ }
+ }
+ }
+ }
+
+ /**
* Indicates if the platform supports a special call screening and call monitoring mode.
* <p>
* When this mode is supported, it is possible to perform call screening and monitoring
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
index e339ae8..167ab65 100644
--- a/media/java/android/media/HwAudioSource.java
+++ b/media/java/android/media/HwAudioSource.java
@@ -37,7 +37,13 @@
private final AudioDeviceInfo mAudioDeviceInfo;
private final AudioAttributes mAudioAttributes;
- private int mNativeHandle;
+ /**
+ * The value of the native handle encodes the HwAudioSource state.
+ * The native handle returned by {@link AudioSystem#startAudioSource} is either valid
+ * (aka > 0, so successfully started) or hosting an error code (negative).
+ * 0 corresponds to an untialized or stopped HwAudioSource.
+ */
+ private int mNativeHandle = 0;
/**
* Class constructor for a hardware audio source based player.
@@ -129,14 +135,18 @@
/**
* Starts the playback from {@link AudioDeviceInfo}.
+ * Starts does not return any error code, caller must check {@link HwAudioSource#isPlaying} to
+ * ensure the state of the HwAudioSource encoded in {@link mNativeHandle}.
*/
public void start() {
Preconditions.checkState(!isPlaying(), "HwAudioSource is currently playing");
mNativeHandle = AudioSystem.startAudioSource(
mAudioDeviceInfo.getPort().activeConfig(),
mAudioAttributes);
- // FIXME: b/174876389 clean up device id reporting
- baseStart(getDeviceId());
+ if (isPlaying()) {
+ // FIXME: b/174876389 clean up device id reporting
+ baseStart(getDeviceId());
+ }
}
private int getDeviceId() {
@@ -162,18 +172,23 @@
/**
* Checks whether the HwAudioSource player is playing.
+ * It checks the state of the HwAudioSource encoded in {@link HwAudioSource#isPlaying}.
+ * 0 corresponds to a stopped or uninitialized HwAudioSource.
+ * Negative value corresponds to a status reported by {@link AudioSystem#startAudioSource} to
+ * indicate a failure when trying to start the HwAudioSource.
+ *
* @return true if currently playing, false otherwise
*/
public boolean isPlaying() {
- return mNativeHandle != 0;
+ return mNativeHandle > 0;
}
/**
* Stops the playback from {@link AudioDeviceInfo}.
*/
public void stop() {
- baseStop();
if (mNativeHandle > 0) {
+ baseStop();
AudioSystem.stopAudioSource(mNativeHandle);
mNativeHandle = 0;
}
diff --git a/media/java/android/media/IAudioModeDispatcher.aidl b/media/java/android/media/IAudioModeDispatcher.aidl
new file mode 100644
index 0000000..6162541
--- /dev/null
+++ b/media/java/android/media/IAudioModeDispatcher.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * AIDL for the AudioService to signal audio mode changes.
+ *
+ * {@hide}
+ */
+oneway interface IAudioModeDispatcher {
+
+ void dispatchAudioModeChanged(int mode);
+
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7a6369cd..c08c368 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -25,6 +25,7 @@
import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher;
+import android.media.IAudioModeDispatcher;
import android.media.IAudioRoutesObserver;
import android.media.IAudioServerStateDispatcher;
import android.media.ICapturePresetDevicesRoleDispatcher;
@@ -383,4 +384,8 @@
in AudioAttributes aa, in String callingPackageName);
long getFadeOutDurationOnFocusLossMillis(in AudioAttributes aa);
+
+ void registerModeDispatcher(IAudioModeDispatcher dispatcher);
+
+ oneway void unregisterModeDispatcher(IAudioModeDispatcher dispatcher);
}
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
index 62dfc51..579a6b2 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
@@ -16,29 +16,39 @@
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/content_parent"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
- android:layout_height="180dp"
+ android:layout_height="wrap_content"
+ android:fitsSystemWindows="true"
+ android:outlineAmbientShadowColor="@android:color/transparent"
+ android:outlineSpotShadowColor="@android:color/transparent"
+ android:background="@android:color/transparent"
android:theme="@style/Theme.CollapsingToolbar.Settings">
<com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout
android:id="@+id/collapsing_toolbar"
- android:background="?android:attr/colorPrimary"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="@dimen/toolbar_one_line_height"
+ android:clipToPadding="false"
+ app:contentScrim="?androidprv:attr/colorSurfaceHeader"
app:maxLines="3"
- app:contentScrim="?android:attr/colorPrimary"
+ app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
+ app:scrimAnimationDuration="50"
+ app:scrimVisibleHeightTrigger="@dimen/scrim_visible_height_trigger"
+ app:statusBarScrim="@empty"
+ app:titleCollapseMode="fade"
app:collapsedTitleTextAppearance="@style/CollapsingToolbarTitle.Collapsed"
- app:statusBarScrim="?android:attr/colorPrimary"
- app:layout_scrollFlags="scroll|exitUntilCollapsed"
- app:expandedTitleMarginStart="18dp"
- app:expandedTitleMarginEnd="18dp"
+ app:expandedTitleTextAppearance="@style/CollapsingToolbarTitle.Expanded"
+ app:expandedTitleMarginStart="@dimen/expanded_title_margin_start"
+ app:expandedTitleMarginEnd="@dimen/expanded_title_margin_end"
app:toolbarId="@id/action_bar">
<Toolbar
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml
index e20775e..878275a0 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml
@@ -15,8 +15,9 @@
limitations under the License.
-->
<resources>
- <style name="Theme.CollapsingToolbar.Settings"
- parent="@style/Theme.MaterialComponents.DayNight">
+ <style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
+ <item name="elevationOverlayEnabled">true</item>
+ <item name="elevationOverlayColor">?attr/colorPrimary</item>
<item name="colorPrimary">@*android:color/primary_dark_device_default_settings</item>
<item name="colorAccent">@*android:color/accent_device_default_dark</item>
</style>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/dimens.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/dimens.xml
new file mode 100644
index 0000000..f0cdaf6
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/dimens.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+<resources>
+ <!-- Collapsing toolbar layout dimensions -->
+ <dimen name="toolbar_one_line_height">226dp</dimen>
+ <dimen name="toolbar_two_lines_height">270dp</dimen>
+ <dimen name="toolbar_three_lines_height">314dp</dimen>
+ <dimen name="scrim_visible_height_trigger">174dp</dimen>
+ <dimen name="expanded_title_margin_start">24dp</dimen>
+ <dimen name="expanded_title_margin_end">24dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml
index 1157a34..2a72a1a 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml
@@ -15,16 +15,11 @@
limitations under the License.
-->
<resources>
- <style name="CollapsingToolbarTitle.Collapsed"
- parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <style name="CollapsingToolbarTitle.Collapsed" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
</style>
- <style name="CollapsingToolbarTitle" parent="CollapsingToolbarTitle.Collapsed">
- <item name="android:textSize">36sp</item>
- </style>
-
- <style name="CollapsingToolbarTitle.MoreThanTwoLines">
- <item name="android:textSize">24sp</item>
+ <style name="CollapsingToolbarTitle.Expanded" parent="CollapsingToolbarTitle.Collapsed">
+ <item name="android:textSize">36dp</item>
</style>
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml
index de545b0..2e7a6a9 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml
@@ -15,8 +15,9 @@
limitations under the License.
-->
<resources>
- <style name="Theme.CollapsingToolbar.Settings"
- parent="@style/Theme.MaterialComponents.DayNight">
+ <style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
+ <item name="elevationOverlayEnabled">true</item>
+ <item name="elevationOverlayColor">?attr/colorPrimary</item>
<item name="colorPrimary">@*android:color/primary_device_default_settings_light</item>
<item name="colorAccent">@*android:color/accent_device_default_light</item>
</style>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/AdjustableToolbarLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/AdjustableToolbarLayout.java
index e75a978..b3053ac 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/AdjustableToolbarLayout.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/AdjustableToolbarLayout.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -51,17 +52,24 @@
initCollapsingToolbar();
}
+ @SuppressWarnings("RestrictTo")
private void initCollapsingToolbar() {
this.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
v.removeOnLayoutChangeListener(this);
- final int count = getLineCount();
+ final int count = getLineCountWithReflection();
if (count > TOOLBAR_MAX_LINE_NUMBER) {
- setExpandedTitleTextAppearance(R.style.CollapsingToolbarTitle_MoreThanTwoLines);
- } else {
- setExpandedTitleTextAppearance(R.style.CollapsingToolbarTitle);
+ final ViewGroup.LayoutParams lp = getLayoutParams();
+ lp.height = getResources()
+ .getDimensionPixelSize(R.dimen.toolbar_three_lines_height);
+ setLayoutParams(lp);
+ } else if (count == TOOLBAR_MAX_LINE_NUMBER) {
+ final ViewGroup.LayoutParams lp = getLayoutParams();
+ lp.height = getResources()
+ .getDimensionPixelSize(R.dimen.toolbar_two_lines_height);
+ setLayoutParams(lp);
}
}
});
@@ -73,9 +81,10 @@
* drawn in a canvas and the text process is wrapped in a CollapsingTextHelper, the way we used
* here is to get the line count from the CollapsingTextHelper via Java Reflection.
*/
- private int getLineCount() {
+ private int getLineCountWithReflection() {
try {
- final Field textHelperField = this.getClass().getDeclaredField("collapsingTextHelper");
+ final Field textHelperField =
+ this.getClass().getSuperclass().getDeclaredField("collapsingTextHelper");
textHelperField.setAccessible(true);
final Object textHelperObj = textHelperField.get(this);
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index b29205d..f5641bd 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -78,16 +78,18 @@
public void setTitle(CharSequence title) {
if (mCollapsingToolbarLayout != null) {
mCollapsingToolbarLayout.setTitle(title);
+ } else {
+ super.setTitle(title);
}
- super.setTitle(title);
}
@Override
public void setTitle(int titleId) {
if (mCollapsingToolbarLayout != null) {
mCollapsingToolbarLayout.setTitle(getText(titleId));
+ } else {
+ super.setTitle(titleId);
}
- super.setTitle(titleId);
}
@Override
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java
index 47551df..c47638a 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java
@@ -27,6 +27,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.core.os.BuildCompat;
import androidx.fragment.app.FragmentActivity;
import com.android.settingslib.transition.SettingsTransitionHelper;
@@ -41,7 +42,7 @@
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ if (BuildCompat.isAtLeastS()) {
getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
SettingsTransitionHelper.applyForwardTransition(this);
SettingsTransitionHelper.applyBackwardTransition(this);
@@ -51,7 +52,7 @@
@Override
public void startActivity(Intent intent) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
+ if (!BuildCompat.isAtLeastS()) {
super.startActivity(intent);
return;
}
@@ -67,7 +68,7 @@
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
+ if (!BuildCompat.isAtLeastS()) {
super.startActivity(intent, options);
return;
}
@@ -82,7 +83,7 @@
@Override
public void startActivityForResult(Intent intent, int requestCode) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || requestCode == DEFAULT_REQUEST) {
+ if (!BuildCompat.isAtLeastS() || requestCode == DEFAULT_REQUEST) {
super.startActivityForResult(intent, requestCode);
return;
}
@@ -98,7 +99,7 @@
@Override
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || requestCode == DEFAULT_REQUEST) {
+ if (!BuildCompat.isAtLeastS() || requestCode == DEFAULT_REQUEST) {
super.startActivityForResult(intent, requestCode, options);
return;
}
diff --git a/packages/SettingsLib/SettingsTheme/res/values/themes.xml b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
index 9c096d2..771fbc2 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
@@ -26,7 +26,11 @@
<!-- Using in SubSettings page including injected settings page -->
<style name="Theme.SubSettingsBase" parent="Theme.SettingsBase">
+ <!-- Suppress the built-in action bar -->
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
+ <!-- Set up edge-to-edge configuration for top app bar -->
+ <item name="android:navigationBarColor">@android:color/transparent</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index b5dbf22..635434b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -242,15 +242,6 @@
Settings.Global.AUDIO_SAFE_VOLUME_STATE,
GlobalSettingsProto.AUDIO_SAFE_VOLUME_STATE);
- final long autoToken = p.start(GlobalSettingsProto.AUTO);
- dumpSetting(s, p,
- Settings.Global.AUTO_TIME,
- GlobalSettingsProto.Auto.TIME);
- dumpSetting(s, p,
- Settings.Global.AUTO_TIME_ZONE,
- GlobalSettingsProto.Auto.TIME_ZONE);
- p.end(autoToken);
-
final long autofillToken = p.start(GlobalSettingsProto.AUTOFILL);
dumpSetting(s, p,
Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
@@ -509,6 +500,15 @@
GlobalSettingsProto.Database.CREATION_BUILDID);
p.end(databaseToken);
+ final long dateTimeToken = p.start(GlobalSettingsProto.DATE_TIME);
+ dumpSetting(s, p,
+ Settings.Global.AUTO_TIME,
+ GlobalSettingsProto.DateTime.AUTO_TIME);
+ dumpSetting(s, p,
+ Settings.Global.AUTO_TIME_ZONE,
+ GlobalSettingsProto.DateTime.AUTO_TIME_ZONE);
+ p.end(dateTimeToken);
+
final long debugToken = p.start(GlobalSettingsProto.DEBUG);
dumpSetting(s, p,
Settings.Global.DEBUG_APP,
@@ -2019,6 +2019,12 @@
SecureSettingsProto.Controls.ENABLED);
p.end(controlsToken);
+ final long dateTimeToken = p.start(SecureSettingsProto.DATE_TIME);
+ dumpSetting(s, p,
+ Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
+ SecureSettingsProto.DateTime.LOCATION_TIME_ZONE_DETECTION_ENABLED);
+ p.end(dateTimeToken);
+
dumpSetting(s, p,
Settings.Secure.DEVICE_PAIRED,
SecureSettingsProto.DEVICE_PAIRED);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index 2f38f0a..4e0204b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -25,6 +25,7 @@
import static android.view.WindowManager.TransitionOldType;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.IRemoteAnimationFinishedCallback;
@@ -153,7 +154,8 @@
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteTransition.Stub() {
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ public void startAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) {
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
@@ -256,6 +258,14 @@
appsCompat, wallpapersCompat, nonAppsCompat,
animationFinishedCallback);
}
+
+ @Override
+ public void mergeAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) {
+ // TODO: hook up merge to recents onTaskAppeared if applicable. Until then, ignore
+ // any incoming merges.
+ }
};
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 26ef145..88d9d68 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -25,6 +25,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
+import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
@@ -65,7 +66,8 @@
@NonNull Executor executor) {
mTransition = new IRemoteTransition.Stub() {
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishedCallback) {
final Runnable finishAdapter = () -> {
try {
@@ -74,7 +76,22 @@
Log.e(TAG, "Failed to call transition finished callback", e);
}
};
- executor.execute(() -> runner.startAnimation(info, t, finishAdapter));
+ executor.execute(() -> runner.startAnimation(transition, info, t, finishAdapter));
+ }
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishedCallback) {
+ final Runnable finishAdapter = () -> {
+ try {
+ finishedCallback.onTransitionFinished(null /* wct */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call transition finished callback", e);
+ }
+ };
+ executor.execute(() -> runner.mergeAnimation(transition, info, t, mergeTarget,
+ finishAdapter));
}
};
}
@@ -84,7 +101,8 @@
RecentsAnimationControllerCompat controller) {
mTransition = new IRemoteTransition.Stub() {
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishedCallback) {
final RemoteAnimationTargetCompat[] apps =
RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
@@ -113,6 +131,13 @@
recents.onAnimationStart(wrapControl, apps, wallpapers, new Rect(0, 0, 0, 0),
new Rect());
}
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishedCallback) {
+ // TODO: hook up merge to onTaskAppeared. Until then, just ignore incoming merges.
+ }
};
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionRunner.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionRunner.java
index 6002bca..accc456 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionRunner.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionRunner.java
@@ -16,6 +16,7 @@
package com.android.systemui.shared.system;
+import android.os.IBinder;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -25,6 +26,17 @@
* Starts a transition animation. Once complete, the implementation should call
* `finishCallback`.
*/
- void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ void startAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
Runnable finishCallback);
+
+ /**
+ * Attempts to merge a transition into the currently-running animation. If merge is not
+ * possible/supported, this should do nothing. Otherwise, the implementation should call
+ * `finishCallback` immediately to indicate that it merged the transition.
+ *
+ * @param transition The transition that wants to be merged into the running animation.
+ * @param mergeTarget The transition to merge into (that this runner is currently animating).
+ */
+ default void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget, Runnable finishCallback) { }
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index dd89f74..3883c00 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -94,7 +94,7 @@
private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL = 6 + 4 + 8;
private static final int FIXED_WIDTH_DIMENS_FOR_SMALL = 4 + 4;
- private static final int MESSAGES_COUNT_OVERFLOW = 7;
+ private static final int MESSAGES_COUNT_OVERFLOW = 6;
private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
@@ -386,7 +386,7 @@
return views;
}
- // Some messaging apps only include up to 7 messages in their notifications.
+ // Some messaging apps only include up to 6 messages in their notifications.
private String getMessagesCountText(int count) {
if (count >= MESSAGES_COUNT_OVERFLOW) {
return mContext.getResources().getString(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index e055585..45c7174 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles;
-import static android.content.pm.PackageManager.FEATURE_CAMERA_TOGGLE;
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -63,7 +62,7 @@
@Override
public boolean isAvailable() {
- return getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE)
+ return mSensorPrivacyController.supportsSensorToggle(CAMERA)
&& whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
"camera_toggle_enabled",
true));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index 9c01bb9..48a1ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles;
-import static android.content.pm.PackageManager.FEATURE_MICROPHONE_TOGGLE;
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -63,8 +62,7 @@
@Override
public boolean isAvailable() {
- return getHost().getContext().getPackageManager()
- .hasSystemFeature(FEATURE_MICROPHONE_TOGGLE)
+ return mSensorPrivacyController.supportsSensorToggle(MICROPHONE)
&& whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
"mic_toggle_enabled",
true));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 3d9c272..f13576c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -49,7 +49,7 @@
IndividualSensorPrivacyController.Callback {
private final KeyguardStateController mKeyguard;
- private IndividualSensorPrivacyController mSensorPrivacyController;
+ protected IndividualSensorPrivacyController mSensorPrivacyController;
/**
* @return Id of the sensor that will be toggled
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index 6f6bf82..a4bb095 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -111,9 +111,10 @@
}
sensorPrivacyController.addCallback(sensorPrivacyListener)
- if (!sensorPrivacyController.isSensorBlocked(sensor)) {
- finish()
- return
+ sensorPrivacyController.addCallback { _, isBlocked ->
+ if (!isBlocked) {
+ dismiss()
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index 718a85a..48e28f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.graphics.PixelFormat
import android.graphics.PointF
+import android.os.SystemProperties
import android.util.DisplayMetrics
import android.view.View
import android.view.WindowManager
@@ -47,7 +48,8 @@
private val context: Context
) {
private var charging: Boolean? = null
- private val rippleEnabled: Boolean = featureFlags.isChargingRippleEnabled
+ private val rippleEnabled: Boolean = featureFlags.isChargingRippleEnabled &&
+ !SystemProperties.getBoolean("persist.debug.suppress-charging-ripple", false)
private val windowLayoutParams = WindowManager.LayoutParams().apply {
width = WindowManager.LayoutParams.MATCH_PARENT
height = WindowManager.LayoutParams.MATCH_PARENT
@@ -104,7 +106,7 @@
}
fun startRipple() {
- if (rippleView.rippleInProgress || rippleView.parent != null) {
+ if (!rippleEnabled || rippleView.rippleInProgress || rippleView.parent != null) {
// Skip if ripple is still playing, or not playing but already added the parent
// (which might happen just before the animation starts or right after
// the animation ends.)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
index 4e6db40..d7d1e73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
@@ -22,6 +22,8 @@
CallbackController<IndividualSensorPrivacyController.Callback> {
void init();
+ boolean supportsSensorToggle(@Sensor int sensor);
+
boolean isSensorBlocked(@Sensor int sensor);
void setSensorBlocked(@Sensor int sensor, boolean blocked);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index beb4b44..f58a7c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -45,13 +45,18 @@
public void init() {
for (int sensor : SENSORS) {
mSensorPrivacyManager.addSensorPrivacyListener(sensor,
- (enabled) -> onSensorPrivacyChanged(sensor, enabled));
+ (s, enabled) -> onSensorPrivacyChanged(sensor, enabled));
mState.put(sensor, mSensorPrivacyManager.isSensorPrivacyEnabled(sensor));
}
}
@Override
+ public boolean supportsSensorToggle(int sensor) {
+ return mSensorPrivacyManager.supportsSensorToggle(sensor);
+ }
+
+ @Override
public boolean isSensorBlocked(@Sensor int sensor) {
return mState.get(sensor, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java
index a2334f3..6f659c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.policy;
-import android.content.Context;
import android.hardware.SensorPrivacyManager;
import androidx.annotation.NonNull;
@@ -26,14 +25,12 @@
import java.util.ArrayList;
import java.util.List;
-import javax.inject.Inject;
-
/**
* Controls sensor privacy state and notification.
*/
@SysUISingleton
public class SensorPrivacyControllerImpl implements SensorPrivacyController,
- SensorPrivacyManager.OnSensorPrivacyChangedListener {
+ SensorPrivacyManager.OnAllSensorPrivacyChangedListener {
private SensorPrivacyManager mSensorPrivacyManager;
private final List<OnSensorPrivacyChangedListener> mListeners = new ArrayList<>(1);
private Object mLock = new Object();
@@ -48,8 +45,8 @@
@Override
public void init() {
- mSensorPrivacyEnabled = mSensorPrivacyManager.isSensorPrivacyEnabled();
- mSensorPrivacyManager.addSensorPrivacyListener(this);
+ mSensorPrivacyEnabled = mSensorPrivacyManager.isAllSensorPrivacyEnabled();
+ mSensorPrivacyManager.addAllSensorPrivacyListener(this);
}
/**
@@ -83,7 +80,7 @@
/**
* Callback invoked by the SensorPrivacyService when sensor privacy state changes.
*/
- public void onSensorPrivacyChanged(boolean enabled) {
+ public void onAllSensorPrivacyChanged(boolean enabled) {
synchronized (mLock) {
mSensorPrivacyEnabled = enabled;
for (OnSensorPrivacyChangedListener listener : mListeners) {
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index fdd929c..329cd71 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -166,6 +166,11 @@
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())
|| Intent.ACTION_MANAGED_PROFILE_ADDED.equals(intent.getAction())) {
+ if (!mDeviceProvisionedController.isCurrentUserSetup()) {
+ Log.i(TAG, "User setup not finished when " + intent.getAction()
+ + " was received. Deferring...");
+ return;
+ }
if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
reevaluateSystemTheme(true /* forceReload */);
} else if (Intent.ACTION_WALLPAPER_CHANGED.equals(intent.getAction())) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index ddf39d1..60e70d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -203,6 +203,22 @@
}
@Test
+ public void onProfileAdded_setsTheme() {
+ mBroadcastReceiver.getValue().onReceive(null,
+ new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
+ verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ }
+
+ @Test
+ public void onProfileAdded_ignoresUntilSetupComplete() {
+ reset(mDeviceProvisionedController);
+ mBroadcastReceiver.getValue().onReceive(null,
+ new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
+ verify(mThemeOverlayApplier, never())
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ }
+
+ @Test
public void onWallpaperColorsChanged_firstEventBeforeUserSetup_shouldBeAccepted() {
// By default, on setup() we make this controller return that the user finished setup
// wizard. This test on the other hand, is testing the setup flow.
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 7057b840..f27e7ff 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,5 +1,5 @@
# Connectivity / Networking
-per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java = file:/services/core/java/com/android/server/net/OWNERS
+per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java,VpnManagerService.java = file:/services/core/java/com/android/server/net/OWNERS
# Threads
per-file DisplayThread.java = michaelwr@google.com, ogunwale@google.com
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index e8bc812..cd3dca9 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -743,6 +743,16 @@
}
}
+ @Override
+ public boolean supportsSensorToggle(int sensor) {
+ if (sensor == MICROPHONE) {
+ return mContext.getResources().getBoolean(R.bool.config_supportsMicToggle);
+ } else if (sensor == CAMERA) {
+ return mContext.getResources().getBoolean(R.bool.config_supportsCamToggle);
+ }
+ throw new IllegalArgumentException("Unable to find value " + sensor);
+ }
+
/**
* Registers a listener to be notified when the sensor privacy state changes.
*/
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 881a047..efb0f4a 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -16,6 +16,7 @@
package com.android.server.apphibernation;
+import static android.app.AppOpsManager.OP_NONE;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.EXTRA_REMOVED_FOR_ALL_USERS;
@@ -25,8 +26,10 @@
import static com.android.server.apphibernation.AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.IActivityManager;
@@ -366,6 +369,49 @@
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
pkgState.hibernated = false;
pkgState.lastUnhibernatedMs = System.currentTimeMillis();
+ // Deliver LOCKED_BOOT_COMPLETE AND BOOT_COMPLETE broadcast so app can re-register
+ // their alarms/jobs/etc.
+ try {
+ Intent lockedBcIntent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
+ .setPackage(packageName);
+ final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED};
+ mIActivityManager.broadcastIntentWithFeature(
+ null /* caller */,
+ null /* callingFeatureId */,
+ lockedBcIntent,
+ null /* resolvedType */,
+ null /* resultTo */,
+ Activity.RESULT_OK,
+ null /* resultData */,
+ null /* resultExtras */,
+ requiredPermissions,
+ null /* excludedPermissions */,
+ OP_NONE,
+ null /* bOptions */,
+ false /* serialized */,
+ false /* sticky */,
+ userId);
+
+ Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED).setPackage(packageName);
+ mIActivityManager.broadcastIntentWithFeature(
+ null /* caller */,
+ null /* callingFeatureId */,
+ bcIntent,
+ null /* resolvedType */,
+ null /* resultTo */,
+ Activity.RESULT_OK,
+ null /* resultData */,
+ null /* resultExtras */,
+ requiredPermissions,
+ null /* excludedPermissions */,
+ OP_NONE,
+ null /* bOptions */,
+ false /* serialized */,
+ false /* sticky */,
+ userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index edacd40..098ce7c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -84,6 +84,7 @@
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.IAudioFocusDispatcher;
+import android.media.IAudioModeDispatcher;
import android.media.IAudioRoutesObserver;
import android.media.IAudioServerStateDispatcher;
import android.media.IAudioService;
@@ -120,6 +121,7 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -312,6 +314,7 @@
private static final int MSG_RECORDING_CONFIG_CHANGE = 37;
private static final int MSG_SET_A2DP_DEV_CONNECTION_STATE = 38;
private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39;
+ private static final int MSG_DISPATCH_AUDIO_MODE = 40;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
@@ -843,8 +846,8 @@
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
- mSupportsMicPrivacyToggle = mContext.getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE);
+ mSupportsMicPrivacyToggle = context.getSystemService(SensorPrivacyManager.class)
+ .supportsSensorToggle(SensorPrivacyManager.Sensors.MICROPHONE);
// Initialize volume
// Priority 1 - Android Property
@@ -4712,6 +4715,8 @@
if (DEBUG_MODE) {
Log.v(TAG, "onUpdateAudioMode: mode successfully set to " + mode);
}
+ sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_MODE, SENDMSG_REPLACE, mode, 0,
+ /*obj*/ null, /*delay*/ 0);
int previousMode = mMode.getAndSet(mode);
// Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
mModeLogger.log(new PhoneStateEvent(requesterPackage, requesterPid,
@@ -4756,6 +4761,38 @@
return mIsCallScreeningModeSupported;
}
+ protected void dispatchMode(int mode) {
+ final int nbDispatchers = mModeDispatchers.beginBroadcast();
+ for (int i = 0; i < nbDispatchers; i++) {
+ try {
+ mModeDispatchers.getBroadcastItem(i).dispatchAudioModeChanged(mode);
+ } catch (RemoteException e) {
+ }
+ }
+ mModeDispatchers.finishBroadcast();
+ }
+
+ final RemoteCallbackList<IAudioModeDispatcher> mModeDispatchers =
+ new RemoteCallbackList<IAudioModeDispatcher>();
+
+ /**
+ * @see {@link AudioManager#addOnModeChangedListener(Executor, AudioManager.OnModeChangedListener)}
+ * @param dispatcher
+ */
+ public void registerModeDispatcher(
+ @NonNull IAudioModeDispatcher dispatcher) {
+ mModeDispatchers.register(dispatcher);
+ }
+
+ /**
+ * @see {@link AudioManager#removeOnModeChangedListener(AudioManager.OnModeChangedListener)}
+ * @param dispatcher
+ */
+ public void unregisterModeDispatcher(
+ @NonNull IAudioModeDispatcher dispatcher) {
+ mModeDispatchers.unregister(dispatcher);
+ }
+
/** @see AudioManager#setRttEnabled() */
@Override
public void setRttEnabled(boolean rttEnabled) {
@@ -7522,6 +7559,10 @@
case MSG_A2DP_DEV_CONFIG_CHANGE:
mDeviceBroker.postBluetoothA2dpDeviceConfigChange((BluetoothDevice) msg.obj);
break;
+
+ case MSG_DISPATCH_AUDIO_MODE:
+ dispatchMode(msg.arg1);
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
index 346af44..e839837 100644
--- a/services/core/java/com/android/server/connectivity/NetworkRanker.java
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -63,7 +63,7 @@
NetworkCapabilities getCapsNoCopy();
}
- private static final boolean USE_POLICY_RANKING = false;
+ private static final boolean USE_POLICY_RANKING = true;
public NetworkRanker() { }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 984f7e6..5447ee3 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -194,12 +194,6 @@
private static final int SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES = 10; // 10 bytes
/**
- * If a previously scheduled sync becomes ready and we are low on storage, it gets
- * pushed back for this amount of time.
- */
- private static final long SYNC_DELAY_ON_LOW_STORAGE = 60*60*1000; // 1 hour
-
- /**
* If a sync becomes ready and it conflicts with an already running sync, it gets
* pushed back for this amount of time.
*/
@@ -242,7 +236,6 @@
volatile private PowerManager.WakeLock mSyncManagerWakeLock;
volatile private boolean mDataConnectionIsConnected = false;
- volatile private boolean mStorageIsLow = false;
private volatile int mNextJobIdOffset = 0;
private final NotificationManager mNotificationMgr;
@@ -312,31 +305,6 @@
return pendingSyncs;
}
- private final BroadcastReceiver mStorageIntentReceiver =
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, "Internal storage is low.");
- }
- mStorageIsLow = true;
- cancelActiveSync(
- SyncStorageEngine.EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL,
- null /* any sync */,
- "storage low");
- } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, "Internal storage is ok.");
- }
- mStorageIsLow = false;
- rescheduleSyncs(EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL,
- "storage ok");
- }
- }
- };
-
private final BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -650,10 +618,6 @@
IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
- intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
- intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
- context.registerReceiver(mStorageIntentReceiver, intentFilter);
-
intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
intentFilter.setPriority(100);
context.registerReceiver(mShutdownIntentReceiver, intentFilter);
@@ -1654,6 +1618,7 @@
new ComponentName(mContext, SyncJobService.class))
.setExtras(syncOperation.toJobInfoExtras())
.setRequiredNetworkType(networkType)
+ .setRequiresStorageNotLow(true)
.setPersisted(true)
.setPriority(priority)
.setFlags(jobFlags);
@@ -2201,7 +2166,9 @@
}
pw.println();
}
- pw.print("Memory low: "); pw.println(mStorageIsLow);
+ Intent storageLowIntent =
+ mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW));
+ pw.print("Storage low: "); pw.println(storageLowIntent != null);
pw.print("Clock valid: "); pw.println(mSyncStorageEngine.isClockValid());
final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
@@ -3188,11 +3155,6 @@
SyncJobService.markSyncStarted(op.jobId);
- if (mStorageIsLow) {
- deferSyncH(op, SYNC_DELAY_ON_LOW_STORAGE, "storage low");
- return;
- }
-
if (op.isPeriodic) {
// Don't allow this periodic to run if a previous instance failed and is currently
// scheduled according to some backoff criteria.
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index 42b0add..91f14de 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -46,6 +46,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -68,12 +69,14 @@
private final class LightsManagerBinderService extends ILightsManager.Stub {
- private final class Session {
+ private final class Session implements Comparable<Session> {
final IBinder mToken;
final SparseArray<LightState> mRequests = new SparseArray<>();
+ final int mPriority;
- Session(IBinder token) {
+ Session(IBinder token, int priority) {
mToken = token;
+ mPriority = priority;
}
void setRequest(int lightId, LightState state) {
@@ -83,6 +86,12 @@
mRequests.remove(lightId);
}
}
+
+ @Override
+ public int compareTo(Session otherSession) {
+ // Sort descending by priority
+ return Integer.compare(otherSession.mPriority, mPriority);
+ }
}
@GuardedBy("LightsService.this")
@@ -150,7 +159,7 @@
}
@Override
- public void openSession(IBinder token) {
+ public void openSession(IBinder token, int priority) {
getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS,
"openSession requires CONTROL_DEVICE_LIGHTS permission");
Preconditions.checkNotNull(token);
@@ -159,7 +168,8 @@
Preconditions.checkState(getSessionLocked(token) == null, "already registered");
try {
token.linkToDeath(() -> closeSessionInternal(token), 0);
- mSessions.add(new Session(token));
+ mSessions.add(new Session(token, priority));
+ Collections.sort(mSessions);
} catch (RemoteException e) {
Slog.e(TAG, "Couldn't open session, client already died" , e);
throw new IllegalArgumentException("Client is already dead.");
@@ -216,10 +226,10 @@
}
private void checkRequestIsValid(int[] lightIds) {
- for (int i = 0; i < lightIds.length; i++) {
- final LightImpl light = mLightsById.get(lightIds[i]);
+ for (int lightId : lightIds) {
+ final LightImpl light = mLightsById.get(lightId);
Preconditions.checkState(light != null && !light.isSystemLight(),
- "Invalid lightId " + lightIds[i]);
+ "Invalid lightId " + lightId);
}
}
diff --git a/services/core/java/com/android/server/locksettings/TEST_MAPPING b/services/core/java/com/android/server/locksettings/TEST_MAPPING
index 4a216ca..b881b44 100644
--- a/services/core/java/com/android/server/locksettings/TEST_MAPPING
+++ b/services/core/java/com/android/server/locksettings/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-large": [
{
"name": "CtsDevicePolicyManagerTestCases",
"options": [
@@ -10,7 +10,9 @@
"exclude-annotation": "android.platform.test.annotations.FlakyTest"
}
]
- },
+ }
+ ],
+ "presubmit": [
{
"name": "FrameworksServicesTests",
"options": [
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index edd43af..47d1629 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -37,9 +37,11 @@
import android.app.admin.DevicePolicyManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.LocusId;
import android.content.pm.ActivityInfo;
@@ -95,6 +97,7 @@
import com.android.server.wm.ActivityTaskManagerInternal;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -146,10 +149,13 @@
private final ActivityManagerInternal mActivityManagerInternal;
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
private final ShortcutServiceInternal mShortcutServiceInternal;
+ private final PackageManagerInternal mPackageManagerInternal;
private final PackageCallbackList<IOnAppsChangedListener> mListeners
= new PackageCallbackList<IOnAppsChangedListener>();
private final DevicePolicyManager mDpm;
+ private final PackageRemovedListener mPackageRemovedListener =
+ new PackageRemovedListener();
private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
@GuardedBy("mListeners")
@@ -175,6 +181,8 @@
LocalServices.getService(ActivityTaskManagerInternal.class));
mShortcutServiceInternal = Objects.requireNonNull(
LocalServices.getService(ShortcutServiceInternal.class));
+ mPackageManagerInternal = Objects.requireNonNull(
+ LocalServices.getService(PackageManagerInternal.class));
mShortcutServiceInternal.addListener(mPackageMonitor);
mShortcutChangeHandler = new ShortcutChangeHandler(mUserManagerInternal);
mShortcutServiceInternal.addShortcutChangeCallback(mShortcutChangeHandler);
@@ -294,6 +302,11 @@
*/
private void startWatchingPackageBroadcasts() {
if (!mIsWatchingPackageBroadcasts) {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED_INTERNAL);
+ filter.addDataScheme("package");
+ mContext.registerReceiverAsUser(mPackageRemovedListener, UserHandle.ALL, filter,
+ /* broadcastPermission= */ null, mCallbackHandler);
mPackageMonitor.register(mContext, UserHandle.ALL, true, mCallbackHandler);
mIsWatchingPackageBroadcasts = true;
}
@@ -307,6 +320,7 @@
Log.d(TAG, "Stopped watching for packages");
}
if (mIsWatchingPackageBroadcasts) {
+ mContext.unregisterReceiver(mPackageRemovedListener);
mPackageMonitor.unregister();
mIsWatchingPackageBroadcasts = false;
}
@@ -395,9 +409,8 @@
if (!canAccessProfile(user.getIdentifier(), "cannot get shouldHideFromSuggestions")) {
return false;
}
- final PackageManagerInternal pmi = LocalServices.getService(
- PackageManagerInternal.class);
- int flags = pmi.getDistractingPackageRestrictions(packageName, user.getIdentifier());
+ final int flags = mPackageManagerInternal.getDistractingPackageRestrictions(packageName,
+ user.getIdentifier());
return (flags & PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS) != 0;
}
@@ -433,16 +446,14 @@
final ArrayList<LauncherActivityInfoInternal> result = new ArrayList<>(
launcherActivities.getList());
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
if (packageName != null) {
// If this hidden app should not be shown, return the original list.
// Otherwise, inject hidden activity that forwards user to app details page.
if (result.size() > 0) {
return launcherActivities;
}
- ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName, /*flags*/ 0,
- callingUid, user.getIdentifier());
+ final ApplicationInfo appInfo = mPackageManagerInternal.getApplicationInfo(
+ packageName, /* flags= */ 0, callingUid, user.getIdentifier());
if (shouldShowSyntheticActivity(user, appInfo)) {
LauncherActivityInfoInternal info = getHiddenAppActivityInfo(packageName,
callingUid, user);
@@ -456,8 +467,9 @@
for (LauncherActivityInfoInternal info : result) {
visiblePackages.add(info.getActivityInfo().packageName);
}
- List<ApplicationInfo> installedPackages = pmInt.getInstalledApplications(0,
- user.getIdentifier(), callingUid);
+ final List<ApplicationInfo> installedPackages =
+ mPackageManagerInternal.getInstalledApplications(/* flags= */ 0,
+ user.getIdentifier(), callingUid);
for (ApplicationInfo applicationInfo : installedPackages) {
if (!visiblePackages.contains(applicationInfo.packageName)) {
if (!shouldShowSyntheticActivity(user, applicationInfo)) {
@@ -483,9 +495,7 @@
if (isManagedProfileAdmin(user, appInfo.packageName)) {
return false;
}
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
- final AndroidPackage pkg = pmInt.getPackage(appInfo.packageName);
+ final AndroidPackage pkg = mPackageManagerInternal.getPackage(appInfo.packageName);
if (pkg == null) {
// Should not happen, but we shouldn't be failing if it does
return false;
@@ -501,13 +511,11 @@
}
private boolean hasDefaultEnableLauncherActivity(@NonNull String packageName) {
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
final Intent matchIntent = new Intent(Intent.ACTION_MAIN);
matchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
matchIntent.setPackage(packageName);
- final List<ResolveInfo> infoList = pmInt.queryIntentActivities(matchIntent,
- matchIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ final List<ResolveInfo> infoList = mPackageManagerInternal.queryIntentActivities(
+ matchIntent, matchIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
PackageManager.MATCH_DISABLED_COMPONENTS, Binder.getCallingUid(),
getCallingUserId());
final int size = infoList.size();
@@ -548,9 +556,7 @@
final int callingUid = injectBinderCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
- final ActivityInfo activityInfo = pmInt.getActivityInfo(component,
+ final ActivityInfo activityInfo = mPackageManagerInternal.getActivityInfo(component,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
callingUid, user.getIdentifier());
@@ -561,8 +567,9 @@
// should not happen
return null;
}
- final IncrementalStatesInfo incrementalStatesInfo = pmInt.getIncrementalStatesInfo(
- component.getPackageName(), callingUid, user.getIdentifier());
+ final IncrementalStatesInfo incrementalStatesInfo =
+ mPackageManagerInternal.getIncrementalStatesInfo(component.getPackageName(),
+ callingUid, user.getIdentifier());
if (incrementalStatesInfo == null) {
// package does not exist; should not happen
return null;
@@ -598,9 +605,7 @@
private List<LauncherActivityInfoInternal> queryIntentLauncherActivities(
Intent intent, int callingUid, UserHandle user) {
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
- List<ResolveInfo> apps = pmInt.queryIntentActivities(intent,
+ final List<ResolveInfo> apps = mPackageManagerInternal.queryIntentActivities(intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
@@ -614,8 +619,9 @@
// should not happen
continue;
}
- final IncrementalStatesInfo incrementalStatesInfo = pmInt.getIncrementalStatesInfo(
- packageName, callingUid, user.getIdentifier());
+ final IncrementalStatesInfo incrementalStatesInfo =
+ mPackageManagerInternal.getIncrementalStatesInfo(packageName, callingUid,
+ user.getIdentifier());
if (incrementalStatesInfo == null) {
// package doesn't exist any more; should not happen
continue;
@@ -639,15 +645,14 @@
final int callingUid = injectBinderCallingUid();
final long identity = Binder.clearCallingIdentity();
try {
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
Intent packageIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT)
.setPackage(component.getPackageName());
- List<ResolveInfo> apps = pmInt.queryIntentActivities(packageIntent,
- packageIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- callingUid, user.getIdentifier());
+ List<ResolveInfo> apps =
+ mPackageManagerInternal.queryIntentActivities(packageIntent,
+ packageIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ callingUid, user.getIdentifier());
// ensure that the component is present in the list
if (!apps.stream().anyMatch(
ri -> component.getClassName().equals(ri.activityInfo.name))) {
@@ -712,9 +717,7 @@
final int callingUid = injectBinderCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
- PackageInfo info = pmInt.getPackageInfo(packageName,
+ final PackageInfo info = mPackageManagerInternal.getPackageInfo(packageName,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
callingUid, user.getIdentifier());
@@ -730,9 +733,8 @@
if (!canAccessProfile(user.getIdentifier(), "Cannot get launcher extras")) {
return null;
}
- final PackageManagerInternal pmi =
- LocalServices.getService(PackageManagerInternal.class);
- return pmi.getSuspendedPackageLauncherExtras(packageName, user.getIdentifier());
+ return mPackageManagerInternal.getSuspendedPackageLauncherExtras(packageName,
+ user.getIdentifier());
}
@Override
@@ -746,10 +748,8 @@
final int callingUid = injectBinderCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
- ApplicationInfo info = pmInt.getApplicationInfo(packageName, flags,
- callingUid, user.getIdentifier());
+ final ApplicationInfo info = mPackageManagerInternal.getApplicationInfo(packageName,
+ flags, callingUid, user.getIdentifier());
return info;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1043,11 +1043,9 @@
return false;
}
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
final int callingUid = injectBinderCallingUid();
- final int state = pmInt.getComponentEnabledSetting(component, callingUid,
- user.getIdentifier());
+ final int state = mPackageManagerInternal.getComponentEnabledSetting(component,
+ callingUid, user.getIdentifier());
switch (state) {
case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
break; // Need to check the manifest's enabled state.
@@ -1061,7 +1059,7 @@
final long ident = Binder.clearCallingIdentity();
try {
- ActivityInfo info = pmInt.getActivityInfo(component,
+ final ActivityInfo info = mPackageManagerInternal.getActivityInfo(component,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
callingUid, user.getIdentifier());
@@ -1160,12 +1158,11 @@
final int callingUid = injectBinderCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
// Check that the component actually has Intent.CATEGORY_LAUCNCHER
// as calling startActivityAsUser ignores the category and just
// resolves based on the component if present.
- List<ResolveInfo> apps = pmInt.queryIntentActivities(launchIntent,
+ final List<ResolveInfo> apps = mPackageManagerInternal.queryIntentActivities(
+ launchIntent,
launchIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
@@ -1229,6 +1226,31 @@
user.getIdentifier(), debugMsg, false);
}
+ /** Returns whether or not the result to the listener should be filtered. */
+ private boolean isPackageVisibleToListener(String packageName, BroadcastCookie cookie) {
+ return !mPackageManagerInternal.filterAppAccess(packageName, cookie.callingUid,
+ cookie.user.getIdentifier());
+ }
+
+ /** Returns whether or not the given UID is in allow list */
+ private static boolean isCallingUidAllowed(int[] allowList, int callingUid) {
+ if (allowList == null) {
+ return true;
+ }
+ return Arrays.binarySearch(allowList, callingUid) > -1;
+ }
+
+ private String[] getFilteredPackageNames(String[] packageNames, BroadcastCookie cookie) {
+ final List<String> filteredPackageNames = new ArrayList<>();
+ for (String packageName : packageNames) {
+ if (!isPackageVisibleToListener(packageName, cookie)) {
+ continue;
+ }
+ filteredPackageNames.add(packageName);
+ }
+ return filteredPackageNames.toArray(new String[filteredPackageNames.size()]);
+ }
+
private int toShortcutsCacheFlags(int cacheFlags) {
int ret = 0;
if (cacheFlags == FLAG_CACHE_NOTIFICATION_SHORTCUTS) {
@@ -1253,19 +1275,17 @@
* loaded, register loading progress listener.
*/
void registerLoadingProgressForIncrementalApps() {
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
final List<UserHandle> users = mUm.getUserProfiles();
if (users == null) {
return;
}
for (UserHandle user : users) {
- pmInt.forEachInstalledPackage(pkg -> {
+ mPackageManagerInternal.forEachInstalledPackage(pkg -> {
final String packageName = pkg.getPackageName();
- if (pmInt.getIncrementalStatesInfo(packageName, Process.myUid(),
- user.getIdentifier()).isLoading()) {
- pmInt.registerInstalledLoadingProgressCallback(packageName,
- new PackageLoadingProgressCallback(packageName, user),
+ if (mPackageManagerInternal.getIncrementalStatesInfo(packageName,
+ Process.myUid(), user.getIdentifier()).isLoading()) {
+ mPackageManagerInternal.registerInstalledLoadingProgressCallback(
+ packageName, new PackageLoadingProgressCallback(packageName, user),
user.getIdentifier());
}
}, user.getIdentifier());
@@ -1398,6 +1418,59 @@
}
}
+ private class PackageRemovedListener extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
+ return;
+ }
+ final String action = intent.getAction();
+ // Handle onPackageRemoved.
+ if (Intent.ACTION_PACKAGE_REMOVED_INTERNAL.equals(action)) {
+ final String packageName = getPackageName(intent);
+ final int[] allowList =
+ intent.getIntArrayExtra(Intent.EXTRA_VISIBILITY_ALLOW_LIST);
+ // If {@link #EXTRA_REPLACING} is true, that will be onPackageChanged case.
+ if (packageName != null && !intent.getBooleanExtra(
+ Intent.EXTRA_REPLACING, /* defaultValue= */ false)) {
+ final UserHandle user = new UserHandle(userId);
+ final int n = mListeners.beginBroadcast();
+ try {
+ for (int i = 0; i < n; i++) {
+ final IOnAppsChangedListener listener =
+ mListeners.getBroadcastItem(i);
+ final BroadcastCookie cookie =
+ (BroadcastCookie) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(cookie.user, user, "onPackageRemoved")) {
+ continue;
+ }
+ if (!isCallingUidAllowed(allowList, cookie.callingUid)) {
+ continue;
+ }
+ try {
+ listener.onPackageRemoved(user, packageName);
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
+ }
+ } finally {
+ mListeners.finishBroadcast();
+ }
+ }
+ }
+ }
+
+ private String getPackageName(Intent intent) {
+ final Uri uri = intent.getData();
+ final String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
+ return pkg;
+ }
+ }
+
private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
// TODO Simplify with lambdas.
@@ -1410,7 +1483,12 @@
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackageAdded")) continue;
+ if (!isEnabledProfileOf(cookie.user, user, "onPackageAdded")) {
+ continue;
+ }
+ if (!isPackageVisibleToListener(packageName, cookie)) {
+ continue;
+ }
try {
listener.onPackageAdded(user, packageName);
} catch (RemoteException re) {
@@ -1421,35 +1499,12 @@
mListeners.finishBroadcast();
}
super.onPackageAdded(packageName, uid);
- PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- pmi.registerInstalledLoadingProgressCallback(packageName,
+ mPackageManagerInternal.registerInstalledLoadingProgressCallback(packageName,
new PackageLoadingProgressCallback(packageName, user),
user.getIdentifier());
}
@Override
- public void onPackageRemoved(String packageName, int uid) {
- UserHandle user = new UserHandle(getChangingUserId());
- final int n = mListeners.beginBroadcast();
- try {
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackageRemoved")) continue;
- try {
- listener.onPackageRemoved(user, packageName);
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
- }
- }
- } finally {
- mListeners.finishBroadcast();
- }
-
- super.onPackageRemoved(packageName, uid);
- }
-
- @Override
public void onPackageModified(String packageName) {
onPackageChanged(packageName);
super.onPackageModified(packageName);
@@ -1462,7 +1517,12 @@
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackageModified")) continue;
+ if (!isEnabledProfileOf(cookie.user, user, "onPackageModified")) {
+ continue;
+ }
+ if (!isPackageVisibleToListener(packageName, cookie)) {
+ continue;
+ }
try {
listener.onPackageChanged(user, packageName);
} catch (RemoteException re) {
@@ -1482,9 +1542,16 @@
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackagesAvailable")) continue;
+ if (!isEnabledProfileOf(cookie.user, user, "onPackagesAvailable")) {
+ continue;
+ }
+ final String[] filteredPackages = getFilteredPackageNames(packages, cookie);
+ // If all packages are filtered, skip notifying listener.
+ if (ArrayUtils.isEmpty(filteredPackages)) {
+ continue;
+ }
try {
- listener.onPackagesAvailable(user, packages, isReplacing());
+ listener.onPackagesAvailable(user, filteredPackages, isReplacing());
} catch (RemoteException re) {
Slog.d(TAG, "Callback failed ", re);
}
@@ -1504,9 +1571,16 @@
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackagesUnavailable")) continue;
+ if (!isEnabledProfileOf(cookie.user, user, "onPackagesUnavailable")) {
+ continue;
+ }
+ final String[] filteredPackages = getFilteredPackageNames(packages, cookie);
+ // If all packages are filtered, skip notifying listener.
+ if (ArrayUtils.isEmpty(filteredPackages)) {
+ continue;
+ }
try {
- listener.onPackagesUnavailable(user, packages, isReplacing());
+ listener.onPackagesUnavailable(user, filteredPackages, isReplacing());
} catch (RemoteException re) {
Slog.d(TAG, "Callback failed ", re);
}
@@ -1521,12 +1595,12 @@
@Override
public void onPackagesSuspended(String[] packages) {
UserHandle user = new UserHandle(getChangingUserId());
- PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
final ArrayList<Pair<String, Bundle>> packagesWithExtras = new ArrayList<>();
final ArrayList<String> packagesWithoutExtras = new ArrayList<>();
for (String pkg : packages) {
- final Bundle launcherExtras = pmi.getSuspendedPackageLauncherExtras(pkg,
- user.getIdentifier());
+ final Bundle launcherExtras =
+ mPackageManagerInternal.getSuspendedPackageLauncherExtras(pkg,
+ user.getIdentifier());
if (launcherExtras != null) {
packagesWithExtras.add(new Pair<>(pkg, launcherExtras));
} else {
@@ -1540,11 +1614,23 @@
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackagesSuspended")) continue;
+ if (!isEnabledProfileOf(cookie.user, user, "onPackagesSuspended")) {
+ continue;
+ }
+ final String[] filteredPackagesWithoutExtras =
+ getFilteredPackageNames(packages, cookie);
+ // If all packages are filtered, skip notifying listener.
+ if (ArrayUtils.isEmpty(filteredPackagesWithoutExtras)) {
+ continue;
+ }
try {
- listener.onPackagesSuspended(user, packagesNullExtras, null);
+ listener.onPackagesSuspended(user, filteredPackagesWithoutExtras,
+ /* launcherExtras= */ null);
for (int idx = 0; idx < packagesWithExtras.size(); idx++) {
Pair<String, Bundle> packageExtraPair = packagesWithExtras.get(idx);
+ if (!isPackageVisibleToListener(packageExtraPair.first, cookie)) {
+ continue;
+ }
listener.onPackagesSuspended(user,
new String[]{packageExtraPair.first},
packageExtraPair.second);
@@ -1566,9 +1652,16 @@
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackagesUnsuspended")) continue;
+ if (!isEnabledProfileOf(cookie.user, user, "onPackagesUnsuspended")) {
+ continue;
+ }
+ final String[] filteredPackages = getFilteredPackageNames(packages, cookie);
+ // If all packages are filtered, skip notifying listener.
+ if (ArrayUtils.isEmpty(filteredPackages)) {
+ continue;
+ }
try {
- listener.onPackagesUnsuspended(user, packages);
+ listener.onPackagesUnsuspended(user, filteredPackages);
} catch (RemoteException re) {
Slog.d(TAG, "Callback failed ", re);
}
@@ -1595,8 +1688,12 @@
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onShortcutChanged")) continue;
-
+ if (!isEnabledProfileOf(cookie.user, user, "onShortcutChanged")) {
+ continue;
+ }
+ if (!isPackageVisibleToListener(packageName, cookie)) {
+ continue;
+ }
final int launcherUserId = cookie.user.getIdentifier();
// Make sure the caller has the permission.
@@ -1668,6 +1765,9 @@
if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) {
continue;
}
+ if (!isPackageVisibleToListener(mPackageName, cookie)) {
+ continue;
+ }
try {
listener.onPackageLoadingProgressChanged(mUser, mPackageName, progress);
} catch (RemoteException re) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index fa126a4..131539e 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -80,6 +80,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Random;
/**
* Helper class for running dexopt command on packages.
@@ -104,6 +105,8 @@
private final ArtStatsLogger mArtStatsLogger = new ArtStatsLogger();
+ private static final Random sRandom = new Random();
+
PackageDexOptimizer(Installer installer, Object installLock, Context context,
String wakeLockTag) {
this.mInstaller = installer;
@@ -262,7 +265,7 @@
if (packageStats != null) {
Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics");
try {
- long sessionId = Math.randomLongInternal();
+ long sessionId = sRandom.nextLong();
ArtStatsLogUtils.writeStatsLog(
mArtStatsLogger,
sessionId,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2cdf40a..d2cbcf0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15033,6 +15033,9 @@
uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
intent.putExtra(Intent.EXTRA_UID, uid);
}
+ if (broadcastAllowList != null && PLATFORM_PACKAGE_NAME.equals(targetPkg)) {
+ intent.putExtra(Intent.EXTRA_VISIBILITY_ALLOW_LIST, broadcastAllowList.get(id));
+ }
intent.putExtra(Intent.EXTRA_USER_HANDLE, id);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
if (DEBUG_BROADCASTS) {
@@ -21001,6 +21004,10 @@
removedPackage, extras, 0 /*flags*/,
installerPackageName, null, broadcastUsers, instantUserIds, null, null);
}
+ packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED_INTERNAL,
+ removedPackage, extras, 0 /*flags*/, PLATFORM_PACKAGE_NAME,
+ null /*finishedReceiver*/, broadcastUsers, instantUserIds,
+ broadcastAllowList, null /*bOptions*/);
if (dataRemoved && !isRemovedPackageSystemUpdate) {
packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 1dc4b78..3dfb835e 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -191,6 +191,10 @@
private boolean mIsInitilized;
+ private boolean mRescanRequired;
+ private boolean mIsNewApp;
+ private List<ShortcutInfo> mManifestShortcuts;
+
private ShortcutPackage(ShortcutUser shortcutUser,
int packageUserId, String packageName, ShortcutPackageInfo spi) {
super(shortcutUser, packageUserId, packageName,
@@ -1124,8 +1128,22 @@
(isNewApp ? "added" : "updated"),
getPackageInfo().getVersionCode(), pi.getLongVersionCode()));
}
-
getPackageInfo().updateFromPackageInfo(pi);
+ if (isAppSearchEnabled()) {
+ // Save the states in memory and resume package rescan when needed
+ mRescanRequired = true;
+ mIsNewApp = isNewApp;
+ mManifestShortcuts = newManifestShortcutList;
+ } else {
+ rescanPackage(isNewApp, newManifestShortcutList);
+ }
+ return true; // true means changed.
+ }
+
+ private void rescanPackage(
+ final boolean isNewApp, @NonNull final List<ShortcutInfo> newManifestShortcutList) {
+ final ShortcutService s = mShortcutUser.mService;
+
final long newVersionCode = getPackageInfo().getVersionCode();
// See if there are any shortcuts that were prevented restoring because the app was of a
@@ -1204,7 +1222,7 @@
// This will send a notification to the launcher, and also save .
// TODO: List changed and removed manifest shortcuts and pass to packageShortcutsChanged()
s.packageShortcutsChanged(getPackageName(), getPackageUserId(), null, null);
- return true; // true means changed.
+ mManifestShortcuts = null;
}
private boolean publishManifestShortcuts(List<ShortcutInfo> newManifestShortcutList) {
@@ -1699,13 +1717,25 @@
return result;
}
+ private boolean hasNoShortcut() {
+ if (!isAppSearchEnabled()) {
+ return getShortcutCount() == 0;
+ }
+ final boolean[] hasAnyShortcut = new boolean[1];
+ forEachShortcutStopWhen(si -> {
+ hasAnyShortcut[0] = true;
+ return true;
+ });
+ return !hasAnyShortcut[0];
+ }
+
@Override
public void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException {
final int size = mShortcuts.size();
final int shareTargetSize = mShareTargets.size();
- if (size == 0 && shareTargetSize == 0 && mApiCallCount == 0) {
+ if (hasNoShortcut() && shareTargetSize == 0 && mApiCallCount == 0) {
return; // nothing to write.
}
@@ -2592,6 +2622,10 @@
if (!wasInitialized) {
restoreParsedShortcuts(false);
}
+ if (mRescanRequired) {
+ mRescanRequired = false;
+ rescanPackage(mIsNewApp, mManifestShortcuts);
+ }
return ConcurrentUtils.waitForFutureNoInterrupt(cb.apply(session), description);
} catch (Exception e) {
Slog.e(TAG, "Failed to initiate app search for shortcut package "
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index a69e9db..007393a 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1319,7 +1319,7 @@
mUsers.put(userId, userPackages);
// Also when a user's data is first accessed, scan all packages.
- injectPostToHandler(() -> checkPackageChanges(userId));
+ checkPackageChanges(userId);
}
return userPackages;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2a0257d..cf18156 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1516,13 +1516,13 @@
}
@Override
- public boolean sharesMediaWithParent(@UserIdInt int userId) {
+ public boolean isMediaSharedWithParent(@UserIdInt int userId) {
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
- "sharesMediaWithParent");
+ "isMediaSharedWithParent");
synchronized (mUsersLock) {
UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
return userTypeDetails != null ? userTypeDetails.isProfile()
- && userTypeDetails.sharesMediaWithParent() : false;
+ && userTypeDetails.isMediaSharedWithParent() : false;
}
}
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 6824f7d..92ec31b 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -154,7 +154,7 @@
*
* <p> Default value is false
*/
- private final boolean mSharesMediaWithParent;
+ private final boolean mIsMediaSharedWithParent;
private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
@UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
@@ -166,7 +166,7 @@
@Nullable Bundle defaultSystemSettings,
@Nullable Bundle defaultSecureSettings,
@Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters,
- boolean sharesMediaWithParent) {
+ boolean isMediaSharedWithParent) {
this.mName = name;
this.mEnabled = enabled;
this.mMaxAllowed = maxAllowed;
@@ -185,7 +185,7 @@
this.mBadgeLabels = badgeLabels;
this.mBadgeColors = badgeColors;
this.mDarkThemeBadgeColors = darkThemeBadgeColors;
- this.mSharesMediaWithParent = sharesMediaWithParent;
+ this.mIsMediaSharedWithParent = isMediaSharedWithParent;
}
/**
@@ -303,8 +303,8 @@
/**
* Returns true if the user has shared media with parent user or false otherwise.
*/
- public boolean sharesMediaWithParent() {
- return mSharesMediaWithParent;
+ public boolean isMediaSharedWithParent() {
+ return mIsMediaSharedWithParent;
}
/** Returns a {@link Bundle} representing the default user restrictions. */
@@ -398,7 +398,7 @@
private @DrawableRes int mIconBadge = Resources.ID_NULL;
private @DrawableRes int mBadgePlain = Resources.ID_NULL;
private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
- private boolean mSharesMediaWithParent = false;
+ private boolean mIsMediaSharedWithParent = false;
public Builder setName(String name) {
mName = name;
@@ -491,10 +491,10 @@
/**
* Sets shared media property for the user.
- * @param sharesMediaWithParent the value to be set, true or false
+ * @param isMediaSharedWithParent the value to be set, true or false
*/
- public Builder setSharesMediaWithParent(boolean sharesMediaWithParent) {
- mSharesMediaWithParent = sharesMediaWithParent;
+ public Builder setIsMediaSharedWithParent(boolean isMediaSharedWithParent) {
+ mIsMediaSharedWithParent = isMediaSharedWithParent;
return this;
}
@@ -527,7 +527,7 @@
mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors,
mDefaultRestrictions, mDefaultSystemSettings, mDefaultSecureSettings,
- mDefaultCrossProfileIntentFilters, mSharesMediaWithParent);
+ mDefaultCrossProfileIntentFilters, mIsMediaSharedWithParent);
}
private boolean hasBadge() {
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index e8421a5..30cb40c 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -121,7 +121,7 @@
.setMaxAllowedPerParent(1)
.setLabel(0)
.setDefaultRestrictions(null)
- .setSharesMediaWithParent(true);
+ .setIsMediaSharedWithParent(true);
}
/**
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 5f5e6a3..8d79a81 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -80,11 +80,11 @@
public int getConnectionUserIdForVolume(VolumeInfo vol) {
final Context volumeUserContext = mContext.createContextAsUser(
UserHandle.of(vol.mountUserId), 0);
- boolean sharesMediaWithParent = volumeUserContext.getSystemService(
- UserManager.class).sharesMediaWithParent();
+ boolean isMediaSharedWithParent = volumeUserContext.getSystemService(
+ UserManager.class).isMediaSharedWithParent();
UserInfo userInfo = mUserManager.getUserInfo(vol.mountUserId);
- if (userInfo != null && sharesMediaWithParent) {
+ if (userInfo != null && isMediaSharedWithParent) {
// Clones use the same connection as their parent
return userInfo.profileGroupId;
} else {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 77bfc5f..6ca3c4b 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -52,6 +52,7 @@
import android.net.NetworkScore;
import android.net.RouteInfo;
import android.net.TelephonyNetworkSpecifier;
+import android.net.TunnelConnectionParams;
import android.net.Uri;
import android.net.annotations.PolicyDirection;
import android.net.ipsec.ike.ChildSessionCallback;
@@ -61,10 +62,10 @@
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionConfiguration;
import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
-import android.net.vcn.VcnControlPlaneIkeConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
@@ -1923,8 +1924,14 @@
@NonNull IpSecTunnelInterface tunnelIface,
@NonNull VcnChildSessionConfiguration childConfig,
@Nullable UnderlyingNetworkRecord underlying) {
- final VcnControlPlaneIkeConfig controlPlaneConfig =
- (VcnControlPlaneIkeConfig) gatewayConnectionConfig.getControlPlaneConfig();
+ final TunnelConnectionParams tunnelParams =
+ gatewayConnectionConfig.getTunnelConnectionParams();
+ if (!(tunnelParams instanceof IkeTunnelConnectionParams)) {
+ throw new IllegalStateException(
+ "TunnelConnectionParams is not IkeTunnelConnectionParams");
+ }
+
+ final IkeTunnelConnectionParams ikeTunnelParams = (IkeTunnelConnectionParams) tunnelParams;
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(tunnelIface.getInterfaceName());
@@ -1943,7 +1950,7 @@
final int underlyingMtu = (underlying == null) ? 0 : underlying.linkProperties.getMtu();
lp.setMtu(
MtuUtils.getMtu(
- controlPlaneConfig.getChildSessionParams().getSaProposals(),
+ ikeTunnelParams.getTunnelModeChildSessionParams().getSaProposals(),
gatewayConnectionConfig.getMaxMtu(),
underlyingMtu));
@@ -2131,19 +2138,32 @@
}
private IkeSessionParams buildIkeParams(@NonNull Network network) {
- final VcnControlPlaneIkeConfig controlPlaneConfig =
- (VcnControlPlaneIkeConfig) mConnectionConfig.getControlPlaneConfig();
- final IkeSessionParams.Builder builder =
- new IkeSessionParams.Builder(controlPlaneConfig.getIkeSessionParams());
- builder.setNetwork(network);
+ final TunnelConnectionParams tunnelConnectionParams =
+ mConnectionConfig.getTunnelConnectionParams();
- return builder.build();
+ if (tunnelConnectionParams instanceof IkeTunnelConnectionParams) {
+ final IkeTunnelConnectionParams ikeTunnelConnectionParams =
+ (IkeTunnelConnectionParams) tunnelConnectionParams;
+ final IkeSessionParams.Builder builder =
+ new IkeSessionParams.Builder(ikeTunnelConnectionParams.getIkeSessionParams());
+ builder.setNetwork(network);
+
+ return builder.build();
+ }
+
+ throw new IllegalStateException("TunnelConnectionParams is not IkeTunnelConnectionParams");
}
private ChildSessionParams buildChildParams() {
- final VcnControlPlaneIkeConfig controlPlaneConfig =
- (VcnControlPlaneIkeConfig) mConnectionConfig.getControlPlaneConfig();
- return controlPlaneConfig.getChildSessionParams();
+ final TunnelConnectionParams tunnelConnectionParams =
+ mConnectionConfig.getTunnelConnectionParams();
+
+ if (tunnelConnectionParams instanceof IkeTunnelConnectionParams) {
+ return ((IkeTunnelConnectionParams) tunnelConnectionParams)
+ .getTunnelModeChildSessionParams();
+ }
+
+ throw new IllegalStateException("TunnelConnectionParams is not IkeTunnelConnectionParams");
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index eb79518..e6d49e4 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -59,6 +59,7 @@
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -188,22 +189,28 @@
}
if (callback != null) {
+ WindowsForAccessibilityObserver observer =
+ mWindowsForAccessibilityObserver.get(displayId);
if (isEmbeddedDisplay(dc)) {
// If this display is an embedded one, its window observer should have been set from
// window manager after setting its parent window. But if its window observer is
// empty, that means this mapping didn't be set, and needs to do this again.
// This happened when accessibility window observer is disabled and enabled again.
- if (mWindowsForAccessibilityObserver.get(displayId) == null) {
+ if (observer == null) {
handleWindowObserverOfEmbeddedDisplay(displayId, dc.getParentWindow());
}
return false;
- } else if (mWindowsForAccessibilityObserver.get(displayId) != null) {
- throw new IllegalStateException(
- "Windows for accessibility callback of display "
- + displayId + " already set!");
+ } else if (observer != null) {
+ final String errorMessage = "Windows for accessibility callback of display "
+ + displayId + " already set!";
+ Slog.e(TAG, errorMessage);
+ if (Build.IS_DEBUGGABLE) {
+ throw new IllegalStateException(errorMessage);
+ }
+ removeObserverOfEmbeddedDisplay(observer);
+ mWindowsForAccessibilityObserver.remove(displayId);
}
- final WindowsForAccessibilityObserver observer =
- new WindowsForAccessibilityObserver(mService, displayId, callback);
+ observer = new WindowsForAccessibilityObserver(mService, displayId, callback);
mWindowsForAccessibilityObserver.put(displayId, observer);
mAllObserversInitialized &= observer.mInitialized;
} else {
@@ -218,9 +225,12 @@
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
if (windowsForA11yObserver == null) {
- throw new IllegalStateException(
- "Windows for accessibility callback of display " + displayId
- + " already cleared!");
+ final String errorMessage = "Windows for accessibility callback of display "
+ + displayId + " already cleared!";
+ Slog.e(TAG, errorMessage);
+ if (Build.IS_DEBUGGABLE) {
+ throw new IllegalStateException(errorMessage);
+ }
}
removeObserverOfEmbeddedDisplay(windowsForA11yObserver);
mWindowsForAccessibilityObserver.remove(displayId);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ee503d5..7607285 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2829,7 +2829,7 @@
if (packageName != null) {
caller = packageName + " " + caller;
}
- if (!canCloseSystemDialogs(pid, uid, process)) {
+ if (!canCloseSystemDialogs(pid, uid)) {
// The app can't close system dialogs, throw only if it targets S+
if (CompatChanges.isChangeEnabled(LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, uid)) {
throw new SecurityException(
@@ -2854,39 +2854,37 @@
return true;
}
- private boolean canCloseSystemDialogs(int pid, int uid,
- @Nullable WindowProcessController process) {
+ private boolean canCloseSystemDialogs(int pid, int uid) {
if (checkPermission(Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, pid, uid)
== PERMISSION_GRANTED) {
return true;
}
- if (process == null) {
- synchronized (mGlobalLock) {
- process = mProcessMap.getProcess(pid);
+ synchronized (mGlobalLock) {
+ // Check all the processes from the given uid, especially since for PendingIntents sent
+ // the pid equals -1
+ ArraySet<WindowProcessController> processes = mProcessMap.getProcesses(uid);
+ if (processes != null) {
+ for (int i = 0, n = processes.size(); i < n; i++) {
+ WindowProcessController process = processes.valueAt(i);
+ // Check if the instrumentation of the process has the permission. This covers
+ // the usual test started from the shell (which has the permission) case. This
+ // is needed for apps targeting SDK level < S but we are also allowing for
+ // targetSdk S+ as a convenience to avoid breaking a bunch of existing tests and
+ // asking them to adopt shell permissions to do this.
+ int sourceUid = process.getInstrumentationSourceUid();
+ if (process.isInstrumenting() && sourceUid != -1 && checkPermission(
+ Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, -1, sourceUid)
+ == PERMISSION_GRANTED) {
+ return true;
+ }
+ // This is the notification trampoline use-case for example, where apps use
+ // Intent.ACSD to close the shade prior to starting an activity.
+ if (process.canCloseSystemDialogsByToken()) {
+ return true;
+ }
+ }
}
- }
- if (process != null) {
- // Check if the instrumentation of the process has the permission. This covers the
- // usual test started from the shell (which has the permission) case. This is needed
- // for apps targeting SDK level < S but we are also allowing for targetSdk S+ as a
- // convenience to avoid breaking a bunch of existing tests and asking them to adopt
- // shell permissions to do this.
- // Note that these getters all read from volatile fields in WindowProcessController, so
- // no need to lock.
- int sourceUid = process.getInstrumentationSourceUid();
- if (process.isInstrumenting() && sourceUid != -1 && checkPermission(
- Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, -1, sourceUid)
- == PERMISSION_GRANTED) {
- return true;
- }
- // This is the notification trampoline use-case for example, where apps use Intent.ACSD
- // to close the shade prior to starting an activity.
- if (process.canCloseSystemDialogsByToken()) {
- return true;
- }
- }
- if (!CompatChanges.isChangeEnabled(LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, uid)) {
- synchronized (mGlobalLock) {
+ if (!CompatChanges.isChangeEnabled(LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, uid)) {
// This covers the case where the app is displaying some UI on top of the
// notification shade and wants to start an activity. The app then sends the intent
// in order to move the notification shade out of the way and show the activity to
@@ -5168,8 +5166,7 @@
@Override
public boolean canCloseSystemDialogs(int pid, int uid) {
- return ActivityTaskManagerService.this.canCloseSystemDialogs(pid, uid,
- null /* process */);
+ return ActivityTaskManagerService.this.canCloseSystemDialogs(pid, uid);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 2b1cf39..9d225e1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2118,8 +2118,14 @@
}
void scheduleProcessStoppingAndFinishingActivitiesIfNeeded() {
- if ((!mStoppingActivities.isEmpty() || !mFinishingActivities.isEmpty())
- && !mHandler.hasMessages(PROCESS_STOPPING_AND_FINISHING_MSG)
+ if (mStoppingActivities.isEmpty() && mFinishingActivities.isEmpty()) {
+ return;
+ }
+ if (mRootWindowContainer.allResumedActivitiesIdle()) {
+ scheduleIdle();
+ return;
+ }
+ if (!mHandler.hasMessages(PROCESS_STOPPING_AND_FINISHING_MSG)
&& mRootWindowContainer.allResumedActivitiesVisible()) {
mHandler.sendEmptyMessage(PROCESS_STOPPING_AND_FINISHING_MSG);
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b9fc8b1..2118eff 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1686,6 +1686,11 @@
return false;
}
+ if (!taskDisplayArea.canHostHomeTask()) {
+ // Can't launch home on a TaskDisplayArea that does not support root home task
+ return false;
+ }
+
if (taskDisplayArea.getDisplayId() != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) {
// Can't launch home on secondary display if device does not support multi-display.
return false;
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 3799067..4d85e7b 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -172,18 +172,33 @@
*/
final boolean mCreatedByOrganizer;
+ /**
+ * True if this TaskDisplayArea can have a home task
+ * {@link WindowConfiguration#ACTIVITY_TYPE_HOME}
+ */
+ private final boolean mCanHostHomeTask;
+
TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
- int displayAreaFeature) {
- this(displayContent, service, name, displayAreaFeature, false /* createdByOrganizer */);
+ int displayAreaFeature) {
+ this(displayContent, service, name, displayAreaFeature, false /* createdByOrganizer */,
+ true /* canHostHomeTask */);
}
TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
- int displayAreaFeature, boolean createdByOrganizer) {
+ int displayAreaFeature, boolean createdByOrganizer) {
+ this(displayContent, service, name, displayAreaFeature, createdByOrganizer,
+ true /* canHostHomeTask */);
+ }
+
+ TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
+ int displayAreaFeature, boolean createdByOrganizer,
+ boolean canHostHomeTask) {
super(service, Type.ANY, name, displayAreaFeature);
mDisplayContent = displayContent;
mRootWindowContainer = service.mRoot;
mAtmService = service.mAtmService;
mCreatedByOrganizer = createdByOrganizer;
+ mCanHostHomeTask = canHostHomeTask;
}
/**
@@ -1667,7 +1682,9 @@
@Nullable
Task getOrCreateRootHomeTask(boolean onTop) {
Task homeTask = getRootHomeTask();
- if (homeTask == null && mDisplayContent.supportsSystemDecorations()) {
+ // Take into account if this TaskDisplayArea can have a home task before trying to
+ // create the root task
+ if (homeTask == null && canHostHomeTask()) {
homeTask = createRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop);
}
return homeTask;
@@ -1882,6 +1899,13 @@
}
/**
+ * Exposes the home task capability of the TaskDisplayArea
+ */
+ boolean canHostHomeTask() {
+ return mDisplayContent.supportsSystemDecorations() && mCanHostHomeTask;
+ }
+
+ /**
* Callback for when the order of the root tasks in the display changes.
*/
interface OnRootTaskOrderChangedListener {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index e4ac1f6..b6109b4 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -106,11 +106,9 @@
private final BLASTSyncEngine mSyncEngine;
private IRemoteTransition mRemoteTransition = null;
- /**
- * This is a leash to put animating surfaces into flatly without clipping/ordering issues. It
- * is a child of all the targets' shared ancestor.
- */
- private SurfaceControl mRootLeash = null;
+ /** Only use for clean-up after binder death! */
+ private SurfaceControl.Transaction mStartTransaction = null;
+ private SurfaceControl.Transaction mFinishTransaction = null;
/**
* Contains change infos for both participants and all ancestors. We have to track ancestors
@@ -229,16 +227,16 @@
setReady(true);
}
- /** The transition has finished animating and is ready to finalize WM state */
- void finishTransition() {
- if (mState < STATE_PLAYING) {
- throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId);
- }
+ /**
+ * Build a transaction that "resets" all the re-parenting and layer changes. This is
+ * intended to be applied at the end of the transition but before the finish callback. This
+ * needs to be passed/applied in shell because until finish is called, shell owns the surfaces.
+ * Additionally, this gives shell the ability to better deal with merged transitions.
+ */
+ private void buildFinishTransaction(SurfaceControl.Transaction t, SurfaceControl rootLeash) {
final Point tmpPos = new Point();
// usually only size 1
final ArraySet<DisplayContent> displays = new ArraySet<>();
- // Immediately apply all surface reparents, don't wait for pending/sync/etc.
- SurfaceControl.Transaction t = mController.mAtm.mWindowManager.mTransactionFactory.get();
for (int i = mTargets.size() - 1; i >= 0; --i) {
final WindowContainer target = mTargets.valueAt(i);
if (target.getParent() != null) {
@@ -247,9 +245,6 @@
// Ensure surfaceControls are re-parented back into the hierarchy.
t.reparent(targetLeash, origParent);
t.setLayer(targetLeash, target.getLastLayer());
- // TODO(shell-transitions): Once all remotables have been moved, see if there is
- // a more appropriate place to do the following. This may
- // involve passing an SF transaction from shell on finish.
target.getRelativePosition(tmpPos);
t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
t.setCornerRadius(targetLeash, 0);
@@ -257,25 +252,26 @@
displays.add(target.getDisplayContent());
}
}
- // Need to update layers on ALL displays (for now) since they were all paused while
- // the animation played.
+ // Need to update layers on involved displays since they were all paused while
+ // the animation played. This puts the layers back into the correct order.
for (int i = displays.size() - 1; i >= 0; --i) {
if (displays.valueAt(i) == null) continue;
displays.valueAt(i).assignChildLayers(t);
}
- // Also pro-actively hide going-invisible activity surfaces in same transaction to
- // prevent flickers due to reparenting and animation z-order mismatch.
- for (int i = mParticipants.size() - 1; i >= 0; --i) {
- final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
- if (ar == null || ar.mVisibleRequested || !ar.isVisible()) continue;
- t.hide(ar.getSurfaceControl());
+ if (rootLeash.isValid()) {
+ t.reparent(rootLeash, null);
}
- if (mRootLeash.isValid()) {
- t.remove(mRootLeash);
+ }
+
+ /**
+ * The transition has finished animating and is ready to finalize WM state. This should not
+ * be called directly; use {@link TransitionController#finishTransition} instead.
+ */
+ void finishTransition() {
+ mStartTransaction = mFinishTransaction = null;
+ if (mState < STATE_PLAYING) {
+ throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId);
}
- mRootLeash = null;
- t.apply();
- t.close();
// Commit all going-invisible containers
for (int i = 0; i < mParticipants.size(); ++i) {
@@ -343,29 +339,60 @@
// Resolve the animating targets from the participants
mTargets = calculateTargets(mParticipants, mChanges);
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges);
- mRootLeash = info.getRootLeash();
handleNonAppWindowsInTransition(displayId, mType, mFlags);
+ // Manually show any activities that are visibleRequested. This is needed to properly
+ // support simultaneous animation queueing/merging. Specifically, if transition A makes
+ // an activity invisible, it's finishTransaction (which is applied *after* the animation)
+ // will hide the activity surface. If transition B then makes the activity visible again,
+ // the normal surfaceplacement logic won't add a show to this start transaction because
+ // the activity visibility hasn't been committed yet. To deal with this, we have to manually
+ // show here in the same way that we manually hide in finishTransaction.
+ for (int i = mParticipants.size() - 1; i >= 0; --i) {
+ final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
+ if (ar == null || !ar.mVisibleRequested) continue;
+ transaction.show(ar.getSurfaceControl());
+ }
+
+ mStartTransaction = transaction;
+ mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
+ buildFinishTransaction(mFinishTransaction, info.getRootLeash());
if (mController.getTransitionPlayer() != null) {
try {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Calling onTransitionReady: %s", info);
- mController.getTransitionPlayer().onTransitionReady(this, info, transaction);
+ mController.getTransitionPlayer().onTransitionReady(
+ this, info, transaction, mFinishTransaction);
} catch (RemoteException e) {
// If there's an exception when trying to send the mergedTransaction to the
// client, we should finish and apply it here so the transactions aren't lost.
- transaction.apply();
- finishTransition();
+ cleanUpOnFailure();
}
} else {
// No player registered, so just finish/apply immediately
- transaction.apply();
- finishTransition();
+ cleanUpOnFailure();
}
mSyncId = -1;
}
+ /**
+ * If the remote failed for any reason, use this to do any appropriate clean-up. Do not call
+ * this directly, it's designed to by called by {@link TransitionController} only.
+ */
+ void cleanUpOnFailure() {
+ // No need to clean-up if this isn't playing yet.
+ if (mState < STATE_PLAYING) return;
+
+ if (mStartTransaction != null) {
+ mStartTransaction.apply();
+ }
+ if (mFinishTransaction != null) {
+ mFinishTransaction.apply();
+ }
+ finishTransition();
+ }
+
private void handleNonAppWindowsInTransition(int displayId,
@WindowManager.TransitionType int transit, int flags) {
final DisplayContent dc =
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 6338f39..cc63c49 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -42,17 +42,24 @@
private static final String TAG = "TransitionController";
private ITransitionPlayer mTransitionPlayer;
- private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> mTransitionPlayer = null;
final ActivityTaskManagerService mAtm;
- /** Currently playing transitions. When finished, records are removed from this list. */
+ /**
+ * Currently playing transitions (in the order they were started). When finished, records are
+ * removed from this list.
+ */
private final ArrayList<Transition> mPlayingTransitions = new ArrayList<>();
- /**
- * The transition currently being constructed (collecting participants).
- * TODO(shell-transitions): When simultaneous transitions are supported, merge this with
- * mPlayingTransitions.
- */
+ private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> {
+ // clean-up/finish any playing transitions.
+ for (int i = 0; i < mPlayingTransitions.size(); ++i) {
+ mPlayingTransitions.get(i).cleanUpOnFailure();
+ }
+ mPlayingTransitions.clear();
+ mTransitionPlayer = null;
+ };
+
+ /** The transition currently being constructed (collecting participants). */
private Transition mCollectingTransition = null;
TransitionController(ActivityTaskManagerService atm) {
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index b7fa796..698e3f7 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -370,6 +370,7 @@
jintArray supportedEffects = nullptr;
jintArray supportedBraking = nullptr;
jintArray supportedPrimitives = nullptr;
+ jintArray primitiveDurations = nullptr;
jfloatArray maxAmplitudes = nullptr;
if (info.supportedEffects.isOk()) {
@@ -390,6 +391,15 @@
env->SetIntArrayRegion(supportedPrimitives, 0, primitives.size(),
reinterpret_cast<jint*>(primitives.data()));
}
+ if (info.primitiveDurations.isOk()) {
+ std::vector<int32_t> durations;
+ for (auto duration : info.primitiveDurations.value()) {
+ durations.push_back(duration.count());
+ }
+ primitiveDurations = env->NewIntArray(durations.size());
+ env->SetIntArrayRegion(primitiveDurations, 0, durations.size(),
+ reinterpret_cast<jint*>(durations.data()));
+ }
if (info.maxAmplitudes.isOk()) {
std::vector<float> amplitudes = info.maxAmplitudes.value();
maxAmplitudes = env->NewFloatArray(amplitudes.size());
@@ -403,7 +413,7 @@
return env->NewObject(sVibratorInfoClass, sVibratorInfoCtor, wrapper->getVibratorId(),
capabilities, supportedEffects, supportedBraking, supportedPrimitives,
- qFactor, frequencyMapping);
+ primitiveDurations, qFactor, frequencyMapping);
}
static const JNINativeMethod method_table[] = {
@@ -450,9 +460,10 @@
sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFFF[F)V");
jclass vibratorInfoClass = FindClassOrDie(env, "android/os/VibratorInfo");
- sVibratorInfoClass = static_cast<jclass>(env->NewGlobalRef(vibratorInfoClass));
- sVibratorInfoCtor = GetMethodIDOrDie(env, sVibratorInfoClass, "<init>",
- "(IJ[I[I[IFLandroid/os/VibratorInfo$FrequencyMapping;)V");
+ sVibratorInfoClass = (jclass)env->NewGlobalRef(vibratorInfoClass);
+ sVibratorInfoCtor =
+ GetMethodIDOrDie(env, sVibratorInfoClass, "<init>",
+ "(IJ[I[I[I[IFLandroid/os/VibratorInfo$FrequencyMapping;)V");
return jniRegisterNativeMethods(env,
"com/android/server/vibrator/VibratorController$NativeWrapper",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9a99679..cda01db 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -629,6 +629,7 @@
private static final String CREDENTIAL_MANAGEMENT_APP_INVALID_ALIAS_MSG =
"The alias provided must be contained in the aliases specified in the credential "
+ "management app's authentication policy";
+ private static final String NOT_SYSTEM_CALLER_MSG = "Only the system can %s";
final Context mContext;
final Injector mInjector;
@@ -3631,7 +3632,8 @@
@Override
public boolean isSeparateProfileChallengeAllowed(int userHandle) {
- enforceSystemCaller("query separate challenge support");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "query separate challenge support"));
ComponentName profileOwner = getProfileOwnerAsUser(userHandle);
// Profile challenge is supported on N or newer release.
@@ -5560,7 +5562,7 @@
public boolean hasKeyPair(String callerPackage, String alias) {
final CallerIdentity caller = getCallerIdentity(callerPackage);
final boolean isCredentialManagementApp = isCredentialManagementApp(caller);
- Preconditions.checkCallAuthorization(canManageCertificates(caller)
+ Preconditions.checkCallAuthorization(canInstallCertificates(caller)
|| isCredentialManagementApp);
if (isCredentialManagementApp) {
Preconditions.checkCallAuthorization(
@@ -5582,17 +5584,22 @@
});
}
- private boolean canManageCertificates(CallerIdentity caller) {
+ private boolean canInstallCertificates(CallerIdentity caller) {
return isProfileOwner(caller) || isDeviceOwner(caller)
|| isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
}
+ private boolean canChooseCertificates(CallerIdentity caller) {
+ return isProfileOwner(caller) || isDeviceOwner(caller)
+ || isCallerDelegate(caller, DELEGATION_CERT_SELECTION);
+ }
+
@Override
public boolean setKeyGrantToWifiAuth(String callerPackage, String alias, boolean hasGrant) {
Preconditions.checkStringNotEmpty(alias, "Alias to grant cannot be empty");
final CallerIdentity caller = getCallerIdentity(callerPackage);
- Preconditions.checkCallAuthorization(canManageCertificates(caller));
+ Preconditions.checkCallAuthorization(canChooseCertificates(caller));
return setKeyChainGrantInternal(alias, hasGrant, Process.WIFI_UID, caller.getUserHandle());
}
@@ -5602,7 +5609,7 @@
Preconditions.checkStringNotEmpty(alias, "Alias to check cannot be empty");
final CallerIdentity caller = getCallerIdentity(callerPackage);
- Preconditions.checkCallAuthorization(canManageCertificates(caller));
+ Preconditions.checkCallAuthorization(canChooseCertificates(caller));
return mInjector.binderWithCleanCallingIdentity(() -> {
try (KeyChainConnection keyChainConnection =
@@ -5632,7 +5639,7 @@
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDeviceOwner(caller)))
- || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_SELECTION)));
final int granteeUid;
try {
@@ -5655,8 +5662,7 @@
try (KeyChainConnection keyChainConnection =
KeyChain.bindAsUser(mContext, userHandle)) {
IKeyChainService keyChain = keyChainConnection.getService();
- keyChain.setGrant(granteeUid, alias, hasGrant);
- return true;
+ return keyChain.setGrant(granteeUid, alias, hasGrant);
} catch (RemoteException e) {
Slogf.e(LOG_TAG, "Setting grant for package.", e);
return false;
@@ -5673,7 +5679,7 @@
@Override
public ParcelableGranteeMap getKeyPairGrants(String callerPackage, String alias) {
final CallerIdentity caller = getCallerIdentity(callerPackage);
- Preconditions.checkCallAuthorization(canManageCertificates(caller));
+ Preconditions.checkCallAuthorization(canChooseCertificates(caller));
final ArrayMap<Integer, Set<String>> result = new ArrayMap<>();
mInjector.binderWithCleanCallingIdentity(() -> {
@@ -5733,7 +5739,7 @@
*/
if (hasProfileOwner(caller.getUserId())) {
// Make sure that the caller is the profile owner or delegate.
- Preconditions.checkCallAuthorization(canManageCertificates(caller));
+ Preconditions.checkCallAuthorization(canInstallCertificates(caller));
// Verify that the managed profile is on an organization-owned device and as such
// the profile owner can access Device IDs.
if (isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId())) {
@@ -5982,7 +5988,8 @@
public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
final IBinder response) {
final CallerIdentity caller = getCallerIdentity();
- enforceSystemCaller("choose private key alias");
+ Preconditions.checkCallAuthorization(isSystemUid(caller),
+ String.format(NOT_SYSTEM_CALLER_MSG, "choose private key alias"));
// If there is a profile owner, redirect to that; otherwise query the device owner.
ComponentName aliasChooser = getProfileOwnerAsUser(caller.getUserId());
@@ -6530,7 +6537,8 @@
@Override
public String getAlwaysOnVpnPackageForUser(int userHandle) {
- enforceSystemCaller("getAlwaysOnVpnPackageForUser");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "call getAlwaysOnVpnPackageForUser"));
synchronized (getLockObject()) {
ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle);
return admin != null ? admin.mAlwaysOnVpnPackage : null;
@@ -6556,7 +6564,8 @@
@Override
public boolean isAlwaysOnVpnLockdownEnabledForUser(int userHandle) {
- enforceSystemCaller("isAlwaysOnVpnLockdownEnabledForUser");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "call isAlwaysOnVpnLockdownEnabledForUser"));
synchronized (getLockObject()) {
ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle);
return admin != null ? admin.mAlwaysOnVpnLockdown : null;
@@ -9155,10 +9164,8 @@
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
if ((mIsWatch || hasUserSetupCompleted(userHandle))) {
- if (!isCallerWithSystemUid()) {
- throw new IllegalStateException("Cannot set the profile owner on a user which is "
- + "already set-up");
- }
+ Preconditions.checkState(isSystemUid(caller),
+ "Cannot set the profile owner on a user which is already set-up");
if (!mIsWatch) {
// Only the default supervision profile owner can be set as profile owner after SUW
@@ -9313,10 +9320,6 @@
}
}
- private boolean isCallerWithSystemUid() {
- return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID);
- }
-
private boolean isSystemUid(CallerIdentity caller) {
return UserHandle.isSameApp(caller.getUid(), Process.SYSTEM_UID);
}
@@ -9747,7 +9750,8 @@
@Override
public ComponentName getRestrictionsProvider(int userHandle) {
- enforceSystemCaller("query the permission provider");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "query the permission provider"));
synchronized (getLockObject()) {
DevicePolicyData userData = getUserData(userHandle);
return userData != null ? userData.mRestrictionsProvider : null;
@@ -10017,7 +10021,9 @@
}
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkStringNotEmpty(packageName, "packageName is null");
- enforceSystemCaller("query if an accessibility service is disabled by admin");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG,
+ "query if an accessibility service is disabled by admin"));
synchronized (getLockObject()) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
@@ -10163,7 +10169,9 @@
}
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkStringNotEmpty(packageName, "packageName is null");
- enforceSystemCaller("query if an input method is disabled by admin");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG,
+ "query if an input method is disabled by admin"));
synchronized (getLockObject()) {
ActiveAdmin admin = getParentOfAdminIfRequired(
@@ -10223,7 +10231,9 @@
}
Preconditions.checkStringNotEmpty(packageName, "packageName is null or empty");
- enforceSystemCaller("query if a notification listener service is permitted");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG,
+ "query if a notification listener service is permitted"));
synchronized (getLockObject()) {
ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
@@ -10236,12 +10246,6 @@
}
}
- private void enforceSystemCaller(String action) {
- if (!isCallerWithSystemUid()) {
- throw new SecurityException("Only the system can " + action);
- }
- }
-
private void maybeSendAdminEnabledBroadcastLocked(int userHandle) {
DevicePolicyData policyData = getUserData(userHandle);
if (policyData.mAdminBroadcastPending) {
@@ -11785,7 +11789,8 @@
@Override
public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) {
- enforceSystemCaller("call notifyLockTaskModeChanged");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "call notifyLockTaskModeChanged"));
synchronized (getLockObject()) {
final DevicePolicyData policy = getUserData(userHandle);
@@ -13688,7 +13693,8 @@
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
- enforceSystemCaller("query support message for user");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "query support message for user"));
synchronized (getLockObject()) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
@@ -13705,7 +13711,8 @@
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
- enforceSystemCaller("query support message for user");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "query support message for user"));
synchronized (getLockObject()) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
@@ -13935,7 +13942,8 @@
if (!mHasFeature) {
return false;
}
- enforceSystemCaller("query restricted pkgs for a specific user");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "query restricted pkgs for a specific user"));
synchronized (getLockObject()) {
final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId);
@@ -14200,7 +14208,7 @@
}
synchronized (getLockObject()) {
- if (!isCallerWithSystemUid()) {
+ if (!isSystemUid(getCallerIdentity())) {
final CallerIdentity caller = getCallerIdentity(admin, packageName);
if (admin != null) {
Preconditions.checkCallAuthorization(
@@ -16598,7 +16606,9 @@
@Override
public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
- enforceSystemCaller("call canProfileOwnerResetPasswordWhenLocked");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG,
+ "call canProfileOwnerResetPasswordWhenLocked"));
synchronized (getLockObject()) {
final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(userId);
if (poAdmin == null
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 6d76ad8..4a6c9be 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.IActivityManager;
@@ -264,6 +265,25 @@
assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
}
+ @Test
+ public void testUnhibernatingPackageForUserSendsBootCompleteBroadcast()
+ throws RemoteException {
+ // GIVEN a hibernating package for a user
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
+
+ // WHEN we unhibernate the package
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, false);
+
+ // THEN we send the boot complete broadcasts
+ ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mIActivityManager, times(2)).broadcastIntentWithFeature(any(), any(),
+ intentArgumentCaptor.capture(), any(), any(), anyInt(), any(), any(), any(), any(),
+ anyInt(), any(), anyBoolean(), anyBoolean(), eq(USER_ID_1));
+ List<Intent> capturedIntents = intentArgumentCaptor.getAllValues();
+ assertEquals(capturedIntents.get(0).getAction(), Intent.ACTION_LOCKED_BOOT_COMPLETED);
+ assertEquals(capturedIntents.get(1).getAction(), Intent.ACTION_BOOT_COMPLETED);
+ }
+
/**
* Add a mock user with one package.
*/
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index fc26611..b76c279 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -175,7 +175,7 @@
final Context userContext = mContext.createPackageContextAsUser("system", 0,
UserHandle.of(userInfo.id));
assertThat(userContext.getSystemService(
- UserManager.class).sharesMediaWithParent()).isTrue();
+ UserManager.class).isMediaSharedWithParent()).isTrue();
List<UserInfo> list = mUserManager.getUsers();
List<UserInfo> cloneUsers = list.stream().filter(
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 1d715c8..0585758 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -156,7 +156,7 @@
mMinFrequency, mResonantFrequency, mFrequencyResolution,
suggestedFrequencyRange, mMaxAmplitudes);
return new VibratorInfo(vibratorId, mCapabilities, mSupportedEffects, mSupportedBraking,
- mSupportedPrimitives, mQFactor, frequencyMapping);
+ mSupportedPrimitives, null, mQFactor, frequencyMapping);
}
private void applyLatency() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
index 2a55083..147a44f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
@@ -28,6 +28,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@@ -48,6 +49,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
/**
* Tests for the {@link DisplayAreaPolicy}.
@@ -183,6 +185,43 @@
taskDisplayArea5, taskDisplayArea1);
}
+ @Test
+ public void testTaskDisplayAreasCanHostHomeTask() {
+ final WindowManagerService wms = mWm;
+ final DisplayContent displayContent = mock(DisplayContent.class);
+ doReturn(true).when(displayContent).isTrusted();
+ doReturn(true).when(displayContent).supportsSystemDecorations();
+ final RootDisplayArea root = new SurfacelessDisplayAreaRoot(wms);
+ final TaskDisplayArea taskDisplayAreaWithHome = new TaskDisplayArea(displayContent, wms,
+ "Tasks1", FEATURE_DEFAULT_TASK_CONTAINER);
+ final TaskDisplayArea taskDisplayAreaWithNoHome = new TaskDisplayArea(displayContent, wms,
+ "Tasks2", FEATURE_VENDOR_FIRST + 1, false, false);
+ final DisplayArea.Tokens ime = new DisplayArea.Tokens(wms, ABOVE_TASKS, "Ime");
+ final DisplayAreaPolicy policy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root)
+ .setImeContainer(ime)
+ .setTaskDisplayAreas(Lists.newArrayList(taskDisplayAreaWithHome,
+ taskDisplayAreaWithNoHome))
+ )
+ .build(wms);
+ assertTaskDisplayAreaPresentAndCanHaveHome(policy, FEATURE_DEFAULT_TASK_CONTAINER, true);
+ assertTaskDisplayAreaPresentAndCanHaveHome(policy, FEATURE_VENDOR_FIRST + 1, false);
+ final Task stackHome = taskDisplayAreaWithHome.getOrCreateRootHomeTask(true);
+ final Task stackNoHome = taskDisplayAreaWithNoHome.getOrCreateRootHomeTask(true);
+ assertNotNull(stackHome);
+ assertNull(stackNoHome);
+ }
+
+ private void assertTaskDisplayAreaPresentAndCanHaveHome(DisplayAreaPolicy policy,
+ int featureId,
+ boolean canHaveHome) {
+ Optional<DisplayArea> optionalDisplayArea = policy.mRoot.mChildren
+ .stream().filter(displayArea -> displayArea.mFeatureId == featureId)
+ .findAny();
+ assertTrue(optionalDisplayArea.isPresent());
+ assertEquals(canHaveHome, optionalDisplayArea.get().asTaskDisplayArea().canHostHomeTask());
+ }
+
private void assertTaskDisplayAreasOrder(DisplayAreaPolicy policy,
TaskDisplayArea... expectTdaOrder) {
List<TaskDisplayArea> expectOrder = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 9ac40ca..9b5d352 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1428,7 +1428,8 @@
@Override
public void onTransitionReady(IBinder transitToken, TransitionInfo transitionInfo,
- SurfaceControl.Transaction transaction) throws RemoteException {
+ SurfaceControl.Transaction transaction, SurfaceControl.Transaction finishT)
+ throws RemoteException {
mLastTransit = Transition.fromBinder(transitToken);
mLastReady = transitionInfo;
}
diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java
index 6bf992e..2b4fb7d 100644
--- a/telephony/java/android/telephony/ims/DelegateStateCallback.java
+++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java
@@ -79,10 +79,30 @@
* messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration
* change event to reduce conditions where the remote application is using a stale IMS
* configuration.
+ * @deprecated This is being removed from API surface, Use
+ * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead.
*/
+ @Deprecated
void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration config);
/**
+ * Call to notify the remote application of a configuration change associated with this
+ * {@link SipDelegate}.
+ * <p>
+ * The remote application will not be able to proceed sending SIP messages until after this
+ * configuration is sent the first time, so this configuration should be sent as soon as the
+ * {@link SipDelegate} has access to these configuration parameters.
+ * <p>
+ * Incoming SIP messages should not be routed to the remote application until AFTER this
+ * configuration change is sent to ensure that the remote application can respond correctly.
+ * Similarly, if there is an event that triggers the IMS configuration to change, incoming SIP
+ * messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration
+ * change event to reduce conditions where the remote application is using a stale IMS
+ * configuration.
+ */
+ void onConfigurationChanged(@NonNull SipDelegateConfiguration config);
+
+ /**
* Call to notify the remote application that the {@link SipDelegate} has modified the IMS
* registration state of the RCS feature tags that were requested as part of the initial
* {@link DelegateRequest}.
diff --git a/telephony/java/android/telephony/ims/SipDelegateConfiguration.aidl b/telephony/java/android/telephony/ims/SipDelegateConfiguration.aidl
new file mode 100644
index 0000000..2fc9c48
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable SipDelegateConfiguration;
diff --git a/telephony/java/android/telephony/ims/SipDelegateConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateConfiguration.java
new file mode 100644
index 0000000..1bf5cad
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateConfiguration.java
@@ -0,0 +1,932 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.InetAddresses;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.Objects;
+
+/**
+ * The IMS registration and other attributes that the {@link SipDelegateConnection} used by the
+ * IMS application will need to be aware of to correctly generate outgoing {@link SipMessage}s.
+ * <p>
+ * The IMS service must generate new instances of this configuration as the IMS configuration
+ * managed by the IMS service changes. Along with each {@link SipDelegateConfiguration} instance
+ * containing the configuration is the "version", which should be incremented every time a new
+ * {@link SipDelegateConfiguration} instance is created. The {@link SipDelegateConnection} will
+ * include the version of the {@link SipDelegateConfiguration} instance that it used in order for
+ * the {@link SipDelegate} to easily identify if the IMS application used a now stale configuration
+ * to generate the {@link SipMessage} and return
+ * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION} in
+ * {@link DelegateMessageCallback#onMessageSendFailure(String, int)} so that the IMS application can
+ * regenerate that {@link SipMessage} using the correct {@link SipDelegateConfiguration}
+ * instance.
+ * <p>
+ * Every time the IMS configuration state changes in the IMS service, a full configuration should
+ * be generated. The new {@link SipDelegateConfiguration} instance should not be an incremental
+ * update.
+ * @see Builder
+ * @hide
+ */
+@SystemApi
+public final class SipDelegateConfiguration implements Parcelable {
+
+ /**
+ * The SIP transport uses UDP.
+ */
+ public static final int SIP_TRANSPORT_UDP = 0;
+
+ /**
+ * The SIP transport uses TCP.
+ */
+ public static final int SIP_TRANSPORT_TCP = 1;
+
+ /**@hide*/
+ @IntDef(prefix = "SIP_TRANSPORT_", value = {
+ SIP_TRANSPORT_UDP,
+ SIP_TRANSPORT_TCP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TransportType {}
+
+ /**
+ * The value returned by {@link #getMaxUdpPayloadSizeBytes()} when it is not defined.
+ */
+ public static final int UDP_PAYLOAD_SIZE_UNDEFINED = -1;
+
+ /**
+ * SIP over IPSec configuration
+ */
+ public static final class IpSecConfiguration {
+ private final int mLocalTxPort;
+ private final int mLocalRxPort;
+ private final int mLastLocalTxPort;
+ private final int mRemoteTxPort;
+ private final int mRemoteRxPort;
+ private final int mLastRemoteTxPort;
+ private final String mSecurityHeader;
+
+ /**
+ * Describes the SIP over IPSec configuration the SipDelegate will need to use.
+ *
+ * @param localTxPort Local SIP port number used to send traffic.
+ * @param localRxPort Local SIP port number used to receive traffic.
+ * @param lastLocalTxPort Local SIP port number used for the previous IPsec security
+ * association.
+ * @param remoteTxPort Remote port number used by the SIP server to send SIP traffic.
+ * @param remoteRxPort Remote port number used by the SIP server to receive incoming SIP
+ * traffic.
+ * @param lastRemoteTxPort Remote port number used by the SIP server to send SIP traffic on
+ * the previous IPSec security association.
+ * @param securityHeader The value of the SIP security verify header.
+ */
+ public IpSecConfiguration(int localTxPort, int localRxPort, int lastLocalTxPort,
+ int remoteTxPort, int remoteRxPort, int lastRemoteTxPort,
+ @NonNull String securityHeader) {
+ mLocalTxPort = localTxPort;
+ mLocalRxPort = localRxPort;
+ mLastLocalTxPort = lastLocalTxPort;
+ mRemoteTxPort = remoteTxPort;
+ mRemoteRxPort = remoteRxPort;
+ mLastRemoteTxPort = lastRemoteTxPort;
+ mSecurityHeader = securityHeader;
+ }
+
+ /**
+ * @return The local SIP port number used to send traffic.
+ */
+ public int getLocalTxPort() {
+ return mLocalTxPort;
+ }
+
+ /**
+ * @return The Local SIP port number used to receive traffic.
+ */
+ public int getLocalRxPort() {
+ return mLocalRxPort;
+ }
+
+ /**
+ * @return The last local SIP port number used for the previous IPsec security association.
+ */
+ public int getLastLocalTxPort() {
+ return mLastLocalTxPort;
+ }
+
+ /**
+ * @return The remote port number used by the SIP server to send SIP traffic.
+ */
+ public int getRemoteTxPort() {
+ return mRemoteTxPort;
+ }
+
+ /**
+ * @return the remote port number used by the SIP server to receive incoming SIP traffic.
+ */
+ public int getRemoteRxPort() {
+ return mRemoteRxPort;
+ }
+
+ /**
+ * @return the remote port number used by the SIP server to send SIP traffic on the previous
+ * IPSec security association.
+ */
+ public int getLastRemoteTxPort() {
+ return mLastRemoteTxPort;
+ }
+
+ /**
+ * @return The value of the SIP security verify header.
+ */
+ public @NonNull String getSipSecurityVerifyHeader() {
+ return mSecurityHeader;
+ }
+
+ /**
+ * Helper for parcelling this object.
+ * @hide
+ */
+ public void addToParcel(Parcel dest) {
+ dest.writeInt(mLocalTxPort);
+ dest.writeInt(mLocalRxPort);
+ dest.writeInt(mLastLocalTxPort);
+ dest.writeInt(mRemoteTxPort);
+ dest.writeInt(mRemoteRxPort);
+ dest.writeInt(mLastRemoteTxPort);
+ dest.writeString(mSecurityHeader);
+ }
+
+ /**
+ * Helper for unparcelling this object.
+ * @hide
+ */
+ public static IpSecConfiguration fromParcel(Parcel source) {
+ return new IpSecConfiguration(source.readInt(), source.readInt(), source.readInt(),
+ source.readInt(), source.readInt(), source.readInt(), source.readString());
+ }
+
+ @Override
+ public String toString() {
+ return "IpSecConfiguration{" + "localTx=" + mLocalTxPort + ", localRx=" + mLocalRxPort
+ + ", lastLocalTx=" + mLastLocalTxPort + ", remoteTx=" + mRemoteTxPort
+ + ", remoteRx=" + mRemoteRxPort + ", lastRemoteTx=" + mLastRemoteTxPort
+ + ", securityHeader=" + mSecurityHeader + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ IpSecConfiguration that = (IpSecConfiguration) o;
+ return mLocalTxPort == that.mLocalTxPort
+ && mLocalRxPort == that.mLocalRxPort
+ && mLastLocalTxPort == that.mLastLocalTxPort
+ && mRemoteTxPort == that.mRemoteTxPort
+ && mRemoteRxPort == that.mRemoteRxPort
+ && mLastRemoteTxPort == that.mLastRemoteTxPort
+ && Objects.equals(mSecurityHeader, that.mSecurityHeader);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mLocalTxPort, mLocalRxPort, mLastLocalTxPort, mRemoteTxPort,
+ mRemoteRxPort, mLastRemoteTxPort, mSecurityHeader);
+ }
+ }
+
+ /**
+ * Creates a new instance of {@link SipDelegateConfiguration} composed from optional
+ * configuration items.
+ */
+ public static final class Builder {
+ private final SipDelegateConfiguration mConfig;
+
+ /**
+ *
+ * @param version The version associated with the {@link SipDelegateConfiguration} instance
+ * being built. See {@link #getVersion()} for more information.
+ * @param transportType The transport type to use for SIP signalling.
+ * @param localAddr The local socket address used for SIP traffic.
+ * @param serverAddr The SIP server or P-CSCF default IP address for sip traffic.
+ * @see InetAddresses#parseNumericAddress(String) for how to create an
+ * {@link InetAddress} without requiring a DNS lookup.
+ */
+ public Builder(@IntRange(from = 0) long version, @TransportType int transportType,
+ @NonNull InetSocketAddress localAddr, @NonNull InetSocketAddress serverAddr) {
+ mConfig = new SipDelegateConfiguration(version, transportType, localAddr,
+ serverAddr);
+ }
+
+ /**
+ * Create a new {@link SipDelegateConfiguration} instance with the same exact configuration
+ * as the passed in instance, except for the version parameter, which will be incremented
+ * by 1.
+ * <p>
+ * This method is useful for cases where only a small subset of configurations have changed
+ * and the new configuration is based off of the old configuration.
+ * @param c The older {@link SipDelegateConfiguration} instance to base this instance's
+ * configuration off of.
+ */
+ public Builder(@NonNull SipDelegateConfiguration c) {
+ mConfig = c.copyAndIncrementVersion();
+ }
+
+ /**
+ * Sets whether or not SIP compact form is enabled for the associated SIP delegate.
+ * <p>
+ * If unset, this configuration defaults to {@code false}.
+ * @param isEnabled {@code true} if SIP compact form is enabled for the associated SIP
+ * Delegate, {@code false} if it is not.
+ * @return this Builder instance with the compact form configuration set.
+ */
+ public @NonNull Builder setSipCompactFormEnabled(boolean isEnabled) {
+ mConfig.mIsSipCompactFormEnabled = isEnabled;
+ return this;
+ }
+
+ /**
+ * Sets whether or not underlying SIP keepalives are enabled for the associated SIP
+ * delegate.
+ * <p>
+ * If unset, this configuration defaults to {@code false}.
+ * @param isEnabled {@code true} if SIP keepalives are enabled for the associated SIP
+ * Delegate, {@code false} if it is not.
+ * @return this Builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipKeepaliveEnabled(boolean isEnabled) {
+ mConfig.mIsSipKeepaliveEnabled = isEnabled;
+ return this;
+ }
+
+ /**
+ * Sets the max SIP payload size in bytes to be sent on UDP. If the SIP message payload is
+ * greater than the max UDP payload size, then TCP must be used.
+ * <p>
+ * If unset, this configuration defaults to {@link #UDP_PAYLOAD_SIZE_UNDEFINED}, or no
+ * size specified.
+ * @param size The maximum SIP payload size in bytes for UDP.
+ * @return this Builder instance with the new configuration set.
+ */
+ public @NonNull Builder setMaxUdpPayloadSizeBytes(@IntRange(from = 1) int size) {
+ mConfig.mMaxUdpPayloadSize = size;
+ return this;
+ }
+
+ /**
+ * Sets the IMS public user identifier.
+ * <p>
+ * If unset, this configuration defaults to {@code null}, or no identifier specified.
+ * @param id The IMS public user identifier.
+ * @return this Builder instance with the new configuration set.
+ */
+ public @NonNull Builder setPublicUserIdentifier(@Nullable String id) {
+ mConfig.mPublicUserIdentifier = id;
+ return this;
+ }
+
+ /**
+ * Sets the IMS private user identifier.
+ * <p>
+ * If unset, this configuration defaults to {@code null}, or no identifier specified.
+ * @param id The IMS private user identifier.
+ * @return this Builder instance with the new configuration set.
+ */
+ public @NonNull Builder setPrivateUserIdentifier(@Nullable String id) {
+ mConfig.mPrivateUserIdentifier = id;
+ return this;
+ }
+
+ /**
+ * Sets the IMS home domain.
+ * <p>
+ * If unset, this configuration defaults to {@code null}, or no domain specified.
+ * @param domain The IMS home domain.
+ * @return this Builder instance with the new configuration set.
+ */
+ public @NonNull Builder setHomeDomain(@Nullable String domain) {
+ mConfig.mHomeDomain = domain;
+ return this;
+ }
+
+ /**
+ * Sets the IMEI of the associated device.
+ * <p>
+ * Application can include the Instance-ID feature tag {@code "+sip.instance"} in the
+ * Contact header with a value of the device IMEI in the form
+ * {@code "urn:gsma:imei:<device IMEI>"}.
+ * <p>
+ * If unset, this configuration defaults to {@code null}, or no IMEI string specified.
+ * @param imei The IMEI of the device.
+ * @return this Builder instance with the new configuration set.
+ */
+ public @NonNull Builder setImei(@Nullable String imei) {
+ mConfig.mImei = imei;
+ return this;
+ }
+
+ /**
+ * Set the optional {@link IpSecConfiguration} instance used if the associated SipDelegate
+ * is communicating over IPSec.
+ * <p>
+ * If unset, this configuration defaults to {@code null}
+ * @param c The IpSecConfiguration instance to set.
+ * @return this Builder instance with IPSec configuration set.
+ */
+ public @NonNull Builder setIpSecConfiguration(@Nullable IpSecConfiguration c) {
+ mConfig.mIpSecConfiguration = c;
+ return this;
+ }
+
+ /**
+ * Describes the Device's Public IP Address and port that is set when Network Address
+ * Translation is enabled and the device is behind a NAT.
+ * <p>
+ * If unset, this configuration defaults to {@code null}
+ * @param addr The {@link InetAddress} representing the device's public IP address and port
+ * when behind a NAT.
+ * @return this Builder instance with the new configuration set.
+ * @see InetAddresses#parseNumericAddress(String) For an example of how to create an
+ * instance of {@link InetAddress} without causing a DNS lookup.
+ */
+ public @NonNull Builder setNatSocketAddress(@Nullable InetSocketAddress addr) {
+ mConfig.mNatAddress = addr;
+ return this;
+ }
+
+ /**
+ * Sets the optional URI of the device's Globally routable user-agent URI (GRUU) if this
+ * feature is enabled for the SIP delegate.
+ * <p>
+ * If unset, this configuration defaults to {@code null}
+ * @param uri The GRUU to set.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setPublicGruuUri(@Nullable Uri uri) {
+ mConfig.mGruu = uri;
+ return this;
+ }
+
+ /**
+ * Sets the SIP authentication header value.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP authentication header's value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipAuthenticationHeader(@Nullable String header) {
+ mConfig.mSipAuthHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP authentication nonce.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param nonce The SIP authentication nonce.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipAuthenticationNonce(@Nullable String nonce) {
+ mConfig.mSipAuthNonce = nonce;
+ return this;
+ }
+
+ /**
+ * Sets the SIP service route header value.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP service route header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipServiceRouteHeader(@Nullable String header) {
+ mConfig.mServiceRouteHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP path header value.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP path header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipPathHeader(@Nullable String header) {
+ mConfig.mPathHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP User-Agent header value.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP User-Agent header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipUserAgentHeader(@Nullable String header) {
+ mConfig.mUserAgentHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP Contact header's User parameter value.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param param The SIP Contact header's User parameter value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipContactUserParameter(@Nullable String param) {
+ mConfig.mContactUserParam = param;
+ return this;
+ }
+
+ /**
+ * Sets the SIP P-Access-Network-Info (P-ANI) header value. Populated for networks that
+ * require this information to be provided as part of outgoing SIP messages.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP P-ANI header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipPaniHeader(@Nullable String header) {
+ mConfig.mPaniHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP P-Last-Access-Network-Info (P-LANI) header value. Populated for
+ * networks that require this information to be provided as part of outgoing SIP messages.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP P-LANI header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipPlaniHeader(@Nullable String header) {
+ mConfig.mPlaniHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP Cellular-Network-Info (CNI) header value (See 3GPP 24.229, section 7.2.15),
+ * populated for networks that require this information to be provided as part of outgoing
+ * SIP messages.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP P-LANI header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipCniHeader(@Nullable String header) {
+ mConfig.mCniHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP P-associated-uri header value.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP P-associated-uri header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipAssociatedUriHeader(@Nullable String header) {
+ mConfig.mAssociatedUriHeader = header;
+ return this;
+ }
+
+ /**
+ * @return A {@link SipDelegateConfiguration} instance with the optional configurations set.
+ */
+ public @NonNull SipDelegateConfiguration build() {
+ return mConfig;
+ }
+ }
+
+ private final long mVersion;
+ private final int mTransportType;
+ private final InetSocketAddress mLocalAddress;
+ private final InetSocketAddress mSipServerAddress;
+ private boolean mIsSipCompactFormEnabled = false;
+ private boolean mIsSipKeepaliveEnabled = false;
+ private int mMaxUdpPayloadSize = -1;
+ private String mPublicUserIdentifier = null;
+ private String mPrivateUserIdentifier = null;
+ private String mHomeDomain = null;
+ private String mImei = null;
+ private Uri mGruu = null;
+ private String mSipAuthHeader = null;
+ private String mSipAuthNonce = null;
+ private String mServiceRouteHeader = null;
+ private String mPathHeader = null;
+ private String mUserAgentHeader = null;
+ private String mContactUserParam = null;
+ private String mPaniHeader = null;
+ private String mPlaniHeader = null;
+ private String mCniHeader = null;
+ private String mAssociatedUriHeader = null;
+ private IpSecConfiguration mIpSecConfiguration = null;
+ private InetSocketAddress mNatAddress = null;
+
+
+ private SipDelegateConfiguration(long version, int transportType,
+ InetSocketAddress localAddress, InetSocketAddress sipServerAddress) {
+ mVersion = version;
+ mTransportType = transportType;
+ mLocalAddress = localAddress;
+ mSipServerAddress = sipServerAddress;
+ }
+
+ private SipDelegateConfiguration(Parcel source) {
+ mVersion = source.readLong();
+ mTransportType = source.readInt();
+ mLocalAddress = readAddressFromParcel(source);
+ mSipServerAddress = readAddressFromParcel(source);
+ mIsSipCompactFormEnabled = source.readBoolean();
+ mIsSipKeepaliveEnabled = source.readBoolean();
+ mMaxUdpPayloadSize = source.readInt();
+ mPublicUserIdentifier = source.readString();
+ mPrivateUserIdentifier = source.readString();
+ mHomeDomain = source.readString();
+ mImei = source.readString();
+ mGruu = source.readParcelable(null);
+ mSipAuthHeader = source.readString();
+ mSipAuthNonce = source.readString();
+ mServiceRouteHeader = source.readString();
+ mPathHeader = source.readString();
+ mUserAgentHeader = source.readString();
+ mContactUserParam = source.readString();
+ mPaniHeader = source.readString();
+ mPlaniHeader = source.readString();
+ mCniHeader = source.readString();
+ mAssociatedUriHeader = source.readString();
+ boolean isIpsecConfigAvailable = source.readBoolean();
+ if (isIpsecConfigAvailable) {
+ mIpSecConfiguration = IpSecConfiguration.fromParcel(source);
+ }
+ boolean isNatConfigAvailable = source.readBoolean();
+ if (isNatConfigAvailable) {
+ mNatAddress = readAddressFromParcel(source);
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mVersion);
+ dest.writeInt(mTransportType);
+ writeAddressToParcel(mLocalAddress, dest);
+ writeAddressToParcel(mSipServerAddress, dest);
+ dest.writeBoolean(mIsSipCompactFormEnabled);
+ dest.writeBoolean(mIsSipKeepaliveEnabled);
+ dest.writeInt(mMaxUdpPayloadSize);
+ dest.writeString(mPublicUserIdentifier);
+ dest.writeString(mPrivateUserIdentifier);
+ dest.writeString(mHomeDomain);
+ dest.writeString(mImei);
+ dest.writeParcelable(mGruu, flags);
+ dest.writeString(mSipAuthHeader);
+ dest.writeString(mSipAuthNonce);
+ dest.writeString(mServiceRouteHeader);
+ dest.writeString(mPathHeader);
+ dest.writeString(mUserAgentHeader);
+ dest.writeString(mContactUserParam);
+ dest.writeString(mPaniHeader);
+ dest.writeString(mPlaniHeader);
+ dest.writeString(mCniHeader);
+ dest.writeString(mAssociatedUriHeader);
+ dest.writeBoolean(mIpSecConfiguration != null);
+ if (mIpSecConfiguration != null) {
+ mIpSecConfiguration.addToParcel(dest);
+ }
+ dest.writeBoolean(mNatAddress != null);
+ if (mNatAddress != null) {
+ writeAddressToParcel(mNatAddress, dest);
+ }
+ }
+
+ /**
+ * @return A copy of this instance with an incremented version.
+ * @hide
+ */
+ public SipDelegateConfiguration copyAndIncrementVersion() {
+ SipDelegateConfiguration c = new SipDelegateConfiguration(getVersion() + 1, mTransportType,
+ mLocalAddress, mSipServerAddress);
+ c.mIsSipCompactFormEnabled = mIsSipCompactFormEnabled;
+ c.mIsSipKeepaliveEnabled = mIsSipKeepaliveEnabled;
+ c.mMaxUdpPayloadSize = mMaxUdpPayloadSize;
+ c.mIpSecConfiguration = mIpSecConfiguration;
+ c.mNatAddress = mNatAddress;
+ c.mPublicUserIdentifier = mPublicUserIdentifier;
+ c.mPrivateUserIdentifier = mPrivateUserIdentifier;
+ c.mHomeDomain = mHomeDomain;
+ c.mImei = mImei;
+ c.mGruu = mGruu;
+ c.mSipAuthHeader = mSipAuthHeader;
+ c.mSipAuthNonce = mSipAuthNonce;
+ c.mServiceRouteHeader = mServiceRouteHeader;
+ c.mPathHeader = mPathHeader;
+ c.mUserAgentHeader = mUserAgentHeader;
+ c.mContactUserParam = mContactUserParam;
+ c.mPaniHeader = mPaniHeader;
+ c.mPlaniHeader = mPlaniHeader;
+ c.mCniHeader = mCniHeader;
+ c.mAssociatedUriHeader = mAssociatedUriHeader;
+ return c;
+ }
+
+ /**
+ * An integer representing the version number of this SipDelegateImsConfiguration.
+ * {@link SipMessage}s that are created using this configuration will also have a this
+ * version number associated with them, which will allow the IMS service to validate that the
+ * {@link SipMessage} was using the latest configuration during creation and not a stale
+ * configuration due to race conditions between the configuration being updated and the RCS
+ * application not receiving the updated configuration before generating a new message.
+ * <p>
+ * The version number should be a positive number that starts at 0 and increments sequentially
+ * as new {@link SipDelegateConfiguration} instances are created to update the IMS
+ * configuration state.
+ *
+ * @return the version number associated with this {@link SipDelegateConfiguration}.
+ */
+ public @IntRange(from = 0) long getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * @return The Transport type of the SIP delegate.
+ */
+ public @TransportType int getTransportType() {
+ return mTransportType;
+ }
+
+ /**
+ * @return The local IP address and port used for SIP traffic.
+ */
+ public @NonNull InetSocketAddress getLocalAddress() {
+ return mLocalAddress;
+ }
+
+ /**
+ * @return The default IP Address and port of the SIP server or P-CSCF used for SIP traffic.
+ */
+ public @NonNull InetSocketAddress getSipServerAddress() {
+ return mSipServerAddress;
+ }
+
+ /**
+ * @return {@code true} if SIP compact form is enabled for the associated SIP Delegate,
+ * {@code false} if it is not.
+ */
+ public boolean isSipCompactFormEnabled() {
+ return mIsSipCompactFormEnabled;
+ }
+
+ /**
+ * @return {@code true} if SIP keepalives are enabled for the associated SIP Delegate,
+ * {@code false} if it is not.
+ */
+ public boolean isSipKeepaliveEnabled() {
+ return mIsSipKeepaliveEnabled;
+ }
+
+ /**
+ * @return The maximum SIP payload size in bytes for UDP or {code -1} if no value was set.
+ */
+ public int getMaxUdpPayloadSizeBytes() {
+ return mMaxUdpPayloadSize;
+ }
+
+ /**
+ * @return The IMS public user identifier or {@code null} if it was not set.
+ */
+ public @Nullable String getPublicUserIdentifier() {
+ return mPublicUserIdentifier;
+ }
+
+ /**
+ * @return The IMS private user identifier or {@code null} if it was not set.
+ */
+ public @Nullable String getPrivateUserIdentifier() {
+ return mPrivateUserIdentifier;
+ }
+
+ /**
+ * @return The IMS home domain or {@code null} if it was not set.
+ */
+ public @Nullable String getHomeDomain() {
+ return mHomeDomain;
+ }
+
+ /**
+ * get the IMEI of the associated device.
+ * <p>
+ * Application can include the Instance-ID feature tag {@code "+sip.instance"} in the Contact
+ * header with a value of the device IMEI in the form {@code "urn:gsma:imei:<device IMEI>"}.
+ * @return The IMEI of the device or {@code null} if it was not set.
+ */
+ public @Nullable String getImei() {
+ return mImei;
+ }
+
+ /**
+ * @return The IPSec configuration that must be used because SIP is communicating over IPSec.
+ * This returns {@code null} SIP is not communicating over IPSec.
+ */
+ public @Nullable IpSecConfiguration getIpSecConfiguration() {
+ return mIpSecConfiguration;
+ }
+
+ /**
+ * @return The public IP address and port of the device due to it being behind a NAT.
+ * This returns {@code null} if the device is not behind a NAT.
+ */
+ public @Nullable InetSocketAddress getNatSocketAddress() {
+ return mNatAddress;
+ }
+
+ /**
+ * @return The device's Globally routable user-agent URI (GRUU) or {@code null} if this feature
+ * is not enabled for the SIP delegate.
+ */
+ public @Nullable Uri getPublicGruuUri() {
+ return mGruu;
+ }
+
+ /**
+ * @return The value of the SIP authentication header or {@code null} if there is none set.
+ */
+ public @Nullable String getSipAuthenticationHeader() {
+ return mSipAuthHeader;
+ }
+
+ /**
+ * @return The value of the SIP authentication nonce or {@code null} if there is none set.
+ */
+ public @Nullable String getSipAuthenticationNonce() {
+ return mSipAuthNonce;
+ }
+
+ /**
+ * @return The value of the SIP service route header or {@code null} if there is none set.
+ */
+ public @Nullable String getSipServiceRouteHeader() {
+ return mServiceRouteHeader;
+ }
+
+ /**
+ * @return The value of the SIP path header or {@code null} if there is none set.
+ */
+ public @Nullable String getSipPathHeader() {
+ return mPathHeader;
+ }
+
+ /**
+ * @return The value of the SIP User-Agent header or {@code null} if there is none set.
+ */
+ public @Nullable String getSipUserAgentHeader() {
+ return mUserAgentHeader;
+ }
+ /**
+ * @return The value of the SIP Contact header's User parameter or {@code null} if there is
+ * none set.
+ */
+ public @Nullable String getSipContactUserParameter() {
+ return mContactUserParam;
+ }
+
+ /**
+ * @return The value of the SIP P-Access-Network-Info (P-ANI) header or {@code null} if there is
+ * none set.
+ */
+ public @Nullable String getSipPaniHeader() {
+ return mPaniHeader;
+ }
+ /**
+ * @return The value of the SIP P-Last-Access-Network-Info (P-LANI) header or {@code null} if
+ * there is none set.
+ */
+ public @Nullable String getSipPlaniHeader() {
+ return mPlaniHeader;
+ }
+
+ /**
+ * @return The value of the SIP Cellular-Network-Info (CNI) header or {@code null} if there is
+ * none set.
+ */
+ public @Nullable String getSipCniHeader() {
+ return mCniHeader;
+ }
+
+ /**
+ * @return The value of the SIP P-associated-uri header or {@code null} if there is none set.
+ */
+ public @Nullable String getSipAssociatedUriHeader() {
+ return mAssociatedUriHeader;
+ }
+
+ private void writeAddressToParcel(InetSocketAddress addr, Parcel dest) {
+ dest.writeByteArray(addr.getAddress().getAddress());
+ dest.writeInt(addr.getPort());
+ }
+
+ private InetSocketAddress readAddressFromParcel(Parcel source) {
+ final byte[] addressBytes = source.createByteArray();
+ final int port = source.readInt();
+ try {
+ return new InetSocketAddress(InetAddress.getByAddress(addressBytes), port);
+ } catch (UnknownHostException e) {
+ // Should not happen, as length of array was verified before parcelling.
+ Log.e("SipDelegateConfiguration", "exception reading address, returning null");
+ return null;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<SipDelegateConfiguration> CREATOR =
+ new Creator<SipDelegateConfiguration>() {
+ @Override
+ public SipDelegateConfiguration createFromParcel(Parcel source) {
+ return new SipDelegateConfiguration(source);
+ }
+
+ @Override
+ public SipDelegateConfiguration[] newArray(int size) {
+ return new SipDelegateConfiguration[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SipDelegateConfiguration that = (SipDelegateConfiguration) o;
+ return mVersion == that.mVersion
+ && mTransportType == that.mTransportType
+ && mIsSipCompactFormEnabled == that.mIsSipCompactFormEnabled
+ && mIsSipKeepaliveEnabled == that.mIsSipKeepaliveEnabled
+ && mMaxUdpPayloadSize == that.mMaxUdpPayloadSize
+ && Objects.equals(mLocalAddress, that.mLocalAddress)
+ && Objects.equals(mSipServerAddress, that.mSipServerAddress)
+ && Objects.equals(mPublicUserIdentifier, that.mPublicUserIdentifier)
+ && Objects.equals(mPrivateUserIdentifier, that.mPrivateUserIdentifier)
+ && Objects.equals(mHomeDomain, that.mHomeDomain)
+ && Objects.equals(mImei, that.mImei)
+ && Objects.equals(mGruu, that.mGruu)
+ && Objects.equals(mSipAuthHeader, that.mSipAuthHeader)
+ && Objects.equals(mSipAuthNonce, that.mSipAuthNonce)
+ && Objects.equals(mServiceRouteHeader, that.mServiceRouteHeader)
+ && Objects.equals(mPathHeader, that.mPathHeader)
+ && Objects.equals(mUserAgentHeader, that.mUserAgentHeader)
+ && Objects.equals(mContactUserParam, that.mContactUserParam)
+ && Objects.equals(mPaniHeader, that.mPaniHeader)
+ && Objects.equals(mPlaniHeader, that.mPlaniHeader)
+ && Objects.equals(mCniHeader, that.mCniHeader)
+ && Objects.equals(mAssociatedUriHeader, that.mAssociatedUriHeader)
+ && Objects.equals(mIpSecConfiguration, that.mIpSecConfiguration)
+ && Objects.equals(mNatAddress, that.mNatAddress);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mVersion, mTransportType, mLocalAddress, mSipServerAddress,
+ mIsSipCompactFormEnabled, mIsSipKeepaliveEnabled, mMaxUdpPayloadSize,
+ mPublicUserIdentifier, mPrivateUserIdentifier, mHomeDomain, mImei, mGruu,
+ mSipAuthHeader, mSipAuthNonce, mServiceRouteHeader, mPathHeader, mUserAgentHeader,
+ mContactUserParam, mPaniHeader, mPlaniHeader, mCniHeader, mAssociatedUriHeader,
+ mIpSecConfiguration, mNatAddress);
+ }
+
+ @Override
+ public String toString() {
+ return "SipDelegateConfiguration{ mVersion=" + mVersion + ", mTransportType="
+ + mTransportType + '}';
+ }
+}
diff --git a/telephony/java/android/telephony/ims/SipDelegateConnection.java b/telephony/java/android/telephony/ims/SipDelegateConnection.java
index d7a19bc..4dbb08d 100644
--- a/telephony/java/android/telephony/ims/SipDelegateConnection.java
+++ b/telephony/java/android/telephony/ims/SipDelegateConnection.java
@@ -48,7 +48,7 @@
* sending the message.
* @param sipMessage The SipMessage to be sent.
* @param configVersion The SipDelegateImsConfiguration version used to construct the
- * SipMessage. See {@link SipDelegateImsConfiguration#getVersion} for more
+ * SipMessage. See {@link SipDelegateConfiguration#getVersion} for more
*/
void sendMessage(@NonNull SipMessage sipMessage, long configVersion);
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
index 0d63f7b..ffbfde6 100644
--- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -21,35 +21,21 @@
import android.annotation.StringDef;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.net.InetAddresses;
+import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
-import android.telephony.ims.stub.SipDelegate;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.net.InetSocketAddress;
/**
- * The IMS registration and other attributes that the {@link SipDelegateConnection} used by the
- * IMS application will need to be aware of to correctly generate outgoing {@link SipMessage}s.
- * <p>
- * The IMS service must generate new instances of this configuration as the IMS configuration
- * managed by the IMS service changes. Along with each {@link SipDelegateImsConfiguration} instance
- * containing the configuration is the "version", which should be incremented every time a new
- * {@link SipDelegateImsConfiguration} instance is created. The {@link SipDelegateConnection} will
- * include the version of the {@link SipDelegateImsConfiguration} instance that it used in order for
- * the {@link SipDelegate} to easily identify if the IMS application used a now stale configuration
- * to generate the {@link SipMessage} and return
- * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION} in
- * {@link DelegateMessageCallback#onMessageSendFailure(String, int)} so that the IMS application can
- * regenerate that {@link SipMessage} using the correct {@link SipDelegateImsConfiguration}
- * instance.
- * <p>
- * Every time the IMS configuration state changes in the IMS service, a full configuration should
- * be generated. The new {@link SipDelegateImsConfiguration} instance should not be an incremental
- * update.
* @hide
+ * @deprecated Use {@link SipDelegateConfiguration} instead.
*/
+@Deprecated
@SystemApi
public final class SipDelegateImsConfiguration implements Parcelable {
@@ -535,4 +521,68 @@
return new SipDelegateImsConfiguration[size];
}
};
+
+ /**
+ * Temporary helper to transition from old form of config to new form.
+ * @return new config
+ * @hide
+ */
+ public SipDelegateConfiguration toNewConfig() {
+ // IP version is now included in call to InetSocketAddr
+ String transportTypeString = getString(KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING);
+ int transportType = (transportTypeString != null
+ && transportTypeString.equals(SIP_TRANSPORT_UDP))
+ ? SipDelegateConfiguration.SIP_TRANSPORT_UDP
+ : SipDelegateConfiguration.SIP_TRANSPORT_TCP;
+ SipDelegateConfiguration.Builder builder = new SipDelegateConfiguration.Builder(mVersion,
+ transportType,
+ getSocketAddr(getString(KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING),
+ getInt(KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT, -1)),
+ getSocketAddr(getString(KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING),
+ getInt(KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT, -1)));
+ builder.setSipCompactFormEnabled(
+ getBoolean(KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL, false));
+ builder.setSipKeepaliveEnabled(
+ getBoolean(KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL, false));
+ builder.setMaxUdpPayloadSizeBytes(getInt(KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT, -1));
+ builder.setPublicUserIdentifier(getString(KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING));
+ builder.setPrivateUserIdentifier(getString(KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING));
+ builder.setHomeDomain(getString(KEY_SIP_CONFIG_HOME_DOMAIN_STRING));
+ builder.setImei(getString(KEY_SIP_CONFIG_IMEI_STRING));
+ builder.setSipAuthenticationHeader(getString(KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING));
+ builder.setSipAuthenticationNonce(getString(KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING));
+ builder.setSipServiceRouteHeader(getString(KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING));
+ builder.setSipPathHeader(getString(KEY_SIP_CONFIG_PATH_HEADER_STRING));
+ builder.setSipUserAgentHeader(getString(KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING));
+ builder.setSipContactUserParameter(getString(KEY_SIP_CONFIG_URI_USER_PART_STRING));
+ builder.setSipPaniHeader(getString(KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING));
+ builder.setSipPlaniHeader(
+ getString(KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING));
+ builder.setSipCniHeader(getString(KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING));
+ builder.setSipAssociatedUriHeader(getString(KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING));
+ if (getBoolean(KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, false)) {
+ builder.setPublicGruuUri(Uri.parse(getString(KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING)));
+ }
+ if (getBoolean(KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL, false)) {
+ builder.setIpSecConfiguration(new SipDelegateConfiguration.IpSecConfiguration(
+ getInt(KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT, -1),
+ getInt(KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT, -1),
+ getInt(KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT, -1),
+ getInt(KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT, -1),
+ getInt(KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT, -1),
+ getInt(KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT, -1),
+ getString(KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING))
+ );
+ }
+ if (getBoolean(KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL, false)) {
+ builder.setNatSocketAddress(getSocketAddr(
+ getString(KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING),
+ getInt(KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT, -1)));
+ }
+ return builder.build();
+ }
+
+ private InetSocketAddress getSocketAddr(String ipAddr, int port) {
+ return new InetSocketAddress(InetAddresses.parseNumericAddress(ipAddr), port);
+ }
}
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 77fe75a..ca01d0f 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -126,12 +126,12 @@
public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9;
/**
- * The outgoing SIP message has not been sent because the {@link SipDelegateImsConfiguration}
+ * The outgoing SIP message has not been sent because the {@link SipDelegateConfiguration}
* version associated with the outgoing {@link SipMessage} is now stale and has failed
* validation checks.
* <p>
* The @link SipMessage} should be recreated using the newest
- * {@link SipDelegateImsConfiguration} and sent again.
+ * {@link SipDelegateConfiguration} and sent again.
*/
public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10;
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl
index ddfcb99..855000b 100644
--- a/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl
@@ -18,6 +18,7 @@
import android.telephony.ims.DelegateRegistrationState;
import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
import android.telephony.ims.SipDelegateImsConfiguration;
import android.telephony.ims.aidl.ISipDelegate;
@@ -30,5 +31,6 @@
void onFeatureTagStatusChanged(in DelegateRegistrationState registrationState,
in List<FeatureTagState> deniedFeatureTags);
void onImsConfigurationChanged(in SipDelegateImsConfiguration registeredSipConfig);
+ void onConfigurationChanged(in SipDelegateConfiguration registeredSipConfig);
void onDestroyed(int reason);
}
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl
index 609ee26..4c3c93d 100644
--- a/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl
@@ -18,6 +18,7 @@
import android.telephony.ims.DelegateRegistrationState;
import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
import android.telephony.ims.SipDelegateImsConfiguration;
import android.telephony.ims.aidl.ISipDelegate;
@@ -29,5 +30,6 @@
void onCreated(ISipDelegate c, in List<FeatureTagState> deniedFeatureTags);
void onFeatureTagRegistrationChanged(in DelegateRegistrationState registrationState);
void onImsConfigurationChanged(in SipDelegateImsConfiguration registeredSipConfig);
+ void onConfigurationChanged(in SipDelegateConfiguration registeredSipConfig);
void onDestroyed(int reason);
}
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
index 6a98d80..c18ab33 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -24,6 +24,7 @@
import android.telephony.ims.DelegateRegistrationState;
import android.telephony.ims.DelegateStateCallback;
import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
import android.telephony.ims.SipDelegateImsConfiguration;
import android.telephony.ims.SipDelegateManager;
import android.telephony.ims.SipMessage;
@@ -166,6 +167,15 @@
}
@Override
+ public void onConfigurationChanged(@NonNull SipDelegateConfiguration config) {
+ try {
+ mStateBinder.onConfigurationChanged(config);
+ } catch (RemoteException e) {
+ // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+ }
+ }
+
+ @Override
public void onDestroyed(int reasonCode) {
mDelegate = null;
try {
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
index 0abb495..47ddcb9 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -21,6 +21,7 @@
import android.os.RemoteException;
import android.telephony.ims.DelegateRegistrationState;
import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
import android.telephony.ims.SipDelegateConnection;
import android.telephony.ims.SipDelegateImsConfiguration;
import android.telephony.ims.SipDelegateManager;
@@ -90,6 +91,17 @@
}
@Override
+ public void onConfigurationChanged(SipDelegateConfiguration registeredSipConfig) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mStateCallback.onConfigurationChanged(registeredSipConfig));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void onDestroyed(int reason) {
invalidateSipDelegateBinder();
final long token = Binder.clearCallingIdentity();
diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
index 02218ea..c078637 100644
--- a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
+++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
@@ -21,6 +21,7 @@
import android.telephony.ims.DelegateRegistrationState;
import android.telephony.ims.DelegateRequest;
import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
import android.telephony.ims.SipDelegateConnection;
import android.telephony.ims.SipDelegateImsConfiguration;
import android.telephony.ims.SipDelegateManager;
@@ -44,7 +45,7 @@
* <p>
* In order to start sending SIP messages, the SIP configuration parameters will need to be
* received, so the messaging application should make no assumptions about these parameters and wait
- * until {@link #onImsConfigurationChanged(SipDelegateImsConfiguration)} has been called. This is
+ * until {@link #onConfigurationChanged(SipDelegateConfiguration)} has been called. This is
* guaranteed to happen after the first {@link #onFeatureTagStatusChanged} if there is at least one
* feature tag that has been successfully associated with the {@link SipDelegateConnection}. If all
* feature tags were denied, no IMS configuration will be sent.
@@ -135,8 +136,32 @@
* not compleed yet.
*
* @param registeredSipConfig The configuration of the IMS stack registered on the IMS network.
+ * @deprecated Will not be in final API, use
+ * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead}.
*/
- void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration registeredSipConfig);
+ @Deprecated
+ default void onImsConfigurationChanged(
+ @NonNull SipDelegateImsConfiguration registeredSipConfig) {
+ onConfigurationChanged(registeredSipConfig.toNewConfig());
+ }
+
+ /**
+ * IMS configuration of the underlying IMS stack used by this IMS application for construction
+ * of the SIP messages that will be sent over the carrier's network.
+ * <p>
+ * There should never be assumptions made about the configuration of the underling IMS stack and
+ * the IMS application should wait for this indication before sending out any outgoing SIP
+ * messages.
+ * <p>
+ * Configuration may change due to IMS registration changes as well as
+ * other optional events on the carrier network. If IMS stack is already registered at the time
+ * of callback registration, then this method shall be invoked with the current configuration.
+ * Otherwise, there may be a delay in this method being called if initial IMS registration has
+ * not compleed yet.
+ *
+ * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network.
+ */
+ default void onConfigurationChanged(@NonNull SipDelegateConfiguration registeredSipConfig) {}
/**
* The previously created {@link SipDelegateConnection} instance delivered via
diff --git a/telephony/java/android/telephony/ims/stub/SipDelegate.java b/telephony/java/android/telephony/ims/stub/SipDelegate.java
index d5198a0..997d00b 100644
--- a/telephony/java/android/telephony/ims/stub/SipDelegate.java
+++ b/telephony/java/android/telephony/ims/stub/SipDelegate.java
@@ -21,8 +21,8 @@
import android.telephony.ims.DelegateMessageCallback;
import android.telephony.ims.DelegateRegistrationState;
import android.telephony.ims.ImsService;
+import android.telephony.ims.SipDelegateConfiguration;
import android.telephony.ims.SipDelegateConnection;
-import android.telephony.ims.SipDelegateImsConfiguration;
import android.telephony.ims.SipDelegateManager;
import android.telephony.ims.SipMessage;
@@ -38,7 +38,7 @@
* modified to include the features managed by these SipDelegates.
* <p>
* This SipDelegate will need to notify the remote application of the registration of these features
- * as well as the associated {@link SipDelegateImsConfiguration} before the application can start
+ * as well as the associated {@link SipDelegateConfiguration} before the application can start
* sending/receiving SIP messages via the transport. See
* {@link android.telephony.ims.DelegateStateCallback} for more information.
* @hide
@@ -55,9 +55,9 @@
* {@link DelegateMessageCallback#onMessageSendFailure(String, int)}.
* @param message The SIP message to be sent over the operator’s network.
* @param configVersion The SipDelegateImsConfiguration version used to construct the
- * SipMessage. See {@link SipDelegateImsConfiguration} for more information. If the
+ * SipMessage. See {@link SipDelegateConfiguration} for more information. If the
* version specified here does not match the most recently constructed
- * {@link SipDelegateImsConfiguration}, this message should fail validation checks and
+ * {@link SipDelegateConfiguration}, this message should fail validation checks and
* {@link DelegateMessageCallback#onMessageSendFailure} should be called with code
* {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION}.
*/
diff --git a/tests/net/common/java/android/net/NetworkTest.java b/tests/net/common/java/android/net/NetworkTest.java
index cd9da8e..7423c73 100644
--- a/tests/net/common/java/android/net/NetworkTest.java
+++ b/tests/net/common/java/android/net/NetworkTest.java
@@ -29,6 +29,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import org.junit.Rule;
@@ -158,16 +159,22 @@
assertEquals(16290598925L, three.getNetworkHandle());
}
+ // getNetId() did not exist in Q
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testGetNetId() {
+ assertEquals(1234, new Network(1234).getNetId());
+ assertEquals(2345, new Network(2345, true).getNetId());
+ }
+
@Test
public void testFromNetworkHandle() {
final Network network = new Network(1234);
- assertEquals(network.getNetId(),
- Network.fromNetworkHandle(network.getNetworkHandle()).getNetId());
+ assertEquals(network.netId, Network.fromNetworkHandle(network.getNetworkHandle()).netId);
}
// Parsing private DNS bypassing handle was not supported until S
@Test @IgnoreUpTo(Build.VERSION_CODES.R)
- public void testFromNetworkHandle_S() {
+ public void testFromNetworkHandlePrivateDnsBypass_S() {
final Network network = new Network(1234, true);
final Network recreatedNetwork = Network.fromNetworkHandle(network.getNetworkHandle());
@@ -175,6 +182,16 @@
assertEquals(network.getNetIdForResolv(), recreatedNetwork.getNetIdForResolv());
}
+ @Test @IgnoreAfter(Build.VERSION_CODES.R)
+ public void testFromNetworkHandlePrivateDnsBypass_R() {
+ final Network network = new Network(1234, true);
+
+ final Network recreatedNetwork = Network.fromNetworkHandle(network.getNetworkHandle());
+ assertEquals(network.netId, recreatedNetwork.netId);
+ // Until R included, fromNetworkHandle would not parse the private DNS bypass flag
+ assertEquals(network.netId, recreatedNetwork.getNetIdForResolv());
+ }
+
@Test
public void testGetPrivateDnsBypassingCopy() {
final Network copy = mNetwork.getPrivateDnsBypassingCopy();
diff --git a/tests/net/integration/AndroidManifest.xml b/tests/net/integration/AndroidManifest.xml
index db18500..2e13689 100644
--- a/tests/net/integration/AndroidManifest.xml
+++ b/tests/net/integration/AndroidManifest.xml
@@ -38,6 +38,8 @@
<!-- Reading DeviceConfig flags -->
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
+ <!-- Querying the resources package -->
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<application android:debuggable="true">
<uses-library android:name="android.test.runner"/>
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 2624a1b..1662082 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2974,15 +2974,9 @@
mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
- // BUG: with the legacy int-based scoring code, the network will no longer linger, even
- // though it's validated and outscored.
- // The new policy-based scoring code fixes this.
- // TODO: remove the line below and replace with the three commented lines when
- // the policy-based scoring code is turned on.
- callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
- // callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
- // callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent);
- // callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent);
+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
callback.assertNoCallback();
diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
index 1348c6a..551b94c 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
+++ b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -16,7 +16,9 @@
package com.android.server.connectivity
+import android.net.NetworkCapabilities
import android.net.NetworkRequest
+import android.net.NetworkScore.KEEP_CONNECTED_NONE
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import org.junit.Test
@@ -32,10 +34,14 @@
class NetworkRankerTest {
private val ranker = NetworkRanker()
- private fun makeNai(satisfy: Boolean, score: Int) = mock(NetworkAgentInfo::class.java).also {
- doReturn(satisfy).`when`(it).satisfies(any())
- doReturn(score).`when`(it).currentScore
- }
+ private fun makeNai(satisfy: Boolean, legacyScore: Int) =
+ mock(NetworkAgentInfo::class.java).also {
+ doReturn(satisfy).`when`(it).satisfies(any())
+ val fs = FullScore(legacyScore, 0 /* policies */, KEEP_CONNECTED_NONE)
+ doReturn(fs).`when`(it).getScore()
+ val nc = NetworkCapabilities.Builder().build()
+ doReturn(nc).`when`(it).getCapsNoCopy()
+ }
@Test
fun testGetBestNetwork() {
diff --git a/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java b/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java
deleted file mode 100644
index 43b80e4..0000000
--- a/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.vcn;
-
-import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
-import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
-import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import android.net.ipsec.ike.ChildSaProposal;
-import android.net.ipsec.ike.IkeFqdnIdentification;
-import android.net.ipsec.ike.IkeSaProposal;
-import android.net.ipsec.ike.IkeSessionParams;
-import android.net.ipsec.ike.SaProposal;
-import android.net.ipsec.ike.TunnelModeChildSessionParams;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class VcnControlPlaneIkeConfigTest {
- private static final IkeSessionParams IKE_PARAMS;
- private static final TunnelModeChildSessionParams CHILD_PARAMS;
-
- static {
- IkeSaProposal ikeProposal =
- new IkeSaProposal.Builder()
- .addEncryptionAlgorithm(
- ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128)
- .addDhGroup(DH_GROUP_2048_BIT_MODP)
- .addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC)
- .build();
-
- final String serverHostname = "192.0.2.100";
- final String testLocalId = "test.client.com";
- final String testRemoteId = "test.server.com";
- final byte[] psk = "psk".getBytes();
-
- IKE_PARAMS =
- new IkeSessionParams.Builder()
- .setServerHostname(serverHostname)
- .addSaProposal(ikeProposal)
- .setLocalIdentification(new IkeFqdnIdentification(testLocalId))
- .setRemoteIdentification(new IkeFqdnIdentification(testRemoteId))
- .setAuthPsk(psk)
- .build();
-
- ChildSaProposal childProposal =
- new ChildSaProposal.Builder()
- .addEncryptionAlgorithm(
- ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128)
- .build();
- CHILD_PARAMS =
- new TunnelModeChildSessionParams.Builder().addSaProposal(childProposal).build();
- }
-
- // Package private for use in VcnGatewayConnectionConfigTest
- static VcnControlPlaneIkeConfig buildTestConfig() {
- return new VcnControlPlaneIkeConfig(IKE_PARAMS, CHILD_PARAMS);
- }
-
- @Test
- public void testGetters() {
- final VcnControlPlaneIkeConfig config = buildTestConfig();
- assertEquals(IKE_PARAMS, config.getIkeSessionParams());
- assertEquals(CHILD_PARAMS, config.getChildSessionParams());
- }
-
- @Test
- public void testPersistableBundle() {
- final VcnControlPlaneIkeConfig config = buildTestConfig();
-
- assertEquals(config, new VcnControlPlaneIkeConfig(config.toPersistableBundle()));
- }
-
- @Test
- public void testConstructConfigWithoutIkeParams() {
- try {
- new VcnControlPlaneIkeConfig(null, CHILD_PARAMS);
- fail("Expect to fail because ikeParams was null");
- } catch (NullPointerException expected) {
- }
- }
-
- @Test
- public void testBuilderConfigWithoutChildParams() {
- try {
- new VcnControlPlaneIkeConfig(IKE_PARAMS, null);
- fail("Expect to fail because childParams was null");
- } catch (NullPointerException expected) {
- }
- }
-}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index db00670..0d3fd3f 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -18,11 +18,12 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.net.NetworkCapabilities;
+import android.net.TunnelConnectionParams;
+import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtilsTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -59,8 +60,8 @@
};
public static final int MAX_MTU = 1360;
- public static final VcnControlPlaneConfig CONTROL_PLANE_CONFIG =
- VcnControlPlaneIkeConfigTest.buildTestConfig();
+ public static final TunnelConnectionParams TUNNEL_CONNECTION_PARAMS =
+ TunnelConnectionParamsUtilsTest.buildTestParams();
public static final String GATEWAY_CONNECTION_NAME_PREFIX = "gatewayConnectionName-";
private static int sGatewayConnectionConfigCount = 0;
@@ -75,7 +76,7 @@
// VcnGatewayConnectionConfigs have a unique name (required by VcnConfig).
return new VcnGatewayConnectionConfig.Builder(
GATEWAY_CONNECTION_NAME_PREFIX + sGatewayConnectionConfigCount++,
- CONTROL_PLANE_CONFIG);
+ TUNNEL_CONNECTION_PARAMS);
}
// Public for use in VcnGatewayConnectionTest
@@ -98,7 +99,7 @@
public void testBuilderRequiresNonNullGatewayConnectionName() {
try {
new VcnGatewayConnectionConfig.Builder(
- null /* gatewayConnectionName */, CONTROL_PLANE_CONFIG)
+ null /* gatewayConnectionName */, TUNNEL_CONNECTION_PARAMS)
.build();
fail("Expected exception due to invalid gateway connection name");
@@ -107,13 +108,13 @@
}
@Test
- public void testBuilderRequiresNonNullControlPlaneConfig() {
+ public void testBuilderRequiresNonNullTunnelConnectionParams() {
try {
new VcnGatewayConnectionConfig.Builder(
- GATEWAY_CONNECTION_NAME_PREFIX, null /* ctrlPlaneConfig */)
+ GATEWAY_CONNECTION_NAME_PREFIX, null /* tunnelConnectionParams */)
.build();
- fail("Expected exception due to invalid control plane config");
+ fail("Expected exception due to the absence of tunnel connection parameters");
} catch (NullPointerException e) {
}
}
@@ -171,8 +172,7 @@
Arrays.sort(underlyingCaps);
assertArrayEquals(UNDERLYING_CAPS, underlyingCaps);
- assertEquals(CONTROL_PLANE_CONFIG, config.getControlPlaneConfig());
- assertFalse(CONTROL_PLANE_CONFIG == config.getControlPlaneConfig());
+ assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams());
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs());
assertEquals(MAX_MTU, config.getMaxMtu());
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
index 4226e64..393787f 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
@@ -52,7 +52,8 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class IkeSessionParamsUtilsTest {
- private static IkeSessionParams.Builder createBuilderMinimum() {
+ // Package private for use in EncryptedTunnelParamsUtilsTest
+ static IkeSessionParams.Builder createBuilderMinimum() {
final InetAddress serverAddress = InetAddresses.parseNumericAddress("192.0.2.100");
// TODO: b/185941731 Make sure all valid IKE_OPTIONS are added and validated.
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
new file mode 100644
index 0000000..0c8ad32
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.persistablebundleutils;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TunnelConnectionParamsUtilsTest {
+ // Public for use in VcnGatewayConnectionConfigTest
+ public static IkeTunnelConnectionParams buildTestParams() {
+ return new IkeTunnelConnectionParams(
+ IkeSessionParamsUtilsTest.createBuilderMinimum().build(),
+ TunnelModeChildSessionParamsUtilsTest.createBuilderMinimum().build());
+ }
+
+ @Test
+ public void testIkeTunnelConnectionParamsToFromPersistableBundle() {
+ final IkeTunnelConnectionParams params = buildTestParams();
+
+ assertEquals(
+ params,
+ TunnelConnectionParamsUtils.fromPersistableBundle(
+ TunnelConnectionParamsUtils.toPersistableBundle(params)));
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java
index b3cd0ab..e0b5f0e 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java
@@ -40,7 +40,8 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class TunnelModeChildSessionParamsUtilsTest {
- private TunnelModeChildSessionParams.Builder createBuilderMinimum() {
+ // Package private for use in EncryptedTunnelParamsUtilsTest
+ static TunnelModeChildSessionParams.Builder createBuilderMinimum() {
final ChildSaProposal saProposal = SaProposalUtilsTest.buildTestChildSaProposal();
return new TunnelModeChildSessionParams.Builder().addSaProposal(saProposal);
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 95a9726..530e636 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -51,10 +51,10 @@
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
-import android.net.vcn.VcnControlPlaneIkeConfig;
import android.net.vcn.VcnManager.VcnErrorCode;
import androidx.test.filters.SmallTest;
@@ -181,8 +181,8 @@
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
final List<ChildSaProposal> saProposals =
- ((VcnControlPlaneIkeConfig) mConfig.getControlPlaneConfig())
- .getChildSessionParams()
+ ((IkeTunnelConnectionParams) mConfig.getTunnelConnectionParams())
+ .getTunnelModeChildSessionParams()
.getSaProposals();
final int expectedMtu =
MtuUtils.getMtu(