Merge "Delete legacy fs-verity support"
diff --git a/Android.bp b/Android.bp
index 60ec7e2..ab85a0a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -140,21 +140,8 @@
name: "framework-all",
installable: false,
static_libs: [
- "android.net.ipsec.ike.impl",
+ "all-framework-module-impl",
"framework-minus-apex",
- "framework-appsearch.impl",
- "framework-connectivity.impl",
- "framework-connectivity-tiramisu.impl",
- "framework-graphics.impl",
- "framework-mediaprovider.impl",
- "framework-permission.impl",
- "framework-permission-s.impl",
- "framework-scheduling.impl",
- "framework-sdkextensions.impl",
- "framework-statsd.impl",
- "framework-tethering.impl",
- "framework-wifi.impl",
- "updatable-media",
],
apex_available: ["//apex_available:platform"],
sdk_version: "core_platform",
@@ -409,7 +396,6 @@
// TODO: remove these annotations as soon as we can use andoid.support.annotations.*
":framework-annotations",
":modules-utils-preconditions-srcs",
- "core/java/android/net/DhcpResults.java",
"core/java/android/util/IndentingPrintWriter.java",
"core/java/android/util/LocalLog.java",
"core/java/com/android/internal/util/HexDump.java",
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index d963e68..6195f257e 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -63,8 +63,8 @@
min_sdk_version: "29",
visibility: [
"//frameworks/av/apex:__subpackages__",
- "//frameworks/base", // For framework-all
"//frameworks/base/apex/media/service",
+ "//frameworks/base/api", // For framework-all
],
}
diff --git a/api/api.go b/api/api.go
index 17649e8..5e5f60e 100644
--- a/api/api.go
+++ b/api/api.go
@@ -27,6 +27,7 @@
const art = "art.module.public.api"
const conscrypt = "conscrypt.module.public.api"
const i18n = "i18n.module.public.api"
+var core_libraries_modules = []string{art, conscrypt, i18n}
// The intention behind this soong plugin is to generate a number of "merged"
// API-related modules that would otherwise require a large amount of very
@@ -199,9 +200,28 @@
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createMergedModuleLibStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) {
+ // This module is for the "framework-all" module, which should not include the core libraries.
+ modules = removeAll(modules, core_libraries_modules)
+ // TODO(b/214988855): remove the line below when framework-bluetooth has an impl jar.
+ modules = remove(modules, "framework-bluetooth")
+ props := libraryProps{}
+ props.Name = proptools.StringPtr("all-framework-module-impl")
+ props.Static_libs = transformArray(modules, "", ".impl")
+ // Media module's impl jar is called "updatable-media"
+ for i, v := range props.Static_libs {
+ if v == "framework-media.impl" {
+ props.Static_libs[i] = "updatable-media"
+ }
+ }
+ props.Sdk_version = proptools.StringPtr("module_current")
+ props.Visibility = []string{"//frameworks/base"}
+ ctx.CreateModule(java.LibraryFactory, &props)
+}
+
+func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) {
// The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes.
- modules = removeAll(modules, []string{art, conscrypt, i18n})
+ modules = removeAll(modules, core_libraries_modules)
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
props.Static_libs = transformArray(modules, "", ".stubs.module_lib")
@@ -269,7 +289,8 @@
createMergedPublicStubs(ctx, bootclasspath)
createMergedSystemStubs(ctx, bootclasspath)
- createMergedModuleLibStubs(ctx, bootclasspath)
+ createMergedFrameworkModuleLibStubs(ctx, bootclasspath)
+ createMergedFrameworkImpl(ctx, bootclasspath)
createMergedAnnotations(ctx, bootclasspath)
diff --git a/core/api/current.txt b/core/api/current.txt
index a32a3a9..a5b1d1f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -29675,6 +29675,7 @@
field public static final int Q = 29; // 0x1d
field public static final int R = 30; // 0x1e
field public static final int S = 31; // 0x1f
+ field public static final int S_V2 = 32; // 0x20
field public static final int TIRAMISU = 10000; // 0x2710
}
@@ -30269,6 +30270,9 @@
method @Nullable public byte[] createByteArray();
method @Nullable public char[] createCharArray();
method @Nullable public double[] createDoubleArray();
+ method @Nullable public <T> T createFixedArray(@NonNull Class<T>, @NonNull int...);
+ method @Nullable public <T, S extends android.os.IInterface> T createFixedArray(@NonNull Class<T>, @NonNull java.util.function.Function<android.os.IBinder,S>, @NonNull int...);
+ method @Nullable public <T, S extends android.os.Parcelable> T createFixedArray(@NonNull Class<T>, @NonNull android.os.Parcelable.Creator<S>, @NonNull int...);
method @Nullable public float[] createFloatArray();
method @Nullable public int[] createIntArray();
method @Nullable public <T extends android.os.IInterface> T[] createInterfaceArray(@NonNull java.util.function.IntFunction<T[]>, @NonNull java.util.function.Function<android.os.IBinder,T>);
@@ -30309,6 +30313,9 @@
method public void readException();
method public void readException(int, String);
method public android.os.ParcelFileDescriptor readFileDescriptor();
+ method public <T> void readFixedArray(@NonNull T);
+ method public <T, S extends android.os.IInterface> void readFixedArray(@NonNull T, @NonNull java.util.function.Function<android.os.IBinder,S>);
+ method public <T, S extends android.os.Parcelable> void readFixedArray(@NonNull T, @NonNull android.os.Parcelable.Creator<S>);
method public float readFloat();
method public void readFloatArray(@NonNull float[]);
method @Deprecated @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader);
@@ -30352,6 +30359,7 @@
method public void setDataCapacity(int);
method public void setDataPosition(int);
method public void setDataSize(int);
+ method public void setPropagateAllowBlocking();
method public void unmarshall(@NonNull byte[], int, int);
method public void writeArray(@Nullable Object[]);
method public void writeBinderArray(@Nullable android.os.IBinder[]);
@@ -30367,6 +30375,7 @@
method public void writeDoubleArray(@Nullable double[]);
method public void writeException(@NonNull Exception);
method public void writeFileDescriptor(@NonNull java.io.FileDescriptor);
+ method public <T> void writeFixedArray(@Nullable T, int, @NonNull int...);
method public void writeFloat(float);
method public void writeFloatArray(@Nullable float[]);
method public void writeInt(int);
@@ -36183,6 +36192,28 @@
method @Deprecated @NonNull public android.security.KeyPairGeneratorSpec.Builder setSubject(@NonNull javax.security.auth.x500.X500Principal);
}
+ public class KeyStoreException extends java.lang.Exception {
+ method public int getNumericErrorCode();
+ method public boolean isSystemError();
+ method public boolean isTransientFailure();
+ method public boolean requiresUserAuthentication();
+ field public static final int ERROR_ATTESTATION_CHALLENGE_TOO_LARGE = 9; // 0x9
+ field public static final int ERROR_ID_ATTESTATION_FAILURE = 8; // 0x8
+ field public static final int ERROR_INCORRECT_USAGE = 13; // 0xd
+ field public static final int ERROR_INTERNAL_SYSTEM_ERROR = 4; // 0x4
+ field public static final int ERROR_KEYMINT_FAILURE = 10; // 0xa
+ field public static final int ERROR_KEYSTORE_FAILURE = 11; // 0xb
+ field public static final int ERROR_KEYSTORE_UNINITIALIZED = 3; // 0x3
+ field public static final int ERROR_KEY_CORRUPTED = 7; // 0x7
+ field public static final int ERROR_KEY_DOES_NOT_EXIST = 6; // 0x6
+ field public static final int ERROR_KEY_NOT_TEMPORALLY_VALID = 14; // 0xe
+ field public static final int ERROR_KEY_OPERATION_EXPIRED = 15; // 0xf
+ field public static final int ERROR_OTHER = 1; // 0x1
+ field public static final int ERROR_PERMISSION_DENIED = 5; // 0x5
+ field public static final int ERROR_UNIMPLEMENTED = 12; // 0xc
+ field public static final int ERROR_USER_AUTHENTICATION_REQUIRED = 2; // 0x2
+ }
+
@Deprecated public final class KeyStoreParameter implements java.security.KeyStore.ProtectionParameter {
method @Deprecated public boolean isEncryptionRequired();
}
@@ -39818,6 +39849,7 @@
field public static final String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
field public static final String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
field public static final String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL = "is_ims_conference_size_enforced_bool";
+ field public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL = "is_opportunistic_subscription_bool";
field public static final String KEY_LTE_ENABLED_BOOL = "lte_enabled_bool";
field public static final String KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY = "lte_rsrq_thresholds_int_array";
field public static final String KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY = "lte_rssnr_thresholds_int_array";
@@ -39902,6 +39934,7 @@
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final String KEY_SMDP_SERVER_ADDRESS_STRING = "smdp_server_address_string";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
+ field public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING = "subscription_group_uuid_string";
field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = "supports_device_to_device_communication_using_dtmf_bool";
field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL = "supports_device_to_device_communication_using_rtp_bool";
@@ -39945,6 +39978,7 @@
field public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING = "wfc_emergency_address_carrier_app_string";
field public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
+ field public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000";
field public static final int SERVICE_CLASS_NONE = 0; // 0x0
field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
field public static final int USSD_OVER_CS_ONLY = 2; // 0x2
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 9e26908..1e4e787 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -140,7 +140,7 @@
method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
method @NonNull public java.util.List<android.bluetooth.BluetoothCodecConfig> getHwOffloadFormatsSupportedForA2dp();
method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio();
- method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BtProfileConnectionInfo);
+ method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BluetoothProfileConnectionInfo);
method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setA2dpSuspended(boolean);
method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean);
method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpEnabled(boolean);
@@ -150,18 +150,18 @@
field public static final int FLAG_FROM_KEY = 4096; // 0x1000
}
- public final class BtProfileConnectionInfo implements android.os.Parcelable {
- method @NonNull public static android.media.BtProfileConnectionInfo a2dpInfo(boolean, int);
- method @NonNull public static android.media.BtProfileConnectionInfo a2dpSinkInfo(int);
+ public final class BluetoothProfileConnectionInfo implements android.os.Parcelable {
+ method @NonNull public static android.media.BluetoothProfileConnectionInfo createA2dpInfo(boolean, int);
+ method @NonNull public static android.media.BluetoothProfileConnectionInfo createA2dpSinkInfo(int);
+ method @NonNull public static android.media.BluetoothProfileConnectionInfo createHearingAidInfo(boolean);
+ method @NonNull public static android.media.BluetoothProfileConnectionInfo createLeAudioInfo(boolean, boolean);
method public int describeContents();
- method public boolean getIsLeOutput();
method public int getProfile();
- method public boolean getSuppressNoisyIntent();
method public int getVolume();
- method @NonNull public static android.media.BtProfileConnectionInfo hearingAidInfo(boolean);
- method @NonNull public static android.media.BtProfileConnectionInfo leAudio(boolean, boolean);
+ method public boolean isLeOutput();
+ method public boolean isSuppressNoisyIntent();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.media.BtProfileConnectionInfo> CREATOR;
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.BluetoothProfileConnectionInfo> CREATOR;
}
public class MediaMetadataRetriever implements java.lang.AutoCloseable {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7c8868e..4480e04 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -145,6 +145,7 @@
field public static final String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
field public static final String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS";
field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
+ field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS";
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
@@ -2954,6 +2955,7 @@
method public void sendKeyEvent(int, boolean);
method public void sendVendorCommand(int, byte[], boolean);
method public void setVendorCommandListener(@NonNull android.hardware.hdmi.HdmiControlManager.VendorCommandListener);
+ method public void setVendorCommandListener(@NonNull android.hardware.hdmi.HdmiControlManager.VendorCommandListener, int);
}
public final class HdmiControlManager {
@@ -7021,7 +7023,10 @@
package android.net {
public class EthernetManager {
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void connectNetwork(@NonNull String, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.BiConsumer<android.net.Network,android.net.EthernetNetworkManagementException>);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void disconnectNetwork(@NonNull String, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.BiConsumer<android.net.Network,android.net.EthernetNetworkManagementException>);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void updateConfiguration(@NonNull String, @NonNull android.net.EthernetNetworkUpdateRequest, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.BiConsumer<android.net.Network,android.net.EthernetNetworkManagementException>);
}
public static interface EthernetManager.TetheredInterfaceCallback {
@@ -7033,6 +7038,22 @@
method public void release();
}
+ public final class EthernetNetworkManagementException extends java.lang.RuntimeException implements android.os.Parcelable {
+ ctor public EthernetNetworkManagementException(@NonNull String);
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkManagementException> CREATOR;
+ }
+
+ public final class EthernetNetworkUpdateRequest implements android.os.Parcelable {
+ ctor public EthernetNetworkUpdateRequest(@NonNull android.net.StaticIpConfiguration, @NonNull android.net.NetworkCapabilities);
+ method public int describeContents();
+ method @NonNull public android.net.StaticIpConfiguration getIpConfig();
+ method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkUpdateRequest> CREATOR;
+ }
+
public final class IpSecManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
@@ -7829,6 +7850,7 @@
}
public static class Build.VERSION {
+ field @NonNull public static final java.util.Set<java.lang.String> KNOWN_CODENAMES;
field @NonNull public static final String PREVIEW_SDK_FINGERPRINT;
}
@@ -13187,6 +13209,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsClientConfiguration> CREATOR;
field public static final String RCS_PROFILE_1_0 = "UP_1.0";
field public static final String RCS_PROFILE_2_3 = "UP_2.3";
+ field public static final String RCS_PROFILE_2_4 = "UP_2.4";
}
public final class RcsContactPresenceTuple implements android.os.Parcelable {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 18ceb25..b531f81 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1707,8 +1707,11 @@
public final class Parcel {
method public boolean allowSquashing();
+ method public int getFlags();
method public int readExceptionCode();
method public void restoreAllowSquashing(boolean);
+ field public static final int FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT = 1; // 0x1
+ field public static final int FLAG_PROPAGATE_ALLOW_BLOCKING = 2; // 0x2
}
public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -2193,8 +2196,8 @@
}
public class KeyStoreException extends java.lang.Exception {
- ctor public KeyStoreException(int, String);
method public int getErrorCode();
+ method public static boolean hasFailureInfoForError(int);
}
}
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 72a432e..7787963 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -149,7 +149,6 @@
filegroup {
name: "framework-services-net-module-wifi-shared-srcs",
srcs: [
- "android/net/DhcpResults.java",
"android/util/LocalLog.java",
],
}
diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS
index 4143bfc..ce5cf67 100644
--- a/core/java/android/hardware/OWNERS
+++ b/core/java/android/hardware/OWNERS
@@ -1,3 +1,9 @@
+# Generic
+etalvala@google.com
+jreck@google.com
+michaelwr@google.com
+sumir@google.com
+
# Camera
per-file *Camera*=cychen@google.com,epeev@google.com,etalvala@google.com,shuzhenwang@google.com,zhijunhe@google.com,jchowdhary@google.com
diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java
index 0c21746..066de11 100644
--- a/core/java/android/hardware/hdmi/HdmiClient.java
+++ b/core/java/android/hardware/hdmi/HdmiClient.java
@@ -17,6 +17,8 @@
public abstract class HdmiClient {
private static final String TAG = "HdmiClient";
+ private static final int UNKNOWN_VENDOR_ID = 0xFFFFFF;
+
/* package */ final IHdmiControlService mService;
private IHdmiVendorCommandListener mIHdmiVendorCommandListener;
@@ -94,11 +96,25 @@
}
/**
- * Sets a listener used to receive incoming vendor-specific command.
+ * Sets a listener used to receive incoming vendor-specific command. This listener will only
+ * receive {@code <Vendor Command>} but will not receive any {@code <Vendor Command with ID>}
+ * messages.
*
* @param listener listener object
*/
public void setVendorCommandListener(@NonNull VendorCommandListener listener) {
+ // Set the vendor ID to INVALID_VENDOR_ID.
+ setVendorCommandListener(listener, UNKNOWN_VENDOR_ID);
+ }
+
+ /**
+ * Sets a listener used to receive incoming vendor-specific command.
+ *
+ * @param listener listener object
+ * @param vendorId The listener is interested in {@code <Vendor Command with ID>} received with
+ * this vendorId and all {@code <Vendor Command>} messages.
+ */
+ public void setVendorCommandListener(@NonNull VendorCommandListener listener, int vendorId) {
if (listener == null) {
throw new IllegalArgumentException("listener cannot be null");
}
@@ -107,7 +123,7 @@
}
try {
IHdmiVendorCommandListener wrappedListener = getListenerWrapper(listener);
- mService.addVendorCommandListener(wrappedListener, getDeviceType());
+ mService.addVendorCommandListener(wrappedListener, vendorId);
mIHdmiVendorCommandListener = wrappedListener;
} catch (RemoteException e) {
Log.e(TAG, "failed to set vendor command listener: ", e);
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
index 9a9e945..45b0689 100644
--- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -221,8 +221,8 @@
}
@Override
- public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
- HdmiControlServiceWrapper.this.addVendorCommandListener(listener, deviceType);
+ public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
+ HdmiControlServiceWrapper.this.addVendorCommandListener(listener, vendorId);
}
@Override
@@ -471,7 +471,7 @@
boolean hasVendorId) {}
/** @hide */
- public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {}
+ public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {}
/** @hide */
public void sendStandby(int deviceType, int deviceId) {}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 7f0e53e..48177e1 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -76,7 +76,7 @@
void askRemoteDeviceToBecomeActiveSource(int physicalAddress);
void sendVendorCommand(int deviceType, int targetAddress, in byte[] params,
boolean hasVendorId);
- void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType);
+ void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId);
void sendStandby(int deviceType, int deviceId);
void setHdmiRecordListener(IHdmiRecordListener callback);
void startOneTouchRecord(int recorderAddress, in byte[] recordSource);
diff --git a/core/java/android/net/DhcpResults.aidl b/core/java/android/net/DhcpResults.aidl
deleted file mode 100644
index f4db3c3..0000000
--- a/core/java/android/net/DhcpResults.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2012, 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;
-
-parcelable DhcpResults;
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
deleted file mode 100644
index 82ba156..0000000
--- a/core/java/android/net/DhcpResults.java
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Copyright (C) 2012 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;
-
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.net.module.util.InetAddressUtils;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * A simple object for retrieving the results of a DHCP request.
- * Optimized (attempted) for that jni interface
- * TODO: remove this class and replace with other existing constructs
- * @hide
- */
-public final class DhcpResults implements Parcelable {
- private static final String TAG = "DhcpResults";
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public LinkAddress ipAddress;
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public InetAddress gateway;
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public final ArrayList<InetAddress> dnsServers = new ArrayList<>();
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public String domains;
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public Inet4Address serverAddress;
-
- /** Vendor specific information (from RFC 2132). */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public String vendorInfo;
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public int leaseDuration;
-
- /** Link MTU option. 0 means unset. */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public int mtu;
-
- public String serverHostName;
-
- @Nullable
- public String captivePortalApiUrl;
-
- public DhcpResults() {
- super();
- }
-
- /**
- * Create a {@link StaticIpConfiguration} based on the DhcpResults.
- */
- public StaticIpConfiguration toStaticIpConfiguration() {
- return new StaticIpConfiguration.Builder()
- .setIpAddress(ipAddress)
- .setGateway(gateway)
- .setDnsServers(dnsServers)
- .setDomains(domains)
- .build();
- }
-
- public DhcpResults(StaticIpConfiguration source) {
- if (source != null) {
- ipAddress = source.getIpAddress();
- gateway = source.getGateway();
- dnsServers.addAll(source.getDnsServers());
- domains = source.getDomains();
- }
- }
-
- /** copy constructor */
- public DhcpResults(DhcpResults source) {
- this(source == null ? null : source.toStaticIpConfiguration());
- if (source != null) {
- serverAddress = source.serverAddress;
- vendorInfo = source.vendorInfo;
- leaseDuration = source.leaseDuration;
- mtu = source.mtu;
- serverHostName = source.serverHostName;
- captivePortalApiUrl = source.captivePortalApiUrl;
- }
- }
-
- /**
- * @see StaticIpConfiguration#getRoutes(String)
- * @hide
- */
- public List<RouteInfo> getRoutes(String iface) {
- return toStaticIpConfiguration().getRoutes(iface);
- }
-
- /**
- * Test if this DHCP lease includes vendor hint that network link is
- * metered, and sensitive to heavy data transfers.
- */
- public boolean hasMeteredHint() {
- if (vendorInfo != null) {
- return vendorInfo.contains("ANDROID_METERED");
- } else {
- return false;
- }
- }
-
- public void clear() {
- ipAddress = null;
- gateway = null;
- dnsServers.clear();
- domains = null;
- serverAddress = null;
- vendorInfo = null;
- leaseDuration = 0;
- mtu = 0;
- serverHostName = null;
- captivePortalApiUrl = null;
- }
-
- @Override
- public String toString() {
- StringBuffer str = new StringBuffer(super.toString());
-
- str.append(" DHCP server ").append(serverAddress);
- str.append(" Vendor info ").append(vendorInfo);
- str.append(" lease ").append(leaseDuration).append(" seconds");
- if (mtu != 0) str.append(" MTU ").append(mtu);
- str.append(" Servername ").append(serverHostName);
- if (captivePortalApiUrl != null) {
- str.append(" CaptivePortalApiUrl ").append(captivePortalApiUrl);
- }
-
- return str.toString();
- }
-
- @Override
- public boolean equals(@Nullable Object obj) {
- if (this == obj) return true;
-
- if (!(obj instanceof DhcpResults)) return false;
-
- DhcpResults target = (DhcpResults)obj;
-
- return toStaticIpConfiguration().equals(target.toStaticIpConfiguration())
- && Objects.equals(serverAddress, target.serverAddress)
- && Objects.equals(vendorInfo, target.vendorInfo)
- && Objects.equals(serverHostName, target.serverHostName)
- && leaseDuration == target.leaseDuration
- && mtu == target.mtu
- && Objects.equals(captivePortalApiUrl, target.captivePortalApiUrl);
- }
-
- /**
- * Implement the Parcelable interface
- */
- public static final @android.annotation.NonNull Creator<DhcpResults> CREATOR =
- new Creator<DhcpResults>() {
- public DhcpResults createFromParcel(Parcel in) {
- return readFromParcel(in);
- }
-
- public DhcpResults[] newArray(int size) {
- return new DhcpResults[size];
- }
- };
-
- /** Implement the Parcelable interface */
- public void writeToParcel(Parcel dest, int flags) {
- toStaticIpConfiguration().writeToParcel(dest, flags);
- dest.writeInt(leaseDuration);
- dest.writeInt(mtu);
- InetAddressUtils.parcelInetAddress(dest, serverAddress, flags);
- dest.writeString(vendorInfo);
- dest.writeString(serverHostName);
- dest.writeString(captivePortalApiUrl);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- private static DhcpResults readFromParcel(Parcel in) {
- final StaticIpConfiguration s = StaticIpConfiguration.CREATOR.createFromParcel(in);
- final DhcpResults dhcpResults = new DhcpResults(s);
- dhcpResults.leaseDuration = in.readInt();
- dhcpResults.mtu = in.readInt();
- dhcpResults.serverAddress = (Inet4Address) InetAddressUtils.unparcelInetAddress(in);
- dhcpResults.vendorInfo = in.readString();
- dhcpResults.serverHostName = in.readString();
- dhcpResults.captivePortalApiUrl = in.readString();
- return dhcpResults;
- }
-
- // Utils for jni population - false on success
- // Not part of the superclass because they're only used by the JNI iterface to the DHCP daemon.
- public boolean setIpAddress(String addrString, int prefixLength) {
- try {
- Inet4Address addr = (Inet4Address) InetAddresses.parseNumericAddress(addrString);
- ipAddress = new LinkAddress(addr, prefixLength);
- } catch (IllegalArgumentException|ClassCastException e) {
- Log.e(TAG, "setIpAddress failed with addrString " + addrString + "/" + prefixLength);
- return true;
- }
- return false;
- }
-
- public boolean setGateway(String addrString) {
- try {
- gateway = InetAddresses.parseNumericAddress(addrString);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "setGateway failed with addrString " + addrString);
- return true;
- }
- return false;
- }
-
- public boolean addDns(String addrString) {
- if (TextUtils.isEmpty(addrString) == false) {
- try {
- dnsServers.add(InetAddresses.parseNumericAddress(addrString));
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "addDns failed with addrString " + addrString);
- return true;
- }
- }
- return false;
- }
-
- public LinkAddress getIpAddress() {
- return ipAddress;
- }
-
- public void setIpAddress(LinkAddress ipAddress) {
- this.ipAddress = ipAddress;
- }
-
- public InetAddress getGateway() {
- return gateway;
- }
-
- public void setGateway(InetAddress gateway) {
- this.gateway = gateway;
- }
-
- public List<InetAddress> getDnsServers() {
- return dnsServers;
- }
-
- /**
- * Add a DNS server to this configuration.
- */
- public void addDnsServer(InetAddress server) {
- dnsServers.add(server);
- }
-
- public String getDomains() {
- return domains;
- }
-
- public void setDomains(String domains) {
- this.domains = domains;
- }
-
- public Inet4Address getServerAddress() {
- return serverAddress;
- }
-
- public void setServerAddress(Inet4Address addr) {
- serverAddress = addr;
- }
-
- public int getLeaseDuration() {
- return leaseDuration;
- }
-
- public void setLeaseDuration(int duration) {
- leaseDuration = duration;
- }
-
- public String getVendorInfo() {
- return vendorInfo;
- }
-
- public void setVendorInfo(String info) {
- vendorInfo = info;
- }
-
- public int getMtu() {
- return mtu;
- }
-
- public void setMtu(int mtu) {
- this.mtu = mtu;
- }
-
- public String getCaptivePortalApiUrl() {
- return captivePortalApiUrl;
- }
-
- public void setCaptivePortalApiUrl(String url) {
- captivePortalApiUrl = url;
- }
-}
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 2a2cbb9..71c63ff 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -516,12 +516,15 @@
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
- if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)
+ boolean warnOnBlocking = mWarnOnBlocking; // Cache it to reduce volatile access.
+
+ if (warnOnBlocking && ((flags & FLAG_ONEWAY) == 0)
&& Binder.sWarnOnBlockingOnCurrentThread.get()) {
// For now, avoid spamming the log by disabling after we've logged
// about this interface at least once
mWarnOnBlocking = false;
+ warnOnBlocking = false;
if (Build.IS_USERDEBUG) {
// Log this as a WTF on userdebug builds.
@@ -568,7 +571,13 @@
}
try {
- return transactNative(code, data, reply, flags);
+ final boolean result = transactNative(code, data, reply, flags);
+
+ if (reply != null && !warnOnBlocking) {
+ reply.addFlags(Parcel.FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT);
+ }
+
+ return result;
} finally {
AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index e519ff3..f46f452 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -31,6 +31,7 @@
import android.sysprop.SocProperties;
import android.sysprop.TelephonyProperties;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Slog;
import android.view.View;
@@ -39,6 +40,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -396,6 +398,17 @@
*/
public static final String CODENAME = getString("ro.build.version.codename");
+ /**
+ * All known codenames starting from {@link VERSION_CODES.Q}.
+ *
+ * <p>This includes in development codenames as well.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull public static final Set<String> KNOWN_CODENAMES =
+ new ArraySet<>(new String[]{"Q", "R", "S", "Sv2", "Tiramisu"});
+
private static final String[] ALL_CODENAMES
= getStringList("ro.build.version.all_codenames", ",");
@@ -1024,7 +1037,7 @@
* will also enable {@link StrictMode.ThreadPolicy.Builder#detectUnbufferedIo}.</li>
* <li>{@link android.provider.DocumentsContract}'s various methods will throw failure
* exceptions back to the caller instead of returning null.
- * <li>{@link View#hasFocusable View.hasFocusable} now includes auto-focusable views.</li>
+ * <li>{@link View#hasFocusable() View.hasFocusable} now includes auto-focusable views.</li>
* <li>{@link android.view.SurfaceView} will no longer always change the underlying
* Surface object when something about it changes; apps need to look at the current
* state of the object to determine which things they are interested in have changed.</li>
@@ -1132,6 +1145,14 @@
public static final int S = 31;
/**
+ * S V2.
+ *
+ * Once more unto the breach, dear friends, once more.
+ *
+ */
+ public static final int S_V2 = 32;
+
+ /**
* Tiramisu.
*/
public static final int TIRAMISU = CUR_DEVELOPMENT;
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 09eac79..13d1d96 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -18,6 +18,7 @@
import static java.util.Objects.requireNonNull;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -53,6 +54,8 @@
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -229,6 +232,25 @@
private RuntimeException mStack;
+ /** @hide */
+ @TestApi
+ public static final int FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT = 1 << 0;
+
+ /** @hide */
+ @TestApi
+ public static final int FLAG_PROPAGATE_ALLOW_BLOCKING = 1 << 1;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT,
+ FLAG_PROPAGATE_ALLOW_BLOCKING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ParcelFlags {}
+
+ @ParcelFlags
+ private int mFlags;
+
/**
* Whether or not to parcel the stack trace of an exception. This has a performance
* impact, so should only be included in specific processes and only on debug builds.
@@ -585,6 +607,40 @@
nativeMarkForBinder(mNativePtr, binder);
}
+ /** @hide */
+ @ParcelFlags
+ @TestApi
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /** @hide */
+ public void setFlags(@ParcelFlags int flags) {
+ mFlags = flags;
+ }
+
+ /** @hide */
+ public void addFlags(@ParcelFlags int flags) {
+ mFlags |= flags;
+ }
+
+ /** @hide */
+ private boolean hasFlags(@ParcelFlags int flags) {
+ return (mFlags & flags) == flags;
+ }
+
+ /**
+ * This method is used by the AIDL compiler for system components. Not intended to be
+ * used by non-system apps.
+ */
+ // Note: Ideally this method should be @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES),
+ // but we need to make this method public due to the way the aidl compiler is compiled.
+ // We don't really need to protect it; even if 3p / non-system apps, nothing would happen.
+ // This would only work when used on a reply parcel by a binder object that's allowed-blocking.
+ public void setPropagateAllowBlocking() {
+ addFlags(FLAG_PROPAGATE_ALLOW_BLOCKING);
+ }
+
/**
* Returns the total amount of data contained in the parcel.
*/
@@ -2079,6 +2135,102 @@
}
/**
+ * Flatten a homogeneous multi-dimensional array with fixed-size. This delegates to other
+ * APIs to write a one-dimensional array. Use {@link #readFixedArray(Object)} or
+ * {@link #createFixedArray(Class, int[])} with the same dimensions to unmarshal.
+ *
+ * @param val The array to be written.
+ * @param parcelableFlags Contextual flags as per
+ * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+ * Used only if val is an array of Parcelable objects.
+ * @param dimensions an array of int representing length of each dimension. The array should be
+ * sized with the exact size of dimensions.
+ *
+ * @see #readFixedArray
+ * @see #createFixedArray
+ * @see #writeBooleanArray
+ * @see #writeByteArray
+ * @see #writeCharArray
+ * @see #writeIntArray
+ * @see #writeLongArray
+ * @see #writeFloatArray
+ * @see #writeDoubleArray
+ * @see #writeBinderArray
+ * @see #writeInterfaceArray
+ * @see #writeTypedArray
+ * @throws BadParcelableException If the array's component type is not supported or if its
+ * size doesn't match with the given dimensions.
+ */
+ public <T> void writeFixedArray(@Nullable T val, int parcelableFlags,
+ @NonNull int... dimensions) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+ writeFixedArrayInternal(val, parcelableFlags, /*index=*/0, dimensions);
+ }
+
+ private <T> void writeFixedArrayInternal(T val, int parcelableFlags, int index,
+ int[] dimensions) {
+ if (index >= dimensions.length) {
+ throw new BadParcelableException("Array has more dimensions than expected: "
+ + dimensions.length);
+ }
+
+ int length = dimensions[index];
+
+ // val should be an array of length N
+ if (val == null) {
+ throw new BadParcelableException("Non-null array shouldn't have a null array.");
+ }
+ if (!val.getClass().isArray()) {
+ throw new BadParcelableException("Not an array: " + val);
+ }
+ if (Array.getLength(val) != length) {
+ throw new BadParcelableException("bad length: expected " + length + ", but got "
+ + Array.getLength(val));
+ }
+
+ // Delegates to other writers if this is a one-dimensional array.
+ // Otherwise, write component arrays with recursive calls.
+
+ final Class<?> componentType = val.getClass().getComponentType();
+ if (!componentType.isArray() && index + 1 != dimensions.length) {
+ throw new BadParcelableException("Array has fewer dimensions than expected: "
+ + dimensions.length);
+ }
+ if (componentType == boolean.class) {
+ writeBooleanArray((boolean[]) val);
+ } else if (componentType == byte.class) {
+ writeByteArray((byte[]) val);
+ } else if (componentType == char.class) {
+ writeCharArray((char[]) val);
+ } else if (componentType == int.class) {
+ writeIntArray((int[]) val);
+ } else if (componentType == long.class) {
+ writeLongArray((long[]) val);
+ } else if (componentType == float.class) {
+ writeFloatArray((float[]) val);
+ } else if (componentType == double.class) {
+ writeDoubleArray((double[]) val);
+ } else if (componentType == IBinder.class) {
+ writeBinderArray((IBinder[]) val);
+ } else if (IInterface.class.isAssignableFrom(componentType)) {
+ writeInterfaceArray((IInterface[]) val);
+ } else if (Parcelable.class.isAssignableFrom(componentType)) {
+ writeTypedArray((Parcelable[]) val, parcelableFlags);
+ } else if (componentType.isArray()) {
+ writeInt(length);
+ for (int i = 0; i < length; i++) {
+ writeFixedArrayInternal(Array.get(val, i), parcelableFlags, index + 1,
+ dimensions);
+ }
+ } else {
+ throw new BadParcelableException("unknown type for fixed-size array: " + componentType);
+ }
+ }
+
+ /**
* Flatten a generic object in to a parcel. The given Object value may
* currently be one of the following types:
*
@@ -2940,7 +3092,15 @@
* Read an object from the parcel at the current dataPosition().
*/
public final IBinder readStrongBinder() {
- return nativeReadStrongBinder(mNativePtr);
+ final IBinder result = nativeReadStrongBinder(mNativePtr);
+
+ // If it's a reply from a method with @PropagateAllowBlocking, then inherit allow-blocking
+ // from the object that returned it.
+ if (result != null && hasFlags(
+ FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT | FLAG_PROPAGATE_ALLOW_BLOCKING)) {
+ Binder.allowBlocking(result);
+ }
+ return result;
}
/**
@@ -3781,6 +3941,317 @@
}
/**
+ * Read a new multi-dimensional array from a parcel. If you want to read Parcelable or
+ * IInterface values, use {@link #readFixedArray(Object, Parcelable.Creator)} or
+ * {@link #readFixedArray(Object, Function)}.
+ * @param val the destination array to hold the read values.
+ *
+ * @see #writeTypedArray
+ * @see #readBooleanArray
+ * @see #readByteArray
+ * @see #readCharArray
+ * @see #readIntArray
+ * @see #readLongArray
+ * @see #readFloatArray
+ * @see #readDoubleArray
+ * @see #readBinderArray
+ * @see #readInterfaceArray
+ * @see #readTypedArray
+ */
+ public <T> void readFixedArray(@NonNull T val) {
+ Class<?> componentType = val.getClass().getComponentType();
+ if (componentType == boolean.class) {
+ readBooleanArray((boolean[]) val);
+ } else if (componentType == byte.class) {
+ readByteArray((byte[]) val);
+ } else if (componentType == char.class) {
+ readCharArray((char[]) val);
+ } else if (componentType == int.class) {
+ readIntArray((int[]) val);
+ } else if (componentType == long.class) {
+ readLongArray((long[]) val);
+ } else if (componentType == float.class) {
+ readFloatArray((float[]) val);
+ } else if (componentType == double.class) {
+ readDoubleArray((double[]) val);
+ } else if (componentType == IBinder.class) {
+ readBinderArray((IBinder[]) val);
+ } else if (componentType.isArray()) {
+ int length = readInt();
+ if (length != Array.getLength(val)) {
+ throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+ + ", but got " + length);
+ }
+ for (int i = 0; i < length; i++) {
+ readFixedArray(Array.get(val, i));
+ }
+ } else {
+ throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+ }
+ }
+
+ /**
+ * Read a new multi-dimensional array of typed interfaces from a parcel.
+ * If you want to read Parcelable values, use
+ * {@link #readFixedArray(Object, Parcelable.Creator)}. For values of other types, use
+ * {@link #readFixedArray(Object)}.
+ * @param val the destination array to hold the read values.
+ */
+ public <T, S extends IInterface> void readFixedArray(@NonNull T val,
+ @NonNull Function<IBinder, S> asInterface) {
+ Class<?> componentType = val.getClass().getComponentType();
+ if (IInterface.class.isAssignableFrom(componentType)) {
+ readInterfaceArray((S[]) val, asInterface);
+ } else if (componentType.isArray()) {
+ int length = readInt();
+ if (length != Array.getLength(val)) {
+ throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+ + ", but got " + length);
+ }
+ for (int i = 0; i < length; i++) {
+ readFixedArray(Array.get(val, i), asInterface);
+ }
+ } else {
+ throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+ }
+ }
+
+ /**
+ * Read a new multi-dimensional array of typed parcelables from a parcel.
+ * If you want to read IInterface values, use
+ * {@link #readFixedArray(Object, Function)}. For values of other types, use
+ * {@link #readFixedArray(Object)}.
+ * @param val the destination array to hold the read values.
+ */
+ public <T, S extends Parcelable> void readFixedArray(@NonNull T val,
+ @NonNull Parcelable.Creator<S> c) {
+ Class<?> componentType = val.getClass().getComponentType();
+ if (Parcelable.class.isAssignableFrom(componentType)) {
+ readTypedArray((S[]) val, c);
+ } else if (componentType.isArray()) {
+ int length = readInt();
+ if (length != Array.getLength(val)) {
+ throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+ + ", but got " + length);
+ }
+ for (int i = 0; i < length; i++) {
+ readFixedArray(Array.get(val, i), c);
+ }
+ } else {
+ throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+ }
+ }
+
+ private void ensureClassHasExpectedDimensions(@NonNull Class<?> cls, int numDimension) {
+ if (numDimension <= 0) {
+ throw new BadParcelableException("Fixed-size array should have dimensions.");
+ }
+
+ for (int i = 0; i < numDimension; i++) {
+ if (!cls.isArray()) {
+ throw new BadParcelableException("Array has fewer dimensions than expected: "
+ + numDimension);
+ }
+ cls = cls.getComponentType();
+ }
+ if (cls.isArray()) {
+ throw new BadParcelableException("Array has more dimensions than expected: "
+ + numDimension);
+ }
+ }
+
+ /**
+ * Read and return a new multi-dimensional array from a parcel. Returns null if the
+ * previously written array object is null. If you want to read Parcelable or
+ * IInterface values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])} or
+ * {@link #createFixedArray(Class, Function, int[])}.
+ * @param cls the Class object for the target array type. (e.g. int[][].class)
+ * @param dimensions an array of int representing length of each dimension.
+ *
+ * @see #writeTypedArray
+ * @see #createBooleanArray
+ * @see #createByteArray
+ * @see #createCharArray
+ * @see #createIntArray
+ * @see #createLongArray
+ * @see #createFloatArray
+ * @see #createDoubleArray
+ * @see #createBinderArray
+ * @see #createInterfaceArray
+ * @see #createTypedArray
+ */
+ @Nullable
+ public <T> T createFixedArray(@NonNull Class<T> cls, @NonNull int... dimensions) {
+ // Check if type matches with dimensions
+ // If type is one-dimensional array, delegate to other creators
+ // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
+
+ ensureClassHasExpectedDimensions(cls, dimensions.length);
+
+ T val = null;
+ final Class<?> componentType = cls.getComponentType();
+ if (componentType == boolean.class) {
+ val = (T) createBooleanArray();
+ } else if (componentType == byte.class) {
+ val = (T) createByteArray();
+ } else if (componentType == char.class) {
+ val = (T) createCharArray();
+ } else if (componentType == int.class) {
+ val = (T) createIntArray();
+ } else if (componentType == long.class) {
+ val = (T) createLongArray();
+ } else if (componentType == float.class) {
+ val = (T) createFloatArray();
+ } else if (componentType == double.class) {
+ val = (T) createDoubleArray();
+ } else if (componentType == IBinder.class) {
+ val = (T) createBinderArray();
+ } else if (componentType.isArray()) {
+ int length = readInt();
+ if (length < 0) {
+ return null;
+ }
+ if (length != dimensions[0]) {
+ throw new BadParcelableException("Bad length: expected " + dimensions[0]
+ + ", but got " + length);
+ }
+
+ // Create a multi-dimensional array with an innermost component type and dimensions
+ Class<?> innermost = componentType.getComponentType();
+ while (innermost.isArray()) {
+ innermost = innermost.getComponentType();
+ }
+ val = (T) Array.newInstance(innermost, dimensions);
+ for (int i = 0; i < length; i++) {
+ readFixedArray(Array.get(val, i));
+ }
+ return val;
+ } else {
+ throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+ }
+
+ // Check if val is null (which is OK) or has the expected size.
+ // This check doesn't have to be multi-dimensional because multi-dimensional arrays
+ // are created with expected dimensions.
+ if (val != null && Array.getLength(val) != dimensions[0]) {
+ throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+ + Array.getLength(val));
+ }
+ return val;
+ }
+
+ /**
+ * Read and return a new multi-dimensional array of typed interfaces from a parcel.
+ * Returns null if the previously written array object is null. If you want to read
+ * Parcelable values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])}.
+ * For values of other types use {@link #createFixedArray(Class, int[])}.
+ * @param cls the Class object for the target array type. (e.g. IFoo[][].class)
+ * @param dimensions an array of int representing length of each dimension.
+ */
+ @Nullable
+ public <T, S extends IInterface> T createFixedArray(@NonNull Class<T> cls,
+ @NonNull Function<IBinder, S> asInterface, @NonNull int... dimensions) {
+ // Check if type matches with dimensions
+ // If type is one-dimensional array, delegate to other creators
+ // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
+
+ ensureClassHasExpectedDimensions(cls, dimensions.length);
+
+ T val = null;
+ final Class<?> componentType = cls.getComponentType();
+ if (IInterface.class.isAssignableFrom(componentType)) {
+ val = (T) createInterfaceArray(n -> (S[]) Array.newInstance(componentType, n),
+ asInterface);
+ } else if (componentType.isArray()) {
+ int length = readInt();
+ if (length < 0) {
+ return null;
+ }
+ if (length != dimensions[0]) {
+ throw new BadParcelableException("Bad length: expected " + dimensions[0]
+ + ", but got " + length);
+ }
+
+ // Create a multi-dimensional array with an innermost component type and dimensions
+ Class<?> innermost = componentType.getComponentType();
+ while (innermost.isArray()) {
+ innermost = innermost.getComponentType();
+ }
+ val = (T) Array.newInstance(innermost, dimensions);
+ for (int i = 0; i < length; i++) {
+ readFixedArray(Array.get(val, i), asInterface);
+ }
+ return val;
+ } else {
+ throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+ }
+
+ // Check if val is null (which is OK) or has the expected size.
+ // This check doesn't have to be multi-dimensional because multi-dimensional arrays
+ // are created with expected dimensions.
+ if (val != null && Array.getLength(val) != dimensions[0]) {
+ throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+ + Array.getLength(val));
+ }
+ return val;
+ }
+
+ /**
+ * Read and return a new multi-dimensional array of typed parcelables from a parcel.
+ * Returns null if the previously written array object is null. If you want to read
+ * IInterface values, use {@link #createFixedArray(Class, Function, int[])}.
+ * For values of other types use {@link #createFixedArray(Class, int[])}.
+ * @param cls the Class object for the target array type. (e.g. Foo[][].class)
+ * @param dimensions an array of int representing length of each dimension.
+ */
+ @Nullable
+ public <T, S extends Parcelable> T createFixedArray(@NonNull Class<T> cls,
+ @NonNull Parcelable.Creator<S> c, @NonNull int... dimensions) {
+ // Check if type matches with dimensions
+ // If type is one-dimensional array, delegate to other creators
+ // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
+
+ ensureClassHasExpectedDimensions(cls, dimensions.length);
+
+ T val = null;
+ final Class<?> componentType = cls.getComponentType();
+ if (Parcelable.class.isAssignableFrom(componentType)) {
+ val = (T) createTypedArray(c);
+ } else if (componentType.isArray()) {
+ int length = readInt();
+ if (length < 0) {
+ return null;
+ }
+ if (length != dimensions[0]) {
+ throw new BadParcelableException("Bad length: expected " + dimensions[0]
+ + ", but got " + length);
+ }
+
+ // Create a multi-dimensional array with an innermost component type and dimensions
+ Class<?> innermost = componentType.getComponentType();
+ while (innermost.isArray()) {
+ innermost = innermost.getComponentType();
+ }
+ val = (T) Array.newInstance(innermost, dimensions);
+ for (int i = 0; i < length; i++) {
+ readFixedArray(Array.get(val, i), c);
+ }
+ return val;
+ } else {
+ throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+ }
+
+ // Check if val is null (which is OK) or has the expected size.
+ // This check doesn't have to be multi-dimensional because multi-dimensional arrays
+ // are created with expected dimensions.
+ if (val != null && Array.getLength(val) != dimensions[0]) {
+ throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+ + Array.getLength(val));
+ }
+ return val;
+ }
+
+ /**
* Write a heterogeneous array of Parcelable objects into the Parcel.
* Each object in the array is written along with its class name, so
* that the correct class can later be instantiated. As a result, this
@@ -4581,6 +5052,7 @@
}
private void freeBuffer() {
+ mFlags = 0;
resetSqaushingState();
if (mOwnsNativeParcelObject) {
nativeFreeBuffer(mNativePtr);
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 3b4f7e2..f900558 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -317,16 +317,35 @@
ErrorCode.MISSING_MIN_MAC_LENGTH; // -58;
public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH =
ErrorCode.UNSUPPORTED_MIN_MAC_LENGTH; // -59;
+ public static final int KM_ERROR_UNSUPPORTED_KDF = ErrorCode.UNSUPPORTED_KDF; // -60
+ public static final int KM_ERROR_UNSUPPORTED_EC_CURVE = ErrorCode.UNSUPPORTED_EC_CURVE; // -61
+ // -62 is KEY_REQUIRES_UPGRADE and is handled by Keystore.
+ public static final int KM_ERROR_ATTESTATION_CHALLENGE_MISSING =
+ ErrorCode.ATTESTATION_CHALLENGE_MISSING; // -63
+ public static final int KM_ERROR_KEYMINT_NOT_CONFIGURED =
+ ErrorCode.KEYMINT_NOT_CONFIGURED; // -64
+ public static final int KM_ERROR_ATTESTATION_APPLICATION_ID_MISSING =
+ ErrorCode.ATTESTATION_APPLICATION_ID_MISSING; // -65;
public static final int KM_ERROR_CANNOT_ATTEST_IDS =
ErrorCode.CANNOT_ATTEST_IDS; // -66;
+ public static final int KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE =
+ ErrorCode.ROLLBACK_RESISTANCE_UNAVAILABLE; // -67;
public static final int KM_ERROR_HARDWARE_TYPE_UNAVAILABLE =
ErrorCode.HARDWARE_TYPE_UNAVAILABLE; // -68;
public static final int KM_ERROR_DEVICE_LOCKED =
ErrorCode.DEVICE_LOCKED; // -72;
+ public static final int KM_ERROR_STORAGE_KEY_UNSUPPORTED =
+ ErrorCode.STORAGE_KEY_UNSUPPORTED; // -77,
+ public static final int KM_ERROR_INCOMPATIBLE_MGF_DIGEST =
+ ErrorCode.INCOMPATIBLE_MGF_DIGEST; // -78,
+ public static final int KM_ERROR_UNSUPPORTED_MGF_DIGEST =
+ ErrorCode.UNSUPPORTED_MGF_DIGEST; // -79,
public static final int KM_ERROR_MISSING_NOT_BEFORE =
ErrorCode.MISSING_NOT_BEFORE; // -80;
public static final int KM_ERROR_MISSING_NOT_AFTER =
ErrorCode.MISSING_NOT_AFTER; // -80;
+ public static final int KM_ERROR_HARDWARE_NOT_YET_AVAILABLE =
+ ErrorCode.HARDWARE_NOT_YET_AVAILABLE; // -85
public static final int KM_ERROR_UNIMPLEMENTED =
ErrorCode.UNIMPLEMENTED; // -100;
public static final int KM_ERROR_VERSION_MISMATCH =
diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS
index 5425c21..c199b96 100644
--- a/core/java/android/util/OWNERS
+++ b/core/java/android/util/OWNERS
@@ -1,6 +1,5 @@
per-file FeatureFlagUtils.java = sbasi@google.com
per-file FeatureFlagUtils.java = tmfang@google.com
-per-file FeatureFlagUtils.java = asapperstein@google.com
per-file AttributeSet.java = file:/core/java/android/content/res/OWNERS
per-file TypedValue.java = file:/core/java/android/content/res/OWNERS
diff --git a/core/java/com/android/internal/util/dump/OWNERS b/core/java/com/android/internal/util/dump/OWNERS
new file mode 100644
index 0000000..ce9302a
--- /dev/null
+++ b/core/java/com/android/internal/util/dump/OWNERS
@@ -0,0 +1,2 @@
+omakoto@google.com
+felipeal@google.com
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 61b91dd..13ca133 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -873,7 +873,7 @@
const char* exceptionToThrow;
char msg[128];
// TransactionTooLargeException is a checked exception, only throw from certain methods.
- // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
+ // TODO(b/28321379): Transaction size is the most common cause for FAILED_TRANSACTION
// but it is not the only one. The Binder driver can return BR_FAILED_REPLY
// for other reasons also, such as if the transaction is malformed or
// refers to an FD that has been closed. We should change the driver
@@ -890,8 +890,9 @@
exceptionToThrow = (canThrowRemoteException)
? "android/os/DeadObjectException"
: "java/lang/RuntimeException";
- snprintf(msg, sizeof(msg)-1,
- "Transaction failed on small parcel; remote process probably died");
+ snprintf(msg, sizeof(msg) - 1,
+ "Transaction failed on small parcel; remote process probably died, but "
+ "this could also be caused by running out of binder buffer space");
}
jniThrowException(env, exceptionToThrow, msg);
} break;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index aacf700..5efc4db 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -202,6 +202,8 @@
*/
static constexpr int STORAGE_DIR_CHECK_TIMEOUT_US = 1000 * 1000 * 60 * 5;
+static void WaitUntilDirReady(const std::string& target, fail_fn_t fail_fn);
+
/**
* A helper class containing accounting information for USAPs.
*/
@@ -1249,7 +1251,11 @@
auto volPath = StringPrintf("%s/%s", externalPrivateMountPath, ent->d_name);
auto cePath = StringPrintf("%s/user", volPath.c_str());
auto dePath = StringPrintf("%s/user_de", volPath.c_str());
+ // Wait until dir user is created.
+ WaitUntilDirReady(cePath.c_str(), fail_fn);
MountAppDataTmpFs(cePath.c_str(), fail_fn);
+ // Wait until dir user_de is created.
+ WaitUntilDirReady(dePath.c_str(), fail_fn);
MountAppDataTmpFs(dePath.c_str(), fail_fn);
}
closedir(dir);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7f95ca9..111b694 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1945,6 +1945,11 @@
<permission android:name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE"
android:protectionLevel="signature" />
+ <!-- @SystemApi @hide Allows an application to manage ethernet networks.
+ <p>Not for use by third-party or privileged applications. -->
+ <permission android:name="android.permission.MANAGE_ETHERNET_NETWORKS"
+ android:protectionLevel="signature" />
+
<!-- ======================================= -->
<!-- Permissions for short range, peripheral networks -->
<!-- ======================================= -->
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 597ea14..12e52c6 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -288,9 +288,8 @@
}
@Override
- public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
- final int deviceType) {
- }
+ public void addVendorCommandListener(
+ final IHdmiVendorCommandListener listener, final int vendorId) {}
@Override
public void sendVendorCommand(final int deviceType, final int targetAddress,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index fa6b086..fcd1799 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -393,6 +393,7 @@
<!-- Permission required for CTS test - TrustTestCases -->
<permission name="android.permission.PROVIDE_TRUST_AGENT" />
<permission name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+ <permission name="android.permission.TRUST_LISTENER" />
<!-- Permissions required for Incremental CTS tests -->
<permission name="com.android.permission.USE_INSTALLER_V2"/>
<permission name="android.permission.LOADER_USAGE_STATS"/>
@@ -461,6 +462,7 @@
<!-- Permission needed for CTS test - WifiManagerTest -->
<permission name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" />
<permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
+ <permission name="android.permission.NEARBY_WIFI_DEVICES" />
<permission name="android.permission.OVERRIDE_WIFI_CONFIG" />
<!-- Permission required for CTS test CarrierMessagingServiceWrapperTest -->
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index cdf746f..f440b69 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -454,7 +454,8 @@
* @param challenge is a non-empty byte array whose contents should be unique, fresh and
* provided by the issuing authority. The value provided is embedded in the
* generated CBOR and enables the issuing authority to verify that the
- * returned proof is fresh.
+ * returned proof is fresh. Implementations are required to support
+ * challenges at least 32 bytes of length.
* @return the COSE_Sign1 data structure above
*/
public @NonNull byte[] proveOwnership(@NonNull byte[] challenge) {
@@ -485,7 +486,8 @@
* @param challenge is a non-empty byte array whose contents should be unique, fresh and
* provided by the issuing authority. The value provided is embedded in the
* generated CBOR and enables the issuing authority to verify that the
- * returned proof is fresh.
+ * returned proof is fresh. Implementations are required to support
+ * challenges at least 32 bytes of length.
* @return the COSE_Sign1 data structure above
*/
public @NonNull byte[] delete(@NonNull byte[] challenge) {
diff --git a/identity/java/android/security/identity/WritableIdentityCredential.java b/identity/java/android/security/identity/WritableIdentityCredential.java
index 305d0ea..6d56964 100644
--- a/identity/java/android/security/identity/WritableIdentityCredential.java
+++ b/identity/java/android/security/identity/WritableIdentityCredential.java
@@ -59,7 +59,8 @@
* @param challenge is a non-empty byte array whose contents should be unique, fresh and
* provided by the issuing authority. The value provided is embedded in the
* attestation extension and enables the issuing authority to verify that the
- * attestation certificate is fresh.
+ * attestation certificate is fresh. Implementations are required to support
+ * challenges at least 32 bytes of length.
* @return the X.509 certificate for this credential's CredentialKey.
*/
public abstract @NonNull Collection<X509Certificate> getCredentialKeyCertificateChain(
diff --git a/keystore/java/android/security/KeyStoreException.java b/keystore/java/android/security/KeyStoreException.java
index 30389a29d..6db2745 100644
--- a/keystore/java/android/security/KeyStoreException.java
+++ b/keystore/java/android/security/KeyStoreException.java
@@ -16,25 +16,448 @@
package android.security;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.security.keymaster.KeymasterDefs;
+import android.system.keystore2.ResponseCode;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
/**
- * KeyStore/keymaster exception with positive error codes coming from the KeyStore and negative
- * ones from keymaster.
+ * Exception containing information about the failure at the Keystore / KeyMint layer while
+ * generating or using a key.
*
- * @hide
+ * The public error codes indicate the cause of the error and the methods indicate whether
+ * it's a system/key issue and whether re-trying the operation (with the same key or a new key)
+ * is likely to succeed.
*/
-@TestApi
public class KeyStoreException extends Exception {
+ /**
+ * This error code is for mapping errors that the caller will not know about. If the caller is
+ * targeting an API level earlier than the one the error was introduced in, then the error will
+ * be mapped to this one.
+ * In API level 33 no errors map to this error.
+ */
+ public static final int ERROR_OTHER = 1;
+ /**
+ * Indicating the key could not be used because the user needs to authenticate first.
+ * See
+ * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserAuthenticationRequired(boolean)}.
+ */
+ public static final int ERROR_USER_AUTHENTICATION_REQUIRED = 2;
+ /**
+ * Indicating that {@code load()} has not been called on the Keystore instance, or an attempt
+ * has been made to generate an authorization bound key while the user has not set a lock
+ * screen knowledge factor (LSKF). Instruct the user to set an LSKF and retry.
+ */
+ public static final int ERROR_KEYSTORE_UNINITIALIZED = 3;
+ /**
+ * An internal system error - refer to {@link #isTransientFailure()} to determine whether
+ * re-trying the operation is likely to yield different results.
+ */
+ public static final int ERROR_INTERNAL_SYSTEM_ERROR = 4;
+ /**
+ * The caller has requested key parameters or operation which are only available to system
+ * or privileged apps.
+ */
+ public static final int ERROR_PERMISSION_DENIED = 5;
+ /**
+ * The key the operation refers to doesn't exist.
+ */
+ public static final int ERROR_KEY_DOES_NOT_EXIST = 6;
+ /**
+ * The key is corrupted and could not be recovered.
+ */
+ public static final int ERROR_KEY_CORRUPTED = 7;
+ /**
+ * The error related to inclusion of device identifiers in the attestation record.
+ */
+ public static final int ERROR_ID_ATTESTATION_FAILURE = 8;
+ /**
+ * The attestation challenge specified is too large.
+ */
+ public static final int ERROR_ATTESTATION_CHALLENGE_TOO_LARGE = 9;
+ /**
+ * General error in the KeyMint layer.
+ */
+ public static final int ERROR_KEYMINT_FAILURE = 10;
+ /**
+ * Failure in the Keystore layer.
+ */
+ public static final int ERROR_KEYSTORE_FAILURE = 11;
+ /**
+ * The feature the caller is trying to use is not implemented by the underlying
+ * KeyMint implementation.
+ * This could happen when an unsupported algorithm is requested, or when trying to import
+ * a key in a format other than raw or PKCS#8.
+ */
+ public static final int ERROR_UNIMPLEMENTED = 12;
+ /**
+ * The feature the caller is trying to use is not compatible with the parameters used to
+ * generate the key. For example, trying to use a key generated for a different signature
+ * algorithm, or a digest not specified during key creation.
+ * Another case is the attempt to generate a symmetric AES key and requesting key attestation.
+ */
+ public static final int ERROR_INCORRECT_USAGE = 13;
+ /**
+ * The key is not currently valid: Either at has expired or it will be valid for use in the
+ * future.
+ */
+ public static final int ERROR_KEY_NOT_TEMPORALLY_VALID = 14;
+ /**
+ * The crypto object the caller has been using held a reference to a KeyMint operation that
+ * has been evacuated (likely due to other concurrent operations taking place).
+ * The caller should re-create the crypto object and try again.
+ */
+ public static final int ERROR_KEY_OPERATION_EXPIRED = 15;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = {"ERROR_"}, value = {
+ ERROR_OTHER,
+ ERROR_USER_AUTHENTICATION_REQUIRED,
+ ERROR_KEYSTORE_UNINITIALIZED,
+ ERROR_INTERNAL_SYSTEM_ERROR,
+ ERROR_PERMISSION_DENIED,
+ ERROR_KEY_DOES_NOT_EXIST,
+ ERROR_KEY_CORRUPTED,
+ ERROR_ID_ATTESTATION_FAILURE,
+ ERROR_ATTESTATION_CHALLENGE_TOO_LARGE,
+ ERROR_KEYMINT_FAILURE,
+ ERROR_KEYSTORE_FAILURE,
+ ERROR_UNIMPLEMENTED,
+ ERROR_INCORRECT_USAGE,
+ ERROR_KEY_NOT_TEMPORALLY_VALID,
+ ERROR_KEY_OPERATION_EXPIRED
+ })
+ public @interface PublicErrorCode {
+ }
+
+ // Constants for encoding information about the error encountered:
+ // Whether the error relates to the system state/implementation as a whole, or a specific key.
+ private static final int IS_SYSTEM_ERROR = 1 << 1;
+ // Whether the error is permanent.
+ private static final int IS_TRANSIENT_ERROR = 1 << 2;
+ // Whether the cause of the error is the user not having authenticated recently.
+ private static final int REQUIRES_USER_AUTHENTICATION = 1 << 3;
+
+ // The internal error code. NOT to be returned directly to callers or made part of the
+ // public API.
private final int mErrorCode;
- public KeyStoreException(int errorCode, String message) {
+ /**
+ * @hide
+ */
+ public KeyStoreException(int errorCode, @Nullable String message) {
super(message);
mErrorCode = errorCode;
}
+ /**
+ * Returns the internal error code. Only for use by the platform.
+ *
+ * @hide
+ */
+ @TestApi
public int getErrorCode() {
return mErrorCode;
}
+
+ /**
+ * Returns one of the error codes exported by the class.
+ *
+ * @return a public error code, one of the values in {@link PublicErrorCode}.
+ */
+ @PublicErrorCode
+ public int getNumericErrorCode() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return failureInfo.errorCode;
+ }
+
+ /**
+ * Returns true if the failure is a transient failure - that is, performing the same operation
+ * again at a late time is likely to succeed.
+ *
+ * If {@link #isSystemError()} returns true, the transient nature of the failure relates to the
+ * device, otherwise relates to the key (so a permanent failure with an existing key likely
+ * requires creating another key to repeat the operation with).
+ */
+ public boolean isTransientFailure() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return (failureInfo.indicators & IS_TRANSIENT_ERROR) != 0;
+ }
+
+ /**
+ * Indicates whether the failure is due to the device being locked.
+ *
+ * @return true if the key operation failed because the user has to authenticate
+ * (e.g. by unlocking the device).
+ */
+ public boolean requiresUserAuthentication() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return (failureInfo.indicators & REQUIRES_USER_AUTHENTICATION) != 0;
+ }
+
+ /**
+ * Indicates whether the error related to the Keystore/KeyMint implementation and not
+ * a specific key.
+ *
+ * @return true if the error is related to the system, not the key in use. System
+ * errors indicate a feature isn't working, whereas key-related errors are likely
+ * to succeed with a new key.
+ */
+ public boolean isSystemError() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return (failureInfo.indicators & IS_SYSTEM_ERROR) != 0;
+ }
+
+ @Override
+ public String toString() {
+ String errorCodes = String.format(" (public error code: %d internal Keystore code: %d)",
+ getNumericErrorCode(), mErrorCode);
+ return super.toString() + errorCodes;
+ }
+
+ private static PublicErrorInformation getErrorInformation(int internalErrorCode) {
+ PublicErrorInformation errorInfo = sErrorCodeToFailureInfo.get(internalErrorCode);
+ if (errorInfo != null) {
+ return errorInfo;
+ }
+
+ /**
+ * KeyStore/keymaster exception with positive error codes coming from the KeyStore and
+ * negative ones from keymaster.
+ * This is a safety fall-back: All error codes should be present in the map.
+ */
+ if (internalErrorCode > 0) {
+ return GENERAL_KEYSTORE_ERROR;
+ } else {
+ return GENERAL_KEYMINT_ERROR;
+ }
+ }
+
+ private static final class PublicErrorInformation {
+ public final int indicators;
+ public final int errorCode;
+
+ PublicErrorInformation(int indicators, @PublicErrorCode int errorCode) {
+ this.indicators = indicators;
+ this.errorCode = errorCode;
+ }
+ }
+
+ private static final PublicErrorInformation GENERAL_KEYMINT_ERROR =
+ new PublicErrorInformation(0, ERROR_KEYMINT_FAILURE);
+
+ private static final PublicErrorInformation GENERAL_KEYSTORE_ERROR =
+ new PublicErrorInformation(0, ERROR_KEYSTORE_FAILURE);
+
+ private static final PublicErrorInformation KEYMINT_UNIMPLEMENTED_ERROR =
+ new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_UNIMPLEMENTED);
+
+ private static final PublicErrorInformation KEYMINT_RETRYABLE_ERROR =
+ new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
+ ERROR_KEYMINT_FAILURE);
+
+ private static final PublicErrorInformation KEYMINT_INCORRECT_USAGE_ERROR =
+ new PublicErrorInformation(0, ERROR_INCORRECT_USAGE);
+
+ private static final PublicErrorInformation KEYMINT_TEMPORAL_VALIDITY_ERROR =
+ new PublicErrorInformation(0, ERROR_KEY_NOT_TEMPORALLY_VALID);
+
+
+ private static final Map<Integer, PublicErrorInformation> sErrorCodeToFailureInfo =
+ new HashMap();
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public static boolean hasFailureInfoForError(int internalErrorCode) {
+ return sErrorCodeToFailureInfo.containsKey(internalErrorCode);
+ }
+
+ static {
+ // KeyMint error codes
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OK, GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ROOT_OF_TRUST_ALREADY_SET,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_PURPOSE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_PURPOSE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_ALGORITHM,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_ALGORITHM,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_SIZE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_BLOCK_MODE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_BLOCK_MODE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MAC_LENGTH,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_PADDING_MODE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_PADDING_MODE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_DIGEST,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_DIGEST,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_EXPIRATION_TIME,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_USER_ID,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_FORMAT,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_KEY_FORMAT,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_VERIFICATION_ALGORITHM,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_EXPORT_OPTIONS_INVALID,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_DELEGATION_NOT_ALLOWED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID,
+ KEYMINT_TEMPORAL_VALIDITY_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_EXPIRED,
+ KEYMINT_TEMPORAL_VALIDITY_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+ new PublicErrorInformation(REQUIRES_USER_AUTHENTICATION,
+ ERROR_USER_AUTHENTICATION_REQUIRED));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OUTPUT_PARAMETER_NULL,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_OPERATION_HANDLE,
+ new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
+ ERROR_KEY_OPERATION_EXPIRED));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INSUFFICIENT_BUFFER_SPACE,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_VERIFICATION_FAILED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_TOO_MANY_OPERATIONS,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNEXPECTED_NULL_POINTER,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_KEY_BLOB,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_NOT_ENCRYPTED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_DECRYPTION_FAILED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_NOT_SIGNED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_VERIFICATION_FAILED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_ARGUMENT,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_TAG,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_TAG,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MEMORY_ALLOCATION_FAILED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORT_PARAMETER_MISMATCH,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_ACCESS_DENIED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OPERATION_CANCELLED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CONCURRENT_ACCESS_CONFLICT,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_BUSY,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_COMMUNICATION_FAILED,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_EC_FIELD,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NONCE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_NONCE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CALLER_NONCE_PROHIBITED,
+ GENERAL_KEYMINT_ERROR);
+ // Error related to MAX_USES_PER_BOOT, restricting the number of uses per boot.
+ // It is not re-tryable.
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_MAX_OPS_EXCEEDED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_MIN_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KDF,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_EC_CURVE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ATTESTATION_CHALLENGE_MISSING,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEYMINT_NOT_CONFIGURED,
+ new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_KEYMINT_FAILURE));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ATTESTATION_APPLICATION_ID_MISSING,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CANNOT_ATTEST_IDS,
+ new PublicErrorInformation(IS_SYSTEM_ERROR,
+ ERROR_ID_ATTESTATION_FAILURE));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_DEVICE_LOCKED,
+ new PublicErrorInformation(IS_SYSTEM_ERROR | REQUIRES_USER_AUTHENTICATION,
+ ERROR_USER_AUTHENTICATION_REQUIRED));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_STORAGE_KEY_UNSUPPORTED,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_MGF_DIGEST,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MGF_DIGEST,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NOT_BEFORE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NOT_AFTER,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ // This should not be exposed to apps as it's handled by Keystore.
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_HARDWARE_NOT_YET_AVAILABLE,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNIMPLEMENTED,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
+ new PublicErrorInformation(IS_SYSTEM_ERROR,
+ ERROR_KEYMINT_FAILURE));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_VERSION_MISMATCH, GENERAL_KEYMINT_ERROR);
+
+ // Keystore error codes
+ sErrorCodeToFailureInfo.put(ResponseCode.LOCKED,
+ new PublicErrorInformation(REQUIRES_USER_AUTHENTICATION,
+ ERROR_USER_AUTHENTICATION_REQUIRED));
+ sErrorCodeToFailureInfo.put(ResponseCode.UNINITIALIZED,
+ new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_KEYSTORE_UNINITIALIZED));
+ sErrorCodeToFailureInfo.put(ResponseCode.SYSTEM_ERROR,
+ new PublicErrorInformation(IS_SYSTEM_ERROR,
+ ERROR_INTERNAL_SYSTEM_ERROR));
+ sErrorCodeToFailureInfo.put(ResponseCode.PERMISSION_DENIED,
+ new PublicErrorInformation(0, ERROR_PERMISSION_DENIED));
+ sErrorCodeToFailureInfo.put(ResponseCode.KEY_NOT_FOUND,
+ new PublicErrorInformation(0, ERROR_KEY_DOES_NOT_EXIST));
+ sErrorCodeToFailureInfo.put(ResponseCode.VALUE_CORRUPTED,
+ new PublicErrorInformation(0, ERROR_KEY_CORRUPTED));
+ sErrorCodeToFailureInfo.put(ResponseCode.KEY_PERMANENTLY_INVALIDATED,
+ new PublicErrorInformation(0, ERROR_KEY_DOES_NOT_EXIST));
+ }
}
diff --git a/media/Android.bp b/media/Android.bp
index 1b7bef5..b7c3a9c 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -100,6 +100,11 @@
vndk: {
enabled: true,
},
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.bluetooth",
+ ],
},
},
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index b7e8c8c..d8893cb 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5806,13 +5806,14 @@
* @param newDevice Bluetooth device connected or null if there is no new devices
* @param previousDevice Bluetooth device disconnected or null if there is no disconnected
* devices
- * @param info contain all info related to the device. {@link BtProfileConnectionInfo}
+ * @param info contain all info related to the device. {@link BluetoothProfileConnectionInfo}
* {@hide}
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
public void handleBluetoothActiveDeviceChanged(@Nullable BluetoothDevice newDevice,
- @Nullable BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) {
+ @Nullable BluetoothDevice previousDevice,
+ @NonNull BluetoothProfileConnectionInfo info) {
final IAudioService service = getService();
try {
service.handleBluetoothActiveDeviceChanged(newDevice, previousDevice, info);
diff --git a/media/java/android/media/BtProfileConnectionInfo.aidl b/media/java/android/media/BluetoothProfileConnectionInfo.aidl
similarity index 93%
rename from media/java/android/media/BtProfileConnectionInfo.aidl
rename to media/java/android/media/BluetoothProfileConnectionInfo.aidl
index 047f06b..0617084 100644
--- a/media/java/android/media/BtProfileConnectionInfo.aidl
+++ b/media/java/android/media/BluetoothProfileConnectionInfo.aidl
@@ -16,5 +16,5 @@
package android.media;
-parcelable BtProfileConnectionInfo;
+parcelable BluetoothProfileConnectionInfo;
diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BluetoothProfileConnectionInfo.java
similarity index 65%
rename from media/java/android/media/BtProfileConnectionInfo.java
rename to media/java/android/media/BluetoothProfileConnectionInfo.java
index 88b9777..c148846 100644
--- a/media/java/android/media/BtProfileConnectionInfo.java
+++ b/media/java/android/media/BluetoothProfileConnectionInfo.java
@@ -26,15 +26,14 @@
* {@hide}
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-public final class BtProfileConnectionInfo implements Parcelable {
-
+public final class BluetoothProfileConnectionInfo implements Parcelable {
private final int mProfile;
private final boolean mSupprNoisy;
private final int mVolume;
private final boolean mIsLeOutput;
- private BtProfileConnectionInfo(int profile, boolean suppressNoisyIntent, int volume,
- boolean isLeOutput) {
+ private BluetoothProfileConnectionInfo(int profile, boolean suppressNoisyIntent,
+ int volume, boolean isLeOutput) {
mProfile = profile;
mSupprNoisy = suppressNoisyIntent;
mVolume = volume;
@@ -45,21 +44,21 @@
* Constructor used by BtHelper when a profile is connected
* {@hide}
*/
- public BtProfileConnectionInfo(int profile) {
+ public BluetoothProfileConnectionInfo(int profile) {
this(profile, false, -1, false);
}
- public static final @NonNull Parcelable.Creator<BtProfileConnectionInfo> CREATOR =
- new Parcelable.Creator<BtProfileConnectionInfo>() {
+ public static final @NonNull Parcelable.Creator<BluetoothProfileConnectionInfo> CREATOR =
+ new Parcelable.Creator<BluetoothProfileConnectionInfo>() {
@Override
- public BtProfileConnectionInfo createFromParcel(Parcel source) {
- return new BtProfileConnectionInfo(source.readInt(), source.readBoolean(),
- source.readInt(), source.readBoolean());
+ public BluetoothProfileConnectionInfo createFromParcel(Parcel source) {
+ return new BluetoothProfileConnectionInfo(source.readInt(),
+ source.readBoolean(), source.readInt(), source.readBoolean());
}
@Override
- public BtProfileConnectionInfo[] newArray(int size) {
- return new BtProfileConnectionInfo[size];
+ public BluetoothProfileConnectionInfo[] newArray(int size) {
+ return new BluetoothProfileConnectionInfo[size];
}
};
@@ -84,10 +83,10 @@
*
* @param volume of device -1 to ignore value
*/
- public static @NonNull BtProfileConnectionInfo a2dpInfo(boolean suppressNoisyIntent,
- int volume) {
- return new BtProfileConnectionInfo(BluetoothProfile.A2DP, suppressNoisyIntent, volume,
- false);
+ public static @NonNull BluetoothProfileConnectionInfo createA2dpInfo(
+ boolean suppressNoisyIntent, int volume) {
+ return new BluetoothProfileConnectionInfo(BluetoothProfile.A2DP, suppressNoisyIntent,
+ volume, false);
}
/**
@@ -96,8 +95,8 @@
*
* @param volume of device -1 to ignore value
*/
- public static @NonNull BtProfileConnectionInfo a2dpSinkInfo(int volume) {
- return new BtProfileConnectionInfo(BluetoothProfile.A2DP_SINK, true, volume, false);
+ public static @NonNull BluetoothProfileConnectionInfo createA2dpSinkInfo(int volume) {
+ return new BluetoothProfileConnectionInfo(BluetoothProfile.A2DP_SINK, true, volume, false);
}
/**
@@ -106,9 +105,10 @@
* @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY}
* intent will not be sent.
*/
- public static @NonNull BtProfileConnectionInfo hearingAidInfo(boolean suppressNoisyIntent) {
- return new BtProfileConnectionInfo(BluetoothProfile.HEARING_AID, suppressNoisyIntent, -1,
- false);
+ public static @NonNull BluetoothProfileConnectionInfo createHearingAidInfo(
+ boolean suppressNoisyIntent) {
+ return new BluetoothProfileConnectionInfo(BluetoothProfile.HEARING_AID, suppressNoisyIntent,
+ -1, false);
}
/**
@@ -119,10 +119,10 @@
*
* @param isLeOutput if true mean the device is an output device, if false it's an input device
*/
- public static @NonNull BtProfileConnectionInfo leAudio(boolean suppressNoisyIntent,
- boolean isLeOutput) {
- return new BtProfileConnectionInfo(BluetoothProfile.LE_AUDIO, suppressNoisyIntent, -1,
- isLeOutput);
+ public static @NonNull BluetoothProfileConnectionInfo createLeAudioInfo(
+ boolean suppressNoisyIntent, boolean isLeOutput) {
+ return new BluetoothProfileConnectionInfo(BluetoothProfile.LE_AUDIO, suppressNoisyIntent,
+ -1, isLeOutput);
}
/**
@@ -136,7 +136,7 @@
* @return {@code true} if {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be
* sent
*/
- public boolean getSuppressNoisyIntent() {
+ public boolean isSuppressNoisyIntent() {
return mSupprNoisy;
}
@@ -153,7 +153,7 @@
* @return {@code true} is the LE device is an output device, {@code false} if it's an input
* device
*/
- public boolean getIsLeOutput() {
+ public boolean isLeOutput() {
return mIsLeOutput;
}
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 5ff56f9..d5a2d07 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -24,7 +24,7 @@
import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
-import android.media.BtProfileConnectionInfo;
+import android.media.BluetoothProfileConnectionInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioModeDispatcher;
import android.media.IAudioRoutesObserver;
@@ -268,7 +268,7 @@
oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
void handleBluetoothActiveDeviceChanged(in BluetoothDevice newDevice,
- in BluetoothDevice previousDevice, in BtProfileConnectionInfo info);
+ in BluetoothDevice previousDevice, in BluetoothProfileConnectionInfo info);
oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult,
in IAudioPolicyCallback pcb);
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index d8f48c2..20d711c 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -103,6 +103,7 @@
private int mDeviceType;
private String mHostType;
private boolean mSkipThumbForHost = false;
+ private volatile boolean mHostIsWindows = false;
private MtpServer mServer;
private MtpStorageManager mManager;
@@ -358,7 +359,7 @@
}
public void addStorage(StorageVolume storage) {
- MtpStorage mtpStorage = mManager.addMtpStorage(storage);
+ MtpStorage mtpStorage = mManager.addMtpStorage(storage, () -> mHostIsWindows);
mStorageMap.put(storage.getPath(), mtpStorage);
if (mServer != null) {
mServer.addStorage(mtpStorage);
@@ -413,6 +414,7 @@
}
mHostType = "";
mSkipThumbForHost = false;
+ mHostIsWindows = false;
}
@VisibleForNative
@@ -736,10 +738,12 @@
: MtpConstants.RESPONSE_GENERAL_ERROR);
case MtpConstants.DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO:
mHostType = stringValue;
+ Log.d(TAG, "setDeviceProperty." + Integer.toHexString(property)
+ + "=" + stringValue);
if (stringValue.startsWith("Android/")) {
- Log.d(TAG, "setDeviceProperty." + Integer.toHexString(property)
- + "=" + stringValue);
mSkipThumbForHost = true;
+ } else if (stringValue.startsWith("Windows/")) {
+ mHostIsWindows = true;
}
return MtpConstants.RESPONSE_OK;
}
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
index 88c32a3..a3754e90 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -19,6 +19,8 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.storage.StorageVolume;
+import java.util.function.Supplier;
+
/**
* This class represents a storage unit on an MTP device.
* Used only for MTP support in USB responder mode.
@@ -33,14 +35,16 @@
private final boolean mRemovable;
private final long mMaxFileSize;
private final String mVolumeName;
+ private final Supplier<Boolean> mIsHostWindows;
- public MtpStorage(StorageVolume volume, int storageId) {
+ public MtpStorage(StorageVolume volume, int storageId, Supplier<Boolean> isHostWindows) {
mStorageId = storageId;
mPath = volume.getPath();
mDescription = volume.getDescription(null);
mRemovable = volume.isRemovable();
mMaxFileSize = volume.getMaxFileSize();
mVolumeName = volume.getMediaStoreVolumeName();
+ mIsHostWindows = isHostWindows;
}
/**
@@ -93,4 +97,13 @@
public String getVolumeName() {
return mVolumeName;
}
+
+ /**
+ * Returns true if the mtp host of this storage is Windows.
+ *
+ * @return is host Windows
+ */
+ public boolean isHostWindows() {
+ return mIsHostWindows.get();
+ }
}
diff --git a/media/java/android/mtp/MtpStorageManager.java b/media/java/android/mtp/MtpStorageManager.java
index 0bede0d..e9426cf 100644
--- a/media/java/android/mtp/MtpStorageManager.java
+++ b/media/java/android/mtp/MtpStorageManager.java
@@ -18,7 +18,11 @@
import android.media.MediaFile;
import android.os.FileObserver;
+import android.os.SystemProperties;
import android.os.storage.StorageVolume;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -35,6 +39,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Supplier;
/**
* MtpStorageManager provides functionality for listing, tracking, and notifying MtpServer of
@@ -199,7 +204,38 @@
}
public long getSize() {
- return mIsDir ? 0 : getPath().toFile().length();
+ return mIsDir ? 0 : maybeApplyTranscodeLengthWorkaround(getPath().toFile().length());
+ }
+
+ private long maybeApplyTranscodeLengthWorkaround(long length) {
+ // Windows truncates transferred files to the size advertised in the object property.
+ if (mStorage.isHostWindows() && isTranscodeMtpEnabled() && isFileTranscodeSupported()) {
+ // If the file supports transcoding, we double the returned size to accommodate
+ // the increase in size from transcoding to AVC. This is the same heuristic
+ // applied in the FUSE daemon (MediaProvider).
+ return length * 2;
+ }
+ return length;
+ }
+
+ private boolean isTranscodeMtpEnabled() {
+ return SystemProperties.getBoolean("sys.fuse.transcode_mtp", false);
+ }
+
+ private boolean isFileTranscodeSupported() {
+ // Check if the file supports transcoding by reading the |st_nlinks| struct stat
+ // field. This will be > 1 if the file supports transcoding. The FUSE daemon
+ // sets the field accordingly to enable the MTP stack workaround some Windows OS
+ // MTP client bug where they ignore the size returned as part of getting the MTP
+ // object, see MtpServer#doGetObject.
+ final Path path = getPath();
+ try {
+ StructStat stat = Os.stat(path.toString());
+ return stat.st_nlink > 1;
+ } catch (ErrnoException e) {
+ Log.w(TAG, "Failed to stat path: " + getPath() + ". Ignoring transcoding.");
+ return false;
+ }
}
public Path getPath() {
@@ -420,10 +456,12 @@
* @param volume Storage to add.
* @return the associated MtpStorage
*/
- public synchronized MtpStorage addMtpStorage(StorageVolume volume) {
+ public synchronized MtpStorage addMtpStorage(StorageVolume volume,
+ Supplier<Boolean> isHostWindows) {
int storageId = ((getNextStorageId() & 0x0000FFFF) << 16) + 1;
- MtpStorage storage = new MtpStorage(volume, storageId);
- MtpObject root = new MtpObject(storage.getPath(), storageId, storage, null, true);
+ MtpStorage storage = new MtpStorage(volume, storageId, isHostWindows);
+ MtpObject root = new MtpObject(storage.getPath(), storageId, storage, /* parent= */ null,
+ /* isDir= */ true);
mRoots.put(storageId, root);
return storage;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java
similarity index 66%
rename from media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java
rename to media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java
index fd66d3b..f23794b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java
@@ -19,7 +19,7 @@
import static org.junit.Assert.assertEquals;
import android.bluetooth.BluetoothProfile;
-import android.media.BtProfileConnectionInfo;
+import android.media.BluetoothProfileConnectionInfo;
import androidx.test.runner.AndroidJUnit4;
@@ -27,22 +27,24 @@
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
-public class BtProfileConnectionInfoTest {
+public class BluetoothProfileConnectionInfoTest {
@Test
public void testCoverageA2dp() {
final boolean supprNoisy = false;
final int volume = 42;
- final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpInfo(supprNoisy, volume);
+ final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo
+ .createA2dpInfo(supprNoisy, volume);
assertEquals(info.getProfile(), BluetoothProfile.A2DP);
- assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
+ assertEquals(info.isSuppressNoisyIntent(), supprNoisy);
assertEquals(info.getVolume(), volume);
}
@Test
public void testCoverageA2dpSink() {
final int volume = 42;
- final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpSinkInfo(volume);
+ final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo
+ .createA2dpSinkInfo(volume);
assertEquals(info.getProfile(), BluetoothProfile.A2DP_SINK);
assertEquals(info.getVolume(), volume);
}
@@ -50,20 +52,21 @@
@Test
public void testCoveragehearingAid() {
final boolean supprNoisy = true;
- final BtProfileConnectionInfo info = BtProfileConnectionInfo.hearingAidInfo(supprNoisy);
+ final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo
+ .createHearingAidInfo(supprNoisy);
assertEquals(info.getProfile(), BluetoothProfile.HEARING_AID);
- assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
+ assertEquals(info.isSuppressNoisyIntent(), supprNoisy);
}
@Test
public void testCoverageLeAudio() {
final boolean supprNoisy = false;
final boolean isLeOutput = true;
- final BtProfileConnectionInfo info = BtProfileConnectionInfo.leAudio(supprNoisy,
- isLeOutput);
+ final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo
+ .createLeAudioInfo(supprNoisy, isLeOutput);
assertEquals(info.getProfile(), BluetoothProfile.LE_AUDIO);
- assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
- assertEquals(info.getIsLeOutput(), isLeOutput);
+ assertEquals(info.isSuppressNoisyIntent(), supprNoisy);
+ assertEquals(info.isLeOutput(), isLeOutput);
}
}
diff --git a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
index fdf6582..eb357f6 100644
--- a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
+++ b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
@@ -159,10 +159,11 @@
Log.d(TAG, "sendObjectInfoChanged: " + id);
objectsInfoChanged.add(id);
}
- }, null);
+ }, /* subdirectories= */ null);
- mainMtpStorage = manager.addMtpStorage(mainStorage);
- secondaryMtpStorage = manager.addMtpStorage(secondaryStorage);
+ mainMtpStorage = manager.addMtpStorage(mainStorage, /* isHostWindows= */ () -> false);
+ secondaryMtpStorage = manager.addMtpStorage(secondaryStorage,
+ /* isHostWindows= */ () -> false);
}
@After
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index f33e118..2201753 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -300,6 +300,9 @@
android_res_nquery; # introduced=29
android_res_nresult; # introduced=29
android_res_nsend; # introduced=29
+ android_tag_socket_with_uid; # introduced=Tiramisu
+ android_tag_socket; # introduced=Tiramisu
+ android_untag_socket; # introduced=Tiramisu
AThermal_acquireManager; # introduced=30
AThermal_releaseManager; # introduced=30
AThermal_getCurrentThermalStatus; # introduced=30
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
index 327b1fb..54538d9 100644
--- a/packages/ConnectivityT/framework-t/Android.bp
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -125,14 +125,14 @@
name: "framework-connectivity-ethernet-sources",
srcs: [
"src/android/net/EthernetManager.java",
+ "src/android/net/EthernetNetworkManagementException.java",
+ "src/android/net/EthernetNetworkManagementException.aidl",
"src/android/net/EthernetNetworkSpecifier.java",
+ "src/android/net/EthernetNetworkUpdateRequest.java",
+ "src/android/net/EthernetNetworkUpdateRequest.aidl",
"src/android/net/IEthernetManager.aidl",
+ "src/android/net/IEthernetNetworkManagementListener.aidl",
"src/android/net/IEthernetServiceListener.aidl",
- "src/android/net/IInternalNetworkManagementListener.aidl",
- "src/android/net/InternalNetworkUpdateRequest.java",
- "src/android/net/InternalNetworkUpdateRequest.aidl",
- "src/android/net/InternalNetworkManagementException.java",
- "src/android/net/InternalNetworkManagementException.aidl",
"src/android/net/ITetheredInterfaceCallback.aidl",
],
path: "src",
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
index ece54df..1f67f6d 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
@@ -19,12 +19,14 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.os.RemoteException;
@@ -320,15 +322,15 @@
}
private static final class InternalNetworkManagementListener
- extends IInternalNetworkManagementListener.Stub {
+ extends IEthernetNetworkManagementListener.Stub {
@NonNull
private final Executor mExecutor;
@NonNull
- private final BiConsumer<Network, InternalNetworkManagementException> mListener;
+ private final BiConsumer<Network, EthernetNetworkManagementException> mListener;
InternalNetworkManagementListener(
@NonNull final Executor executor,
- @NonNull final BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @NonNull final BiConsumer<Network, EthernetNetworkManagementException> listener) {
Objects.requireNonNull(executor, "Pass a non-null executor");
Objects.requireNonNull(listener, "Pass a non-null listener");
mExecutor = executor;
@@ -338,14 +340,14 @@
@Override
public void onComplete(
@Nullable final Network network,
- @Nullable final InternalNetworkManagementException e) {
+ @Nullable final EthernetNetworkManagementException e) {
mExecutor.execute(() -> mListener.accept(network, e));
}
}
private InternalNetworkManagementListener getInternalNetworkManagementListener(
@Nullable final Executor executor,
- @Nullable final BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable final BiConsumer<Network, EthernetNetworkManagementException> listener) {
if (null != listener) {
Objects.requireNonNull(executor, "Pass a non-null executor, or a null listener");
}
@@ -358,11 +360,43 @@
return proxy;
}
- private void updateConfiguration(
+ /**
+ * Updates the configuration of an automotive device's ethernet network.
+ *
+ * The {@link EthernetNetworkUpdateRequest} {@code request} argument describes how to update the
+ * configuration for this network.
+ * Use {@link StaticIpConfiguration.Builder} to build a {@code StaticIpConfiguration} object for
+ * this network to put inside the {@code request}.
+ * Similarly, use {@link NetworkCapabilities.Builder} to build a {@code NetworkCapabilities}
+ * object for this network to put inside the {@code request}.
+ *
+ * If non-null, the listener will be called exactly once after this is called, unless
+ * a synchronous exception was thrown.
+ *
+ * @param iface the name of the interface to act upon.
+ * @param request the {@link EthernetNetworkUpdateRequest} used to set an ethernet network's
+ * {@link StaticIpConfiguration} and {@link NetworkCapabilities} values.
+ * @param executor an {@link Executor} to execute the listener on. Optional if listener is null.
+ * @param listener an optional {@link BiConsumer} to listen for completion of the operation.
+ * @throws SecurityException if the process doesn't hold
+ * {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}.
+ * @throws UnsupportedOperationException if called on a non-automotive device or on an
+ * unsupported interface.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.MANAGE_ETHERNET_NETWORKS})
+ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ public void updateConfiguration(
@NonNull String iface,
- @NonNull InternalNetworkUpdateRequest request,
+ @NonNull EthernetNetworkUpdateRequest request,
@Nullable @CallbackExecutor Executor executor,
- @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
+ Objects.requireNonNull(iface, "iface must be non-null");
+ Objects.requireNonNull(request, "request must be non-null");
final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
executor, listener);
try {
@@ -372,10 +406,34 @@
}
}
- private void connectNetwork(
+ /**
+ * Set an ethernet network's link state up.
+ *
+ * When the link is successfully turned up, the listener will be called with the resulting
+ * network. If any error or unexpected condition happens while the system tries to turn the
+ * interface up, the listener will be called with an appropriate exception.
+ * The listener is guaranteed to be called exactly once for each call to this method, but this
+ * may take an unbounded amount of time depending on the actual network conditions.
+ *
+ * @param iface the name of the interface to act upon.
+ * @param executor an {@link Executor} to execute the listener on. Optional if listener is null.
+ * @param listener an optional {@link BiConsumer} to listen for completion of the operation.
+ * @throws SecurityException if the process doesn't hold
+ * {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}.
+ * @throws UnsupportedOperationException if called on a non-automotive device.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.MANAGE_ETHERNET_NETWORKS})
+ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ public void connectNetwork(
@NonNull String iface,
@Nullable @CallbackExecutor Executor executor,
- @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
+ Objects.requireNonNull(iface, "iface must be non-null");
final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
executor, listener);
try {
@@ -385,10 +443,33 @@
}
}
- private void disconnectNetwork(
+ /**
+ * Set an ethernet network's link state down.
+ *
+ * When the link is successfully turned down, the listener will be called with the network that
+ * was torn down, if any. If any error or unexpected condition happens while the system tries to
+ * turn the interface down, the listener will be called with an appropriate exception.
+ * The listener is guaranteed to be called exactly once for each call to this method.
+ *
+ * @param iface the name of the interface to act upon.
+ * @param executor an {@link Executor} to execute the listener on. Optional if listener is null.
+ * @param listener an optional {@link BiConsumer} to listen for completion of the operation.
+ * @throws SecurityException if the process doesn't hold
+ * {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}.
+ * @throws UnsupportedOperationException if called on a non-automotive device.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.MANAGE_ETHERNET_NETWORKS})
+ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ public void disconnectNetwork(
@NonNull String iface,
@Nullable @CallbackExecutor Executor executor,
- @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
+ Objects.requireNonNull(iface, "iface must be non-null");
final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
executor, listener);
try {
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl
similarity index 93%
rename from packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl
index dcce706..adf9e5a 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl
@@ -16,4 +16,4 @@
package android.net;
- parcelable InternalNetworkManagementException;
\ No newline at end of file
+ parcelable EthernetNetworkManagementException;
\ No newline at end of file
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java
new file mode 100644
index 0000000..a69cc55
--- /dev/null
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/** @hide */
+@SystemApi
+public final class EthernetNetworkManagementException
+ extends RuntimeException implements Parcelable {
+
+ /* @hide */
+ public EthernetNetworkManagementException(@NonNull final String errorMessage) {
+ super(errorMessage);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getMessage());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+ final EthernetNetworkManagementException that = (EthernetNetworkManagementException) obj;
+
+ return Objects.equals(getMessage(), that.getMessage());
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(getMessage());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<EthernetNetworkManagementException> CREATOR =
+ new Parcelable.Creator<EthernetNetworkManagementException>() {
+ @Override
+ public EthernetNetworkManagementException[] newArray(int size) {
+ return new EthernetNetworkManagementException[size];
+ }
+
+ @Override
+ public EthernetNetworkManagementException createFromParcel(@NonNull Parcel source) {
+ return new EthernetNetworkManagementException(source.readString());
+ }
+ };
+}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.aidl
similarity index 93%
rename from packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.aidl
index da00cb9..debc348 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.aidl
@@ -16,4 +16,4 @@
package android.net;
- parcelable InternalNetworkUpdateRequest;
\ No newline at end of file
+ parcelable EthernetNetworkUpdateRequest;
\ No newline at end of file
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
similarity index 79%
rename from packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
rename to packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
index f42c4b7..e879e40 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
@@ -17,13 +17,15 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/** @hide */
-public final class InternalNetworkUpdateRequest implements Parcelable {
+@SystemApi
+public final class EthernetNetworkUpdateRequest implements Parcelable {
@NonNull
private final StaticIpConfiguration mIpConfig;
@NonNull
@@ -39,8 +41,7 @@
return new NetworkCapabilities(mNetworkCapabilities);
}
- /** @hide */
- public InternalNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
+ public EthernetNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
@NonNull final NetworkCapabilities networkCapabilities) {
Objects.requireNonNull(ipConfig);
Objects.requireNonNull(networkCapabilities);
@@ -48,7 +49,7 @@
mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
}
- private InternalNetworkUpdateRequest(@NonNull final Parcel source) {
+ private EthernetNetworkUpdateRequest(@NonNull final Parcel source) {
Objects.requireNonNull(source);
mIpConfig = StaticIpConfiguration.CREATOR.createFromParcel(source);
mNetworkCapabilities = NetworkCapabilities.CREATOR.createFromParcel(source);
@@ -56,7 +57,7 @@
@Override
public String toString() {
- return "InternalNetworkUpdateRequest{"
+ return "EthernetNetworkUpdateRequest{"
+ "mIpConfig=" + mIpConfig
+ ", mNetworkCapabilities=" + mNetworkCapabilities + '}';
}
@@ -65,7 +66,7 @@
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- InternalNetworkUpdateRequest that = (InternalNetworkUpdateRequest) o;
+ EthernetNetworkUpdateRequest that = (EthernetNetworkUpdateRequest) o;
return Objects.equals(that.getIpConfig(), mIpConfig)
&& Objects.equals(that.getNetworkCapabilities(), mNetworkCapabilities);
@@ -88,16 +89,16 @@
}
@NonNull
- public static final Parcelable.Creator<InternalNetworkUpdateRequest> CREATOR =
- new Parcelable.Creator<InternalNetworkUpdateRequest>() {
+ public static final Parcelable.Creator<EthernetNetworkUpdateRequest> CREATOR =
+ new Parcelable.Creator<EthernetNetworkUpdateRequest>() {
@Override
- public InternalNetworkUpdateRequest[] newArray(int size) {
- return new InternalNetworkUpdateRequest[size];
+ public EthernetNetworkUpdateRequest[] newArray(int size) {
+ return new EthernetNetworkUpdateRequest[size];
}
@Override
- public InternalNetworkUpdateRequest createFromParcel(@NonNull Parcel source) {
- return new InternalNetworkUpdateRequest(source);
+ public EthernetNetworkUpdateRequest createFromParcel(@NonNull Parcel source) {
+ return new EthernetNetworkUpdateRequest(source);
}
};
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
index e688bea..544d02b 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
@@ -18,8 +18,8 @@
import android.net.IpConfiguration;
import android.net.IEthernetServiceListener;
-import android.net.IInternalNetworkManagementListener;
-import android.net.InternalNetworkUpdateRequest;
+import android.net.IEthernetNetworkManagementListener;
+import android.net.EthernetNetworkUpdateRequest;
import android.net.ITetheredInterfaceCallback;
/**
@@ -38,8 +38,8 @@
void setIncludeTestInterfaces(boolean include);
void requestTetheredInterface(in ITetheredInterfaceCallback callback);
void releaseTetheredInterface(in ITetheredInterfaceCallback callback);
- void updateConfiguration(String iface, in InternalNetworkUpdateRequest request,
- in IInternalNetworkManagementListener listener);
- void connectNetwork(String iface, in IInternalNetworkManagementListener listener);
- void disconnectNetwork(String iface, in IInternalNetworkManagementListener listener);
+ void updateConfiguration(String iface, in EthernetNetworkUpdateRequest request,
+ in IEthernetNetworkManagementListener listener);
+ void connectNetwork(String iface, in IEthernetNetworkManagementListener listener);
+ void disconnectNetwork(String iface, in IEthernetNetworkManagementListener listener);
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl
similarity index 80%
rename from packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl
index 69cde3b..93edccf 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl
@@ -16,10 +16,10 @@
package android.net;
-import android.net.InternalNetworkManagementException;
+import android.net.EthernetNetworkManagementException;
import android.net.Network;
/** @hide */
-oneway interface IInternalNetworkManagementListener {
- void onComplete(in Network network, in InternalNetworkManagementException exception);
+oneway interface IEthernetNetworkManagementListener {
+ void onComplete(in Network network, in EthernetNetworkManagementException exception);
}
\ No newline at end of file
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
deleted file mode 100644
index 7f4e403..0000000
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
+++ /dev/null
@@ -1,59 +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;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public final class InternalNetworkManagementException
- extends RuntimeException implements Parcelable {
-
- /* @hide */
- public InternalNetworkManagementException(@NonNull final Throwable t) {
- super(t);
- }
-
- private InternalNetworkManagementException(@NonNull final Parcel source) {
- super(source.readString());
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString(getCause().getMessage());
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- public static final Parcelable.Creator<InternalNetworkManagementException> CREATOR =
- new Parcelable.Creator<InternalNetworkManagementException>() {
- @Override
- public InternalNetworkManagementException[] newArray(int size) {
- return new InternalNetworkManagementException[size];
- }
-
- @Override
- public InternalNetworkManagementException createFromParcel(@NonNull Parcel source) {
- return new InternalNetworkManagementException(source);
- }
- };
-}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
index 0f21e55..512fbce 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
@@ -16,6 +16,9 @@
package android.net.nsd;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
@@ -23,15 +26,22 @@
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkRequest;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.Objects;
@@ -278,9 +288,180 @@
private final SparseArray mListenerMap = new SparseArray();
private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>();
private final Object mMapLock = new Object();
+ // Map of listener key sent by client -> per-network discovery tracker
+ @GuardedBy("mPerNetworkDiscoveryMap")
+ private final ArrayMap<Integer, PerNetworkDiscoveryTracker>
+ mPerNetworkDiscoveryMap = new ArrayMap<>();
private final ServiceHandler mHandler;
+ private class PerNetworkDiscoveryTracker {
+ final String mServiceType;
+ final int mProtocolType;
+ final DiscoveryListener mBaseListener;
+ final ArrayMap<Network, DelegatingDiscoveryListener> mPerNetworkListeners =
+ new ArrayMap<>();
+
+ final NetworkCallback mNetworkCb = new NetworkCallback() {
+ @Override
+ public void onAvailable(@NonNull Network network) {
+ final DelegatingDiscoveryListener wrappedListener = new DelegatingDiscoveryListener(
+ network, mBaseListener);
+ mPerNetworkListeners.put(network, wrappedListener);
+ discoverServices(mServiceType, mProtocolType, network, wrappedListener);
+ }
+
+ @Override
+ public void onLost(@NonNull Network network) {
+ final DelegatingDiscoveryListener listener = mPerNetworkListeners.get(network);
+ if (listener == null) return;
+ listener.notifyAllServicesLost();
+ // Listener will be removed from map in discovery stopped callback
+ stopServiceDiscovery(listener);
+ }
+ };
+
+ // Accessed from mHandler
+ private boolean mStopRequested;
+
+ public void start(@NonNull NetworkRequest request) {
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+ cm.registerNetworkCallback(request, mNetworkCb, mHandler);
+ mHandler.post(() -> mBaseListener.onDiscoveryStarted(mServiceType));
+ }
+
+ /**
+ * Stop discovery on all networks tracked by this class.
+ *
+ * This will request all underlying listeners to stop, and the last one to stop will call
+ * onDiscoveryStopped or onStopDiscoveryFailed.
+ *
+ * Must be called on the handler thread.
+ */
+ public void requestStop() {
+ mHandler.post(() -> {
+ mStopRequested = true;
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+ cm.unregisterNetworkCallback(mNetworkCb);
+ if (mPerNetworkListeners.size() == 0) {
+ mBaseListener.onDiscoveryStopped(mServiceType);
+ return;
+ }
+ for (int i = 0; i < mPerNetworkListeners.size(); i++) {
+ final DelegatingDiscoveryListener listener = mPerNetworkListeners.valueAt(i);
+ stopServiceDiscovery(listener);
+ }
+ });
+ }
+
+ private PerNetworkDiscoveryTracker(String serviceType, int protocolType,
+ DiscoveryListener baseListener) {
+ mServiceType = serviceType;
+ mProtocolType = protocolType;
+ mBaseListener = baseListener;
+ }
+
+ /**
+ * Subset of NsdServiceInfo that is tracked to generate service lost notifications when a
+ * network is lost.
+ *
+ * Service lost notifications only contain service name, type and network, so only track
+ * that information (Network is known from the listener). This also implements
+ * equals/hashCode for usage in maps.
+ */
+ private class TrackedNsdInfo {
+ private final String mServiceName;
+ private final String mServiceType;
+ TrackedNsdInfo(NsdServiceInfo info) {
+ mServiceName = info.getServiceName();
+ mServiceType = info.getServiceType();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mServiceName, mServiceType);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof TrackedNsdInfo)) return false;
+ final TrackedNsdInfo other = (TrackedNsdInfo) obj;
+ return Objects.equals(mServiceName, other.mServiceName)
+ && Objects.equals(mServiceType, other.mServiceType);
+ }
+ }
+
+ private class DelegatingDiscoveryListener implements DiscoveryListener {
+ private final Network mNetwork;
+ private final DiscoveryListener mWrapped;
+ private final ArraySet<TrackedNsdInfo> mFoundInfo = new ArraySet<>();
+
+ private DelegatingDiscoveryListener(Network network, DiscoveryListener listener) {
+ mNetwork = network;
+ mWrapped = listener;
+ }
+
+ void notifyAllServicesLost() {
+ for (int i = 0; i < mFoundInfo.size(); i++) {
+ final TrackedNsdInfo trackedInfo = mFoundInfo.valueAt(i);
+ final NsdServiceInfo serviceInfo = new NsdServiceInfo(
+ trackedInfo.mServiceName, trackedInfo.mServiceType);
+ serviceInfo.setNetwork(mNetwork);
+ mWrapped.onServiceLost(serviceInfo);
+ }
+ }
+
+ @Override
+ public void onStartDiscoveryFailed(String serviceType, int errorCode) {
+ // The delegated listener is used when NsdManager takes care of starting/stopping
+ // discovery on multiple networks. Failure to start on one network is not a global
+ // failure to be reported up, as other networks may succeed: just log.
+ Log.e(TAG, "Failed to start discovery for " + serviceType + " on " + mNetwork
+ + " with code " + errorCode);
+ mPerNetworkListeners.remove(mNetwork);
+ }
+
+ @Override
+ public void onDiscoveryStarted(String serviceType) {
+ // Wrapped listener was called upon registration, it is not called for discovery
+ // on each network
+ }
+
+ @Override
+ public void onStopDiscoveryFailed(String serviceType, int errorCode) {
+ Log.e(TAG, "Failed to stop discovery for " + serviceType + " on " + mNetwork
+ + " with code " + errorCode);
+ mPerNetworkListeners.remove(mNetwork);
+ if (mStopRequested && mPerNetworkListeners.size() == 0) {
+ // Do not report onStopDiscoveryFailed when some underlying listeners failed:
+ // this does not mean that all listeners did, and onStopDiscoveryFailed is not
+ // actionable anyway. Just report that discovery stopped.
+ mWrapped.onDiscoveryStopped(serviceType);
+ }
+ }
+
+ @Override
+ public void onDiscoveryStopped(String serviceType) {
+ mPerNetworkListeners.remove(mNetwork);
+ if (mStopRequested && mPerNetworkListeners.size() == 0) {
+ mWrapped.onDiscoveryStopped(serviceType);
+ }
+ }
+
+ @Override
+ public void onServiceFound(NsdServiceInfo serviceInfo) {
+ mFoundInfo.add(new TrackedNsdInfo(serviceInfo));
+ mWrapped.onServiceFound(serviceInfo);
+ }
+
+ @Override
+ public void onServiceLost(NsdServiceInfo serviceInfo) {
+ mFoundInfo.remove(new TrackedNsdInfo(serviceInfo));
+ mWrapped.onServiceLost(serviceInfo);
+ }
+ }
+ }
+
/**
* Create a new Nsd instance. Applications use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -634,6 +815,14 @@
}
/**
+ * Same as {@link #discoverServices(String, int, Network, DiscoveryListener)} with a null
+ * {@link Network}.
+ */
+ public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) {
+ discoverServices(serviceType, protocolType, (Network) null, listener);
+ }
+
+ /**
* Initiate service discovery to browse for instances of a service type. Service discovery
* consumes network bandwidth and will continue until the application calls
* {@link #stopServiceDiscovery}.
@@ -657,11 +846,13 @@
* @param serviceType The service type being discovered. Examples include "_http._tcp" for
* http services or "_ipp._tcp" for printers
* @param protocolType The service discovery protocol
+ * @param network Network to discover services on, or null to discover on all available networks
* @param listener The listener notifies of a successful discovery and is used
* to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}.
* Cannot be null. Cannot be in use for an active service discovery.
*/
- public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) {
+ public void discoverServices(@NonNull String serviceType, int protocolType,
+ @Nullable Network network, @NonNull DiscoveryListener listener) {
if (TextUtils.isEmpty(serviceType)) {
throw new IllegalArgumentException("Service type cannot be empty");
}
@@ -669,6 +860,7 @@
NsdServiceInfo s = new NsdServiceInfo();
s.setServiceType(serviceType);
+ s.setNetwork(network);
int key = putListener(listener, s);
try {
@@ -679,6 +871,67 @@
}
/**
+ * Initiate service discovery to browse for instances of a service type. Service discovery
+ * consumes network bandwidth and will continue until the application calls
+ * {@link #stopServiceDiscovery}.
+ *
+ * <p> The function call immediately returns after sending a request to start service
+ * discovery to the framework. The application is notified of a success to initiate
+ * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure
+ * through {@link DiscoveryListener#onStartDiscoveryFailed}.
+ *
+ * <p> Upon successful start, application is notified when a service is found with
+ * {@link DiscoveryListener#onServiceFound} or when a service is lost with
+ * {@link DiscoveryListener#onServiceLost}.
+ *
+ * <p> Upon failure to start, service discovery is not active and application does
+ * not need to invoke {@link #stopServiceDiscovery}
+ *
+ * <p> The application should call {@link #stopServiceDiscovery} when discovery of this
+ * service type is no longer required, and/or whenever the application is paused or
+ * stopped.
+ *
+ * <p> During discovery, new networks may connect or existing networks may disconnect - for
+ * example if wifi is reconnected. When a service was found on a network that disconnects,
+ * {@link DiscoveryListener#onServiceLost} will be called. If a new network connects that
+ * matches the {@link NetworkRequest}, {@link DiscoveryListener#onServiceFound} will be called
+ * for services found on that network. Applications that do not want to track networks
+ * themselves are encouraged to use this method instead of other overloads of
+ * {@code discoverServices}, as they will receive proper notifications when a service becomes
+ * available or unavailable due to network changes.
+ *
+ * @param serviceType The service type being discovered. Examples include "_http._tcp" for
+ * http services or "_ipp._tcp" for printers
+ * @param protocolType The service discovery protocol
+ * @param networkRequest Request specifying networks that should be considered when discovering
+ * @param listener The listener notifies of a successful discovery and is used
+ * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}.
+ * Cannot be null. Cannot be in use for an active service discovery.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ public void discoverServices(@NonNull String serviceType, int protocolType,
+ @NonNull NetworkRequest networkRequest, @NonNull DiscoveryListener listener) {
+ if (TextUtils.isEmpty(serviceType)) {
+ throw new IllegalArgumentException("Service type cannot be empty");
+ }
+ Objects.requireNonNull(networkRequest, "NetworkRequest cannot be null");
+ checkProtocol(protocolType);
+
+ NsdServiceInfo s = new NsdServiceInfo();
+ s.setServiceType(serviceType);
+
+ final int baseListenerKey = putListener(listener, s);
+
+ final PerNetworkDiscoveryTracker discoveryInfo = new PerNetworkDiscoveryTracker(
+ serviceType, protocolType, listener);
+
+ synchronized (mPerNetworkDiscoveryMap) {
+ mPerNetworkDiscoveryMap.put(baseListenerKey, discoveryInfo);
+ discoveryInfo.start(networkRequest);
+ }
+ }
+
+ /**
* Stop service discovery initiated with {@link #discoverServices}. An active service
* discovery is notified to the application with {@link DiscoveryListener#onDiscoveryStarted}
* and it stays active until the application invokes a stop service discovery. A successful
@@ -696,6 +949,14 @@
*/
public void stopServiceDiscovery(DiscoveryListener listener) {
int id = getListenerKey(listener);
+ // If this is a PerNetworkDiscovery request, handle it as such
+ synchronized (mPerNetworkDiscoveryMap) {
+ final PerNetworkDiscoveryTracker info = mPerNetworkDiscoveryMap.get(id);
+ if (info != null) {
+ info.requestStop();
+ return;
+ }
+ }
try {
mService.stopDiscovery(id);
} catch (RemoteException e) {
diff --git a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java
index 0946499..8506db1 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java
@@ -17,7 +17,9 @@
package android.net.nsd;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
+import android.net.Network;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -49,6 +51,9 @@
private int mPort;
+ @Nullable
+ private Network mNetwork;
+
public NsdServiceInfo() {
}
@@ -307,18 +312,37 @@
return txtRecord;
}
- public String toString() {
- StringBuffer sb = new StringBuffer();
+ /**
+ * Get the network where the service can be found.
+ *
+ * This is never null if this {@link NsdServiceInfo} was obtained from
+ * {@link NsdManager#discoverServices} or {@link NsdManager#resolveService}.
+ */
+ @Nullable
+ public Network getNetwork() {
+ return mNetwork;
+ }
+ /**
+ * Set the network where the service can be found.
+ * @param network The network, or null to search for, or to announce, the service on all
+ * connected networks.
+ */
+ public void setNetwork(@Nullable Network network) {
+ mNetwork = network;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
sb.append("name: ").append(mServiceName)
.append(", type: ").append(mServiceType)
.append(", host: ").append(mHost)
- .append(", port: ").append(mPort);
+ .append(", port: ").append(mPort)
+ .append(", network: ").append(mNetwork);
byte[] txtRecord = getTxtRecord();
- if (txtRecord != null) {
- sb.append(", txtRecord: ").append(new String(txtRecord, StandardCharsets.UTF_8));
- }
+ sb.append(", txtRecord: ").append(new String(txtRecord, StandardCharsets.UTF_8));
return sb.toString();
}
@@ -352,6 +376,8 @@
}
dest.writeString(key);
}
+
+ dest.writeParcelable(mNetwork, 0);
}
/** Implement the Parcelable interface */
@@ -381,6 +407,7 @@
}
info.mTxtRecord.put(in.readString(), valueArray);
}
+ info.mNetwork = in.readParcelable(null, Network.class);
return info;
}
diff --git a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
deleted file mode 100644
index 8aca1d6..0000000
--- a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.os.StrictMode;
-import android.util.Log;
-
-import dalvik.system.SocketTagger;
-
-import java.io.FileDescriptor;
-import java.net.SocketException;
-
-/**
- * Assigns tags to sockets for traffic stats.
- * @hide
- */
-public final class NetworkManagementSocketTagger extends SocketTagger {
- private static final String TAG = "NetworkManagementSocketTagger";
- private static final boolean LOGD = false;
-
- private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() {
- @Override
- protected SocketTags initialValue() {
- return new SocketTags();
- }
- };
-
- public static void install() {
- SocketTagger.set(new NetworkManagementSocketTagger());
- }
-
- public static int setThreadSocketStatsTag(int tag) {
- final int old = threadSocketTags.get().statsTag;
- threadSocketTags.get().statsTag = tag;
- return old;
- }
-
- public static int getThreadSocketStatsTag() {
- return threadSocketTags.get().statsTag;
- }
-
- public static int setThreadSocketStatsUid(int uid) {
- final int old = threadSocketTags.get().statsUid;
- threadSocketTags.get().statsUid = uid;
- return old;
- }
-
- public static int getThreadSocketStatsUid() {
- return threadSocketTags.get().statsUid;
- }
-
- @Override
- public void tag(FileDescriptor fd) throws SocketException {
- final SocketTags options = threadSocketTags.get();
- if (LOGD) {
- Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
- + Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid);
- }
- if (options.statsTag == -1) {
- StrictMode.noteUntaggedSocket();
- }
- // TODO: skip tagging when options would be no-op
- tagSocketFd(fd, options.statsTag, options.statsUid);
- }
-
- private void tagSocketFd(FileDescriptor fd, int tag, int uid) {
- if (tag == -1 && uid == -1) return;
-
- final int errno = native_tagSocketFd(fd, tag, uid);
- if (errno < 0) {
- Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", "
- + tag + ", "
- + uid + ") failed with errno" + errno);
- }
- }
-
- @Override
- public void untag(FileDescriptor fd) throws SocketException {
- if (LOGD) {
- Log.i(TAG, "untagSocket(" + fd.getInt$() + ")");
- }
- unTagSocketFd(fd);
- }
-
- private void unTagSocketFd(FileDescriptor fd) {
- final SocketTags options = threadSocketTags.get();
- if (options.statsTag == -1 && options.statsUid == -1) return;
-
- final int errno = native_untagSocketFd(fd);
- if (errno < 0) {
- Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno);
- }
- }
-
- public static class SocketTags {
- public int statsTag = -1;
- public int statsUid = -1;
- }
-
- private static native int native_tagSocketFd(FileDescriptor fd, int tag, int uid);
- private static native int native_untagSocketFd(FileDescriptor fd);
-}
diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp
index 24bc91d..d05370f 100644
--- a/packages/ConnectivityT/service/Android.bp
+++ b/packages/ConnectivityT/service/Android.bp
@@ -28,6 +28,11 @@
"src/com/android/server/net/NetworkStats*.java",
"src/com/android/server/net/BpfInterfaceMapUpdater.java",
"src/com/android/server/net/InterfaceMapValue.java",
+ "src/com/android/server/net/CookieTagMapKey.java",
+ "src/com/android/server/net/CookieTagMapValue.java",
+ "src/com/android/server/net/StatsMapKey.java",
+ "src/com/android/server/net/StatsMapValue.java",
+ "src/com/android/server/net/UidStatsMapKey.java",
],
path: "src",
visibility: [
diff --git a/packages/ConnectivityT/service/src/com/android/server/NsdService.java b/packages/ConnectivityT/service/src/com/android/server/NsdService.java
index 497107d..ddf6d2c 100644
--- a/packages/ConnectivityT/service/src/com/android/server/NsdService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/NsdService.java
@@ -19,6 +19,9 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
import android.net.nsd.INsdManager;
import android.net.nsd.INsdManagerCallback;
import android.net.nsd.INsdServiceConnector;
@@ -44,6 +47,9 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
@@ -60,6 +66,7 @@
private static final boolean DBG = true;
private static final long CLEANUP_DELAY_MS = 10000;
+ private static final int IFACE_IDX_ANY = 0;
private final Context mContext;
private final NsdStateMachine mNsdStateMachine;
@@ -297,7 +304,7 @@
maybeStartDaemon();
id = getUniqueId();
- if (discoverServices(id, args.serviceInfo.getServiceType())) {
+ if (discoverServices(id, args.serviceInfo)) {
if (DBG) {
Log.d(TAG, "Discover " + msg.arg2 + " " + id
+ args.serviceInfo.getServiceType());
@@ -430,13 +437,38 @@
}
switch (code) {
case NativeResponseCode.SERVICE_FOUND:
- /* NNN uniqueId serviceName regType domain */
+ /* NNN uniqueId serviceName regType domain interfaceIdx netId */
servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
+ final int foundNetId;
+ try {
+ foundNetId = Integer.parseInt(cooked[6]);
+ } catch (NumberFormatException e) {
+ Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]);
+ break;
+ }
+ if (foundNetId == 0L) {
+ // Ignore services that do not have a Network: they are not usable
+ // by apps, as they would need privileged permissions to use
+ // interfaces that do not have an associated Network.
+ break;
+ }
+ servInfo.setNetwork(new Network(foundNetId));
clientInfo.onServiceFound(clientId, servInfo);
break;
case NativeResponseCode.SERVICE_LOST:
- /* NNN uniqueId serviceName regType domain */
+ /* NNN uniqueId serviceName regType domain interfaceIdx netId */
+ final int lostNetId;
+ try {
+ lostNetId = Integer.parseInt(cooked[6]);
+ } catch (NumberFormatException e) {
+ Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]);
+ break;
+ }
servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
+ // The network could be null if it was torn down when the service is lost
+ // TODO: avoid returning null in that case, possibly by remembering found
+ // services on the same interface index and their network at the time
+ servInfo.setNetwork(lostNetId == 0 ? null : new Network(lostNetId));
clientInfo.onServiceLost(clientId, servInfo);
break;
case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
@@ -461,7 +493,7 @@
/* NNN regId errorCode */
break;
case NativeResponseCode.SERVICE_RESOLVED:
- /* NNN resolveId fullName hostName port txtlen txtdata */
+ /* NNN resolveId fullName hostName port txtlen txtdata interfaceIdx */
int index = 0;
while (index < cooked[2].length() && cooked[2].charAt(index) != '.') {
if (cooked[2].charAt(index) == '\\') {
@@ -473,6 +505,7 @@
Log.e(TAG, "Invalid service found " + raw);
break;
}
+
String name = cooked[2].substring(0, index);
String rest = cooked[2].substring(index);
String type = rest.replace(".local.", "");
@@ -483,12 +516,13 @@
clientInfo.mResolvedService.setServiceType(type);
clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
clientInfo.mResolvedService.setTxtRecords(cooked[6]);
+ // Network will be added after SERVICE_GET_ADDR_SUCCESS
stopResolveService(id);
removeRequestMap(clientId, id, clientInfo);
int id2 = getUniqueId();
- if (getAddrInfo(id2, cooked[3])) {
+ if (getAddrInfo(id2, cooked[3], cooked[7] /* interfaceIdx */)) {
storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
} else {
clientInfo.onResolveServiceFailed(
@@ -513,12 +547,31 @@
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
- /* NNN resolveId hostname ttl addr */
+ /* NNN resolveId hostname ttl addr interfaceIdx netId */
+ Network network = null;
try {
- clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
+ final int netId = Integer.parseInt(cooked[6]);
+ network = netId == 0L ? null : new Network(netId);
+ } catch (NumberFormatException e) {
+ Log.wtf(TAG, "Invalid network in GET_ADDR_SUCCESS: " + cooked[6], e);
+ }
+
+ InetAddress serviceHost = null;
+ try {
+ serviceHost = InetAddress.getByName(cooked[4]);
+ } catch (UnknownHostException e) {
+ Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
+ }
+
+ // If the resolved service is on an interface without a network, consider it
+ // as a failure: it would not be usable by apps as they would need
+ // privileged permissions.
+ if (network != null && serviceHost != null) {
+ clientInfo.mResolvedService.setHost(serviceHost);
+ clientInfo.mResolvedService.setNetwork(network);
clientInfo.onResolveServiceSucceeded(
clientId, clientInfo.mResolvedService);
- } catch (java.net.UnknownHostException e) {
+ } else {
clientInfo.onResolveServiceFailed(
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
@@ -815,8 +868,15 @@
return mDaemon.execute("update", regId, t.size(), t.getRawData());
}
- private boolean discoverServices(int discoveryId, String serviceType) {
- return mDaemon.execute("discover", discoveryId, serviceType);
+ private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) {
+ final Network network = serviceInfo.getNetwork();
+ final int discoverInterface = getNetworkInterfaceIndex(network);
+ if (network != null && discoverInterface == IFACE_IDX_ANY) {
+ Log.e(TAG, "Interface to discover service on not found");
+ return false;
+ }
+ return mDaemon.execute("discover", discoveryId, serviceInfo.getServiceType(),
+ discoverInterface);
}
private boolean stopServiceDiscovery(int discoveryId) {
@@ -824,17 +884,61 @@
}
private boolean resolveService(int resolveId, NsdServiceInfo service) {
- String name = service.getServiceName();
- String type = service.getServiceType();
- return mDaemon.execute("resolve", resolveId, name, type, "local.");
+ final String name = service.getServiceName();
+ final String type = service.getServiceType();
+ final Network network = service.getNetwork();
+ final int resolveInterface = getNetworkInterfaceIndex(network);
+ if (network != null && resolveInterface == IFACE_IDX_ANY) {
+ Log.e(TAG, "Interface to resolve service on not found");
+ return false;
+ }
+ return mDaemon.execute("resolve", resolveId, name, type, "local.", resolveInterface);
+ }
+
+ /**
+ * Guess the interface to use to resolve or discover a service on a specific network.
+ *
+ * This is an imperfect guess, as for example the network may be gone or not yet fully
+ * registered. This is fine as failing is correct if the network is gone, and a client
+ * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
+ * this is to support the legacy mdnsresponder implementation, which historically resolved
+ * services on an unspecified network.
+ */
+ private int getNetworkInterfaceIndex(Network network) {
+ if (network == null) return IFACE_IDX_ANY;
+
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+ if (cm == null) {
+ Log.wtf(TAG, "No ConnectivityManager for resolveService");
+ return IFACE_IDX_ANY;
+ }
+ final LinkProperties lp = cm.getLinkProperties(network);
+ if (lp == null) return IFACE_IDX_ANY;
+
+ // Only resolve on non-stacked interfaces
+ final NetworkInterface iface;
+ try {
+ iface = NetworkInterface.getByName(lp.getInterfaceName());
+ } catch (SocketException e) {
+ Log.e(TAG, "Error querying interface", e);
+ return IFACE_IDX_ANY;
+ }
+
+ if (iface == null) {
+ Log.e(TAG, "Interface not found: " + lp.getInterfaceName());
+ return IFACE_IDX_ANY;
+ }
+
+ return iface.getIndex();
}
private boolean stopResolveService(int resolveId) {
return mDaemon.execute("stop-resolve", resolveId);
}
- private boolean getAddrInfo(int resolveId, String hostname) {
- return mDaemon.execute("getaddrinfo", resolveId, hostname);
+ private boolean getAddrInfo(int resolveId, String hostname, String interfaceIdx) {
+ // interfaceIdx is always obtained (as string) from the service resolved callback
+ return mDaemon.execute("getaddrinfo", resolveId, hostname, interfaceIdx);
}
private boolean stopGetAddrInfo(int resolveId) {
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java
new file mode 100644
index 0000000..443e5b3
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Key for cookie tag map.
+ */
+public class CookieTagMapKey extends Struct {
+ @Field(order = 0, type = Type.S64)
+ public final long socketCookie;
+
+ public CookieTagMapKey(final long socketCookie) {
+ this.socketCookie = socketCookie;
+ }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java
new file mode 100644
index 0000000..93b9195
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Value for cookie tag map.
+ */
+public class CookieTagMapValue extends Struct {
+ @Field(order = 0, type = Type.U32)
+ public final long uid;
+
+ @Field(order = 1, type = Type.U32)
+ public final long tag;
+
+ public CookieTagMapValue(final long uid, final long tag) {
+ this.uid = uid;
+ this.tag = tag;
+ }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java
new file mode 100644
index 0000000..ea8d836
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Key for both stats maps.
+ */
+public class StatsMapKey extends Struct {
+ @Field(order = 0, type = Type.U32)
+ public final long uid;
+
+ @Field(order = 1, type = Type.U32)
+ public final long tag;
+
+ @Field(order = 2, type = Type.U32)
+ public final long counterSet;
+
+ @Field(order = 3, type = Type.U32)
+ public final long ifaceIndex;
+
+ public StatsMapKey(final long uid, final long tag, final long counterSet,
+ final long ifaceIndex) {
+ this.uid = uid;
+ this.tag = tag;
+ this.counterSet = counterSet;
+ this.ifaceIndex = ifaceIndex;
+ }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java
new file mode 100644
index 0000000..48f26ce
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Value used for both stats maps and uid stats map.
+ */
+public class StatsMapValue extends Struct {
+ @Field(order = 0, type = Type.U63)
+ public final long rxPackets;
+
+ @Field(order = 1, type = Type.U63)
+ public final long rxBytes;
+
+ @Field(order = 2, type = Type.U63)
+ public final long txPackets;
+
+ @Field(order = 3, type = Type.U63)
+ public final long txBytes;
+
+ public StatsMapValue(final long rxPackets, final long rxBytes, final long txPackets,
+ final long txBytes) {
+ this.rxPackets = rxPackets;
+ this.rxBytes = rxBytes;
+ this.txPackets = txPackets;
+ this.txBytes = txBytes;
+ }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java
new file mode 100644
index 0000000..2849f94
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Key for uid stats map.
+ */
+public class UidStatsMapKey extends Struct {
+ @Field(order = 0, type = Type.U32)
+ public final long uid;
+
+ public UidStatsMapKey(final long uid) {
+ this.uid = uid;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 99e3160..df19c67 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -173,8 +173,10 @@
}
public BluetoothDevice getActiveDevice() {
- if (mService == null) return null;
- return mService.getActiveDevice();
+ if (mBluetoothAdapter == null) return null;
+ final List<BluetoothDevice> activeDevices = mBluetoothAdapter
+ .getActiveDevices(BluetoothProfile.A2DP);
+ return (activeDevices.size() > 0) ? activeDevices.get(0) : null;
}
@Override
@@ -221,7 +223,7 @@
}
public boolean supportsHighQualityAudio(BluetoothDevice device) {
- BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice();
if (bluetoothDevice == null) {
return false;
}
@@ -234,7 +236,7 @@
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public boolean isHighQualityAudioEnabled(BluetoothDevice device) {
- BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice();
if (bluetoothDevice == null) {
return false;
}
@@ -260,7 +262,7 @@
}
public void setHighQualityAudioEnabled(BluetoothDevice device, boolean enabled) {
- BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice();
if (bluetoothDevice == null) {
return;
}
@@ -286,7 +288,7 @@
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public String getHighQualityAudioOptionLabel(BluetoothDevice device) {
- BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice();
int unknownCodecId = R.string.bluetooth_profile_a2dp_high_quality_unknown_codec;
if (bluetoothDevice == null || !supportsHighQualityAudio(device)
|| getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index ddee433..233e798 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -500,6 +500,17 @@
}
/**
+ * Get identity address from remote device
+ * @return {@link BluetoothDevice#getIdentityAddress()} if
+ * {@link BluetoothDevice#getIdentityAddress()} is not null otherwise return
+ * {@link BluetoothDevice#getAddress()}
+ */
+ public String getIdentityAddress() {
+ final String identityAddress = mDevice.getIdentityAddress();
+ return TextUtils.isEmpty(identityAddress) ? getAddress() : identityAddress;
+ }
+
+ /**
* Get name from remote device
* @return {@link BluetoothDevice#getAlias()} if
* {@link BluetoothDevice#getAlias()} is not null otherwise return
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 9dd329e..b44d914 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -132,10 +132,12 @@
}
public BluetoothDevice getActiveDevice() {
- if (mService == null) {
+ if (mBluetoothAdapter == null) {
return null;
}
- return mService.getActiveDevice();
+ final List<BluetoothDevice> activeDevices = mBluetoothAdapter
+ .getActiveDevices(BluetoothProfile.HEADSET);
+ return (activeDevices.size() > 0) ? activeDevices.get(0) : null;
}
public boolean isAudioOn() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index a1fba4a..ff1368a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -168,8 +168,10 @@
}
public List<BluetoothDevice> getActiveDevices() {
- if (mService == null) return new ArrayList<>();
- return mService.getActiveDevices();
+ if (mBluetoothAdapter == null) {
+ return new ArrayList<>();
+ }
+ return mBluetoothAdapter.getActiveDevices(BluetoothProfile.HEARING_AID);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
index 209507a..db6d41e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
@@ -21,12 +21,12 @@
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
@@ -177,10 +177,10 @@
}
public List<BluetoothDevice> getActiveDevices() {
- if (mService == null) {
+ if (mBluetoothAdapter == null) {
return new ArrayList<>();
}
- return mService.getActiveDevices();
+ return mBluetoothAdapter.getActiveDevices(BluetoothProfile.LE_AUDIO);
}
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index f167721..d7b366e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -60,6 +60,8 @@
private BluetoothDevice mDevice;
@Mock
private BluetoothA2dp mBluetoothA2dp;
+ @Mock
+ private BluetoothAdapter mBluetoothAdapter;
private BluetoothProfile.ServiceListener mServiceListener;
private A2dpProfile mProfile;
@@ -72,7 +74,8 @@
mProfile = new A2dpProfile(mContext, mDeviceManager, mProfileManager);
mServiceListener = mShadowBluetoothAdapter.getServiceListener();
mServiceListener.onServiceConnected(BluetoothProfile.A2DP, mBluetoothA2dp);
- when(mBluetoothA2dp.getActiveDevice()).thenReturn(mDevice);
+ when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.A2DP)))
+ .thenReturn(Arrays.asList(mDevice));
}
@Test
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5b2e8ae..074d85c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -190,6 +190,9 @@
<uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" />
+ <!-- Permission required for processes that don't own the focused window to switch
+ touch mode state -->
+ <uses-permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE" />
<!-- Permission required to test onPermissionsChangedListener -->
<uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
<uses-permission android:name="android.permission.SET_KEYBOARD_LAYOUT" />
@@ -504,6 +507,7 @@
<!-- Permission needed for CTS test - WifiManagerTest -->
<uses-permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" />
<uses-permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
+ <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
<!-- Permission required for CTS tests to enable/disable rate limiting toasts. -->
@@ -532,6 +536,7 @@
<!-- Permissions required for CTS test - TrustTestCases -->
<uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
<uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+ <uses-permission android:name="android.permission.TRUST_LISTENER" />
<!-- Permission required for CTS test - CtsGameManagerTestCases -->
<uses-permission android:name="android.permission.MANAGE_GAME_MODE" />
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 48f5b51..a0575cf 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -484,7 +484,9 @@
HashMap<String, Long> timestamps = readTimestamps();
try {
if (proto) {
- db.addFile(TAG_TOMBSTONE_PROTO, tombstone, 0);
+ if (recordFileTimestamp(tombstone, timestamps)) {
+ db.addFile(TAG_TOMBSTONE_PROTO, tombstone, 0);
+ }
} else {
final String headers = getBootHeadersToLogAndUpdate();
addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
@@ -526,16 +528,10 @@
if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled
File file = new File(filename);
- long fileTime = file.lastModified();
- if (fileTime <= 0) return; // File does not exist
-
- if (timestamps.containsKey(filename) && timestamps.get(filename) == fileTime) {
- return; // Already logged this particular file
+ if (!recordFileTimestamp(file, timestamps)) {
+ return;
}
- timestamps.put(filename, fileTime);
-
-
String fileContents = FileUtils.readTextFile(file, maxSize, TAG_TRUNCATED);
String text = headers + fileContents + footers;
// Create an additional report for system server native crashes, with a special tag.
@@ -548,6 +544,19 @@
addTextToDropBox(db, tag, text, filename, maxSize);
}
+ private static boolean recordFileTimestamp(File file, HashMap<String, Long> timestamps) {
+ final long fileTime = file.lastModified();
+ if (fileTime <= 0) return false; // File does not exist
+
+ final String filename = file.getPath();
+ if (timestamps.containsKey(filename) && timestamps.get(filename) == fileTime) {
+ return false; // Already logged this particular file
+ }
+
+ timestamps.put(filename, fileTime);
+ return true;
+ }
+
private static void addTextToDropBox(DropBoxManager db, String tag, String text,
String filename, int maxSize) {
Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 6a7afd9..2d328d8 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -539,7 +539,13 @@
@GuardedBy("mLock")
private void notifyAllPolicyListenersLocked() {
for (final PolicyListenerBinderDeath policyListener : mRegisteredPolicyListeners.values()) {
- Binder.withCleanCallingIdentity(() -> policyListener.mListener.onPolicyChanged());
+ Binder.withCleanCallingIdentity(() -> {
+ try {
+ policyListener.mListener.onPolicyChanged();
+ } catch (RemoteException e) {
+ logDbg("VcnStatusCallback threw on VCN status change", e);
+ }
+ });
}
}
@@ -548,8 +554,13 @@
@NonNull ParcelUuid subGroup, @VcnStatusCode int statusCode) {
for (final VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
if (isCallbackPermissioned(cbInfo, subGroup)) {
- Binder.withCleanCallingIdentity(
- () -> cbInfo.mCallback.onVcnStatusChanged(statusCode));
+ Binder.withCleanCallingIdentity(() -> {
+ try {
+ cbInfo.mCallback.onVcnStatusChanged(statusCode);
+ } catch (RemoteException e) {
+ logDbg("VcnStatusCallback threw on VCN status change", e);
+ }
+ });
}
}
}
@@ -1222,13 +1233,17 @@
// Notify all registered StatusCallbacks for this subGroup
for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
if (isCallbackPermissioned(cbInfo, mSubGroup)) {
- Binder.withCleanCallingIdentity(
- () ->
- cbInfo.mCallback.onGatewayConnectionError(
- gatewayConnectionName,
- errorCode,
- exceptionClass,
- exceptionMessage));
+ Binder.withCleanCallingIdentity(() -> {
+ try {
+ cbInfo.mCallback.onGatewayConnectionError(
+ gatewayConnectionName,
+ errorCode,
+ exceptionClass,
+ exceptionMessage);
+ } catch (RemoteException e) {
+ logDbg("VcnStatusCallback threw on VCN status change", e);
+ }
+ });
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 2eb59b2..54987cf 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -29,7 +29,7 @@
import android.media.AudioManager;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
-import android.media.BtProfileConnectionInfo;
+import android.media.BluetoothProfileConnectionInfo;
import android.media.IAudioRoutesObserver;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICommunicationDeviceDispatcher;
@@ -517,12 +517,12 @@
/*package*/ static final class BtDeviceChangedData {
final @Nullable BluetoothDevice mNewDevice;
final @Nullable BluetoothDevice mPreviousDevice;
- final @NonNull BtProfileConnectionInfo mInfo;
+ final @NonNull BluetoothProfileConnectionInfo mInfo;
final @NonNull String mEventSource;
BtDeviceChangedData(@Nullable BluetoothDevice newDevice,
@Nullable BluetoothDevice previousDevice,
- @NonNull BtProfileConnectionInfo info, @NonNull String eventSource) {
+ @NonNull BluetoothProfileConnectionInfo info, @NonNull String eventSource) {
mNewDevice = newDevice;
mPreviousDevice = previousDevice;
mInfo = info;
@@ -554,9 +554,9 @@
mDevice = device;
mState = state;
mProfile = d.mInfo.getProfile();
- mSupprNoisy = d.mInfo.getSuppressNoisyIntent();
+ mSupprNoisy = d.mInfo.isSuppressNoisyIntent();
mVolume = d.mInfo.getVolume();
- mIsLeOutput = d.mInfo.getIsLeOutput();
+ mIsLeOutput = d.mInfo.isLeOutput();
mEventSource = d.mEventSource;
mAudioSystemDevice = audioDevice;
mMusicDevice = AudioSystem.DEVICE_NONE;
@@ -627,7 +627,7 @@
audioDevice = AudioSystem.DEVICE_OUT_HEARING_AID;
break;
case BluetoothProfile.LE_AUDIO:
- if (d.mInfo.getIsLeOutput()) {
+ if (d.mInfo.isLeOutput()) {
audioDevice = AudioSystem.DEVICE_OUT_BLE_HEADSET;
} else {
audioDevice = AudioSystem.DEVICE_IN_BLE_HEADSET;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index e00c8a3..070725e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -83,7 +83,7 @@
import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
-import android.media.BtProfileConnectionInfo;
+import android.media.BluetoothProfileConnectionInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioModeDispatcher;
import android.media.IAudioRoutesObserver;
@@ -6301,14 +6301,14 @@
* See AudioManager.handleBluetoothActiveDeviceChanged(...)
*/
public void handleBluetoothActiveDeviceChanged(BluetoothDevice newDevice,
- BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) {
+ BluetoothDevice previousDevice, @NonNull BluetoothProfileConnectionInfo info) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH_STACK)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Bluetooth is the only caller allowed");
}
if (info == null) {
- throw new IllegalArgumentException("Illegal null BtProfileConnectionInfo for device "
- + previousDevice + " -> " + newDevice);
+ throw new IllegalArgumentException("Illegal null BluetoothProfileConnectionInfo for"
+ + " device " + previousDevice + " -> " + newDevice);
}
final int profile = info.getProfile();
if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 9273a5d..1c29935 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -31,7 +31,7 @@
import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
-import android.media.BtProfileConnectionInfo;
+import android.media.BluetoothProfileConnectionInfo;
import android.os.Binder;
import android.os.UserHandle;
import android.provider.Settings;
@@ -495,11 +495,13 @@
if (state == BluetoothProfile.STATE_CONNECTED) {
mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(btDevice, null,
- new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener"));
+ new BluetoothProfileConnectionInfo(profile),
+ "mBluetoothProfileServiceListener"));
} else {
mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(null, btDevice,
- new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener"));
+ new BluetoothProfileConnectionInfo(profile),
+ "mBluetoothProfileServiceListener"));
}
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 38df5f8..a0c5aa3 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -639,7 +639,7 @@
clipboard.primaryClipListeners.getBroadcastItem(i)
.dispatchPrimaryClipChanged();
}
- } catch (RemoteException e) {
+ } catch (RemoteException | SecurityException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index c27293c..27f22c4 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -829,17 +829,12 @@
protected int handleVendorCommandWithId(HdmiCecMessage message) {
byte[] params = message.getParams();
int vendorId = HdmiUtils.threeBytesToInt(params);
- if (vendorId == mService.getVendorId()) {
- if (!mService.invokeVendorCommandListenersOnReceived(
- mDeviceType, message.getSource(), message.getDestination(), params, true)) {
- return Constants.ABORT_REFUSED;
- }
- } else if (message.getDestination() != Constants.ADDR_BROADCAST
- && message.getSource() != Constants.ADDR_UNREGISTERED) {
- Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
- return Constants.ABORT_UNRECOGNIZED_OPCODE;
- } else {
+ if (message.getDestination() == Constants.ADDR_BROADCAST
+ || message.getSource() == Constants.ADDR_UNREGISTERED) {
Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
+ } else if (!mService.invokeVendorCommandListenersOnReceived(
+ mDeviceType, message.getSource(), message.getDestination(), params, true)) {
+ return Constants.ABORT_REFUSED;
}
return Constants.HANDLED;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
index 6f7473d..57fe9e6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
@@ -133,6 +133,7 @@
addHandler(Constants.MESSAGE_SET_OSD_NAME, mBypasser);
addHandler(Constants.MESSAGE_DEVICE_VENDOR_ID, mBypasser);
addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBypasser);
+ addHandler(Constants.MESSAGE_GIVE_FEATURES, mBypasser);
addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index a571dc0..8dadf5a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1582,11 +1582,11 @@
class VendorCommandListenerRecord implements IBinder.DeathRecipient {
private final IHdmiVendorCommandListener mListener;
- private final int mDeviceType;
+ private final int mVendorId;
- public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
+ VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int vendorId) {
mListener = listener;
- mDeviceType = deviceType;
+ mVendorId = vendorId;
}
@Override
@@ -2077,10 +2077,10 @@
}
@Override
- public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
- final int deviceType) {
+ public void addVendorCommandListener(
+ final IHdmiVendorCommandListener listener, final int vendorId) {
initBinderCall();
- HdmiControlService.this.addVendorCommandListener(listener, deviceType);
+ HdmiControlService.this.addVendorCommandListener(listener, vendorId);
}
@Override
@@ -3219,8 +3219,9 @@
}
}
- private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
- VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
+ @VisibleForTesting
+ void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
+ VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, vendorId);
try {
listener.asBinder().linkToDeath(record, 0);
} catch (RemoteException e) {
@@ -3239,8 +3240,14 @@
return false;
}
for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
- if (record.mDeviceType != deviceType) {
- continue;
+ if (hasVendorId) {
+ int vendorId =
+ ((params[0] & 0xFF) << 16)
+ + ((params[1] & 0xFF) << 8)
+ + (params[2] & 0xFF);
+ if (record.mVendorId != vendorId) {
+ continue;
+ }
}
try {
record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId);
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index 9e1445c..bdea679 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -30,7 +30,7 @@
import android.content.Intent;
import android.media.AudioManager;
import android.media.AudioSystem;
-import android.media.BtProfileConnectionInfo;
+import android.media.BluetoothProfileConnectionInfo;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -100,7 +100,7 @@
mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null,
- BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource"));
+ BluetoothProfileConnectionInfo.createA2dpInfo(true, 1), "testSource"));
Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS);
verify(mSpyDevInventory, times(1)).setBluetoothActiveDevice(
any(AudioDeviceBroker.BtDeviceInfo.class)
@@ -208,13 +208,13 @@
// first connection: ensure the device is connected as a starting condition for the test
mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null,
- BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource"));
+ BluetoothProfileConnectionInfo.createA2dpInfo(true, 1), "testSource"));
Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
// disconnection
mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(null, mFakeBtDevice,
- BtProfileConnectionInfo.a2dpInfo(false, -1), "testSource"));
+ BluetoothProfileConnectionInfo.createA2dpInfo(false, -1), "testSource"));
if (delayAfterDisconnection > 0) {
Thread.sleep(delayAfterDisconnection);
}
@@ -222,7 +222,7 @@
// reconnection
mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null,
- BtProfileConnectionInfo.a2dpInfo(true, 2), "testSource"));
+ BluetoothProfileConnectionInfo.createA2dpInfo(true, 2), "testSource"));
Thread.sleep(AudioService.BECOMING_NOISY_DELAY_MS + MAX_MESSAGE_HANDLING_DELAY_MS);
// Verify disconnection has been cancelled and we're seeing two connections attempts,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 01bd04c..4c39768 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -43,6 +43,7 @@
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
+import android.hardware.hdmi.IHdmiVendorCommandListener;
import android.os.Binder;
import android.os.IPowerManager;
import android.os.IThermalService;
@@ -886,6 +887,114 @@
}
@Test
+ public void addVendorCommandListener_receiveCallback_VendorCmdNoIdTest() {
+ int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+ int sourceAddress = Constants.ADDR_TV;
+ byte[] params = {0x00, 0x01, 0x02, 0x03};
+ int vendorId = 0x123456;
+
+ VendorCommandListener vendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage vendorCommandNoId =
+ HdmiCecMessageBuilder.buildVendorCommand(sourceAddress, destAddress, params);
+ mNativeWrapper.onCecMessage(vendorCommandNoId);
+ mTestLooper.dispatchAll();
+ assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue();
+ assertThat(vendorCmdListener.mParamsCorrect).isTrue();
+ assertThat(vendorCmdListener.mHasVendorId).isFalse();
+ }
+
+ @Test
+ public void addVendorCommandListener_receiveCallback_VendorCmdWithIdTest() {
+ int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+ int sourceAddress = Constants.ADDR_TV;
+ byte[] params = {0x00, 0x01, 0x02, 0x03};
+ int vendorId = 0x123456;
+
+ VendorCommandListener vendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage vendorCommandWithId =
+ HdmiCecMessageBuilder.buildVendorCommandWithId(
+ sourceAddress, destAddress, vendorId, params);
+ mNativeWrapper.onCecMessage(vendorCommandWithId);
+ mTestLooper.dispatchAll();
+ assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue();
+ assertThat(vendorCmdListener.mParamsCorrect).isTrue();
+ assertThat(vendorCmdListener.mHasVendorId).isTrue();
+ }
+
+ @Test
+ public void addVendorCommandListener_noCallback_VendorCmdDiffIdTest() {
+ int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+ int sourceAddress = Constants.ADDR_TV;
+ byte[] params = {0x00, 0x01, 0x02, 0x03};
+ int vendorId = 0x123456;
+ int diffVendorId = 0x345678;
+
+ VendorCommandListener vendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage vendorCommandWithDiffId =
+ HdmiCecMessageBuilder.buildVendorCommandWithId(
+ sourceAddress, destAddress, diffVendorId, params);
+ mNativeWrapper.onCecMessage(vendorCommandWithDiffId);
+ mTestLooper.dispatchAll();
+ assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isFalse();
+ }
+
+ private static class VendorCommandListener extends IHdmiVendorCommandListener.Stub {
+ boolean mVendorCommandCallbackReceived = false;
+ boolean mParamsCorrect = false;
+ boolean mHasVendorId = false;
+
+ int mSourceAddress;
+ int mDestAddress;
+ byte[] mParams;
+ int mVendorId;
+
+ VendorCommandListener(int sourceAddress, int destAddress, byte[] params, int vendorId) {
+ this.mSourceAddress = sourceAddress;
+ this.mDestAddress = destAddress;
+ this.mParams = params.clone();
+ this.mVendorId = vendorId;
+ }
+
+ @Override
+ public void onReceived(
+ int sourceAddress, int destAddress, byte[] params, boolean hasVendorId) {
+ mVendorCommandCallbackReceived = true;
+ if (mSourceAddress == sourceAddress && mDestAddress == destAddress) {
+ byte[] expectedParams;
+ if (hasVendorId) {
+ // If the command has vendor ID, we have to add it to mParams.
+ expectedParams = new byte[params.length];
+ expectedParams[0] = (byte) ((mVendorId >> 16) & 0xFF);
+ expectedParams[1] = (byte) ((mVendorId >> 8) & 0xFF);
+ expectedParams[2] = (byte) (mVendorId & 0xFF);
+ System.arraycopy(mParams, 0, expectedParams, 3, mParams.length);
+ } else {
+ expectedParams = params.clone();
+ }
+ if (Arrays.equals(expectedParams, params)) {
+ mParamsCorrect = true;
+ }
+ }
+ mHasVendorId = hasVendorId;
+ }
+
+ @Override
+ public void onControlStateChanged(boolean enabled, int reason) {}
+ }
+
+ @Test
public void dispatchMessageToLocalDevice_broadcastMessage_returnsHandled() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildStandby(
Constants.ADDR_TV,
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 1252965..9a991a1 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -107,6 +107,25 @@
}
}
+ /**
+ * Convenience method for running the provided action in the provided
+ * executor enclosed in
+ * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity}
+ *
+ * Any exception thrown by the given action will need to be handled by caller.
+ *
+ */
+ public static void runWithCleanCallingIdentity(
+ @NonNull Runnable action, @NonNull Executor executor) {
+ if (action != null) {
+ if (executor != null) {
+ executor.execute(() -> runWithCleanCallingIdentity(action));
+ } else {
+ runWithCleanCallingIdentity(action);
+ }
+ }
+ }
+
/**
* Convenience method for running the provided action enclosed in
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 9a764a0..43d07b9 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4402,12 +4402,10 @@
"carrier_auto_cancel_cs_notification";
/**
- * Passing this value as {@link KEY_SUBSCRIPTION_GROUP_UUID_STRING} will remove the
+ * Passing this value as {@link #KEY_SUBSCRIPTION_GROUP_UUID_STRING} will remove the
* subscription from a group instead of adding it to a group.
*
- * TODO: Expose in a future release.
- *
- * @hide
+ * <p>This value will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
*/
public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000";
@@ -4420,9 +4418,7 @@
* <p>If set to {@link #REMOVE_GROUP_UUID_STRING}, then the subscription will be removed from
* its current group.
*
- * TODO: unhide this key.
- *
- * @hide
+ * <p>This key will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
*/
public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING =
"subscription_group_uuid_string";
@@ -4447,17 +4443,15 @@
"data_switch_validation_min_gap_long";
/**
- * A boolean property indicating whether this subscription should be managed as an opportunistic
- * subscription.
- *
- * If true, then this subscription will be selected based on available coverage and will not be
- * available for a user in settings menus for selecting macro network providers. If unset,
- * defaults to “false”.
- *
- * TODO: unhide this key.
- *
- * @hide
- */
+ * A boolean property indicating whether this subscription should be managed as an opportunistic
+ * subscription.
+ *
+ * If true, then this subscription will be selected based on available coverage and will not be
+ * available for a user in settings menus for selecting macro network providers. If unset,
+ * defaults to “false”.
+ *
+ * <p>This key will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
+ */
public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL =
"is_opportunistic_subscription_bool";
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 051d6c5..13d8c7b 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -259,9 +259,10 @@
}
/**
- * Unthrottles the APN on the current transport. There is no matching "APN throttle" method.
- * Instead, the APN is throttled when {@link IDataService#setupDataCall} fails within
- * the time specified by {@link DataCallResponse#getRetryDurationMillis}.
+ * Unthrottles the APN on the current transport.
+ * The APN is throttled when {@link IDataService#setupDataCall} fails within
+ * the time specified by {@link DataCallResponse#getRetryDurationMillis} and will remain
+ * throttled until this method is called.
* <p/>
* see: {@link DataCallResponse#getRetryDurationMillis}
*
@@ -282,9 +283,9 @@
/**
* Unthrottles the DataProfile on the current transport.
- * There is no matching "DataProfile throttle" method.
- * Instead, the DataProfile is throttled when {@link IDataService#setupDataCall} fails within
- * the time specified by {@link DataCallResponse#getRetryDurationMillis}.
+ * The DataProfile is throttled when {@link IDataService#setupDataCall} fails within
+ * the time specified by {@link DataCallResponse#getRetryDurationMillis} and will remain
+ * throttled until this method is called.
* <p/>
* see: {@link DataCallResponse#getRetryDurationMillis}
*
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index dfe5e6c9..d65286f 100755
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -27,10 +27,12 @@
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsVideoCallProvider;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
/**
* Provides the call initiation/termination, and media exchange between two IMS endpoints.
@@ -522,6 +524,7 @@
private final IImsCallSession miSession;
private boolean mClosed = false;
private Listener mListener;
+ private Executor mListenerExecutor = Runnable::run;
/** @hide */
public ImsCallSession(IImsCallSession iSession) {
@@ -538,9 +541,9 @@
}
/** @hide */
- public ImsCallSession(IImsCallSession iSession, Listener listener) {
+ public ImsCallSession(IImsCallSession iSession, Listener listener, Executor executor) {
this(iSession);
- setListener(listener);
+ setListener(listener, executor);
}
/**
@@ -738,10 +741,14 @@
* override the previous listener.
*
* @param listener to listen to the session events of this object
+ * @param executor an Executor that will execute callbacks
* @hide
*/
- public void setListener(Listener listener) {
+ public void setListener(Listener listener, Executor executor) {
mListener = listener;
+ if (executor != null) {
+ mListenerExecutor = executor;
+ }
}
/**
@@ -1205,44 +1212,56 @@
*/
@Override
public void callSessionInitiating(ImsCallProfile profile) {
- if (mListener != null) {
- mListener.callSessionInitiating(ImsCallSession.this, profile);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionInitiating(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionProgressing(ImsStreamMediaProfile profile) {
- if (mListener != null) {
- mListener.callSessionProgressing(ImsCallSession.this, profile);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionProgressing(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionInitiated(ImsCallProfile profile) {
- if (mListener != null) {
- mListener.callSessionStarted(ImsCallSession.this, profile);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionStarted(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionInitiatingFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionTerminated(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1250,44 +1269,56 @@
*/
@Override
public void callSessionHeld(ImsCallProfile profile) {
- if (mListener != null) {
- mListener.callSessionHeld(ImsCallSession.this, profile);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionHeld(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionHoldReceived(ImsCallProfile profile) {
- if (mListener != null) {
- mListener.callSessionHoldReceived(ImsCallSession.this, profile);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionHoldReceived(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionResumed(ImsCallProfile profile) {
- if (mListener != null) {
- mListener.callSessionResumed(ImsCallSession.this, profile);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionResumed(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionResumeReceived(ImsCallProfile profile) {
- if (mListener != null) {
- mListener.callSessionResumeReceived(ImsCallSession.this, profile);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionResumeReceived(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1310,15 +1341,17 @@
*/
@Override
public void callSessionMergeComplete(IImsCallSession newSession) {
- if (mListener != null) {
- if (newSession != null) {
- // New session created after conference
- mListener.callSessionMergeComplete(new ImsCallSession(newSession));
- } else {
- // Session already exists. Hence no need to pass
- mListener.callSessionMergeComplete(null);
- }
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ if (newSession != null) {
+ // New session created after conference
+ mListener.callSessionMergeComplete(new ImsCallSession(newSession));
+ } else {
+ // Session already exists. Hence no need to pass
+ mListener.callSessionMergeComplete(null);
+ }
+ }
+ }, mListenerExecutor);
}
/**
@@ -1328,9 +1361,11 @@
*/
@Override
public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1338,23 +1373,29 @@
*/
@Override
public void callSessionUpdated(ImsCallProfile profile) {
- if (mListener != null) {
- mListener.callSessionUpdated(ImsCallSession.this, profile);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionUpdated(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionUpdateReceived(ImsCallProfile profile) {
- if (mListener != null) {
- mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1363,26 +1404,33 @@
@Override
public void callSessionConferenceExtended(IImsCallSession newSession,
ImsCallProfile profile) {
- if (mListener != null) {
- mListener.callSessionConferenceExtended(ImsCallSession.this,
- new ImsCallSession(newSession), profile);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtended(ImsCallSession.this,
+ new ImsCallSession(newSession), profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtendFailed(
+ ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionConferenceExtendReceived(IImsCallSession newSession,
ImsCallProfile profile) {
- if (mListener != null) {
- mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
- new ImsCallSession(newSession), profile);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
+ new ImsCallSession(newSession), profile);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1391,32 +1439,41 @@
*/
@Override
public void callSessionInviteParticipantsRequestDelivered() {
- if (mListener != null) {
- mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionInviteParticipantsRequestDelivered(
+ ImsCallSession.this);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
- reasonInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
+ reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionRemoveParticipantsRequestDelivered() {
- if (mListener != null) {
- mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
- reasonInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
+ reasonInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1424,9 +1481,11 @@
*/
@Override
public void callSessionConferenceStateUpdated(ImsConferenceState state) {
- if (mListener != null) {
- mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1434,9 +1493,12 @@
*/
@Override
public void callSessionUssdMessageReceived(int mode, String ussdMessage) {
- if (mListener != null) {
- mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode,
+ ussdMessage);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1452,10 +1514,12 @@
*/
@Override
public void callSessionMayHandover(int srcNetworkType, int targetNetworkType) {
- if (mListener != null) {
- mListener.callSessionMayHandover(ImsCallSession.this, srcNetworkType,
- targetNetworkType);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionMayHandover(ImsCallSession.this, srcNetworkType,
+ targetNetworkType);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1464,10 +1528,12 @@
@Override
public void callSessionHandover(int srcNetworkType, int targetNetworkType,
ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- mListener.callSessionHandover(ImsCallSession.this, srcNetworkType,
- targetNetworkType, reasonInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionHandover(ImsCallSession.this, srcNetworkType,
+ targetNetworkType, reasonInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1476,10 +1542,12 @@
@Override
public void callSessionHandoverFailed(int srcNetworkType, int targetNetworkType,
ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- mListener.callSessionHandoverFailed(ImsCallSession.this, srcNetworkType,
- targetNetworkType, reasonInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionHandoverFailed(ImsCallSession.this, srcNetworkType,
+ targetNetworkType, reasonInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1487,9 +1555,11 @@
*/
@Override
public void callSessionTtyModeReceived(int mode) {
- if (mListener != null) {
- mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1499,16 +1569,22 @@
* otherwise.
*/
public void callSessionMultipartyStateChanged(boolean isMultiParty) {
- if (mListener != null) {
- mListener.callSessionMultipartyStateChanged(ImsCallSession.this, isMultiParty);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionMultipartyStateChanged(ImsCallSession.this,
+ isMultiParty);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppServiceInfo ) {
- if (mListener != null) {
- mListener.callSessionSuppServiceReceived(ImsCallSession.this, suppServiceInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionSuppServiceReceived(ImsCallSession.this,
+ suppServiceInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1516,9 +1592,12 @@
*/
@Override
public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) {
- if (mListener != null) {
- mListener.callSessionRttModifyRequestReceived(ImsCallSession.this, callProfile);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRttModifyRequestReceived(ImsCallSession.this,
+ callProfile);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1526,9 +1605,11 @@
*/
@Override
public void callSessionRttModifyResponseReceived(int status) {
- if (mListener != null) {
- mListener.callSessionRttModifyResponseReceived(status);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRttModifyResponseReceived(status);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1536,9 +1617,11 @@
*/
@Override
public void callSessionRttMessageReceived(String rttMessage) {
- if (mListener != null) {
- mListener.callSessionRttMessageReceived(rttMessage);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRttMessageReceived(rttMessage);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1546,23 +1629,29 @@
*/
@Override
public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
- if (mListener != null) {
- mListener.callSessionRttAudioIndicatorChanged(profile);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRttAudioIndicatorChanged(profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionTransferred() {
- if (mListener != null) {
- mListener.callSessionTransferred(ImsCallSession.this);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionTransferred(ImsCallSession.this);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1571,9 +1660,11 @@
*/
@Override
public void callSessionDtmfReceived(char dtmf) {
- if (mListener != null) {
- mListener.callSessionDtmfReceived(dtmf);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionDtmfReceived(dtmf);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1581,9 +1672,11 @@
*/
@Override
public void callQualityChanged(CallQuality callQuality) {
- if (mListener != null) {
- mListener.callQualityChanged(callQuality);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callQualityChanged(callQuality);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1593,10 +1686,12 @@
@Override
public void callSessionRtpHeaderExtensionsReceived(
@NonNull List<RtpHeaderExtension> extensions) {
- if (mListener != null) {
- mListener.callSessionRtpHeaderExtensionsReceived(
- new ArraySet<RtpHeaderExtension>(extensions));
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRtpHeaderExtensionsReceived(
+ new ArraySet<RtpHeaderExtension>(extensions));
+ }
+ }, mListenerExecutor);
}
}
diff --git a/telephony/java/android/telephony/ims/RcsClientConfiguration.java b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
index c25ace0..f367e40 100644
--- a/telephony/java/android/telephony/ims/RcsClientConfiguration.java
+++ b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
@@ -34,7 +34,7 @@
/**@hide*/
@StringDef(prefix = "RCS_PROFILE_",
- value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3})
+ value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3, RCS_PROFILE_2_4})
public @interface StringRcsProfile {}
/**
@@ -45,6 +45,10 @@
* RCS profile UP 2.3
*/
public static final String RCS_PROFILE_2_3 = "UP_2.3";
+ /**
+ * RCS profile UP 2.4
+ */
+ public static final String RCS_PROFILE_2_4 = "UP_2.4";
private String mRcsVersion;
private String mRcsProfile;
@@ -58,8 +62,8 @@
* @param rcsVersion The parameter identifies the RCS version supported
* by the client. Refer to GSMA RCC.07 "rcs_version" parameter.
* @param rcsProfile Identifies a fixed set of RCS services that are
- * supported by the client. See {@link #RCS_PROFILE_1_0 } or
- * {@link #RCS_PROFILE_2_3 }
+ * supported by the client. See {@link #RCS_PROFILE_1_0 },
+ * {@link #RCS_PROFILE_2_3 } or {@link #RCS_PROFILE_2_4 }
* @param clientVendor Identifies the vendor providing the RCS client.
* @param clientVersion Identifies the RCS client version. Refer to GSMA
* RCC.07 "client_version" parameter.
@@ -80,8 +84,8 @@
* @param rcsVersion The parameter identifies the RCS version supported
* by the client. Refer to GSMA RCC.07 "rcs_version" parameter.
* @param rcsProfile Identifies a fixed set of RCS services that are
- * supported by the client. See {@link #RCS_PROFILE_1_0 } or
- * {@link #RCS_PROFILE_2_3 }
+ * supported by the client. See {@link #RCS_PROFILE_1_0 },
+ * {@link #RCS_PROFILE_2_3 } or {@link #RCS_PROFILE_2_4 }
* @param clientVendor Identifies the vendor providing the RCS client.
* @param clientVersion Identifies the RCS client version. Refer to GSMA
* RCC.07 "client_version" parameter.
diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml
index c30d761..21256d8 100644
--- a/tests/SilkFX/AndroidManifest.xml
+++ b/tests/SilkFX/AndroidManifest.xml
@@ -20,17 +20,20 @@
<uses-sdk android:minSdkVersion="30"/>
<uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application android:label="SilkFX"
android:theme="@android:style/Theme.Material">
<activity android:name=".Main"
android:label="SilkFX Demos"
+ android:banner="@drawable/background1"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
@@ -41,13 +44,16 @@
<activity android:name=".materials.GlassActivity"
android:label="Glass Examples"
- android:banner="@drawable/background1"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name=".materials.BackgroundBlurActivity"
+ android:theme="@style/Theme.BackgroundBlurTheme"
+ android:exported="true">
+ </activity>
+
</application>
</manifest>
diff --git a/tests/SilkFX/res/drawable/background_blur_drawable.xml b/tests/SilkFX/res/drawable/background_blur_drawable.xml
new file mode 100644
index 0000000..173ca99
--- /dev/null
+++ b/tests/SilkFX/res/drawable/background_blur_drawable.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="#20FFFFFF"/>
+ <corners android:radius="10dp"/>
+</shape>
diff --git a/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml
new file mode 100644
index 0000000..bd8942d
--- /dev/null
+++ b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="10dp"/>
+</shape>
diff --git a/tests/SilkFX/res/layout/activity_background_blur.xml b/tests/SilkFX/res/layout/activity_background_blur.xml
new file mode 100644
index 0000000..f13c088
--- /dev/null
+++ b/tests/SilkFX/res/layout/activity_background_blur.xml
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/background"
+ android:layout_width="390dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:padding="15dp"
+ android:orientation="vertical"
+ tools:context=".materials.BackgroundBlurActivity">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:padding="10dp"
+ android:textColor="#ffffffff"
+ android:text="Hello blurry world!"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Background blur"/>
+
+ <SeekBar
+ android:id="@+id/set_background_blur"
+ android:min="0"
+ android:max="300"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/background_blur_radius"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="#ffffffff"
+ android:ems="3"
+ android:gravity="center"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:text="TODO"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Background alpha"/>
+
+ <SeekBar
+ android:id="@+id/set_background_alpha"
+ android:min="0"
+ android:max="100"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/background_alpha"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="#ffffffff"
+ android:ems="3"
+ android:gravity="center"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:text="TODO"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Blur behind"/>
+
+ <SeekBar
+ android:id="@+id/set_blur_behind"
+ android:min="0"
+ android:max="300"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/blur_behind_radius"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textColor="#ffffffff"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:ems="3"
+ android:text="TODO"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Dim amount"/>
+
+ <SeekBar
+ android:id="@+id/set_dim_amount"
+ android:min="0"
+ android:max="100"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/dim_amount"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textColor="#ffffffff"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:ems="3"
+ android:text="TODO"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginTop="5dp"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <Button
+ android:id="@+id/toggle_blur_enabled"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Disable blur"
+ android:onClick="toggleForceBlurDisabled"/>
+
+ <Button
+ android:id="@+id/toggle_battery_saving_mode"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TODO"
+ android:onClick="toggleBatterySavingMode"/>
+ </LinearLayout>
+ <requestFocus/>
+
+</LinearLayout>
diff --git a/tests/SilkFX/res/values/style.xml b/tests/SilkFX/res/values/style.xml
new file mode 100644
index 0000000..66edbb5
--- /dev/null
+++ b/tests/SilkFX/res/values/style.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+<!-- Styles for immersive actions UI. -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="Theme.BackgroundBlurTheme" parent= "Theme.AppCompat.Dialog">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBlurBehindEnabled">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:windowElevation">0dp</item>
+ <item name="buttonStyle">@style/AppTheme.Button</item>
+ <item name="colorAccent">#bbffffff</item>
+ </style>
+ <style name="AppTheme.Button" parent="Widget.AppCompat.Button">
+ <item name="android:textColor">#ffffffff</item>
+ </style>
+
+</resources>
diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
index 9ed8d2f..7132ae8 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/Main.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -30,6 +30,7 @@
import com.android.test.silkfx.app.EXTRA_TITLE
import com.android.test.silkfx.hdr.GlowActivity
import com.android.test.silkfx.materials.GlassActivity
+import com.android.test.silkfx.materials.BackgroundBlurActivity
import kotlin.reflect.KClass
class Demo(val name: String, val makeIntent: (Context) -> Intent) {
@@ -51,7 +52,8 @@
Demo("Blingy Notifications", R.layout.bling_notifications)
)),
DemoGroup("Materials", listOf(
- Demo("Glass", GlassActivity::class)
+ Demo("Glass", GlassActivity::class),
+ Demo("Background Blur", BackgroundBlurActivity::class)
))
)
@@ -126,4 +128,4 @@
AllDemos.forEachIndexed { index, _ -> list.expandGroup(index) }
}
-}
\ No newline at end of file
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
new file mode 100644
index 0000000..9d17d38
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.silkfx.materials
+
+import android.app.Activity
+import android.content.Intent
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.PaintDrawable
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.provider.Settings
+import android.util.TypedValue
+import android.view.View
+import android.view.WindowManager
+import android.widget.ImageView
+import android.widget.SeekBar
+import android.widget.Switch
+import android.widget.TextView
+import com.android.test.silkfx.R
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable
+import android.widget.LinearLayout
+import android.widget.Button
+
+import android.view.ViewRootImpl
+
+class BackgroundBlurActivity : Activity(), SeekBar.OnSeekBarChangeListener {
+ var mBackgroundDrawable = PaintDrawable(Color.WHITE)
+ var mBackgroundBlurRadius = 50
+ var mAlphaWithBlur = 0.2f
+ var mAlphaNoBlur = 0.5f
+
+ var mBlurBehindRadius = 10
+ var mDimAmountWithBlur = 0.2f
+ var mDimAmountNoBlur = 0.2f
+
+ var mBlurForceDisabled = false
+ var mBatterySavingModeOn = false
+
+ lateinit var blurBackgroundSeekBar: SeekBar
+ lateinit var backgroundAlphaSeekBar : SeekBar
+ lateinit var blurBehindSeekBar : SeekBar
+ lateinit var dimAmountSeekBar : SeekBar
+
+ val blurEnabledListener = { enabled : Boolean ->
+ blurBackgroundSeekBar.setProgress(mBackgroundBlurRadius)
+ blurBehindSeekBar.setProgress(mBlurBehindRadius)
+
+ if (enabled) {
+ setBackgroundBlur(mBackgroundBlurRadius)
+ setBackgroundColorAlpha(mAlphaWithBlur)
+
+ setBlurBehind(mBlurBehindRadius)
+ setDimAmount(mDimAmountWithBlur)
+
+ backgroundAlphaSeekBar.setProgress((mAlphaWithBlur * 100).toInt())
+ dimAmountSeekBar.setProgress((mDimAmountWithBlur * 100).toInt())
+ } else {
+ setBackgroundColorAlpha(mAlphaNoBlur)
+ setDimAmount(mDimAmountNoBlur)
+
+ backgroundAlphaSeekBar.setProgress((mAlphaNoBlur * 100).toInt())
+ dimAmountSeekBar.setProgress((mDimAmountNoBlur * 100).toInt())
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_background_blur)
+
+ window.addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND)
+ window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+
+ mBackgroundDrawable.setCornerRadius(30f)
+ window.setBackgroundDrawable(mBackgroundDrawable)
+
+ mBatterySavingModeOn =
+ Settings.Global.getInt(getContentResolver(), Settings.Global.LOW_POWER_MODE, 0) == 1
+ setBatterySavingModeOn(mBatterySavingModeOn)
+
+ blurBackgroundSeekBar = requireViewById(R.id.set_background_blur)
+ backgroundAlphaSeekBar = requireViewById(R.id.set_background_alpha)
+ blurBehindSeekBar = requireViewById(R.id.set_blur_behind)
+ dimAmountSeekBar = requireViewById(R.id.set_dim_amount)
+
+ arrayOf(blurBackgroundSeekBar, backgroundAlphaSeekBar, blurBehindSeekBar, dimAmountSeekBar)
+ .forEach {
+ it.setOnSeekBarChangeListener(this)
+ }
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ getWindowManager().addCrossWindowBlurEnabledListener(blurEnabledListener)
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ getWindowManager().removeCrossWindowBlurEnabledListener(blurEnabledListener)
+ }
+
+ override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+ when (seekBar) {
+ blurBackgroundSeekBar -> setBackgroundBlur(progress)
+ backgroundAlphaSeekBar -> setBackgroundColorAlpha(progress / 100.0f)
+ blurBehindSeekBar -> setBlurBehind(progress)
+ dimAmountSeekBar -> setDimAmount(progress / 100.0f)
+ else -> throw IllegalArgumentException("Unknown seek bar")
+ }
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar?) {}
+ override fun onStopTrackingTouch(seekBar: SeekBar?) {}
+
+ fun setBlurDisabled(disabled: Boolean) {
+ mBlurForceDisabled = disabled
+ Settings.Global.putInt(getContentResolver(), Settings.Global.DISABLE_WINDOW_BLURS,
+ if (mBlurForceDisabled) 1 else 0)
+ (findViewById(R.id.toggle_blur_enabled) as Button)
+ .setText(if (mBlurForceDisabled) "Enable blurs" else "Disable blurs")
+ }
+
+ fun toggleForceBlurDisabled(v: View) {
+ setBlurDisabled(!mBlurForceDisabled)
+ }
+
+ fun setBackgroundBlur(radius: Int) {
+ mBackgroundBlurRadius = radius
+ (findViewById(R.id.background_blur_radius) as TextView).setText(radius.toString())
+ window.setBackgroundBlurRadius(mBackgroundBlurRadius)
+ }
+
+ fun setBlurBehind(radius: Int) {
+ mBlurBehindRadius = radius
+ (findViewById(R.id.blur_behind_radius) as TextView).setText(radius.toString())
+ window.getAttributes().setBlurBehindRadius(mBlurBehindRadius)
+ window.setAttributes(window.getAttributes())
+ }
+
+ fun setDimAmount(amount: Float) {
+ if (getWindowManager().isCrossWindowBlurEnabled()) {
+ mDimAmountWithBlur = amount
+ } else {
+ mDimAmountNoBlur = amount
+ }
+ (findViewById(R.id.dim_amount) as TextView).setText("%.2f".format(amount))
+ window.getAttributes().dimAmount = amount
+ window.setAttributes(window.getAttributes())
+ }
+
+ fun setBatterySavingModeOn(on: Boolean) {
+ mBatterySavingModeOn = on
+ Settings.Global.putInt(getContentResolver(),
+ Settings.Global.LOW_POWER_MODE, if (on) 1 else 0)
+ (findViewById(R.id.toggle_battery_saving_mode) as Button).setText(
+ if (on) "Exit low power mode" else "Enter low power mode")
+ }
+
+ fun toggleBatterySavingMode(v: View) {
+ setBatterySavingModeOn(!mBatterySavingModeOn)
+ }
+
+ fun setBackgroundColorAlpha(alpha: Float) {
+ if (getWindowManager().isCrossWindowBlurEnabled()) {
+ mAlphaWithBlur = alpha
+ } else {
+ mAlphaNoBlur = alpha
+ }
+ (findViewById(R.id.background_alpha) as TextView).setText("%.2f".format(alpha))
+ mBackgroundDrawable.setAlpha((alpha * 255f).toInt())
+ getWindowManager().updateViewLayout(window.getDecorView(), window.getAttributes())
+ }
+}