Merge "hostapd: add 60GHz(WiGig) support"
diff --git a/audio/7.0/config/Android.bp b/audio/7.0/config/Android.bp
index 015c424..f67cc7c 100644
--- a/audio/7.0/config/Android.bp
+++ b/audio/7.0/config/Android.bp
@@ -1,5 +1,6 @@
 xsd_config {
     name: "audio_policy_configuration_V7_0",
     srcs: ["audio_policy_configuration.xsd"],
-    package_name: "audio.policy.configuration.V7_0",
+    package_name: "android.audio.policy.configuration.V7_0",
+    nullability: true,
 }
diff --git a/audio/7.0/config/api/current.txt b/audio/7.0/config/api/current.txt
index 453ed16..0b2e4a4 100644
--- a/audio/7.0/config/api/current.txt
+++ b/audio/7.0/config/api/current.txt
@@ -1,571 +1,572 @@
 // Signature format: 2.0
-package audio.policy.configuration.V7_0 {
+package android.audio.policy.configuration.V7_0 {
 
   public class AttachedDevices {
     ctor public AttachedDevices();
-    method public java.util.List<java.lang.String> getItem();
+    method @Nullable public java.util.List<java.lang.String> getItem();
   }
 
   public enum AudioChannelMask {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_1;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_10;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_11;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_12;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_13;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_14;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_15;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_16;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_17;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_18;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_19;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_20;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_21;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_22;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_23;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_24;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_3;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_4;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_5;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_6;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_7;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_8;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_9;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_2POINT0POINT2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_2POINT1POINT2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_3POINT0POINT2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_3POINT1POINT2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_5POINT1;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_6;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_FRONT_BACK;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_MONO;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_STEREO;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_VOICE_CALL_MONO;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_NONE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT0POINT2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT1;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT1POINT2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_3POINT0POINT2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_3POINT1;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_3POINT1POINT2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1POINT2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1POINT4;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1_BACK;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1_SIDE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_6POINT1;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_7POINT1;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_7POINT1POINT2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_7POINT1POINT4;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_HAPTIC_AB;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_MONO;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_MONO_HAPTIC_A;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_PENTA;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_QUAD;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_QUAD_BACK;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_QUAD_SIDE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_STEREO;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_SURROUND;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_TRI;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_TRI_BACK;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_10;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_11;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_12;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_13;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_14;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_15;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_16;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_17;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_18;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_19;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_20;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_21;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_22;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_23;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_24;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_3;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_4;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_5;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_6;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_7;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_8;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_9;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_2POINT0POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_2POINT1POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_3POINT0POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_3POINT1POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_5POINT1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_6;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_FRONT_BACK;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_MONO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_STEREO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_VOICE_CALL_MONO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_NONE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT0POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT1POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_3POINT0POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_3POINT1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_3POINT1POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1POINT4;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1_BACK;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1_SIDE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_6POINT1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_7POINT1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_7POINT1POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_7POINT1POINT4;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_HAPTIC_AB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_MONO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_MONO_HAPTIC_A;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_PENTA;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_QUAD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_QUAD_BACK;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_QUAD_SIDE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_STEREO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_SURROUND;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_TRI;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_TRI_BACK;
   }
 
   public enum AudioContentType {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_MOVIE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_MUSIC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_SONIFICATION;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_SPEECH;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_UNKNOWN;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_MOVIE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_MUSIC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_SONIFICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_SPEECH;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_UNKNOWN;
   }
 
   public enum AudioDevice {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_AMBIENT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_AUX_DIGITAL;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BACK_MIC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BLE_HEADSET;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BLUETOOTH_BLE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BUILTIN_MIC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BUS;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_COMMUNICATION;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_DEFAULT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_ECHO_REFERENCE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_FM_TUNER;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_HDMI;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_HDMI_ARC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_IP;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_LINE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_LOOPBACK;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_PROXY;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_REMOTE_SUBMIX;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_SPDIF;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_STUB;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_TELEPHONY_RX;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_TV_TUNER;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_USB_ACCESSORY;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_USB_DEVICE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_USB_HEADSET;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_VOICE_CALL;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_WIRED_HEADSET;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_NONE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_AUX_DIGITAL;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_AUX_LINE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLE_HEADSET;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLE_SPEAKER;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BUS;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_DEFAULT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_EARPIECE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_ECHO_CANCELLER;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_FM;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HDMI;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HDMI_ARC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HEARING_AID;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_IP;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_LINE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_PROXY;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_SPDIF;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_SPEAKER;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_SPEAKER_SAFE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_STUB;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_TELEPHONY_TX;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_USB_ACCESSORY;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_USB_DEVICE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_USB_HEADSET;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_WIRED_HEADSET;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_AMBIENT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_AUX_DIGITAL;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BACK_MIC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BLE_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BLUETOOTH_BLE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BUILTIN_MIC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BUS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_COMMUNICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_DEFAULT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_ECHO_REFERENCE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_FM_TUNER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_HDMI;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_HDMI_ARC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_IP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_LINE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_LOOPBACK;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_PROXY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_REMOTE_SUBMIX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_SPDIF;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_STUB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_TELEPHONY_RX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_TV_TUNER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_USB_ACCESSORY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_USB_DEVICE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_USB_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_VOICE_CALL;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_WIRED_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_NONE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_AUX_DIGITAL;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_AUX_LINE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLE_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLE_SPEAKER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BUS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_DEFAULT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_EARPIECE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_ECHO_CANCELLER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_FM;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HDMI;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HDMI_ARC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HEARING_AID;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_IP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_LINE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_PROXY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_SPDIF;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_SPEAKER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_STUB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_TELEPHONY_TX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_USB_ACCESSORY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_USB_DEVICE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_USB_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_WIRED_HEADSET;
   }
 
   public enum AudioFormat {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADIF;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_ELD;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_ERLC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_HE_V1;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_HE_V2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_LC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_LD;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_LTP;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_MAIN;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_SCALABLE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_SSR;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_XHE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ELD;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ERLC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_HE_V1;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_HE_V2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LATM;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LATM_HE_V1;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LATM_HE_V2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LATM_LC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LD;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LTP;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_MAIN;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_SCALABLE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_SSR;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_XHE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AC3;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AC4;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_ALAC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AMR_NB;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AMR_WB;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AMR_WB_PLUS;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APTX;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APTX_ADAPTIVE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APTX_HD;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APTX_TWSP;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_CELT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DOLBY_TRUEHD;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DSD;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DTS;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DTS_HD;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRCB;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRCNW;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRCWB;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_E_AC3;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_E_AC3_JOC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_FLAC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_HE_AAC_V1;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_HE_AAC_V2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_IEC61937;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_LC3;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_LDAC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_LHDC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_LHDC_LL;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MAT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MAT_1_0;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MAT_2_0;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MAT_2_1;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MP2;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MP3;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_OPUS;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_16_BIT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_24_BIT_PACKED;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_32_BIT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_8_24_BIT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_8_BIT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_FLOAT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_QCELP;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_SBC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_VORBIS;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_WMA;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_WMA_PRO;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADIF;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_ELD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_ERLC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_HE_V1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_HE_V2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_LC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_LD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_LTP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_MAIN;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_SCALABLE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_SSR;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_XHE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ELD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ERLC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_HE_V1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_HE_V2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LATM;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LATM_HE_V1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LATM_HE_V2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LATM_LC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LTP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_MAIN;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_SCALABLE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_SSR;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_XHE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AC3;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AC4;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_ALAC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AMR_NB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AMR_WB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AMR_WB_PLUS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APTX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APTX_ADAPTIVE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APTX_HD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APTX_TWSP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_CELT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DOLBY_TRUEHD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DSD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DTS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DTS_HD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRCB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRCNW;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRCWB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_E_AC3;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_E_AC3_JOC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_FLAC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_HE_AAC_V1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_HE_AAC_V2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_IEC61937;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_LC3;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_LDAC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_LHDC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_LHDC_LL;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MAT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MAT_1_0;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MAT_2_0;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MAT_2_1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MP2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MP3;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_OPUS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_16_BIT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_24_BIT_PACKED;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_32_BIT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_8_24_BIT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_8_BIT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_FLOAT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_QCELP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_SBC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_VORBIS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_WMA;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_WMA_PRO;
   }
 
   public enum AudioGainMode {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.AudioGainMode AUDIO_GAIN_MODE_CHANNELS;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioGainMode AUDIO_GAIN_MODE_JOINT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioGainMode AUDIO_GAIN_MODE_RAMP;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioGainMode AUDIO_GAIN_MODE_CHANNELS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioGainMode AUDIO_GAIN_MODE_JOINT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioGainMode AUDIO_GAIN_MODE_RAMP;
   }
 
   public enum AudioInOutFlag {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_DIRECT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_FAST;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_HW_AV_SYNC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_HW_HOTWORD;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_MMAP_NOIRQ;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_NONE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_RAW;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_SYNC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_VOIP_TX;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_DIRECT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_DIRECT_PCM;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_FAST;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_HW_AV_SYNC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_INCALL_MUSIC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_NONE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_NON_BLOCKING;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_PRIMARY;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_RAW;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_SYNC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_TTS;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_VOIP_RX;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_DIRECT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_FAST;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_HW_AV_SYNC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_HW_HOTWORD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_MMAP_NOIRQ;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_NONE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_RAW;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_SYNC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_VOIP_TX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_DIRECT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_DIRECT_PCM;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_FAST;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_HW_AV_SYNC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_INCALL_MUSIC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_NONE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_NON_BLOCKING;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_PRIMARY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_RAW;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_SYNC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_TTS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_VOIP_RX;
   }
 
   public class AudioPolicyConfiguration {
     ctor public AudioPolicyConfiguration();
-    method public audio.policy.configuration.V7_0.GlobalConfiguration getGlobalConfiguration();
-    method public java.util.List<audio.policy.configuration.V7_0.Modules> getModules();
-    method public audio.policy.configuration.V7_0.SurroundSound getSurroundSound();
-    method public audio.policy.configuration.V7_0.Version getVersion();
-    method public java.util.List<audio.policy.configuration.V7_0.Volumes> getVolumes();
-    method public void setGlobalConfiguration(audio.policy.configuration.V7_0.GlobalConfiguration);
-    method public void setSurroundSound(audio.policy.configuration.V7_0.SurroundSound);
-    method public void setVersion(audio.policy.configuration.V7_0.Version);
+    method @Nullable public android.audio.policy.configuration.V7_0.GlobalConfiguration getGlobalConfiguration();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Modules> getModules();
+    method @Nullable public android.audio.policy.configuration.V7_0.SurroundSound getSurroundSound();
+    method @Nullable public android.audio.policy.configuration.V7_0.Version getVersion();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Volumes> getVolumes();
+    method public void setGlobalConfiguration(@Nullable android.audio.policy.configuration.V7_0.GlobalConfiguration);
+    method public void setSurroundSound(@Nullable android.audio.policy.configuration.V7_0.SurroundSound);
+    method public void setVersion(@Nullable android.audio.policy.configuration.V7_0.Version);
   }
 
   public enum AudioSource {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_CAMCORDER;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_DEFAULT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_ECHO_REFERENCE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_FM_TUNER;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_HOTWORD;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_MIC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_REMOTE_SUBMIX;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_UNPROCESSED;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_CALL;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_COMMUNICATION;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_DOWNLINK;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_PERFORMANCE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_RECOGNITION;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_UPLINK;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_CAMCORDER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_DEFAULT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_ECHO_REFERENCE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_FM_TUNER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_HOTWORD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_MIC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_REMOTE_SUBMIX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_UNPROCESSED;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_CALL;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_COMMUNICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_DOWNLINK;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_PERFORMANCE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_RECOGNITION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_UPLINK;
   }
 
   public enum AudioStreamType {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_ACCESSIBILITY;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_ALARM;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_ASSISTANT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_BLUETOOTH_SCO;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_CALL_ASSISTANT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_DTMF;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_ENFORCED_AUDIBLE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_MUSIC;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_NOTIFICATION;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_PATCH;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_REROUTING;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_RING;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_SYSTEM;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_TTS;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_VOICE_CALL;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_ACCESSIBILITY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_ALARM;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_ASSISTANT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_BLUETOOTH_SCO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_CALL_ASSISTANT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_DTMF;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_ENFORCED_AUDIBLE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_MUSIC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_NOTIFICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_PATCH;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_REROUTING;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_RING;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_SYSTEM;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_TTS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_VOICE_CALL;
   }
 
   public enum AudioUsage {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ALARM;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ANNOUNCEMENT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ASSISTANCE_SONIFICATION;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ASSISTANT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_CALL_ASSISTANT;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_EMERGENCY;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_GAME;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_MEDIA;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_NOTIFICATION;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_SAFETY;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_UNKNOWN;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_VEHICLE_STATUS;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_VIRTUAL_SOURCE;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_VOICE_COMMUNICATION;
-    enum_constant public static final audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ALARM;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ANNOUNCEMENT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ASSISTANCE_SONIFICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ASSISTANT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_CALL_ASSISTANT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_EMERGENCY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_GAME;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_MEDIA;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_NOTIFICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_SAFETY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_UNKNOWN;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_VEHICLE_STATUS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_VIRTUAL_SOURCE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_VOICE_COMMUNICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
   }
 
   public enum DeviceCategory {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_EARPIECE;
-    enum_constant public static final audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_EXT_MEDIA;
-    enum_constant public static final audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_HEADSET;
-    enum_constant public static final audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_HEARING_AID;
-    enum_constant public static final audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_SPEAKER;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_EARPIECE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_EXT_MEDIA;
+    enum_constant public static final android.audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_HEARING_AID;
+    enum_constant public static final android.audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_SPEAKER;
   }
 
   public class DevicePorts {
     ctor public DevicePorts();
-    method public java.util.List<audio.policy.configuration.V7_0.DevicePorts.DevicePort> getDevicePort();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.DevicePorts.DevicePort> getDevicePort();
   }
 
   public static class DevicePorts.DevicePort {
     ctor public DevicePorts.DevicePort();
-    method public String getAddress();
-    method public java.util.List<audio.policy.configuration.V7_0.AudioFormat> getEncodedFormats();
-    method public audio.policy.configuration.V7_0.Gains getGains();
-    method public java.util.List<audio.policy.configuration.V7_0.Profile> getProfile();
-    method public audio.policy.configuration.V7_0.Role getRole();
-    method public String getTagName();
-    method public String getType();
-    method public boolean get_default();
-    method public void setAddress(String);
-    method public void setEncodedFormats(java.util.List<audio.policy.configuration.V7_0.AudioFormat>);
-    method public void setGains(audio.policy.configuration.V7_0.Gains);
-    method public void setRole(audio.policy.configuration.V7_0.Role);
-    method public void setTagName(String);
-    method public void setType(String);
-    method public void set_default(boolean);
+    method @Nullable public String getAddress();
+    method @Nullable public java.util.List<java.lang.String> getEncodedFormats();
+    method @Nullable public android.audio.policy.configuration.V7_0.Gains getGains();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Profile> getProfile();
+    method @Nullable public android.audio.policy.configuration.V7_0.Role getRole();
+    method @Nullable public String getTagName();
+    method @Nullable public String getType();
+    method @Nullable public boolean get_default();
+    method public void setAddress(@Nullable String);
+    method public void setEncodedFormats(@Nullable java.util.List<java.lang.String>);
+    method public void setGains(@Nullable android.audio.policy.configuration.V7_0.Gains);
+    method public void setRole(@Nullable android.audio.policy.configuration.V7_0.Role);
+    method public void setTagName(@Nullable String);
+    method public void setType(@Nullable String);
+    method public void set_default(@Nullable boolean);
   }
 
   public enum EngineSuffix {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.EngineSuffix _default;
-    enum_constant public static final audio.policy.configuration.V7_0.EngineSuffix configurable;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.EngineSuffix _default;
+    enum_constant public static final android.audio.policy.configuration.V7_0.EngineSuffix configurable;
   }
 
   public class Gains {
     ctor public Gains();
-    method public java.util.List<audio.policy.configuration.V7_0.Gains.Gain> getGain();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Gains.Gain> getGain();
   }
 
   public static class Gains.Gain {
     ctor public Gains.Gain();
-    method public audio.policy.configuration.V7_0.AudioChannelMask getChannel_mask();
-    method public int getDefaultValueMB();
-    method public int getMaxRampMs();
-    method public int getMaxValueMB();
-    method public int getMinRampMs();
-    method public int getMinValueMB();
-    method public java.util.List<audio.policy.configuration.V7_0.AudioGainMode> getMode();
-    method public String getName();
-    method public int getStepValueMB();
-    method public boolean getUseForVolume();
-    method public void setChannel_mask(audio.policy.configuration.V7_0.AudioChannelMask);
-    method public void setDefaultValueMB(int);
-    method public void setMaxRampMs(int);
-    method public void setMaxValueMB(int);
-    method public void setMinRampMs(int);
-    method public void setMinValueMB(int);
-    method public void setMode(java.util.List<audio.policy.configuration.V7_0.AudioGainMode>);
-    method public void setName(String);
-    method public void setStepValueMB(int);
-    method public void setUseForVolume(boolean);
+    method @Nullable public android.audio.policy.configuration.V7_0.AudioChannelMask getChannel_mask();
+    method @Nullable public int getDefaultValueMB();
+    method @Nullable public int getMaxRampMs();
+    method @Nullable public int getMaxValueMB();
+    method @Nullable public int getMinRampMs();
+    method @Nullable public int getMinValueMB();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.AudioGainMode> getMode();
+    method @Nullable public String getName();
+    method @Nullable public int getStepValueMB();
+    method @Nullable public boolean getUseForVolume();
+    method public void setChannel_mask(@Nullable android.audio.policy.configuration.V7_0.AudioChannelMask);
+    method public void setDefaultValueMB(@Nullable int);
+    method public void setMaxRampMs(@Nullable int);
+    method public void setMaxValueMB(@Nullable int);
+    method public void setMinRampMs(@Nullable int);
+    method public void setMinValueMB(@Nullable int);
+    method public void setMode(@Nullable java.util.List<android.audio.policy.configuration.V7_0.AudioGainMode>);
+    method public void setName(@Nullable String);
+    method public void setStepValueMB(@Nullable int);
+    method public void setUseForVolume(@Nullable boolean);
   }
 
   public class GlobalConfiguration {
     ctor public GlobalConfiguration();
-    method public boolean getCall_screen_mode_supported();
-    method public audio.policy.configuration.V7_0.EngineSuffix getEngine_library();
-    method public boolean getSpeaker_drc_enabled();
-    method public void setCall_screen_mode_supported(boolean);
-    method public void setEngine_library(audio.policy.configuration.V7_0.EngineSuffix);
-    method public void setSpeaker_drc_enabled(boolean);
+    method @Nullable public boolean getCall_screen_mode_supported();
+    method @Nullable public android.audio.policy.configuration.V7_0.EngineSuffix getEngine_library();
+    method @Nullable public boolean getSpeaker_drc_enabled();
+    method public void setCall_screen_mode_supported(@Nullable boolean);
+    method public void setEngine_library(@Nullable android.audio.policy.configuration.V7_0.EngineSuffix);
+    method public void setSpeaker_drc_enabled(@Nullable boolean);
   }
 
   public enum HalVersion {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.HalVersion _2_0;
-    enum_constant public static final audio.policy.configuration.V7_0.HalVersion _3_0;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.HalVersion _2_0;
+    enum_constant public static final android.audio.policy.configuration.V7_0.HalVersion _3_0;
   }
 
   public class MixPorts {
     ctor public MixPorts();
-    method public java.util.List<audio.policy.configuration.V7_0.MixPorts.MixPort> getMixPort();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.MixPorts.MixPort> getMixPort();
   }
 
   public static class MixPorts.MixPort {
     ctor public MixPorts.MixPort();
-    method public java.util.List<audio.policy.configuration.V7_0.AudioInOutFlag> getFlags();
-    method public audio.policy.configuration.V7_0.Gains getGains();
-    method public long getMaxActiveCount();
-    method public long getMaxOpenCount();
-    method public String getName();
-    method public java.util.List<audio.policy.configuration.V7_0.AudioUsage> getPreferredUsage();
-    method public java.util.List<audio.policy.configuration.V7_0.Profile> getProfile();
-    method public audio.policy.configuration.V7_0.Role getRole();
-    method public void setFlags(java.util.List<audio.policy.configuration.V7_0.AudioInOutFlag>);
-    method public void setGains(audio.policy.configuration.V7_0.Gains);
-    method public void setMaxActiveCount(long);
-    method public void setMaxOpenCount(long);
-    method public void setName(String);
-    method public void setPreferredUsage(java.util.List<audio.policy.configuration.V7_0.AudioUsage>);
-    method public void setRole(audio.policy.configuration.V7_0.Role);
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.AudioInOutFlag> getFlags();
+    method @Nullable public android.audio.policy.configuration.V7_0.Gains getGains();
+    method @Nullable public long getMaxActiveCount();
+    method @Nullable public long getMaxOpenCount();
+    method @Nullable public String getName();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.AudioUsage> getPreferredUsage();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Profile> getProfile();
+    method @Nullable public android.audio.policy.configuration.V7_0.Role getRole();
+    method public void setFlags(@Nullable java.util.List<android.audio.policy.configuration.V7_0.AudioInOutFlag>);
+    method public void setGains(@Nullable android.audio.policy.configuration.V7_0.Gains);
+    method public void setMaxActiveCount(@Nullable long);
+    method public void setMaxOpenCount(@Nullable long);
+    method public void setName(@Nullable String);
+    method public void setPreferredUsage(@Nullable java.util.List<android.audio.policy.configuration.V7_0.AudioUsage>);
+    method public void setRole(@Nullable android.audio.policy.configuration.V7_0.Role);
   }
 
   public enum MixType {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.MixType mix;
-    enum_constant public static final audio.policy.configuration.V7_0.MixType mux;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.MixType mix;
+    enum_constant public static final android.audio.policy.configuration.V7_0.MixType mux;
   }
 
   public class Modules {
     ctor public Modules();
-    method public java.util.List<audio.policy.configuration.V7_0.Modules.Module> getModule();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Modules.Module> getModule();
   }
 
   public static class Modules.Module {
     ctor public Modules.Module();
-    method public audio.policy.configuration.V7_0.AttachedDevices getAttachedDevices();
-    method public String getDefaultOutputDevice();
-    method public audio.policy.configuration.V7_0.DevicePorts getDevicePorts();
-    method public audio.policy.configuration.V7_0.HalVersion getHalVersion();
-    method public audio.policy.configuration.V7_0.MixPorts getMixPorts();
-    method public String getName();
-    method public audio.policy.configuration.V7_0.Routes getRoutes();
-    method public void setAttachedDevices(audio.policy.configuration.V7_0.AttachedDevices);
-    method public void setDefaultOutputDevice(String);
-    method public void setDevicePorts(audio.policy.configuration.V7_0.DevicePorts);
-    method public void setHalVersion(audio.policy.configuration.V7_0.HalVersion);
-    method public void setMixPorts(audio.policy.configuration.V7_0.MixPorts);
-    method public void setName(String);
-    method public void setRoutes(audio.policy.configuration.V7_0.Routes);
+    method @Nullable public android.audio.policy.configuration.V7_0.AttachedDevices getAttachedDevices();
+    method @Nullable public String getDefaultOutputDevice();
+    method @Nullable public android.audio.policy.configuration.V7_0.DevicePorts getDevicePorts();
+    method @Nullable public android.audio.policy.configuration.V7_0.HalVersion getHalVersion();
+    method @Nullable public android.audio.policy.configuration.V7_0.MixPorts getMixPorts();
+    method @Nullable public String getName();
+    method @Nullable public android.audio.policy.configuration.V7_0.Routes getRoutes();
+    method public void setAttachedDevices(@Nullable android.audio.policy.configuration.V7_0.AttachedDevices);
+    method public void setDefaultOutputDevice(@Nullable String);
+    method public void setDevicePorts(@Nullable android.audio.policy.configuration.V7_0.DevicePorts);
+    method public void setHalVersion(@Nullable android.audio.policy.configuration.V7_0.HalVersion);
+    method public void setMixPorts(@Nullable android.audio.policy.configuration.V7_0.MixPorts);
+    method public void setName(@Nullable String);
+    method public void setRoutes(@Nullable android.audio.policy.configuration.V7_0.Routes);
   }
 
   public class Profile {
     ctor public Profile();
-    method public java.util.List<audio.policy.configuration.V7_0.AudioChannelMask> getChannelMasks();
-    method public String getFormat();
-    method public String getName();
-    method public java.util.List<java.math.BigInteger> getSamplingRates();
-    method public void setChannelMasks(java.util.List<audio.policy.configuration.V7_0.AudioChannelMask>);
-    method public void setFormat(String);
-    method public void setName(String);
-    method public void setSamplingRates(java.util.List<java.math.BigInteger>);
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.AudioChannelMask> getChannelMasks();
+    method @Nullable public String getFormat();
+    method @Nullable public String getName();
+    method @Nullable public java.util.List<java.math.BigInteger> getSamplingRates();
+    method public void setChannelMasks(@Nullable java.util.List<android.audio.policy.configuration.V7_0.AudioChannelMask>);
+    method public void setFormat(@Nullable String);
+    method public void setName(@Nullable String);
+    method public void setSamplingRates(@Nullable java.util.List<java.math.BigInteger>);
   }
 
   public class Reference {
     ctor public Reference();
-    method public String getName();
-    method public java.util.List<java.lang.String> getPoint();
-    method public void setName(String);
+    method @Nullable public String getName();
+    method @Nullable public java.util.List<java.lang.String> getPoint();
+    method public void setName(@Nullable String);
   }
 
   public enum Role {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.Role sink;
-    enum_constant public static final audio.policy.configuration.V7_0.Role source;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.Role sink;
+    enum_constant public static final android.audio.policy.configuration.V7_0.Role source;
   }
 
   public class Routes {
     ctor public Routes();
-    method public java.util.List<audio.policy.configuration.V7_0.Routes.Route> getRoute();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Routes.Route> getRoute();
   }
 
   public static class Routes.Route {
     ctor public Routes.Route();
-    method public String getSink();
-    method public String getSources();
-    method public audio.policy.configuration.V7_0.MixType getType();
-    method public void setSink(String);
-    method public void setSources(String);
-    method public void setType(audio.policy.configuration.V7_0.MixType);
+    method @Nullable public String getSink();
+    method @Nullable public String getSources();
+    method @Nullable public android.audio.policy.configuration.V7_0.MixType getType();
+    method public void setSink(@Nullable String);
+    method public void setSources(@Nullable String);
+    method public void setType(@Nullable android.audio.policy.configuration.V7_0.MixType);
   }
 
   public class SurroundFormats {
     ctor public SurroundFormats();
-    method public java.util.List<audio.policy.configuration.V7_0.SurroundFormats.Format> getFormat();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.SurroundFormats.Format> getFormat();
   }
 
   public static class SurroundFormats.Format {
     ctor public SurroundFormats.Format();
-    method public audio.policy.configuration.V7_0.AudioFormat getName();
-    method public java.util.List<audio.policy.configuration.V7_0.AudioFormat> getSubformats();
-    method public void setName(audio.policy.configuration.V7_0.AudioFormat);
-    method public void setSubformats(java.util.List<audio.policy.configuration.V7_0.AudioFormat>);
+    method @Nullable public String getName();
+    method @Nullable public java.util.List<java.lang.String> getSubformats();
+    method public void setName(@Nullable String);
+    method public void setSubformats(@Nullable java.util.List<java.lang.String>);
   }
 
   public class SurroundSound {
     ctor public SurroundSound();
-    method public audio.policy.configuration.V7_0.SurroundFormats getFormats();
-    method public void setFormats(audio.policy.configuration.V7_0.SurroundFormats);
+    method @Nullable public android.audio.policy.configuration.V7_0.SurroundFormats getFormats();
+    method public void setFormats(@Nullable android.audio.policy.configuration.V7_0.SurroundFormats);
   }
 
   public enum Version {
-    method public String getRawName();
-    enum_constant public static final audio.policy.configuration.V7_0.Version _1_0;
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.Version _1_0;
   }
 
   public class Volume {
     ctor public Volume();
-    method public audio.policy.configuration.V7_0.DeviceCategory getDeviceCategory();
-    method public java.util.List<java.lang.String> getPoint();
-    method public String getRef();
-    method public audio.policy.configuration.V7_0.AudioStreamType getStream();
-    method public void setDeviceCategory(audio.policy.configuration.V7_0.DeviceCategory);
-    method public void setRef(String);
-    method public void setStream(audio.policy.configuration.V7_0.AudioStreamType);
+    method @Nullable public android.audio.policy.configuration.V7_0.DeviceCategory getDeviceCategory();
+    method @Nullable public java.util.List<java.lang.String> getPoint();
+    method @Nullable public String getRef();
+    method @Nullable public android.audio.policy.configuration.V7_0.AudioStreamType getStream();
+    method public void setDeviceCategory(@Nullable android.audio.policy.configuration.V7_0.DeviceCategory);
+    method public void setRef(@Nullable String);
+    method public void setStream(@Nullable android.audio.policy.configuration.V7_0.AudioStreamType);
   }
 
   public class Volumes {
     ctor public Volumes();
-    method public java.util.List<audio.policy.configuration.V7_0.Reference> getReference();
-    method public java.util.List<audio.policy.configuration.V7_0.Volume> getVolume();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Reference> getReference();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Volume> getVolume();
   }
 
   public class XmlParser {
     ctor public XmlParser();
-    method public static audio.policy.configuration.V7_0.AudioPolicyConfiguration read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method @Nullable public static android.audio.policy.configuration.V7_0.AudioPolicyConfiguration read(@NonNull java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method @Nullable public static String readText(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
   }
 
 }
diff --git a/audio/7.0/config/audio_policy_configuration.xsd b/audio/7.0/config/audio_policy_configuration.xsd
index 6784828..a735c6d 100644
--- a/audio/7.0/config/audio_policy_configuration.xsd
+++ b/audio/7.0/config/audio_policy_configuration.xsd
@@ -181,6 +181,7 @@
             <xs:enumeration value="AUDIO_OUTPUT_FLAG_MMAP_NOIRQ" />
             <xs:enumeration value="AUDIO_OUTPUT_FLAG_VOIP_RX" />
             <xs:enumeration value="AUDIO_OUTPUT_FLAG_INCALL_MUSIC" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD" />
             <xs:enumeration value="AUDIO_INPUT_FLAG_NONE" />
             <xs:enumeration value="AUDIO_INPUT_FLAG_FAST" />
             <xs:enumeration value="AUDIO_INPUT_FLAG_HW_HOTWORD" />
@@ -774,13 +775,13 @@
         </xs:sequence>
     </xs:complexType>
     <xs:simpleType name="audioFormatsList">
-        <xs:list itemType="audioFormat" />
+        <xs:list itemType="extendableAudioFormat" />
     </xs:simpleType>
     <xs:complexType name="surroundFormats">
         <xs:sequence>
             <xs:element name="format" minOccurs="0" maxOccurs="unbounded">
                 <xs:complexType>
-                    <xs:attribute name="name" type="audioFormat" use="required"/>
+                    <xs:attribute name="name" type="extendableAudioFormat" use="required"/>
                     <xs:attribute name="subformats" type="audioFormatsList" />
                 </xs:complexType>
             </xs:element>
diff --git a/audio/7.0/types.hal b/audio/7.0/types.hal
index 6cac9c9..2d42129 100644
--- a/audio/7.0/types.hal
+++ b/audio/7.0/types.hal
@@ -44,8 +44,10 @@
  * A substitute for POSIX timespec.
  */
 struct TimeSpec {
-    uint64_t tvSec;   // seconds
-    uint64_t tvNSec;  // nanoseconds
+    /** Seconds. */
+    uint64_t tvSec;
+    /** Nanoseconds. */
+    uint64_t tvNSec;
 };
 
 struct ParameterValue {
@@ -85,8 +87,10 @@
  * Used by streams opened in mmap mode.
  */
 struct MmapPosition {
-    int64_t  timeNanoseconds; // time stamp in ns, CLOCK_MONOTONIC
-    int32_t  positionFrames;  // increasing 32 bit frame count reset when IStream.stop() is called
+    /** Timestamp in ns, CLOCK_MONOTONIC. */
+    int64_t  timeNanoseconds;
+    /** Increasing 32 bit frame count reset when IStream.stop() is called. */
+    int32_t  positionFrames;
 };
 
 /**
@@ -128,9 +132,12 @@
  */
 @export(name="audio_microphone_channel_mapping_t", value_prefix="AUDIO_MICROPHONE_CHANNEL_MAPPING_")
 enum AudioMicrophoneChannelMapping : uint32_t {
-    UNUSED      = 0, /* Channel not used */
-    DIRECT      = 1, /* Channel used and signal not processed */
-    PROCESSED   = 2, /* Channel used and signal has some processing */
+    /** Channel not used. */
+    UNUSED      = 0,
+    /** Channel used and signal not processed. */
+    DIRECT      = 1,
+    /** Channel used and signal has some processing. */
+    PROCESSED   = 2,
 };
 
 /**
@@ -179,7 +186,8 @@
  * Used by StreamIn and Device
  */
 struct MicrophoneInfo {
-    /** Unique alphanumeric id for microphone. Guaranteed to be the same
+    /**
+     * Unique alphanumeric id for microphone. Guaranteed to be the same
      * even after rebooting.
      */
     string                                  deviceId;
@@ -187,18 +195,21 @@
      * Device specific information
      */
     DeviceAddress                           deviceAddress;
-    /** Each element of the vector must describe the channel with the same
-     *  index.
+    /**
+     * Each element of the vector must describe the channel with the same
+     * index.
      */
     vec<AudioMicrophoneChannelMapping>      channelMapping;
     /** Location of the microphone in regard to the body of the device */
     AudioMicrophoneLocation                 location;
-    /** Identifier to help group related microphones together
-     *  e.g. microphone arrays should belong to the same group
+    /**
+     * Identifier to help group related microphones together
+     * e.g. microphone arrays should belong to the same group
      */
     AudioMicrophoneGroup                    group;
-    /** Index of this microphone within the group.
-     *  (group, index) must be unique within the same device.
+    /**
+     * Index of this microphone within the group.
+     * (group, index) must be unique within the same device.
      */
     uint32_t                                indexInTheGroup;
     /** Level in dBFS produced by a 1000 Hz tone at 94 dB SPL */
@@ -209,17 +220,20 @@
     float                                   minSpl;
     /** Standard polar pattern of the microphone */
     AudioMicrophoneDirectionality           directionality;
-    /** Vector with ordered frequency responses (from low to high frequencies)
-     *  with the frequency response of the microphone.
-     *  Levels are in dB, relative to level at 1000 Hz
+    /**
+     * Vector with ordered frequency responses (from low to high frequencies)
+     * with the frequency response of the microphone.
+     * Levels are in dB, relative to level at 1000 Hz
      */
     vec<AudioFrequencyResponsePoint>        frequencyResponse;
-    /** Position of the microphone's capsule in meters, from the
-     *  bottom-left-back corner of the bounding box of device.
+    /**
+     * Position of the microphone's capsule in meters, from the
+     * bottom-left-back corner of the bounding box of device.
      */
     AudioMicrophoneCoordinate               position;
-    /** Normalized point to signal the main orientation of the microphone's
-     *  capsule. sqrt(x^2 + y^2 + z^2) = 1
+    /**
+     * Normalized point to signal the main orientation of the microphone's
+     * capsule. sqrt(x^2 + y^2 + z^2) = 1
      */
     AudioMicrophoneCoordinate               orientation;
 };
@@ -262,7 +276,6 @@
     // frameworks/base/media/java/android/media/AudioTrack.java
     /**
      * Disable any Dual Mono presentation effect.
-     *
      */
     OFF = 0,
     /**
diff --git a/audio/common/7.0/enums/include/audio_policy_configuration_V7_0-enums.h b/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
similarity index 83%
rename from audio/common/7.0/enums/include/audio_policy_configuration_V7_0-enums.h
rename to audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
index cedcab3..414eede 100644
--- a/audio/common/7.0/enums/include/audio_policy_configuration_V7_0-enums.h
+++ b/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef AUDIO_POLICY_CONFIGURATION_V7_0_ENUMS_H
-#define AUDIO_POLICY_CONFIGURATION_V7_0_ENUMS_H
+#ifndef ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0_ENUMS_H
+#define ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0_ENUMS_H
 
 #include <sys/types.h>
+#include <algorithm>
+#include <cctype>
 
-#include <audio_policy_configuration_V7_0.h>
+#include <android_audio_policy_configuration_V7_0.h>
 
-namespace audio::policy::configuration::V7_0 {
+namespace android::audio::policy::configuration::V7_0 {
 
 static inline size_t getChannelCount(AudioChannelMask mask) {
     switch (mask) {
@@ -210,6 +212,43 @@
     return isOutputDevice(stringToAudioDevice(device));
 }
 
-}  // namespace audio::policy::configuration::V7_0
+static inline bool isVendorExtension(const std::string& device) {
+    // Must match the "vendorExtension" rule from the XSD file.
+    static const std::string vendorPrefix = "VX_";
+    return device.size() > vendorPrefix.size() &&
+           device.substr(0, vendorPrefix.size()) == vendorPrefix &&
+           std::all_of(device.begin() + vendorPrefix.size(), device.end(),
+                       [](unsigned char c) { return c == '_' || std::isalnum(c); });
+}
 
-#endif  // AUDIO_POLICY_CONFIGURATION_V7_0_ENUMS_H
+static inline bool isUnknownAudioChannelMask(const std::string& mask) {
+    return stringToAudioChannelMask(mask) == AudioChannelMask::UNKNOWN;
+}
+
+static inline bool isUnknownAudioDevice(const std::string& device) {
+    return stringToAudioDevice(device) == AudioDevice::UNKNOWN && !isVendorExtension(device);
+}
+
+static inline bool isUnknownAudioFormat(const std::string& format) {
+    return stringToAudioFormat(format) == AudioFormat::UNKNOWN && !isVendorExtension(format);
+}
+
+static inline bool isUnknownAudioGainMode(const std::string& mode) {
+    return stringToAudioGainMode(mode) == AudioGainMode::UNKNOWN;
+}
+
+static inline bool isUnknownAudioSource(const std::string& source) {
+    return stringToAudioSource(source) == AudioSource::UNKNOWN;
+}
+
+static inline bool isUnknownAudioStreamType(const std::string& streamType) {
+    return stringToAudioStreamType(streamType) == AudioStreamType::UNKNOWN;
+}
+
+static inline bool isUnknownAudioUsage(const std::string& usage) {
+    return stringToAudioUsage(usage) == AudioUsage::UNKNOWN;
+}
+
+}  // namespace android::audio::policy::configuration::V7_0
+
+#endif  // ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0_ENUMS_H
diff --git a/audio/common/7.0/example/Effect.cpp b/audio/common/7.0/example/Effect.cpp
index 423754d..9d5ab31 100644
--- a/audio/common/7.0/example/Effect.cpp
+++ b/audio/common/7.0/example/Effect.cpp
@@ -17,7 +17,7 @@
 #define LOG_TAG "EffectsFactory7.0"
 #include <log/log.h>
 
-#include <audio_policy_configuration_V7_0.h>
+#include <android_audio_policy_configuration_V7_0.h>
 
 #include "Effect.h"
 
@@ -28,7 +28,7 @@
 using namespace ::android::hardware::audio::common::V7_0;
 // Make an alias for enumerations generated from the APM config XSD.
 namespace xsd {
-using namespace ::audio::policy::configuration::V7_0;
+using namespace ::android::audio::policy::configuration::V7_0;
 }
 
 namespace android::hardware::audio::effect::V7_0::implementation {
diff --git a/audio/common/7.0/types.hal b/audio/common/7.0/types.hal
index 631d524..ed56c73 100644
--- a/audio/common/7.0/types.hal
+++ b/audio/common/7.0/types.hal
@@ -118,9 +118,9 @@
  * Base configuration attributes applicable to any stream of audio.
  */
 struct AudioConfigBase {
-    AudioFormat format;                 // 'DEFAULT' means 'unspecified'
+    AudioFormat format;                 // empty means 'unspecified'
     uint32_t sampleRateHz;              // 0 means 'unspecified'
-    vec<AudioChannelMask> channelMask;  // empty means 'unspecified'
+    AudioChannelMask channelMask;       // empty means 'unspecified'
 };
 
 /**
@@ -167,10 +167,18 @@
     AudioDevice deviceType;
     safe_union Address {
         /**
-         * The address may be left unspecified if 'device' specifies
-         * a physical device unambiguously.
+         * String uniquely identifying the device among other devices
+         * of the same type. Can be empty in case there is only one device
+         * of this type.
+         *
+         * Depending on the device type, its id may be assigned by the framework
+         * (this is done for REMOTE_SUBMIX), or specified in the audio policy
+         * configuration file (typically done for BUS devices), or assigned
+         * by the HAL service. In any case, both framework and HAL must
+         * never attempt to parse the value of the id. If the address must
+         * be parsed, one of the members below must be used instead of 'id'.
          */
-        Monostate unspecified;
+        string id;
         /** IEEE 802 MAC address. Set for Bluetooth devices. */
         uint8_t[6] mac;
         /** IPv4 Address. Set for IPv4 devices. */
@@ -182,10 +190,6 @@
             int32_t card;
             int32_t device;
         } alsa;
-        /** Arbitrary BUS device unique address. Not interpreted by the framework. */
-        string bus;
-        /** Arbitrary REMOTE_SUBMIX device unique address. Not interpreted by the HAL. */
-        string rSubmix;
     } address;
 };
 
@@ -328,14 +332,22 @@
  * A gain stage is always attached to an audio port.
  */
 struct AudioGain {
-    vec<AudioGainMode> mode; // modes of operation
-    AudioChannelMask channelMask; // channels which gain can be controlled
-    int32_t minValue;     // minimum gain value in millibels
-    int32_t maxValue;     // maximum gain value in millibels
-    int32_t defaultValue; // default gain value in millibels
-    uint32_t stepValue;   // gain step in millibels
-    uint32_t minRampMs;   // minimum ramp duration in ms
-    uint32_t maxRampMs;   // maximum ramp duration in ms
+    /** Modes of operation. */
+    vec<AudioGainMode> mode;
+    /** Channels which gain can be controlled. */
+    AudioChannelMask channelMask;
+    /** Minimum gain value in millibels. */
+    int32_t minValue;
+    /** Maximum gain value in millibels. */
+    int32_t maxValue;
+    /** Default gain value in millibels. */
+    int32_t defaultValue;
+    /** Gain step in millibels. */
+    uint32_t stepValue;
+    /** Ramp duration in ms. */
+    uint32_t minRampMs;
+    /** Maximum ramp duration in ms. */
+    uint32_t maxRampMs;
 };
 
 /**
@@ -343,16 +355,20 @@
  * given port.
  */
 struct AudioGainConfig {
-    int32_t index;  // index of the corresponding AudioGain in AudioPort.gains
-    vec<AudioGainMode> mode; // modes of operation
-    AudioChannelMask channelMask;  // channels which gain value follows
+    /** Index of the corresponding AudioGain in AudioPort.gains. */
+    int32_t index;
+    /** Modes of operation. */
+    vec<AudioGainMode> mode;
+    /** Channels which gain value follows. */
+    AudioChannelMask channelMask;
     /**
      * Gain values in millibels for each channel ordered from LSb to MSb in
      * channel mask. The number of values is 1 in joint mode or
-     * popcount(channel_mask).
+     * the number of channels in the channel mask.
      */
-    int32_t[4 * 8] values;
-    uint32_t rampDurationMs;  // ramp duration in ms
+    vec<int32_t> values;
+    /** Ramp duration in ms. */
+    uint32_t rampDurationMs;
 };
 
 
@@ -409,7 +425,7 @@
      * parameters (or none) may be set. See the documentation of the
      * AudioConfigBase struct.
      */
-    AudioConfigBase config;
+    AudioConfigBase base;
     /** Associated gain control. */
     safe_union OptionalGain {
         Monostate unspecified;
@@ -439,6 +455,8 @@
     vec<AudioProfile> profiles;
     /** List of gain controls attached to the port. */
     vec<AudioGain> gains;
+    /** Parameters that depend on the actual port role. */
+    AudioPortExtendedInfo ext;
     /**
      * Current configuration of the audio port, may have all the fields left
      * unspecified.
diff --git a/audio/common/all-versions/default/7.0/HidlUtils.cpp b/audio/common/all-versions/default/7.0/HidlUtils.cpp
new file mode 100644
index 0000000..1a66282
--- /dev/null
+++ b/audio/common/all-versions/default/7.0/HidlUtils.cpp
@@ -0,0 +1,847 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#define LOG_TAG "HidlUtils"
+#include <log/log.h>
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <common/all-versions/VersionUtils.h>
+
+#include "HidlUtils.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace common {
+namespace CPP_VERSION {
+namespace implementation {
+
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+#define CONVERT_CHECKED(expr, result)                   \
+    if (status_t status = (expr); status != NO_ERROR) { \
+        result = status;                                \
+    }
+
+status_t HidlUtils::audioIndexChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                 AudioChannelMask* channelMask) {
+    *channelMask = audio_channel_index_mask_to_string(halChannelMask);
+    if (!channelMask->empty() && !xsd::isUnknownAudioChannelMask(*channelMask)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown index channel mask value 0x%X", halChannelMask);
+    *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioInputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                 AudioChannelMask* channelMask) {
+    *channelMask = audio_channel_in_mask_to_string(halChannelMask);
+    if (!channelMask->empty() && !xsd::isUnknownAudioChannelMask(*channelMask)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown input channel mask value 0x%X", halChannelMask);
+    *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioOutputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                  AudioChannelMask* channelMask) {
+    *channelMask = audio_channel_out_mask_to_string(halChannelMask);
+    if (!channelMask->empty() && !xsd::isUnknownAudioChannelMask(*channelMask)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown output channel mask value 0x%X", halChannelMask);
+    *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioChannelMaskFromHal(audio_channel_mask_t halChannelMask, bool isInput,
+                                            AudioChannelMask* channelMask) {
+    if (halChannelMask != AUDIO_CHANNEL_NONE) {
+        if (audio_channel_mask_is_valid(halChannelMask)) {
+            switch (audio_channel_mask_get_representation(halChannelMask)) {
+                case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+                    return isInput ? audioInputChannelMaskFromHal(halChannelMask, channelMask)
+                                   : audioOutputChannelMaskFromHal(halChannelMask, channelMask);
+                case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+                    // Index masks do not have direction.
+                    return audioIndexChannelMaskFromHal(halChannelMask, channelMask);
+                    // no default
+            }
+        }
+        *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+        return BAD_VALUE;
+    }
+    *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return NO_ERROR;
+}
+
+status_t HidlUtils::audioChannelMaskToHal(const AudioChannelMask& channelMask,
+                                          audio_channel_mask_t* halChannelMask) {
+    if (!xsd::isUnknownAudioChannelMask(channelMask) &&
+        audio_channel_mask_from_string(channelMask.c_str(), halChannelMask)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown channel mask \"%s\"", channelMask.c_str());
+    *halChannelMask = AUDIO_CHANNEL_NONE;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioConfigBaseFromHal(const audio_config_base_t& halConfigBase, bool isInput,
+                                           AudioConfigBase* configBase) {
+    status_t result = NO_ERROR;
+    configBase->sampleRateHz = halConfigBase.sample_rate;
+    CONVERT_CHECKED(
+            audioChannelMaskFromHal(halConfigBase.channel_mask, isInput, &configBase->channelMask),
+            result);
+    CONVERT_CHECKED(audioFormatFromHal(halConfigBase.format, &configBase->format), result);
+    return result;
+}
+
+status_t HidlUtils::audioConfigBaseToHal(const AudioConfigBase& configBase,
+                                         audio_config_base_t* halConfigBase) {
+    status_t result = NO_ERROR;
+    halConfigBase->sample_rate = configBase.sampleRateHz;
+    CONVERT_CHECKED(audioChannelMaskToHal(configBase.channelMask, &halConfigBase->channel_mask),
+                    result);
+    CONVERT_CHECKED(audioFormatToHal(configBase.format, &halConfigBase->format), result);
+    return result;
+}
+
+status_t HidlUtils::audioDeviceTypeFromHal(audio_devices_t halDevice, AudioDevice* device) {
+    *device = audio_device_to_string(halDevice);
+    if (!device->empty() && !xsd::isUnknownAudioDevice(*device)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio device value 0x%X", halDevice);
+    *device = toString(xsd::AudioDevice::AUDIO_DEVICE_NONE);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioDeviceTypeToHal(const AudioDevice& device, audio_devices_t* halDevice) {
+    if (!xsd::isUnknownAudioDevice(device) && audio_device_from_string(device.c_str(), halDevice)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio device \"%s\"", device.c_str());
+    *halDevice = AUDIO_DEVICE_NONE;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioFormatFromHal(audio_format_t halFormat, AudioFormat* format) {
+    *format = audio_format_to_string(halFormat);
+    if (!format->empty() && !xsd::isUnknownAudioFormat(*format)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio format value 0x%X", halFormat);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioFormatToHal(const AudioFormat& format, audio_format_t* halFormat) {
+    if (!xsd::isUnknownAudioFormat(format) && audio_format_from_string(format.c_str(), halFormat)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio format \"%s\"", format.c_str());
+    *halFormat = AUDIO_FORMAT_DEFAULT;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioGainModeMaskFromHal(audio_gain_mode_t halGainModeMask,
+                                             hidl_vec<AudioGainMode>* gainModeMask) {
+    status_t status = NO_ERROR;
+    std::vector<AudioGainMode> result;
+    for (uint32_t bit = 0; bit < sizeof(audio_gain_mode_t) * 8; ++bit) {
+        audio_gain_mode_t flag = static_cast<audio_gain_mode_t>(1u << bit);
+        if ((flag & halGainModeMask) == flag) {
+            AudioGainMode flagStr = audio_gain_mode_to_string(flag);
+            if (!flagStr.empty() && !xsd::isUnknownAudioGainMode(flagStr)) {
+                result.push_back(flagStr);
+            } else {
+                ALOGE("Unknown audio gain mode value 0x%X", flag);
+                status = BAD_VALUE;
+            }
+        }
+    }
+    *gainModeMask = result;
+    return status;
+}
+
+status_t HidlUtils::audioGainModeMaskToHal(const hidl_vec<AudioGainMode>& gainModeMask,
+                                           audio_gain_mode_t* halGainModeMask) {
+    status_t status = NO_ERROR;
+    *halGainModeMask = {};
+    for (const auto& gainMode : gainModeMask) {
+        audio_gain_mode_t halGainMode;
+        if (!xsd::isUnknownAudioGainMode(gainMode) &&
+            audio_gain_mode_from_string(gainMode.c_str(), &halGainMode)) {
+            *halGainModeMask = static_cast<audio_gain_mode_t>(*halGainModeMask | halGainMode);
+        } else {
+            ALOGE("Unknown audio gain mode \"%s\"", gainMode.c_str());
+            status = BAD_VALUE;
+        }
+    }
+    return status;
+}
+
+status_t HidlUtils::audioSourceFromHal(audio_source_t halSource, AudioSource* source) {
+    *source = audio_source_to_string(halSource);
+    if (!source->empty() && !xsd::isUnknownAudioSource(*source)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio source value 0x%X", halSource);
+    *source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioSourceToHal(const AudioSource& source, audio_source_t* halSource) {
+    if (!xsd::isUnknownAudioSource(source) && audio_source_from_string(source.c_str(), halSource)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio source \"%s\"", source.c_str());
+    *halSource = AUDIO_SOURCE_DEFAULT;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioStreamTypeFromHal(audio_stream_type_t halStreamType,
+                                           AudioStreamType* streamType) {
+    *streamType = audio_stream_type_to_string(halStreamType);
+    if (!streamType->empty() && !xsd::isUnknownAudioStreamType(*streamType)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio stream type value 0x%X", halStreamType);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioStreamTypeToHal(const AudioStreamType& streamType,
+                                         audio_stream_type_t* halStreamType) {
+    if (!xsd::isUnknownAudioStreamType(streamType) &&
+        audio_stream_type_from_string(streamType.c_str(), halStreamType)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio stream type \"%s\"", streamType.c_str());
+    *halStreamType = AUDIO_STREAM_DEFAULT;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioConfigFromHal(const audio_config_t& halConfig, bool isInput,
+                                       AudioConfig* config) {
+    status_t result = NO_ERROR;
+    audio_config_base_t halConfigBase = {halConfig.sample_rate, halConfig.channel_mask,
+                                         halConfig.format};
+    CONVERT_CHECKED(audioConfigBaseFromHal(halConfigBase, isInput, &config->base), result);
+    CONVERT_CHECKED(audioOffloadInfoFromHal(halConfig.offload_info, &config->offloadInfo), result);
+    config->frameCount = halConfig.frame_count;
+    return result;
+}
+
+status_t HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig) {
+    status_t result = NO_ERROR;
+    *halConfig = AUDIO_CONFIG_INITIALIZER;
+    audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
+    CONVERT_CHECKED(audioConfigBaseToHal(config.base, &halConfigBase), result);
+    halConfig->sample_rate = halConfigBase.sample_rate;
+    halConfig->channel_mask = halConfigBase.channel_mask;
+    halConfig->format = halConfigBase.format;
+    CONVERT_CHECKED(audioOffloadInfoToHal(config.offloadInfo, &halConfig->offload_info), result);
+    halConfig->frame_count = config.frameCount;
+    return result;
+}
+
+status_t HidlUtils::audioGainConfigFromHal(const struct audio_gain_config& halConfig, bool isInput,
+                                           AudioGainConfig* config) {
+    status_t result = NO_ERROR;
+    config->index = halConfig.index;
+    CONVERT_CHECKED(audioGainModeMaskFromHal(halConfig.mode, &config->mode), result);
+    CONVERT_CHECKED(audioChannelMaskFromHal(halConfig.channel_mask, isInput, &config->channelMask),
+                    result);
+    if (halConfig.mode & AUDIO_GAIN_MODE_JOINT) {
+        config->values.resize(1);
+        config->values[0] = halConfig.values[0];
+    }
+    if (halConfig.mode & (AUDIO_GAIN_MODE_CHANNELS | AUDIO_GAIN_MODE_RAMP)) {
+        config->values.resize(__builtin_popcount(halConfig.channel_mask));
+        for (size_t i = 0; i < config->values.size(); ++i) {
+            config->values[i] = halConfig.values[i];
+        }
+    }
+    config->rampDurationMs = halConfig.ramp_duration_ms;
+    return result;
+}
+
+status_t HidlUtils::audioGainConfigToHal(const AudioGainConfig& config,
+                                         struct audio_gain_config* halConfig) {
+    status_t result = NO_ERROR;
+    halConfig->index = config.index;
+    CONVERT_CHECKED(audioGainModeMaskToHal(config.mode, &halConfig->mode), result);
+    CONVERT_CHECKED(audioChannelMaskToHal(config.channelMask, &halConfig->channel_mask), result);
+    memset(halConfig->values, 0, sizeof(halConfig->values));
+    if (halConfig->mode & AUDIO_GAIN_MODE_JOINT) {
+        if (config.values.size() > 0) {
+            halConfig->values[0] = config.values[0];
+        } else {
+            ALOGE("Empty values vector in AudioGainConfig");
+            result = BAD_VALUE;
+        }
+    }
+    if (halConfig->mode & (AUDIO_GAIN_MODE_CHANNELS | AUDIO_GAIN_MODE_RAMP)) {
+        size_t channelCount = __builtin_popcount(halConfig->channel_mask);
+        size_t valuesCount = config.values.size();
+        if (channelCount != valuesCount) {
+            ALOGE("Wrong number of values in AudioGainConfig, expected: %zu, found: %zu",
+                  channelCount, valuesCount);
+            result = BAD_VALUE;
+            if (channelCount < valuesCount) {
+                valuesCount = channelCount;
+            }
+        }
+        for (size_t i = 0; i < valuesCount; ++i) {
+            halConfig->values[i] = config.values[i];
+        }
+    }
+    halConfig->ramp_duration_ms = config.rampDurationMs;
+    return result;
+}
+
+status_t HidlUtils::audioGainFromHal(const struct audio_gain& halGain, bool isInput,
+                                     AudioGain* gain) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioGainModeMaskFromHal(halGain.mode, &gain->mode), result);
+    CONVERT_CHECKED(audioChannelMaskFromHal(halGain.channel_mask, isInput, &gain->channelMask),
+                    result);
+    gain->minValue = halGain.min_value;
+    gain->maxValue = halGain.max_value;
+    gain->defaultValue = halGain.default_value;
+    gain->stepValue = halGain.step_value;
+    gain->minRampMs = halGain.min_ramp_ms;
+    gain->maxRampMs = halGain.max_ramp_ms;
+    return result;
+}
+
+status_t HidlUtils::audioGainToHal(const AudioGain& gain, struct audio_gain* halGain) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioGainModeMaskToHal(gain.mode, &halGain->mode), result);
+    CONVERT_CHECKED(audioChannelMaskToHal(gain.channelMask, &halGain->channel_mask), result);
+    halGain->min_value = gain.minValue;
+    halGain->max_value = gain.maxValue;
+    halGain->default_value = gain.defaultValue;
+    halGain->step_value = gain.stepValue;
+    halGain->min_ramp_ms = gain.minRampMs;
+    halGain->max_ramp_ms = gain.maxRampMs;
+    return result;
+}
+
+status_t HidlUtils::audioUsageFromHal(audio_usage_t halUsage, AudioUsage* usage) {
+    if (halUsage == AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST ||
+        halUsage == AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT ||
+        halUsage == AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED ||
+        halUsage == AUDIO_USAGE_NOTIFICATION_EVENT) {
+        halUsage = AUDIO_USAGE_NOTIFICATION;
+    }
+    *usage = audio_usage_to_string(halUsage);
+    if (!usage->empty() && !xsd::isUnknownAudioUsage(*usage)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio usage %d", halUsage);
+    *usage = toString(xsd::AudioUsage::AUDIO_USAGE_UNKNOWN);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioUsageToHal(const AudioUsage& usage, audio_usage_t* halUsage) {
+    if (!xsd::isUnknownAudioUsage(usage) && audio_usage_from_string(usage.c_str(), halUsage)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio usage \"%s\"", usage.c_str());
+    *halUsage = AUDIO_USAGE_UNKNOWN;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
+                                            AudioOffloadInfo* offload) {
+    status_t result = NO_ERROR;
+    audio_config_base_t halConfigBase = {halOffload.sample_rate, halOffload.channel_mask,
+                                         halOffload.format};
+    CONVERT_CHECKED(audioConfigBaseFromHal(halConfigBase, false /*isInput*/, &offload->base),
+                    result);
+    CONVERT_CHECKED(audioStreamTypeFromHal(halOffload.stream_type, &offload->streamType), result);
+    offload->bitRatePerSecond = halOffload.bit_rate;
+    offload->durationMicroseconds = halOffload.duration_us;
+    offload->hasVideo = halOffload.has_video;
+    offload->isStreaming = halOffload.is_streaming;
+    offload->bitWidth = halOffload.bit_width;
+    offload->bufferSize = halOffload.offload_buffer_size;
+    CONVERT_CHECKED(audioUsageFromHal(halOffload.usage, &offload->usage), result);
+    if (halOffload.version >= AUDIO_OFFLOAD_INFO_VERSION_0_2) {
+        offload->encapsulationMode =
+                static_cast<AudioEncapsulationMode>(halOffload.encapsulation_mode);
+        offload->contentId = halOffload.content_id;
+        offload->syncId = halOffload.sync_id;
+    } else {
+        offload->encapsulationMode = AudioEncapsulationMode::NONE;
+        offload->contentId = 0;
+        offload->syncId = 0;
+    }
+    return result;
+}
+
+status_t HidlUtils::audioOffloadInfoToHal(const AudioOffloadInfo& offload,
+                                          audio_offload_info_t* halOffload) {
+    status_t result = NO_ERROR;
+    *halOffload = AUDIO_INFO_INITIALIZER;
+    audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
+    CONVERT_CHECKED(audioConfigBaseToHal(offload.base, &halConfigBase), result);
+    halOffload->sample_rate = halConfigBase.sample_rate;
+    halOffload->channel_mask = halConfigBase.channel_mask;
+    halOffload->format = halConfigBase.format;
+    CONVERT_CHECKED(audioStreamTypeToHal(offload.streamType, &halOffload->stream_type), result);
+    halOffload->bit_rate = offload.bitRatePerSecond;
+    halOffload->duration_us = offload.durationMicroseconds;
+    halOffload->has_video = offload.hasVideo;
+    halOffload->is_streaming = offload.isStreaming;
+    halOffload->bit_width = offload.bitWidth;
+    halOffload->offload_buffer_size = offload.bufferSize;
+    CONVERT_CHECKED(audioUsageToHal(offload.usage, &halOffload->usage), result);
+    halOffload->encapsulation_mode =
+            static_cast<audio_encapsulation_mode_t>(offload.encapsulationMode);
+    halOffload->content_id = offload.contentId;
+    halOffload->sync_id = offload.syncId;
+    return result;
+}
+
+status_t HidlUtils::audioPortConfigFromHal(const struct audio_port_config& halConfig,
+                                           AudioPortConfig* config) {
+    status_t result = NO_ERROR;
+    bool isInput = false;
+    config->id = halConfig.id;
+    CONVERT_CHECKED(audioPortExtendedInfoFromHal(halConfig.role, halConfig.type,
+                                                 halConfig.ext.device, halConfig.ext.mix,
+                                                 halConfig.ext.session, &config->ext, &isInput),
+                    result);
+    if (audio_port_config_has_input_direction(&halConfig) != isInput) {
+        ALOGE("Inconsistent port config direction data, is input: %d (hal) != %d (converter)",
+              audio_port_config_has_input_direction(&halConfig), isInput);
+        result = BAD_VALUE;
+    }
+    if (halConfig.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+        config->base.sampleRateHz = halConfig.sample_rate;
+    } else {
+        config->base.sampleRateHz = {};
+    }
+    if (halConfig.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+        CONVERT_CHECKED(
+                audioChannelMaskFromHal(halConfig.channel_mask, isInput, &config->base.channelMask),
+                result);
+    } else {
+        config->base.channelMask = {};
+    }
+    if (halConfig.config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+        CONVERT_CHECKED(audioFormatFromHal(halConfig.format, &config->base.format), result);
+    } else {
+        config->base.format = {};
+    }
+    if (halConfig.config_mask & AUDIO_PORT_CONFIG_GAIN) {
+        config->gain.config({});
+        CONVERT_CHECKED(audioGainConfigFromHal(halConfig.gain, isInput, &config->gain.config()),
+                        result);
+    } else {
+        config->gain.unspecified({});
+    }
+    return result;
+}
+
+status_t HidlUtils::audioPortConfigToHal(const AudioPortConfig& config,
+                                         struct audio_port_config* halConfig) {
+    status_t result = NO_ERROR;
+    memset(halConfig, 0, sizeof(audio_port_config));
+    halConfig->id = config.id;
+    halConfig->config_mask = {};
+    if (config.base.sampleRateHz != 0) {
+        halConfig->config_mask |= AUDIO_PORT_CONFIG_SAMPLE_RATE;
+        halConfig->sample_rate = config.base.sampleRateHz;
+    }
+    if (!config.base.channelMask.empty()) {
+        halConfig->config_mask |= AUDIO_PORT_CONFIG_CHANNEL_MASK;
+        CONVERT_CHECKED(audioChannelMaskToHal(config.base.channelMask, &halConfig->channel_mask),
+                        result);
+    }
+    if (!config.base.format.empty()) {
+        halConfig->config_mask |= AUDIO_PORT_CONFIG_FORMAT;
+        CONVERT_CHECKED(audioFormatToHal(config.base.format, &halConfig->format), result);
+    }
+    if (config.gain.getDiscriminator() ==
+        AudioPortConfig::OptionalGain::hidl_discriminator::config) {
+        halConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+        CONVERT_CHECKED(audioGainConfigToHal(config.gain.config(), &halConfig->gain), result);
+    }
+    CONVERT_CHECKED(audioPortExtendedInfoToHal(config.ext, &halConfig->role, &halConfig->type,
+                                               &halConfig->ext.device, &halConfig->ext.mix,
+                                               &halConfig->ext.session),
+                    result);
+    return result;
+}
+
+status_t HidlUtils::audioPortExtendedInfoFromHal(
+        audio_port_role_t role, audio_port_type_t type,
+        const struct audio_port_config_device_ext& device,
+        const struct audio_port_config_mix_ext& mix,
+        const struct audio_port_config_session_ext& session, AudioPortExtendedInfo* ext,
+        bool* isInput) {
+    status_t result = NO_ERROR;
+    *isInput = false;
+    switch (type) {
+        case AUDIO_PORT_TYPE_NONE:
+            ext->unspecified({});
+            break;
+        case AUDIO_PORT_TYPE_DEVICE: {
+            *isInput = role == AUDIO_PORT_ROLE_SOURCE;
+            ext->device({});
+            CONVERT_CHECKED(deviceAddressFromHal(device.type, device.address, &ext->device()),
+                            result);
+            break;
+        }
+        case AUDIO_PORT_TYPE_MIX: {
+            *isInput = role == AUDIO_PORT_ROLE_SINK;
+            ext->mix({});
+            ext->mix().ioHandle = mix.handle;
+            if (role == AUDIO_PORT_ROLE_SOURCE) {
+                ext->mix().useCase.stream({});
+                CONVERT_CHECKED(
+                        audioStreamTypeFromHal(mix.usecase.stream, &ext->mix().useCase.stream()),
+                        result);
+            } else if (role == AUDIO_PORT_ROLE_SINK) {
+                ext->mix().useCase.source({});
+                CONVERT_CHECKED(
+                        audioSourceFromHal(mix.usecase.source, &ext->mix().useCase.source()),
+                        result);
+            }
+            break;
+        }
+        case AUDIO_PORT_TYPE_SESSION: {
+            ext->session(session.session);
+            break;
+        }
+    }
+    return result;
+}
+
+status_t HidlUtils::audioPortExtendedInfoToHal(const AudioPortExtendedInfo& ext,
+                                               audio_port_role_t* role, audio_port_type_t* type,
+                                               struct audio_port_config_device_ext* device,
+                                               struct audio_port_config_mix_ext* mix,
+                                               struct audio_port_config_session_ext* session) {
+    status_t result = NO_ERROR;
+    switch (ext.getDiscriminator()) {
+        case AudioPortExtendedInfo::hidl_discriminator::unspecified:
+            *role = AUDIO_PORT_ROLE_NONE;
+            *type = AUDIO_PORT_TYPE_NONE;
+            break;
+        case AudioPortExtendedInfo::hidl_discriminator::device:
+            *role = xsd::isOutputDevice(ext.device().deviceType) ? AUDIO_PORT_ROLE_SINK
+                                                                 : AUDIO_PORT_ROLE_SOURCE;
+            *type = AUDIO_PORT_TYPE_DEVICE;
+            CONVERT_CHECKED(deviceAddressToHal(ext.device(), &device->type, device->address),
+                            result);
+            break;
+        case AudioPortExtendedInfo::hidl_discriminator::mix:
+            *type = AUDIO_PORT_TYPE_MIX;
+            switch (ext.mix().useCase.getDiscriminator()) {
+                case AudioPortExtendedInfo::AudioPortMixExt::UseCase::hidl_discriminator::stream:
+                    *role = AUDIO_PORT_ROLE_SOURCE;
+                    CONVERT_CHECKED(
+                            audioStreamTypeToHal(ext.mix().useCase.stream(), &mix->usecase.stream),
+                            result);
+                    break;
+                case AudioPortExtendedInfo::AudioPortMixExt::UseCase::hidl_discriminator::source:
+                    *role = AUDIO_PORT_ROLE_SINK;
+                    CONVERT_CHECKED(
+                            audioSourceToHal(ext.mix().useCase.source(), &mix->usecase.source),
+                            result);
+                    break;
+            }
+            mix->handle = ext.mix().ioHandle;
+            break;
+        case AudioPortExtendedInfo::hidl_discriminator::session:
+            *role = AUDIO_PORT_ROLE_NONE;
+            *type = AUDIO_PORT_TYPE_SESSION;
+            session->session = static_cast<audio_session_t>(ext.session());
+            break;
+    }
+    return result;
+}
+
+status_t HidlUtils::audioPortFromHal(const struct audio_port& halPort, AudioPort* port) {
+    struct audio_port_v7 halPortV7 = {};
+    audio_populate_audio_port_v7(&halPort, &halPortV7);
+    return audioPortFromHal(halPortV7, port);
+}
+
+status_t HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port* halPort) {
+    status_t result = NO_ERROR;
+    struct audio_port_v7 halPortV7 = {};
+    CONVERT_CHECKED(audioPortToHal(port, &halPortV7), result);
+    if (!audio_populate_audio_port(&halPortV7, halPort)) {
+        result = BAD_VALUE;
+    }
+    return result;
+}
+
+status_t HidlUtils::audioPortFromHal(const struct audio_port_v7& halPort, AudioPort* port) {
+    status_t result = NO_ERROR;
+    bool isInput = false;
+    port->id = halPort.id;
+    port->name.setToExternal(halPort.name, strlen(halPort.name));
+    // HAL uses slightly different but convertible structures for the extended info in port
+    // and port config structures.
+    struct audio_port_config_device_ext halDevice = {};
+    struct audio_port_config_mix_ext halMix = {};
+    struct audio_port_config_session_ext halSession = {};
+    switch (halPort.type) {
+        case AUDIO_PORT_TYPE_NONE:
+            break;
+        case AUDIO_PORT_TYPE_DEVICE:
+            halDevice.type = halPort.ext.device.type;
+            memcpy(halDevice.address, halPort.ext.device.address, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+            break;
+        case AUDIO_PORT_TYPE_MIX:
+            halMix.handle = halPort.ext.mix.handle;
+            break;
+        case AUDIO_PORT_TYPE_SESSION:
+            halSession.session = halPort.ext.session.session;
+            break;
+    }
+    CONVERT_CHECKED(audioPortExtendedInfoFromHal(halPort.role, halPort.type, halDevice, halMix,
+                                                 halSession, &port->ext, &isInput),
+                    result);
+    port->profiles.resize(halPort.num_audio_profiles);
+    for (size_t i = 0; i < halPort.num_audio_profiles; ++i) {
+        CONVERT_CHECKED(audioProfileFromHal(halPort.audio_profiles[i], isInput, &port->profiles[i]),
+                        result);
+    }
+    port->gains.resize(halPort.num_gains);
+    for (size_t i = 0; i < halPort.num_gains; ++i) {
+        CONVERT_CHECKED(audioGainFromHal(halPort.gains[i], isInput, &port->gains[i]), result);
+    }
+    CONVERT_CHECKED(audioPortConfigFromHal(halPort.active_config, &port->activeConfig), result);
+    return result;
+}
+
+status_t HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port_v7* halPort) {
+    status_t result = NO_ERROR;
+    halPort->id = port.id;
+    strncpy(halPort->name, port.name.c_str(), AUDIO_PORT_MAX_NAME_LEN);
+    halPort->name[AUDIO_PORT_MAX_NAME_LEN - 1] = '\0';
+    if (port.name.size() >= AUDIO_PORT_MAX_NAME_LEN) {
+        ALOGE("HIDL Audio Port name is too long: %zu", port.name.size());
+        result = BAD_VALUE;
+    }
+    halPort->num_audio_profiles = port.profiles.size();
+    if (halPort->num_audio_profiles > AUDIO_PORT_MAX_AUDIO_PROFILES) {
+        ALOGE("HIDL Audio Port has too many profiles: %u", halPort->num_audio_profiles);
+        halPort->num_audio_profiles = AUDIO_PORT_MAX_AUDIO_PROFILES;
+        result = BAD_VALUE;
+    }
+    for (size_t i = 0; i < halPort->num_audio_profiles; ++i) {
+        CONVERT_CHECKED(audioProfileToHal(port.profiles[i], &halPort->audio_profiles[i]), result);
+    }
+    halPort->num_gains = port.gains.size();
+    if (halPort->num_gains > AUDIO_PORT_MAX_GAINS) {
+        ALOGE("HIDL Audio Port has too many gains: %u", halPort->num_gains);
+        halPort->num_gains = AUDIO_PORT_MAX_GAINS;
+        result = BAD_VALUE;
+    }
+    for (size_t i = 0; i < halPort->num_gains; ++i) {
+        CONVERT_CHECKED(audioGainToHal(port.gains[i], &halPort->gains[i]), result);
+    }
+    // HAL uses slightly different but convertible structures for the extended info in port
+    // and port config structures.
+    struct audio_port_config_device_ext halDevice = {};
+    struct audio_port_config_mix_ext halMix = {};
+    struct audio_port_config_session_ext halSession = {};
+    CONVERT_CHECKED(audioPortExtendedInfoToHal(port.ext, &halPort->role, &halPort->type, &halDevice,
+                                               &halMix, &halSession),
+                    result);
+    switch (halPort->type) {
+        case AUDIO_PORT_TYPE_NONE:
+            break;
+        case AUDIO_PORT_TYPE_DEVICE:
+            halPort->ext.device.type = halDevice.type;
+            memcpy(halPort->ext.device.address, halDevice.address, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+            break;
+        case AUDIO_PORT_TYPE_MIX:
+            halPort->ext.mix.handle = halMix.handle;
+            break;
+        case AUDIO_PORT_TYPE_SESSION:
+            halPort->ext.session.session = halSession.session;
+            break;
+    }
+    CONVERT_CHECKED(audioPortConfigToHal(port.activeConfig, &halPort->active_config), result);
+    return result;
+}
+
+status_t HidlUtils::audioProfileFromHal(const struct audio_profile& halProfile, bool isInput,
+                                        AudioProfile* profile) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioFormatFromHal(halProfile.format, &profile->format), result);
+    profile->sampleRates.resize(halProfile.num_sample_rates);
+    for (size_t i = 0; i < halProfile.num_sample_rates; ++i) {
+        profile->sampleRates[i] = halProfile.sample_rates[i];
+    }
+    profile->channelMasks.resize(halProfile.num_channel_masks);
+    for (size_t i = 0; i < halProfile.num_channel_masks; ++i) {
+        CONVERT_CHECKED(audioChannelMaskFromHal(halProfile.channel_masks[i], isInput,
+                                                &profile->channelMasks[i]),
+                        result);
+    }
+    return result;
+}
+
+status_t HidlUtils::audioProfileToHal(const AudioProfile& profile,
+                                      struct audio_profile* halProfile) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioFormatToHal(profile.format, &halProfile->format), result);
+    memset(halProfile->sample_rates, 0, sizeof(halProfile->sample_rates));
+    halProfile->num_sample_rates = profile.sampleRates.size();
+    if (halProfile->num_sample_rates > AUDIO_PORT_MAX_SAMPLING_RATES) {
+        ALOGE("HIDL Audio profile has too many sample rates: %u", halProfile->num_sample_rates);
+        halProfile->num_sample_rates = AUDIO_PORT_MAX_SAMPLING_RATES;
+        result = BAD_VALUE;
+    }
+    for (size_t i = 0; i < halProfile->num_sample_rates; ++i) {
+        halProfile->sample_rates[i] = profile.sampleRates[i];
+    }
+    memset(halProfile->channel_masks, 0, sizeof(halProfile->channel_masks));
+    halProfile->num_channel_masks = profile.channelMasks.size();
+    if (halProfile->num_channel_masks > AUDIO_PORT_MAX_CHANNEL_MASKS) {
+        ALOGE("HIDL Audio profile has too many channel masks: %u", halProfile->num_channel_masks);
+        halProfile->num_channel_masks = AUDIO_PORT_MAX_CHANNEL_MASKS;
+        result = BAD_VALUE;
+    }
+    for (size_t i = 0; i < halProfile->num_channel_masks; ++i) {
+        CONVERT_CHECKED(
+                audioChannelMaskToHal(profile.channelMasks[i], &halProfile->channel_masks[i]),
+                status);
+    }
+    return result;
+}
+
+status_t HidlUtils::deviceAddressFromHal(audio_devices_t halDeviceType,
+                                         const char* halDeviceAddress, DeviceAddress* device) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioDeviceTypeFromHal(halDeviceType, &device->deviceType), result);
+    if (audio_is_a2dp_out_device(halDeviceType) || audio_is_a2dp_in_device(halDeviceType)) {
+        device->address.mac({});
+        if (halDeviceAddress != nullptr) {
+            auto& mac = device->address.mac();
+            int status = sscanf(halDeviceAddress, "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX", &mac[0], &mac[1],
+                                &mac[2], &mac[3], &mac[4], &mac[5]);
+            if (status != 6) {
+                ALOGE("BT A2DP device \"%s\" MAC address \"%s\" is invalid",
+                      device->deviceType.c_str(), halDeviceAddress);
+                result = BAD_VALUE;
+            }
+        } else {
+            ALOGE("BT A2DP device \"%s\" does not have a MAC address", halDeviceAddress);
+            result = BAD_VALUE;
+        }
+    } else if (halDeviceType == AUDIO_DEVICE_OUT_IP || halDeviceType == AUDIO_DEVICE_IN_IP) {
+        device->address.ipv4({});
+        if (halDeviceAddress != nullptr) {
+            auto& ipv4 = device->address.ipv4();
+            int status = sscanf(halDeviceAddress, "%hhu.%hhu.%hhu.%hhu", &ipv4[0], &ipv4[1],
+                                &ipv4[2], &ipv4[3]);
+            if (status != 4) {
+                ALOGE("IP device \"%s\" IPv4 address \"%s\" is invalid", device->deviceType.c_str(),
+                      halDeviceAddress);
+                result = BAD_VALUE;
+            }
+        } else {
+            ALOGE("IP device \"%s\" does not have an IPv4 address", device->deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else if (audio_is_usb_out_device(halDeviceType) || audio_is_usb_in_device(halDeviceType)) {
+        device->address.alsa({});
+        if (halDeviceAddress != nullptr) {
+            auto& alsa = device->address.alsa();
+            int status = sscanf(halDeviceAddress, "card=%d;device=%d", &alsa.card, &alsa.device);
+            if (status != 2) {
+                ALOGE("USB device \"%s\" ALSA address \"%s\" is invalid",
+                      device->deviceType.c_str(), halDeviceAddress);
+                result = BAD_VALUE;
+            }
+        } else {
+            ALOGE("USB device \"%s\" does not have ALSA address", device->deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else {
+        // Any other device type uses the 'id' field.
+        device->address.id(halDeviceAddress != nullptr ? halDeviceAddress : "");
+    }
+    return result;
+}
+
+status_t HidlUtils::deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+                                       char* halDeviceAddress) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioDeviceTypeToHal(device.deviceType, halDeviceType), result);
+    memset(halDeviceAddress, 0, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+    if (audio_is_a2dp_out_device(*halDeviceType) || audio_is_a2dp_in_device(*halDeviceType)) {
+        if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::mac) {
+            const auto& mac = device.address.mac();
+            snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN,
+                     "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4],
+                     mac[5]);
+        } else {
+            ALOGE("BT A2DP device \"%s\" does not have MAC address set", device.deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else if (*halDeviceType == AUDIO_DEVICE_OUT_IP || *halDeviceType == AUDIO_DEVICE_IN_IP) {
+        if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::ipv4) {
+            const auto& ipv4 = device.address.ipv4();
+            snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%d.%d.%d.%d", ipv4[0],
+                     ipv4[1], ipv4[2], ipv4[3]);
+        } else {
+            ALOGE("IP device \"%s\" does not have IPv4 address set", device.deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else if (audio_is_usb_out_device(*halDeviceType) || audio_is_usb_in_device(*halDeviceType)) {
+        if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::alsa) {
+            const auto& alsa = device.address.alsa();
+            snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "card=%d;device=%d", alsa.card,
+                     alsa.device);
+        } else {
+            ALOGE("USB device \"%s\" does not have ALSA address set", device.deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else {
+        // Any other device type uses the 'id' field.
+        if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::id) {
+            snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%s",
+                     device.address.id().c_str());
+        }
+    }
+    return result;
+}
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace common
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/common/all-versions/default/Android.bp b/audio/common/all-versions/default/Android.bp
index a72c8dc..b83a58a 100644
--- a/audio/common/all-versions/default/Android.bp
+++ b/audio/common/all-versions/default/Android.bp
@@ -39,14 +39,19 @@
     ],
 }
 
+filegroup {
+    name: "android.hardware.audio.common-util@2-6",
+    srcs: [
+        "HidlUtils.cpp",
+        "UuidUtils.cpp",
+    ],
+}
+
 cc_defaults {
     name: "android.hardware.audio.common-util_default",
     defaults: ["hidl_defaults"],
 
     vendor_available: true,
-    srcs: [
-        "HidlUtils.cpp",
-    ],
 
     export_include_dirs: ["."],
 
@@ -69,6 +74,7 @@
 cc_library_shared {
     name: "android.hardware.audio.common@2.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [":android.hardware.audio.common-util@2-6"],
     shared_libs: [
         "android.hardware.audio.common@2.0",
     ],
@@ -82,6 +88,7 @@
 cc_library_shared {
     name: "android.hardware.audio.common@4.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [":android.hardware.audio.common-util@2-6"],
     shared_libs: [
         "android.hardware.audio.common@4.0",
     ],
@@ -95,6 +102,7 @@
 cc_library_shared {
     name: "android.hardware.audio.common@5.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [":android.hardware.audio.common-util@2-6"],
     shared_libs: [
         "android.hardware.audio.common@5.0",
     ],
@@ -108,6 +116,7 @@
 cc_library_shared {
     name: "android.hardware.audio.common@6.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [":android.hardware.audio.common-util@2-6"],
     shared_libs: [
         "android.hardware.audio.common@6.0",
     ],
@@ -118,12 +127,18 @@
     ],
 }
 
-cc_library_shared {
-    enabled: false,
+cc_library {
     name: "android.hardware.audio.common@7.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [
+        "7.0/HidlUtils.cpp",
+        "UuidUtils.cpp",
+    ],
     shared_libs: [
         "android.hardware.audio.common@7.0",
+        "android.hardware.audio.common@7.0-enums",
+        "libbase",
+        "libxml2",
     ],
     cflags: [
         "-DMAJOR_VERSION=7",
@@ -131,3 +146,35 @@
         "-include common/all-versions/VersionMacro.h",
     ],
 }
+
+// Note: this isn't a VTS test, but rather a unit test
+// to verify correctness of conversion utilities.
+cc_test {
+    name: "android.hardware.audio.common@7.0-util_tests",
+    defaults: ["android.hardware.audio.common-util_default"],
+
+    srcs: ["tests/hidlutils_tests.cpp"],
+
+    // Use static linking to allow running in presubmit on
+    // targets that don't have HAL V7.
+    static_libs: [
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.common@7.0-util",
+        "android.hardware.audio.common@7.0",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libxml2",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+
+    test_suites: ["device-tests"],
+}
diff --git a/audio/common/all-versions/default/HidlUtils.cpp b/audio/common/all-versions/default/HidlUtils.cpp
index a470c9c..ab3c1c7 100644
--- a/audio/common/all-versions/default/HidlUtils.cpp
+++ b/audio/common/all-versions/default/HidlUtils.cpp
@@ -37,13 +37,14 @@
     return status;
 }
 
-void HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig) {
+status_t HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig) {
     memset(halConfig, 0, sizeof(audio_config_t));
     halConfig->sample_rate = config.sampleRateHz;
     halConfig->channel_mask = static_cast<audio_channel_mask_t>(config.channelMask);
     halConfig->format = static_cast<audio_format_t>(config.format);
     audioOffloadInfoToHal(config.offloadInfo, &halConfig->offload_info);
     halConfig->frame_count = config.frameCount;
+    return NO_ERROR;
 }
 
 void HidlUtils::audioGainConfigFromHal(const struct audio_gain_config& halConfig,
@@ -57,8 +58,8 @@
     config->rampDurationMs = halConfig.ramp_duration_ms;
 }
 
-void HidlUtils::audioGainConfigToHal(const AudioGainConfig& config,
-                                     struct audio_gain_config* halConfig) {
+status_t HidlUtils::audioGainConfigToHal(const AudioGainConfig& config,
+                                         struct audio_gain_config* halConfig) {
     halConfig->index = config.index;
     halConfig->mode = static_cast<audio_gain_mode_t>(config.mode);
     halConfig->channel_mask = static_cast<audio_channel_mask_t>(config.channelMask);
@@ -67,6 +68,7 @@
         halConfig->values[i] = config.values[i];
     }
     halConfig->ramp_duration_ms = config.rampDurationMs;
+    return NO_ERROR;
 }
 
 void HidlUtils::audioGainFromHal(const struct audio_gain& halGain, AudioGain* gain) {
@@ -80,7 +82,7 @@
     gain->maxRampMs = halGain.max_ramp_ms;
 }
 
-void HidlUtils::audioGainToHal(const AudioGain& gain, struct audio_gain* halGain) {
+status_t HidlUtils::audioGainToHal(const AudioGain& gain, struct audio_gain* halGain) {
     halGain->mode = static_cast<audio_gain_mode_t>(gain.mode);
     halGain->channel_mask = static_cast<audio_channel_mask_t>(gain.channelMask);
     halGain->min_value = gain.minValue;
@@ -89,22 +91,26 @@
     halGain->step_value = gain.stepValue;
     halGain->min_ramp_ms = gain.minRampMs;
     halGain->max_ramp_ms = gain.maxRampMs;
+    return NO_ERROR;
 }
 
-AudioUsage HidlUtils::audioUsageFromHal(const audio_usage_t halUsage) {
+status_t HidlUtils::audioUsageFromHal(audio_usage_t halUsage, AudioUsage* usage) {
     switch (halUsage) {
         case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
         case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
         case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
         case AUDIO_USAGE_NOTIFICATION_EVENT:
-            return AudioUsage::NOTIFICATION;
+            *usage = AudioUsage::NOTIFICATION;
+            break;
         default:
-            return static_cast<AudioUsage>(halUsage);
+            *usage = static_cast<AudioUsage>(halUsage);
     }
+    return NO_ERROR;
 }
 
-audio_usage_t HidlUtils::audioUsageToHal(const AudioUsage usage) {
-    return static_cast<audio_usage_t>(usage);
+status_t HidlUtils::audioUsageToHal(const AudioUsage& usage, audio_usage_t* halUsage) {
+    *halUsage = static_cast<audio_usage_t>(usage);
+    return NO_ERROR;
 }
 
 status_t HidlUtils::audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
@@ -119,7 +125,7 @@
     offload->isStreaming = halOffload.is_streaming;
     offload->bitWidth = halOffload.bit_width;
     offload->bufferSize = halOffload.offload_buffer_size;
-    offload->usage = audioUsageFromHal(halOffload.usage);
+    audioUsageFromHal(halOffload.usage, &offload->usage);
 #if MAJOR_VERSION >= 6
     if (halOffload.version >= AUDIO_OFFLOAD_INFO_VERSION_0_2) {
         offload->encapsulationMode =
@@ -139,11 +145,11 @@
         return BAD_VALUE;
     }
 #endif
-    return OK;
+    return NO_ERROR;
 }
 
-void HidlUtils::audioOffloadInfoToHal(const AudioOffloadInfo& offload,
-                                      audio_offload_info_t* halOffload) {
+status_t HidlUtils::audioOffloadInfoToHal(const AudioOffloadInfo& offload,
+                                          audio_offload_info_t* halOffload) {
     *halOffload = AUDIO_INFO_INITIALIZER;
     halOffload->sample_rate = offload.sampleRateHz;
     halOffload->channel_mask = static_cast<audio_channel_mask_t>(offload.channelMask);
@@ -155,7 +161,7 @@
     halOffload->is_streaming = offload.isStreaming;
     halOffload->bit_width = offload.bitWidth;
     halOffload->offload_buffer_size = offload.bufferSize;
-    halOffload->usage = audioUsageToHal(offload.usage);
+    audioUsageToHal(offload.usage, &halOffload->usage);
 #if MAJOR_VERSION >= 6
     halOffload->encapsulation_mode =
             static_cast<audio_encapsulation_mode_t>(offload.encapsulationMode);
@@ -164,10 +170,11 @@
 #else
     // offload doesn't contain encapsulationMode, contentId, syncId, so this is OK.
 #endif
+    return NO_ERROR;
 }
 
-void HidlUtils::audioPortConfigFromHal(const struct audio_port_config& halConfig,
-                                       AudioPortConfig* config) {
+status_t HidlUtils::audioPortConfigFromHal(const struct audio_port_config& halConfig,
+                                           AudioPortConfig* config) {
     config->id = halConfig.id;
     config->role = AudioPortRole(halConfig.role);
     config->type = AudioPortType(halConfig.type);
@@ -201,10 +208,11 @@
             break;
         }
     }
+    return NO_ERROR;
 }
 
-void HidlUtils::audioPortConfigToHal(const AudioPortConfig& config,
-                                     struct audio_port_config* halConfig) {
+status_t HidlUtils::audioPortConfigToHal(const AudioPortConfig& config,
+                                         struct audio_port_config* halConfig) {
     memset(halConfig, 0, sizeof(audio_port_config));
     halConfig->id = config.id;
     halConfig->role = static_cast<audio_port_role_t>(config.role);
@@ -242,27 +250,10 @@
             break;
         }
     }
+    return NO_ERROR;
 }
 
-void HidlUtils::audioPortConfigsFromHal(unsigned int numHalConfigs,
-                                        const struct audio_port_config* halConfigs,
-                                        hidl_vec<AudioPortConfig>* configs) {
-    configs->resize(numHalConfigs);
-    for (unsigned int i = 0; i < numHalConfigs; ++i) {
-        audioPortConfigFromHal(halConfigs[i], &(*configs)[i]);
-    }
-}
-
-std::unique_ptr<audio_port_config[]> HidlUtils::audioPortConfigsToHal(
-    const hidl_vec<AudioPortConfig>& configs) {
-    std::unique_ptr<audio_port_config[]> halConfigs(new audio_port_config[configs.size()]);
-    for (size_t i = 0; i < configs.size(); ++i) {
-        audioPortConfigToHal(configs[i], &halConfigs[i]);
-    }
-    return halConfigs;
-}
-
-void HidlUtils::audioPortFromHal(const struct audio_port& halPort, AudioPort* port) {
+status_t HidlUtils::audioPortFromHal(const struct audio_port& halPort, AudioPort* port) {
     port->id = halPort.id;
     port->role = AudioPortRole(halPort.role);
     port->type = AudioPortType(halPort.type);
@@ -305,9 +296,10 @@
             break;
         }
     }
+    return NO_ERROR;
 }
 
-void HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port* halPort) {
+status_t HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port* halPort) {
     memset(halPort, 0, sizeof(audio_port));
     halPort->id = port.id;
     halPort->role = static_cast<audio_port_role_t>(port.role);
@@ -356,22 +348,7 @@
             break;
         }
     }
-}
-
-void HidlUtils::uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid) {
-    uuid->timeLow = halUuid.timeLow;
-    uuid->timeMid = halUuid.timeMid;
-    uuid->versionAndTimeHigh = halUuid.timeHiAndVersion;
-    uuid->variantAndClockSeqHigh = halUuid.clockSeq;
-    memcpy(uuid->node.data(), halUuid.node, uuid->node.size());
-}
-
-void HidlUtils::uuidToHal(const Uuid& uuid, audio_uuid_t* halUuid) {
-    halUuid->timeLow = uuid.timeLow;
-    halUuid->timeMid = uuid.timeMid;
-    halUuid->timeHiAndVersion = uuid.versionAndTimeHigh;
-    halUuid->clockSeq = uuid.variantAndClockSeqHigh;
-    memcpy(halUuid->node, uuid.node.data(), uuid.node.size());
+    return NO_ERROR;
 }
 
 }  // namespace implementation
diff --git a/audio/common/all-versions/default/HidlUtils.h b/audio/common/all-versions/default/HidlUtils.h
index ef6dee3..4e609ca 100644
--- a/audio/common/all-versions/default/HidlUtils.h
+++ b/audio/common/all-versions/default/HidlUtils.h
@@ -34,40 +34,123 @@
 
 using namespace ::android::hardware::audio::common::CPP_VERSION;
 
-class HidlUtils {
-  public:
-    // A failure here indicates a platform config that is incompatible with
-    // the compiled HIDL interface version.
+struct HidlUtils {
+#if MAJOR_VERSION < 7
     static status_t audioConfigFromHal(const audio_config_t& halConfig, AudioConfig* config);
-
-    static void audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig);
     static void audioGainConfigFromHal(const struct audio_gain_config& halConfig,
                                        AudioGainConfig* config);
-    static void audioGainConfigToHal(const AudioGainConfig& config,
-                                     struct audio_gain_config* halConfig);
     static void audioGainFromHal(const struct audio_gain& halGain, AudioGain* gain);
-    static void audioGainToHal(const AudioGain& gain, struct audio_gain* halGain);
-    static AudioUsage audioUsageFromHal(const audio_usage_t halUsage);
-    static audio_usage_t audioUsageToHal(const AudioUsage usage);
-    // A failure here indicates a platform offload info that is incompatible with
-    // the compiled HIDL interface version.
+#else
+    static status_t audioConfigFromHal(const audio_config_t& halConfig, bool isInput,
+                                       AudioConfig* config);
+    static status_t audioGainConfigFromHal(const struct audio_gain_config& halConfig, bool isInput,
+                                           AudioGainConfig* config);
+    static status_t audioGainFromHal(const struct audio_gain& halGain, bool isInput,
+                                     AudioGain* gain);
+#endif
+    static status_t audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig);
+    static status_t audioGainConfigToHal(const AudioGainConfig& config,
+                                         struct audio_gain_config* halConfig);
+    static status_t audioGainToHal(const AudioGain& gain, struct audio_gain* halGain);
+    static status_t audioUsageFromHal(audio_usage_t halUsage, AudioUsage* usage);
+    static status_t audioUsageToHal(const AudioUsage& usage, audio_usage_t* halUsage);
     static status_t audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
                                             AudioOffloadInfo* offload);
-    static void audioOffloadInfoToHal(const AudioOffloadInfo& offload,
-                                      audio_offload_info_t* halOffload);
-    static void audioPortConfigFromHal(const struct audio_port_config& halConfig,
-                                       AudioPortConfig* config);
-    static void audioPortConfigToHal(const AudioPortConfig& config,
-                                     struct audio_port_config* halConfig);
-    static void audioPortConfigsFromHal(unsigned int numHalConfigs,
-                                        const struct audio_port_config* halConfigs,
-                                        hidl_vec<AudioPortConfig>* configs);
+    static status_t audioOffloadInfoToHal(const AudioOffloadInfo& offload,
+                                          audio_offload_info_t* halOffload);
+    static status_t audioPortConfigFromHal(const struct audio_port_config& halConfig,
+                                           AudioPortConfig* config);
+    static status_t audioPortConfigToHal(const AudioPortConfig& config,
+                                         struct audio_port_config* halConfig);
+    static status_t audioPortConfigsFromHal(unsigned int numHalConfigs,
+                                            const struct audio_port_config* halConfigs,
+                                            hidl_vec<AudioPortConfig>* configs) {
+        status_t result = NO_ERROR;
+        configs->resize(numHalConfigs);
+        for (unsigned int i = 0; i < numHalConfigs; ++i) {
+            if (status_t status = audioPortConfigFromHal(halConfigs[i], &(*configs)[i]);
+                status != NO_ERROR) {
+                result = status;
+            }
+        }
+        return result;
+    }
+    static status_t audioPortConfigsToHal(const hidl_vec<AudioPortConfig>& configs,
+                                          std::unique_ptr<audio_port_config[]>* halConfigs) {
+        status_t result = NO_ERROR;
+        halConfigs->reset(new audio_port_config[configs.size()]);
+        for (size_t i = 0; i < configs.size(); ++i) {
+            if (status_t status = audioPortConfigToHal(configs[i], &(*halConfigs)[i]);
+                status != NO_ERROR) {
+                result = status;
+            }
+        }
+        return result;
+    }
+
+    // PLEASE DO NOT USE, will be removed in a couple of days
     static std::unique_ptr<audio_port_config[]> audioPortConfigsToHal(
-            const hidl_vec<AudioPortConfig>& configs);
-    static void audioPortFromHal(const struct audio_port& halPort, AudioPort* port);
-    static void audioPortToHal(const AudioPort& port, struct audio_port* halPort);
-    static void uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid);
-    static void uuidToHal(const Uuid& uuid, audio_uuid_t* halUuid);
+            const hidl_vec<AudioPortConfig>& configs) {
+        std::unique_ptr<audio_port_config[]> halConfigs;
+        (void)audioPortConfigsToHal(configs, &halConfigs);
+        return halConfigs;
+    }
+
+    static status_t audioPortFromHal(const struct audio_port& halPort, AudioPort* port);
+    static status_t audioPortToHal(const AudioPort& port, struct audio_port* halPort);
+#if MAJOR_VERSION >= 7
+    static status_t audioChannelMaskFromHal(audio_channel_mask_t halChannelMask, bool isInput,
+                                            AudioChannelMask* channelMask);
+    static status_t audioChannelMaskToHal(const AudioChannelMask& channelMask,
+                                          audio_channel_mask_t* halChannelMask);
+    static status_t audioConfigBaseFromHal(const audio_config_base_t& halConfigBase, bool isInput,
+                                           AudioConfigBase* configBase);
+    static status_t audioConfigBaseToHal(const AudioConfigBase& configBase,
+                                         audio_config_base_t* halConfigBase);
+    static status_t audioDeviceTypeFromHal(audio_devices_t halDevice, AudioDevice* device);
+    static status_t audioDeviceTypeToHal(const AudioDevice& device, audio_devices_t* halDevice);
+    static status_t audioFormatFromHal(audio_format_t halFormat, AudioFormat* format);
+    static status_t audioFormatToHal(const AudioFormat& format, audio_format_t* halFormat);
+    static status_t audioGainModeMaskFromHal(audio_gain_mode_t halGainModeMask,
+                                             hidl_vec<AudioGainMode>* gainModeMask);
+    static status_t audioGainModeMaskToHal(const hidl_vec<AudioGainMode>& gainModeMask,
+                                           audio_gain_mode_t* halGainModeMask);
+    static status_t audioPortFromHal(const struct audio_port_v7& halPort, AudioPort* port);
+    static status_t audioPortToHal(const AudioPort& port, struct audio_port_v7* halPort);
+    static status_t audioProfileFromHal(const struct audio_profile& halProfile, bool isInput,
+                                        AudioProfile* profile);
+    static status_t audioProfileToHal(const AudioProfile& profile,
+                                      struct audio_profile* halProfile);
+    static status_t audioSourceFromHal(audio_source_t halSource, AudioSource* source);
+    static status_t audioSourceToHal(const AudioSource& source, audio_source_t* halSource);
+    static status_t audioStreamTypeFromHal(audio_stream_type_t halStreamType,
+                                           AudioStreamType* streamType);
+    static status_t audioStreamTypeToHal(const AudioStreamType& streamType,
+                                         audio_stream_type_t* halStreamType);
+    static status_t deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+                                       char* halDeviceAddress);
+    static status_t deviceAddressFromHal(audio_devices_t halDeviceType,
+                                         const char* halDeviceAddress, DeviceAddress* device);
+
+  private:
+    static status_t audioIndexChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                 AudioChannelMask* channelMask);
+    static status_t audioInputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                 AudioChannelMask* channelMask);
+    static status_t audioOutputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                  AudioChannelMask* channelMask);
+    static status_t audioPortExtendedInfoFromHal(
+            audio_port_role_t role, audio_port_type_t type,
+            const struct audio_port_config_device_ext& device,
+            const struct audio_port_config_mix_ext& mix,
+            const struct audio_port_config_session_ext& session, AudioPortExtendedInfo* ext,
+            bool* isInput);
+    static status_t audioPortExtendedInfoToHal(const AudioPortExtendedInfo& ext,
+                                               audio_port_role_t* role, audio_port_type_t* type,
+                                               struct audio_port_config_device_ext* device,
+                                               struct audio_port_config_mix_ext* mix,
+                                               struct audio_port_config_session_ext* session);
+#endif
 };
 
 }  // namespace implementation
diff --git a/audio/common/all-versions/default/TEST_MAPPING b/audio/common/all-versions/default/TEST_MAPPING
new file mode 100644
index 0000000..4316ccf
--- /dev/null
+++ b/audio/common/all-versions/default/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "android.hardware.audio.common@7.0-util_tests"
+    }
+  ]
+}
diff --git a/audio/common/all-versions/default/UuidUtils.cpp b/audio/common/all-versions/default/UuidUtils.cpp
new file mode 100644
index 0000000..85edc7b
--- /dev/null
+++ b/audio/common/all-versions/default/UuidUtils.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UuidUtils.h"
+
+#include <common/all-versions/VersionUtils.h>
+#include <string.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace common {
+namespace CPP_VERSION {
+namespace implementation {
+
+void UuidUtils::uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid) {
+    uuid->timeLow = halUuid.timeLow;
+    uuid->timeMid = halUuid.timeMid;
+    uuid->versionAndTimeHigh = halUuid.timeHiAndVersion;
+    uuid->variantAndClockSeqHigh = halUuid.clockSeq;
+    memcpy(uuid->node.data(), halUuid.node, uuid->node.size());
+}
+
+void UuidUtils::uuidToHal(const Uuid& uuid, audio_uuid_t* halUuid) {
+    halUuid->timeLow = uuid.timeLow;
+    halUuid->timeMid = uuid.timeMid;
+    halUuid->timeHiAndVersion = uuid.versionAndTimeHigh;
+    halUuid->clockSeq = uuid.variantAndClockSeqHigh;
+    memcpy(halUuid->node, uuid.node.data(), uuid.node.size());
+}
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace common
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/common/all-versions/default/UuidUtils.h b/audio/common/all-versions/default/UuidUtils.h
new file mode 100644
index 0000000..38db48a
--- /dev/null
+++ b/audio/common/all-versions/default/UuidUtils.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef android_hardware_audio_Uuid_Utils_H_
+#define android_hardware_audio_Uuid_Utils_H_
+
+// clang-format off
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+#include <system/audio.h>
+
+using ::android::hardware::hidl_vec;
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace common {
+namespace CPP_VERSION {
+namespace implementation {
+
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+
+class UuidUtils {
+  public:
+    static void uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid);
+    static void uuidToHal(const Uuid& uuid, audio_uuid_t* halUuid);
+};
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace common
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_audio_Uuid_Utils_H_
diff --git a/audio/common/all-versions/default/tests/hidlutils_tests.cpp b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
new file mode 100644
index 0000000..bfc99e6
--- /dev/null
+++ b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
@@ -0,0 +1,631 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <array>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#define LOG_TAG "HidlUtils_Test"
+#include <log/log.h>
+
+#include <HidlUtils.h>
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <system/audio.h>
+#include <xsdc/XsdcSupport.h>
+
+using namespace android;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+static constexpr audio_channel_mask_t kInvalidHalChannelMask =
+        static_cast<audio_channel_mask_t>(0xFFFFFFFFU);
+static constexpr audio_devices_t kInvalidHalDevice = static_cast<audio_devices_t>(0xFFFFFFFFU);
+static constexpr audio_format_t kInvalidHalFormat = static_cast<audio_format_t>(0xFFFFFFFFU);
+static constexpr audio_gain_mode_t kInvalidHalGainMode =
+        static_cast<audio_gain_mode_t>(0xFFFFFFFFU);
+static constexpr audio_source_t kInvalidHalSource = static_cast<audio_source_t>(0xFFFFFFFFU);
+static constexpr audio_stream_type_t kInvalidHalStreamType =
+        static_cast<audio_stream_type_t>(0xFFFFFFFFU);
+static constexpr audio_usage_t kInvalidHalUsage = static_cast<audio_usage_t>(0xFFFFFFFFU);
+
+TEST(HidlUtils, ConvertInvalidChannelMask) {
+    AudioChannelMask invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(AUDIO_CHANNEL_INVALID,
+                                                            false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(AUDIO_CHANNEL_INVALID, true /*isInput*/,
+                                                            &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(kInvalidHalChannelMask,
+                                                            false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(kInvalidHalChannelMask,
+                                                            true /*isInput*/, &invalid));
+    audio_channel_mask_t halInvalid;
+    // INVALID channel mask is not in XSD thus it's not allowed for transfer over HIDL.
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskToHal("AUDIO_CHANNEL_INVALID", &halInvalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskToHal("random string", &halInvalid));
+}
+
+// Might move these to the audio_policy_configuration_V7_0-enums library
+// if there would be usages in the default wrapper code. In that case,
+// it would be better to reimplement these methods using a proper switch statement
+// over all known enum values.
+static bool isInputChannelMask(xsd::AudioChannelMask channelMask) {
+    return toString(channelMask).find("_CHANNEL_IN_") != std::string::npos;
+}
+
+static bool isOutputChannelMask(xsd::AudioChannelMask channelMask) {
+    return toString(channelMask).find("_CHANNEL_OUT_") != std::string::npos;
+}
+
+static bool isIndexChannelMask(xsd::AudioChannelMask channelMask) {
+    return toString(channelMask).find("_CHANNEL_INDEX_") != std::string::npos;
+}
+
+TEST(HidlUtils, ConvertChannelMask) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioChannelMask>{}) {
+        const AudioChannelMask channelMask = toString(enumVal);
+        audio_channel_mask_t halChannelMask, halChannelMaskBack;
+        AudioChannelMask channelMaskBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMaskToHal(channelMask, &halChannelMask))
+                << "Conversion of \"" << channelMask << "\" failed";
+        EXPECT_EQ(enumVal != xsd::AudioChannelMask::AUDIO_CHANNEL_NONE,
+                  audio_channel_mask_is_valid(halChannelMask))
+                << "Validity of \"" << channelMask << "\" is not as expected";
+        if (bool isInput = isInputChannelMask(enumVal); isInput || isOutputChannelMask(enumVal)) {
+            EXPECT_EQ(NO_ERROR,
+                      HidlUtils::audioChannelMaskFromHal(halChannelMask, isInput, &channelMaskBack))
+                    << "Conversion of " << (isInput ? "input" : "output") << " channel mask "
+                    << halChannelMask << " failed";
+            // Due to aliased values, the result of 'fromHal' might not be the same
+            // as 'channelMask', thus we need to compare the results of 'toHal' conversion instead.
+            EXPECT_EQ(NO_ERROR,
+                      HidlUtils::audioChannelMaskToHal(channelMaskBack, &halChannelMaskBack))
+                    << "Conversion of \"" << channelMaskBack << "\" failed";
+            EXPECT_EQ(halChannelMask, halChannelMaskBack);
+        } else if (isIndexChannelMask(enumVal) ||
+                   enumVal == xsd::AudioChannelMask::AUDIO_CHANNEL_NONE) {
+            // Conversions for indexed masks and "none" must not depend on the provided direction.
+            EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMaskFromHal(halChannelMask, true /*isInput*/,
+                                                                   &channelMaskBack))
+                    << "Conversion of indexed / none channel mask " << halChannelMask
+                    << " failed (as input channel mask)";
+            EXPECT_EQ(channelMask, channelMaskBack);
+            EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMaskFromHal(
+                                        halChannelMask, false /*isInput*/, &channelMaskBack))
+                    << "Conversion of indexed / none channel mask " << halChannelMask
+                    << " failed (as output channel mask)";
+            EXPECT_EQ(channelMask, channelMaskBack);
+        } else {
+            FAIL() << "Unrecognized channel mask \"" << channelMask << "\"";
+        }
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidConfigBase) {
+    AudioConfigBase invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseFromHal({.sample_rate = 0,
+                                                            .channel_mask = kInvalidHalChannelMask,
+                                                            .format = kInvalidHalFormat},
+                                                           false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseFromHal({.sample_rate = 0,
+                                                            .channel_mask = kInvalidHalChannelMask,
+                                                            .format = kInvalidHalFormat},
+                                                           true /*isInput*/, &invalid));
+    audio_config_base_t halInvalid;
+    invalid.sampleRateHz = 0;
+    invalid.channelMask = "random string";
+    invalid.format = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertConfigBase) {
+    AudioConfigBase configBase;
+    configBase.sampleRateHz = 44100;
+    configBase.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    configBase.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    audio_config_base_t halConfigBase;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseToHal(configBase, &halConfigBase));
+    AudioConfigBase configBaseBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigBaseFromHal(halConfigBase, false /*isInput*/, &configBaseBack));
+    EXPECT_EQ(configBase, configBaseBack);
+}
+
+TEST(HidlUtils, ConvertInvalidDeviceType) {
+    AudioDevice invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioDeviceTypeFromHal(kInvalidHalDevice, &invalid));
+    audio_devices_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioDeviceTypeToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertDeviceType) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioDevice>{}) {
+        const AudioDevice deviceType = toString(enumVal);
+        audio_devices_t halDeviceType, halDeviceTypeBack;
+        AudioDevice deviceTypeBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioDeviceTypeToHal(deviceType, &halDeviceType))
+                << "Conversion of \"" << deviceType << "\" failed";
+        if (enumVal != xsd::AudioDevice::AUDIO_DEVICE_NONE) {
+            EXPECT_TRUE(audio_is_input_device(halDeviceType) ||
+                        audio_is_output_device(halDeviceType))
+                    << "Device \"" << deviceType << "\" is neither input, nor output device";
+        } else {
+            EXPECT_FALSE(audio_is_input_device(halDeviceType));
+            EXPECT_FALSE(audio_is_output_device(halDeviceType));
+        }
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioDeviceTypeFromHal(halDeviceType, &deviceTypeBack))
+                << "Conversion of device type " << halDeviceType << " failed";
+        // Due to aliased values, the result of 'fromHal' might not be the same
+        // as 'deviceType', thus we need to compare the results of 'toHal' conversion instead.
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioDeviceTypeToHal(deviceTypeBack, &halDeviceTypeBack))
+                << "Conversion of \"" << deviceTypeBack << "\" failed";
+        EXPECT_EQ(halDeviceType, halDeviceTypeBack);
+    }
+}
+
+// The enums module is too small to have unit tests on its own.
+TEST(HidlUtils, VendorExtension) {
+    EXPECT_TRUE(xsd::isVendorExtension("VX_GOOGLE_VR_42"));
+    EXPECT_FALSE(xsd::isVendorExtension("random string"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_"));
+    EXPECT_FALSE(xsd::isVendorExtension("VX_GOOGLE_$$"));
+}
+
+TEST(HidlUtils, ConvertInvalidDeviceAddress) {
+    DeviceAddress invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER,
+                                                         nullptr, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER,
+                                                         "", &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_IP, nullptr, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_IP, "", &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_USB_HEADSET, nullptr, &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_USB_HEADSET, "", &invalid));
+
+    audio_devices_t halInvalid;
+    char halAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+    invalid = {};
+    invalid.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressToHal(invalid, &halInvalid, halAddress));
+    invalid.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_IP);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressToHal(invalid, &halInvalid, halAddress));
+    invalid.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_USB_HEADSET);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressToHal(invalid, &halInvalid, halAddress));
+}
+
+static void ConvertDeviceAddress(const DeviceAddress& device) {
+    audio_devices_t halDeviceType;
+    char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+    EXPECT_EQ(NO_ERROR, HidlUtils::deviceAddressToHal(device, &halDeviceType, halDeviceAddress));
+    DeviceAddress deviceBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::deviceAddressFromHal(halDeviceType, halDeviceAddress, &deviceBack));
+    EXPECT_EQ(device, deviceBack);
+}
+
+TEST(HidlUtils, ConvertUniqueDeviceAddress) {
+    DeviceAddress speaker;
+    speaker.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+    ConvertDeviceAddress(speaker);
+}
+
+TEST(HidlUtils, ConvertA2dpDeviceAddress) {
+    DeviceAddress a2dpSpeaker;
+    a2dpSpeaker.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
+    a2dpSpeaker.address.mac(std::array<uint8_t, 6>{1, 2, 3, 4, 5, 6});
+    ConvertDeviceAddress(a2dpSpeaker);
+}
+
+TEST(HidlUtils, ConvertIpv4DeviceAddress) {
+    DeviceAddress ipv4;
+    ipv4.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_IP);
+    ipv4.address.ipv4(std::array<uint8_t, 4>{1, 2, 3, 4});
+    ConvertDeviceAddress(ipv4);
+}
+
+TEST(HidlUtils, ConvertUsbDeviceAddress) {
+    DeviceAddress usbHeadset;
+    usbHeadset.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_USB_HEADSET);
+    usbHeadset.address.alsa({1, 2});
+    ConvertDeviceAddress(usbHeadset);
+}
+
+TEST(HidlUtils, ConvertBusDeviceAddress) {
+    DeviceAddress bus;
+    bus.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_BUS);
+    bus.address.id("bus_device");
+    ConvertDeviceAddress(bus);
+}
+
+TEST(HidlUtils, ConvertRSubmixDeviceAddress) {
+    DeviceAddress rSubmix;
+    rSubmix.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_REMOTE_SUBMIX);
+    rSubmix.address.id(AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS);
+    ConvertDeviceAddress(rSubmix);
+}
+
+TEST(HidlUtils, ConvertVendorDeviceAddress) {
+    // The address part is not mandatory, both cases must work.
+    {
+        DeviceAddress vendor;
+        vendor.deviceType = "VX_GOOGLE_VR";
+        audio_devices_t halDeviceType;
+        char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+        // Ignore the result. Vendors will also add the extended device into
+        // the list of devices in audio-hal-enums.h. Without that, the conversion
+        // officially fails, but it still maps the device type to NONE.
+        (void)HidlUtils::deviceAddressToHal(vendor, &halDeviceType, halDeviceAddress);
+        EXPECT_EQ(AUDIO_DEVICE_NONE, halDeviceType);
+        EXPECT_EQ(0, strnlen(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN));
+    }
+    {
+        DeviceAddress vendor;
+        vendor.deviceType = "VX_GOOGLE_VR";
+        vendor.address.id("vr1");
+        audio_devices_t halDeviceType;
+        char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+        // Ignore the result. Vendors will also add the extended device into
+        // the list of devices in audio-hal-enums.h. Without that, the conversion
+        // officially fails, but it still maps the device type to NONE and converts
+        // the address.
+        (void)HidlUtils::deviceAddressToHal(vendor, &halDeviceType, halDeviceAddress);
+        EXPECT_EQ(AUDIO_DEVICE_NONE, halDeviceType);
+        EXPECT_EQ(0, strncmp("vr1", halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN));
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidFormat) {
+    AudioFormat invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatFromHal(kInvalidHalFormat, &invalid));
+    audio_format_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertFormat) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioFormat>{}) {
+        const AudioFormat format = toString(enumVal);
+        audio_format_t halFormat;
+        AudioFormat formatBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioFormatToHal(format, &halFormat))
+                << "Conversion of \"" << format << "\" failed";
+        EXPECT_TRUE(audio_is_valid_format(halFormat))
+                << "Converted format \"" << format << "\" is invalid";
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioFormatFromHal(halFormat, &formatBack))
+                << "Conversion of format " << halFormat << " failed";
+        EXPECT_EQ(format, formatBack);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidGainModeMask) {
+    hidl_vec<AudioGainMode> invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainModeMaskFromHal(kInvalidHalGainMode, &invalid));
+    audio_gain_mode_t halInvalid;
+    invalid.resize(1);
+    invalid[0] = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainModeMaskToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertGainModeMask) {
+    hidl_vec<AudioGainMode> emptyGainModes;
+    audio_gain_mode_t halEmptyGainModes;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainModeMaskToHal(emptyGainModes, &halEmptyGainModes));
+    hidl_vec<AudioGainMode> emptyGainModesBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioGainModeMaskFromHal(halEmptyGainModes, &emptyGainModesBack));
+    EXPECT_EQ(emptyGainModes, emptyGainModesBack);
+
+    std::vector<std::string> allEnumValues;
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioGainMode>{}) {
+        allEnumValues.push_back(toString(enumVal));
+    }
+    hidl_vec<AudioGainMode> allGainModes;
+    allGainModes.resize(allEnumValues.size());
+    for (size_t i = 0; i < allEnumValues.size(); ++i) {
+        allGainModes[i] = allEnumValues[i];
+    }
+    audio_gain_mode_t halAllGainModes;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainModeMaskToHal(allGainModes, &halAllGainModes));
+    hidl_vec<AudioGainMode> allGainModesBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainModeMaskFromHal(halAllGainModes, &allGainModesBack));
+    EXPECT_EQ(allGainModes, allGainModesBack);
+}
+
+TEST(HidlUtils, ConvertInvalidSource) {
+    AudioSource invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioSourceFromHal(kInvalidHalSource, &invalid));
+    audio_source_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioSourceToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertSource) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioSource>{}) {
+        const AudioSource source = toString(enumVal);
+        audio_source_t halSource;
+        AudioSource sourceBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioSourceToHal(source, &halSource))
+                << "Conversion of \"" << source << "\" failed";
+        EXPECT_EQ(enumVal != xsd::AudioSource::AUDIO_SOURCE_DEFAULT,
+                  audio_is_valid_audio_source(halSource))
+                << "Validity of \"" << source << "\" is not as expected";
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioSourceFromHal(halSource, &sourceBack))
+                << "Conversion of source " << halSource << " failed";
+        EXPECT_EQ(source, sourceBack);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidStreamType) {
+    AudioStreamType invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioStreamTypeFromHal(kInvalidHalStreamType, &invalid));
+    audio_stream_type_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioStreamTypeToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertStreamType) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioStreamType>{}) {
+        const AudioStreamType streamType = toString(enumVal);
+        audio_stream_type_t halStreamType;
+        AudioStreamType streamTypeBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioStreamTypeToHal(streamType, &halStreamType))
+                << "Conversion of \"" << streamType << "\" failed";
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioStreamTypeFromHal(halStreamType, &streamTypeBack))
+                << "Conversion of stream type " << halStreamType << " failed";
+        EXPECT_EQ(streamType, streamTypeBack);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidGain) {
+    AudioGain invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainFromHal({.mode = kInvalidHalGainMode},
+                                                     false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainFromHal({.mode = kInvalidHalGainMode},
+                                                     true /*isInput*/, &invalid));
+    struct audio_gain halInvalid;
+    invalid.mode.resize(1);
+    invalid.mode[0] = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertGain) {
+    AudioGain gain = {};
+    gain.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    struct audio_gain halGain;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainToHal(gain, &halGain));
+    AudioGain gainBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainFromHal(halGain, false /*isInput*/, &gainBack));
+    EXPECT_EQ(gain, gainBack);
+    struct audio_gain halGainBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainToHal(gainBack, &halGainBack));
+    EXPECT_TRUE(audio_gains_are_equal(&halGain, &halGainBack));
+}
+
+TEST(HidlUtils, ConvertInvalidGainConfig) {
+    AudioGainConfig invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainConfigFromHal({.mode = kInvalidHalGainMode},
+                                                           false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainConfigFromHal({.mode = kInvalidHalGainMode},
+                                                           true /*isInput*/, &invalid));
+    struct audio_gain_config halInvalid;
+    invalid.mode.resize(1);
+    invalid.mode[0] = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainConfigToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertGainConfig) {
+    AudioGainConfig gainConfig = {};
+    gainConfig.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    struct audio_gain_config halGainConfig;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainConfigToHal(gainConfig, &halGainConfig));
+    AudioGainConfig gainConfigBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioGainConfigFromHal(halGainConfig, false /*isInput*/, &gainConfigBack));
+    EXPECT_EQ(gainConfig, gainConfigBack);
+    struct audio_gain_config halGainConfigBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainConfigToHal(gainConfigBack, &halGainConfigBack));
+    EXPECT_TRUE(audio_gain_config_are_equal(&halGainConfig, &halGainConfigBack));
+}
+
+TEST(HidlUtils, ConvertInvalidUsage) {
+    AudioUsage invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioUsageFromHal(kInvalidHalUsage, &invalid));
+    audio_usage_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioUsageToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertUsage) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioUsage>{}) {
+        const AudioUsage usage = toString(enumVal);
+        audio_usage_t halUsage;
+        AudioUsage usageBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioUsageToHal(usage, &halUsage))
+                << "Conversion of \"" << usage << "\" failed";
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioUsageFromHal(halUsage, &usageBack))
+                << "Conversion of usage " << halUsage << " failed";
+        EXPECT_EQ(usage, usageBack);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidOffloadInfo) {
+    AudioOffloadInfo invalid;
+    audio_offload_info_t halInvalid = AUDIO_INFO_INITIALIZER;
+    halInvalid.channel_mask = AUDIO_CHANNEL_INVALID;
+    halInvalid.format = kInvalidHalFormat;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioOffloadInfoFromHal(halInvalid, &invalid));
+    invalid.base.channelMask = "random string";
+    invalid.base.format = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioOffloadInfoToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertOffloadInfo) {
+    AudioOffloadInfo offloadInfo = {};
+    offloadInfo.base.sampleRateHz = 44100;
+    offloadInfo.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    offloadInfo.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    offloadInfo.streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC);
+    offloadInfo.bitRatePerSecond = 320;
+    offloadInfo.durationMicroseconds = -1;
+    offloadInfo.bitWidth = 16;
+    offloadInfo.bufferSize = 1024;
+    offloadInfo.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA);
+    offloadInfo.encapsulationMode = AudioEncapsulationMode::ELEMENTARY_STREAM;
+    offloadInfo.contentId = 42;
+    offloadInfo.syncId = 13;
+    audio_offload_info_t halOffloadInfo;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioOffloadInfoToHal(offloadInfo, &halOffloadInfo));
+    AudioOffloadInfo offloadInfoBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioOffloadInfoFromHal(halOffloadInfo, &offloadInfoBack));
+    EXPECT_EQ(offloadInfo, offloadInfoBack);
+}
+
+TEST(HidlUtils, ConvertInvalidConfig) {
+    AudioConfig invalid;
+    audio_config_t halInvalid = AUDIO_CONFIG_INITIALIZER;
+    halInvalid.channel_mask = AUDIO_CHANNEL_INVALID;
+    halInvalid.format = kInvalidHalFormat;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigFromHal(halInvalid, false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigFromHal(halInvalid, true /*isInput*/, &invalid));
+    invalid.base.channelMask = "random string";
+    invalid.base.format = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertConfig) {
+    AudioConfig config = {};
+    config.base.sampleRateHz = 44100;
+    config.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    config.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    config.offloadInfo.base = config.base;
+    config.offloadInfo.streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC);
+    config.offloadInfo.bitRatePerSecond = 320;
+    config.offloadInfo.durationMicroseconds = -1;
+    config.offloadInfo.bitWidth = 16;
+    config.offloadInfo.bufferSize = 1024;
+    config.offloadInfo.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA);
+    config.offloadInfo.encapsulationMode = AudioEncapsulationMode::ELEMENTARY_STREAM;
+    config.offloadInfo.contentId = 42;
+    config.offloadInfo.syncId = 13;
+    audio_config_t halConfig;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(config, &halConfig));
+    AudioConfig configBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigFromHal(halConfig, false /*isInput*/, &configBack));
+    EXPECT_EQ(config, configBack);
+}
+
+TEST(HidlUtils, ConvertInvalidAudioProfile) {
+    AudioProfile invalid;
+    struct audio_profile halInvalid = {};
+    halInvalid.format = kInvalidHalFormat;
+    halInvalid.num_sample_rates = 0;
+    halInvalid.num_channel_masks = 1;
+    halInvalid.channel_masks[0] = kInvalidHalChannelMask;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioProfileFromHal(halInvalid, false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioProfileFromHal(halInvalid, true /*isInput*/, &invalid));
+    invalid.format = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioProfileToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioProfile) {
+    AudioProfile profile = {};
+    profile.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    profile.sampleRates.resize(2);
+    profile.sampleRates[0] = 44100;
+    profile.sampleRates[1] = 48000;
+    profile.channelMasks.resize(2);
+    profile.channelMasks[0] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO);
+    profile.channelMasks[1] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    struct audio_profile halProfile;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioProfileToHal(profile, &halProfile));
+    AudioProfile profileBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioProfileFromHal(halProfile, false /*isInput*/, &profileBack));
+    EXPECT_EQ(profile, profileBack);
+}
+
+TEST(HidlUtils, ConvertInvalidAudioPortConfig) {
+    AudioPortConfig invalid;
+    struct audio_port_config halInvalid = {};
+    halInvalid.type = AUDIO_PORT_TYPE_MIX;
+    halInvalid.role = AUDIO_PORT_ROLE_NONE;  // note: this is valid.
+    halInvalid.config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK;
+    halInvalid.channel_mask = AUDIO_CHANNEL_INVALID;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortConfigFromHal(halInvalid, &invalid));
+    invalid.base.channelMask = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortConfigToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioPortConfig) {
+    AudioPortConfig config = {};
+    config.id = 42;
+    config.base.sampleRateHz = 44100;
+    config.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    config.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    config.gain.config({});
+    config.gain.config().channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    config.ext.device({});
+    config.ext.device().deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+    struct audio_port_config halConfig;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortConfigToHal(config, &halConfig));
+    AudioPortConfig configBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortConfigFromHal(halConfig, &configBack));
+    EXPECT_EQ(config, configBack);
+    struct audio_port_config halConfigBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortConfigToHal(configBack, &halConfigBack));
+    EXPECT_TRUE(audio_port_configs_are_equal(&halConfig, &halConfigBack));
+}
+
+TEST(HidlUtils, ConvertInvalidAudioPort) {
+    AudioPort invalid;
+    struct audio_port_v7 halInvalid = {};
+    halInvalid.type = AUDIO_PORT_TYPE_MIX;
+    halInvalid.role = AUDIO_PORT_ROLE_NONE;  // note: this is valid.
+    halInvalid.num_audio_profiles = 1;
+    halInvalid.audio_profiles[0].format = kInvalidHalFormat;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortFromHal(halInvalid, &invalid));
+    invalid.profiles.resize(1);
+    invalid.profiles[0].format = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioPort) {
+    AudioPort port = {};
+    port.id = 42;
+    port.name = "test";
+    port.profiles.resize(1);
+    port.profiles[0].format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    port.profiles[0].sampleRates.resize(2);
+    port.profiles[0].sampleRates[0] = 44100;
+    port.profiles[0].sampleRates[1] = 48000;
+    port.profiles[0].channelMasks.resize(2);
+    port.profiles[0].channelMasks[0] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO);
+    port.profiles[0].channelMasks[1] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    port.gains.resize(1);
+    port.gains[0].channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    port.ext.device({});
+    port.ext.device().deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+    // active config left unspecified.
+    struct audio_port_v7 halPort;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortToHal(port, &halPort));
+    AudioPort portBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortFromHal(halPort, &portBack));
+    EXPECT_EQ(port, portBack);
+    struct audio_port_v7 halPortBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortToHal(portBack, &halPortBack));
+    EXPECT_TRUE(audio_ports_v7_are_equal(&halPort, &halPortBack));
+}
diff --git a/audio/core/all-versions/default/Conversions.cpp b/audio/core/all-versions/default/Conversions.cpp
index 0db210a..28d8f78 100644
--- a/audio/core/all-versions/default/Conversions.cpp
+++ b/audio/core/all-versions/default/Conversions.cpp
@@ -27,6 +27,7 @@
 namespace CPP_VERSION {
 namespace implementation {
 
+// TODO(mnaganov): Use method from HidlUtils for V7
 std::string deviceAddressToHal(const DeviceAddress& address) {
     // HAL assumes that the address is NUL-terminated.
     char halAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp
index 6260ba1..3c28159 100644
--- a/audio/core/all-versions/default/Device.cpp
+++ b/audio/core/all-versions/default/Device.cpp
@@ -283,8 +283,10 @@
         const hidl_vec<AudioPortConfig>& sinks) {
     Result retval(Result::NOT_SUPPORTED);
     if (version() >= AUDIO_DEVICE_API_VERSION_3_0) {
-        std::unique_ptr<audio_port_config[]> halSources(HidlUtils::audioPortConfigsToHal(sources));
-        std::unique_ptr<audio_port_config[]> halSinks(HidlUtils::audioPortConfigsToHal(sinks));
+        std::unique_ptr<audio_port_config[]> halSources;
+        HidlUtils::audioPortConfigsToHal(sources, &halSources);
+        std::unique_ptr<audio_port_config[]> halSinks;
+        HidlUtils::audioPortConfigsToHal(sinks, &halSinks);
         audio_patch_handle_t halPatch = static_cast<audio_patch_handle_t>(patch);
         retval = analyzeStatus("create_audio_patch",
                                mDevice->create_audio_patch(mDevice, sources.size(), &halSources[0],
diff --git a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
index 2466fd1..1612d3c 100644
--- a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
@@ -17,7 +17,7 @@
 #include "AudioPrimaryHidlHalTest.h"
 
 #if MAJOR_VERSION >= 7
-#include <audio_policy_configuration_V7_0.h>
+#include <android_audio_policy_configuration_V7_0.h>
 #include <xsdc/XsdcSupport.h>
 
 using android::xsdc_enum_range;
@@ -67,8 +67,7 @@
         auto flags = hidl_bitfield<AudioInputFlag>(AudioInputFlag::NONE);
         const SinkMetadata initMetadata = {{{.source = AudioSource::MIC, .gain = 1}}};
 #elif MAJOR_VERSION >= 7
-        config.base.channelMask.resize(1);
-        config.base.channelMask[0] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO);
+        config.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO);
         config.base.sampleRateHz = 8000;
         config.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
         hidl_vec<hidl_string> flags;
@@ -236,7 +235,7 @@
 #if MAJOR_VERSION <= 6
     hidl_enum_range<AudioSource> range;
 #elif MAJOR_VERSION >= 7
-    xsdc_enum_range<audio::policy::configuration::V7_0::AudioSource> range;
+    xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioSource> range;
 #endif
     // Test all possible track configuration
     for (auto source : range) {
@@ -272,8 +271,8 @@
     hidl_enum_range<AudioUsage> usageRange;
     hidl_enum_range<AudioContentType> contentRange;
 #elif MAJOR_VERSION >= 7
-    xsdc_enum_range<audio::policy::configuration::V7_0::AudioUsage> usageRange;
-    xsdc_enum_range<audio::policy::configuration::V7_0::AudioContentType> contentRange;
+    xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioUsage> usageRange;
+    xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioContentType> contentRange;
 #endif
     // Test all possible track configuration
     for (auto usage : usageRange) {
diff --git a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
index 63eaea8..941c4bd 100644
--- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -26,8 +26,7 @@
         for (auto sampleRate : sampleRates) {
             AudioConfig config{};
             // leave offloadInfo to 0
-            config.base.channelMask.resize(1);
-            config.base.channelMask[0] = toString(channelMask);
+            config.base.channelMask = toString(channelMask);
             config.base.sampleRateHz = sampleRate;
             config.base.format = format;
             configs.push_back(config);
diff --git a/audio/core/all-versions/vts/functional/Android.bp b/audio/core/all-versions/vts/functional/Android.bp
index c7bfe08..a809a92 100644
--- a/audio/core/all-versions/vts/functional/Android.bp
+++ b/audio/core/all-versions/vts/functional/Android.bp
@@ -22,6 +22,8 @@
         "libxml2",
     ],
     shared_libs: [
+        "audioclient-types-aidl-unstable-cpp",
+        "libaudioclient_aidl_conversion",
         "libbinder",
         "libfmq",
     ],
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index 0a9d5ee..1ead47c 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -43,8 +43,8 @@
 #include PATH(android/hardware/audio/FILE_VERSION/types.h)
 #include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
 #if MAJOR_VERSION >= 7
-#include <audio_policy_configuration_V7_0-enums.h>
-#include <audio_policy_configuration_V7_0.h>
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <android_audio_policy_configuration_V7_0.h>
 #endif
 
 #include <fmq/EventFlag.h>
@@ -91,7 +91,7 @@
 #if MAJOR_VERSION >= 7
 // Make an alias for enumerations generated from the APM config XSD.
 namespace xsd {
-using namespace ::audio::policy::configuration::CPP_VERSION;
+using namespace ::android::audio::policy::configuration::CPP_VERSION;
 }
 #endif
 
diff --git a/audio/effect/7.0/types.hal b/audio/effect/7.0/types.hal
index fe4ee51..b0a0709 100644
--- a/audio/effect/7.0/types.hal
+++ b/audio/effect/7.0/types.hal
@@ -202,16 +202,26 @@
  * enumeration of the effect engines present in a library.
  */
 struct EffectDescriptor {
-    Uuid type;                   // UUID of to the OpenSL ES interface implemented
-                                 // by this effect
-    Uuid uuid;                   // UUID for this particular implementation
-    bitfield<EffectFlags> flags; // effect engine capabilities/requirements flags
-    uint16_t cpuLoad;            // CPU load indication expressed in 0.1 MIPS units
-                                 // as estimated on an ARM9E core (ARMv5TE) with 0 WS
-    uint16_t memoryUsage;        // data memory usage expressed in KB and includes
-                                 // only dynamically allocated memory
-    uint8_t[64] name;            // human readable effect name
-    uint8_t[64] implementor;     // human readable effect implementor name
+    /** UUID of to the OpenSL ES interface implemented by this effect. */
+    Uuid type;
+    /** UUID for this particular implementation. */
+    Uuid uuid;
+    /** Effect engine capabilities/requirements flags. */
+    bitfield<EffectFlags> flags;
+    /**
+     * CPU load indication expressed in 0.1 MIPS units as estimated on
+     * an ARM9E core (ARMv5TE) with 0 WS.
+     */
+    uint16_t cpuLoad;
+    /**
+     * Data memory usage expressed in KB and includes only dynamically
+     * allocated memory.
+     */
+    uint16_t memoryUsage;
+    /** Human readable effect name. */
+    uint8_t[64] name;
+    /** Human readable effect implementor name. */
+    uint8_t[64] implementor;
 };
 
 /**
@@ -242,11 +252,16 @@
  */
 @export(name="", value_prefix="EFFECT_CONFIG_")
 enum EffectConfigParameters : int32_t {
-    BUFFER = 0x0001,    // buffer field
-    SMP_RATE = 0x0002,  // samplingRate
-    CHANNELS = 0x0004,  // channels
-    FORMAT = 0x0008,    // format
-    ACC_MODE = 0x0010,  // accessMode
+    /** Buffer field. */
+    BUFFER = 0x0001,
+    /** Sampling rate. */
+    SMP_RATE = 0x0002,
+    /** Channels. */
+    CHANNELS = 0x0004,
+    /** Format. */
+    FORMAT = 0x0008,
+    /** Access mode. */
+    ACC_MODE = 0x0010,
     // Note that the 2.0 ALL have been moved to an helper function
 };
 
@@ -270,21 +285,23 @@
 
 @export(name="effect_feature_e", value_prefix="EFFECT_FEATURE_")
 enum EffectFeature : int32_t {
-    AUX_CHANNELS, // supports auxiliary channels
-                  // (e.g. dual mic noise suppressor)
+    /** Supports auxiliary channels (e.g. dual mic noise suppressor). */
+    AUX_CHANNELS,
     CNT
 };
 
 struct EffectAuxChannelsConfig {
-    vec<AudioChannelMask> mainChannels;  // channel mask for main channels
-    vec<AudioChannelMask> auxChannels;   // channel mask for auxiliary channels
+    /** Channel mask for main channels. */
+    vec<AudioChannelMask> mainChannels;
+    /** Channel mask for auxiliary channels. */
+    vec<AudioChannelMask> auxChannels;
 };
 
 struct EffectOffloadParameter {
-    bool isOffload;          // true if the playback thread the effect
-                             // is attached to is offloaded
-    AudioIoHandle ioHandle;  // io handle of the playback thread
-                             // the effect is attached to
+    /** True if the playback thread the effect is attached to is offloaded. */
+    bool isOffload;
+    /** I/O handle of the playback thread the effect is attached to. */
+    AudioIoHandle ioHandle;
 };
 
 /**
diff --git a/audio/effect/all-versions/default/Conversions.cpp b/audio/effect/all-versions/default/Conversions.cpp
index b1c0b0d..0cc8767 100644
--- a/audio/effect/all-versions/default/Conversions.cpp
+++ b/audio/effect/all-versions/default/Conversions.cpp
@@ -15,7 +15,7 @@
  */
 
 #include "Conversions.h"
-#include "HidlUtils.h"
+#include "UuidUtils.h"
 
 #include <memory.h>
 #include <stdio.h>
@@ -31,12 +31,12 @@
 namespace CPP_VERSION {
 namespace implementation {
 
-using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::UuidUtils;
 
 void effectDescriptorFromHal(const effect_descriptor_t& halDescriptor,
                              EffectDescriptor* descriptor) {
-    HidlUtils::uuidFromHal(halDescriptor.type, &descriptor->type);
-    HidlUtils::uuidFromHal(halDescriptor.uuid, &descriptor->uuid);
+    UuidUtils::uuidFromHal(halDescriptor.type, &descriptor->type);
+    UuidUtils::uuidFromHal(halDescriptor.uuid, &descriptor->uuid);
     descriptor->flags = EnumBitfield<EffectFlags>(halDescriptor.flags);
     descriptor->cpuLoad = halDescriptor.cpuLoad;
     descriptor->memoryUsage = halDescriptor.memoryUsage;
diff --git a/audio/effect/all-versions/default/EffectsFactory.cpp b/audio/effect/all-versions/default/EffectsFactory.cpp
index acce7de..b265d3d 100644
--- a/audio/effect/all-versions/default/EffectsFactory.cpp
+++ b/audio/effect/all-versions/default/EffectsFactory.cpp
@@ -24,10 +24,10 @@
 #include "Effect.h"
 #include "EnvironmentalReverbEffect.h"
 #include "EqualizerEffect.h"
-#include "HidlUtils.h"
 #include "LoudnessEnhancerEffect.h"
 #include "NoiseSuppressionEffect.h"
 #include "PresetReverbEffect.h"
+#include "UuidUtils.h"
 #include "VirtualizerEffect.h"
 #include "VisualizerEffect.h"
 #include "common/all-versions/default/EffectMap.h"
@@ -53,7 +53,7 @@
 namespace CPP_VERSION {
 namespace implementation {
 
-using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::UuidUtils;
 
 // static
 sp<IEffect> EffectsFactory::dispatchEffectInstanceCreation(const effect_descriptor_t& halDescriptor,
@@ -135,7 +135,7 @@
 
 Return<void> EffectsFactory::getDescriptor(const Uuid& uuid, getDescriptor_cb _hidl_cb) {
     effect_uuid_t halUuid;
-    HidlUtils::uuidToHal(uuid, &halUuid);
+    UuidUtils::uuidToHal(uuid, &halUuid);
     effect_descriptor_t halDescriptor;
     status_t status = EffectGetDescriptor(&halUuid, &halDescriptor);
     EffectDescriptor descriptor;
@@ -170,7 +170,7 @@
 Return<void> EffectsFactory::createEffectImpl(const Uuid& uuid, int32_t session, int32_t ioHandle,
                                               int32_t device, createEffect_cb _hidl_cb) {
     effect_uuid_t halUuid;
-    HidlUtils::uuidToHal(uuid, &halUuid);
+    UuidUtils::uuidToHal(uuid, &halUuid);
     effect_handle_t handle;
     Result retval(Result::OK);
     status_t status;
diff --git a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
index b64f105..199a8a5 100644
--- a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
@@ -28,8 +28,8 @@
 #include <android/hidl/allocator/1.0/IAllocator.h>
 #include <android/hidl/memory/1.0/IMemory.h>
 #if MAJOR_VERSION >= 7
-#include <audio_policy_configuration_V7_0-enums.h>
-#include <audio_policy_configuration_V7_0.h>
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <android_audio_policy_configuration_V7_0.h>
 #endif
 
 #include <common/all-versions/VersionUtils.h>
@@ -54,7 +54,7 @@
 #if MAJOR_VERSION >= 7
 // Make an alias for enumerations generated from the APM config XSD.
 namespace xsd {
-using namespace ::audio::policy::configuration::CPP_VERSION;
+using namespace ::android::audio::policy::configuration::CPP_VERSION;
 }
 #endif
 
@@ -262,8 +262,8 @@
     *channelCount = audio_channel_count_from_out_mask(
         static_cast<audio_channel_mask_t>(currentConfig.outputCfg.channels));
 #else
-    *channelCount =
-            audio::policy::configuration::V7_0::getChannelCount(currentConfig.outputCfg.channels);
+    *channelCount = android::audio::policy::configuration::V7_0::getChannelCount(
+            currentConfig.outputCfg.channels);
     ASSERT_NE(*channelCount, 0);
 #endif
 }
diff --git a/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/AudioFocusChange.aidl b/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/AudioFocusChange.aidl
index fc35fa9..98fa4e8 100644
--- a/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/AudioFocusChange.aidl
+++ b/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/AudioFocusChange.aidl
@@ -27,7 +27,7 @@
     GAIN_TRANSIENT = 2,
     GAIN_TRANSIENT_MAY_DUCK = 3,
     GAIN_TRANSIENT_EXCLUSIVE = 4,
-    LOSS = -1, // -1 * GAIN,
-    LOSS_TRANSIENT = -2, // -1 * GAIN_TRANSIENT,
-    LOSS_TRANSIENT_CAN_DUCK = -3, // -1 * GAIN_TRANSIENT_MAY_DUCK,
+    LOSS = -1 * GAIN,
+    LOSS_TRANSIENT = -1 * GAIN_TRANSIENT,
+    LOSS_TRANSIENT_CAN_DUCK = -1 * GAIN_TRANSIENT_MAY_DUCK,
 }
\ No newline at end of file
diff --git a/automotive/audiocontrol/aidl/default/AudioControl.cpp b/automotive/audiocontrol/aidl/default/AudioControl.cpp
index b637310..5e09b4f 100644
--- a/automotive/audiocontrol/aidl/default/AudioControl.cpp
+++ b/automotive/audiocontrol/aidl/default/AudioControl.cpp
@@ -23,7 +23,7 @@
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 
-#include <audio_policy_configuration_V7_0.h>
+#include <android_audio_policy_configuration_V7_0.h>
 #include <private/android_filesystem_config.h>
 
 #include <stdio.h>
@@ -35,7 +35,7 @@
 using ::std::string;
 
 namespace xsd {
-using namespace audio::policy::configuration::V7_0;
+using namespace ::android::audio::policy::configuration::V7_0;
 }
 
 namespace {
diff --git a/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp b/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp
index c734e29..f33b8a5 100644
--- a/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp
+++ b/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp
@@ -33,10 +33,10 @@
 using android::hardware::automotive::audiocontrol::BnFocusListener;
 using android::hardware::automotive::audiocontrol::IAudioControl;
 
-#include "audio_policy_configuration_V7_0.h"
+#include "android_audio_policy_configuration_V7_0.h"
 
 namespace xsd {
-using namespace audio::policy::configuration::V7_0;
+using namespace android::audio::policy::configuration::V7_0;
 }
 
 class AudioControlAidl : public testing::TestWithParam<std::string> {
diff --git a/automotive/can/1.0/default/libnl++/Android.bp b/automotive/can/1.0/default/libnl++/Android.bp
index 4042b16..90e1002 100644
--- a/automotive/can/1.0/default/libnl++/Android.bp
+++ b/automotive/can/1.0/default/libnl++/Android.bp
@@ -25,6 +25,7 @@
         "protocols/generic/Generic.cpp",
         "protocols/generic/GenericMessageBase.cpp",
         "protocols/generic/Unknown.cpp",
+        "protocols/generic/families/Nl80211.cpp",
         "protocols/route/Link.cpp",
         "protocols/route/Route.cpp",
         "protocols/route/structs.cpp",
diff --git a/automotive/can/1.0/default/libnl++/printer.cpp b/automotive/can/1.0/default/libnl++/printer.cpp
index e6cada2..f08897e 100644
--- a/automotive/can/1.0/default/libnl++/printer.cpp
+++ b/automotive/can/1.0/default/libnl++/printer.cpp
@@ -51,24 +51,24 @@
     printFlag(NLM_F_DUMP_FILTERED, "DUMP_FILTERED");
 
     switch (genre) {
-        case protocols::MessageGenre::UNKNOWN:
+        case protocols::MessageGenre::Unknown:
             break;
-        case protocols::MessageGenre::GET:
+        case protocols::MessageGenre::Get:
             printFlag(NLM_F_DUMP, "DUMP");  // ROOT | MATCH
             printFlag(NLM_F_ROOT, "ROOT");
             printFlag(NLM_F_MATCH, "MATCH");
             printFlag(NLM_F_ATOMIC, "ATOMIC");
             break;
-        case protocols::MessageGenre::NEW:
+        case protocols::MessageGenre::New:
             printFlag(NLM_F_REPLACE, "REPLACE");
             printFlag(NLM_F_EXCL, "EXCL");
             printFlag(NLM_F_CREATE, "CREATE");
             printFlag(NLM_F_APPEND, "APPEND");
             break;
-        case protocols::MessageGenre::DELETE:
+        case protocols::MessageGenre::Delete:
             printFlag(NLM_F_NONREC, "NONREC");
             break;
-        case protocols::MessageGenre::ACK:
+        case protocols::MessageGenre::Ack:
             printFlag(NLM_F_CAPPED, "CAPPED");
             printFlag(NLM_F_ACK_TLVS, "ACK_TLVS");
             break;
@@ -99,11 +99,25 @@
 static void toStream(std::stringstream& ss, const Buffer<nlattr> attr,
                      const protocols::AttributeMap& attrMap) {
     using DataType = protocols::AttributeDefinition::DataType;
+    using Flags = protocols::AttributeDefinition::Flags;
     const auto attrtype = attrMap[attr->nla_type];
 
-    ss << attrtype.name << ": ";
+    ss << attrtype.name;
+
+    if (attrtype.dataType == DataType::Flag && attr.data<uint8_t>().getRaw().len() == 0) return;
+    ss << ": ";
+
+    if (attrtype.flags == Flags::Verbose) {
+        const auto raw = attr.data<uint8_t>();
+        ss << "{len=" << raw.getRaw().len();
+        ss << ", crc=" << std::hex << std::setw(4) << crc16(raw) << std::dec;
+        ss << "}";
+        return;
+    }
+
     switch (attrtype.dataType) {
         case DataType::Raw:
+        case DataType::Flag:
             toStream(ss, attr.data<uint8_t>());
             break;
         case DataType::Nested: {
@@ -117,13 +131,19 @@
             ss << '}';
             break;
         }
+        case DataType::StringNul:
         case DataType::String: {
             const auto str = attr.data<char>().getRaw();
-            ss << '"' << printableOnly({str.ptr(), str.len()}) << '"';
+            auto len = str.len();
+            if (attrtype.dataType == DataType::StringNul && len > 0 && str.ptr()[len - 1] == '\0') {
+                len--;
+            }
+
+            ss << '"' << printableOnly({str.ptr(), len}) << '"';
             break;
         }
         case DataType::Uint:
-            ss << attr.data<uint32_t>().copyFirst();
+            ss << attr.data<uint64_t>().copyFirst();
             break;
         case DataType::Struct: {
             const auto structToStream =
@@ -147,10 +167,12 @@
     }
     protocols::NetlinkProtocol& protocolDescr = *protocolMaybe;
 
-    const auto msgDescMaybe = protocolDescr.getMessageDescriptor(hdr->nlmsg_type);
+    auto msgDescMaybe = protocolDescr.getMessageDescriptor(hdr->nlmsg_type);
     const auto msgDetails =
             protocols::MessageDescriptor::getMessageDetails(msgDescMaybe, hdr->nlmsg_type);
 
+    if (msgDescMaybe.has_value()) msgDescMaybe->get().track(hdr);
+
     ss << "nlmsg{" << protocolDescr.getName() << " ";
 
     ss << "hdr={";
diff --git a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp
index c93d865..aaf24a5 100644
--- a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.cpp
@@ -56,15 +56,17 @@
 
 MessageDescriptor::MessageDetails MessageDescriptor::getMessageDetails(nlmsgtype_t msgtype) const {
     const auto it = mMessageDetails.find(msgtype);
-    if (it == mMessageDetails.end()) return {std::to_string(msgtype), MessageGenre::UNKNOWN};
+    if (it == mMessageDetails.end()) return {std::to_string(msgtype), MessageGenre::Unknown};
     return it->second;
 }
 
 MessageDescriptor::MessageDetails MessageDescriptor::getMessageDetails(
-        const std::optional<std::reference_wrapper<const MessageDescriptor>>& msgDescMaybe,
+        const std::optional<std::reference_wrapper<MessageDescriptor>>& msgDescMaybe,
         nlmsgtype_t msgtype) {
     if (msgDescMaybe.has_value()) return msgDescMaybe->get().getMessageDetails(msgtype);
-    return {std::to_string(msgtype), protocols::MessageGenre::UNKNOWN};
+    return {std::to_string(msgtype), protocols::MessageGenre::Unknown};
 }
 
+void MessageDescriptor::track(const Buffer<nlmsghdr> /* hdr */) {}
+
 }  // namespace android::nl::protocols
diff --git a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h
index bd0e60f..8bed5e7 100644
--- a/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h
+++ b/automotive/can/1.0/default/libnl++/protocols/MessageDefinition.h
@@ -54,17 +54,64 @@
  */
 struct AttributeDefinition {
     enum class DataType : uint8_t {
+        /**
+         * Binary blob (or attribute of unknown type).
+         */
         Raw,
+
+        /**
+         * Nested attribute (with or without NLA_F_NESTED).
+         */
         Nested,
+
+        /**
+         * Non-null terminated string.
+         *
+         * The length of the string is determined by the size of an attribute.
+         */
         String,
+
+        /**
+         * Null terminated string.
+         */
+        StringNul,
+
+        /**
+         * Unsigned integer of size 8, 16, 32 or 64 bits.
+         */
         Uint,
+
+        /**
+         * Structure which printer is defined in ops ToStream variant.
+         */
         Struct,
+
+        /**
+         * Flag attribute.
+         *
+         * The attribute doesn't have any contents. The flag is set when the attribute is present,
+         * it's not when it's absent from attribute list.
+         */
+        Flag,
+    };
+    enum class Flags : uint8_t {
+        Verbose = (1 << 0),
     };
     using ToStream = std::function<void(std::stringstream& ss, const Buffer<nlattr> attr)>;
 
     std::string name;
     DataType dataType = DataType::Raw;
     std::variant<AttributeMap, ToStream> ops = AttributeMap{};
+
+    /**
+     * Attribute flags.
+     *
+     * It's not really a bitmask flag set (since you are not supposed to compare enum class by
+     * bitmask), but std::set<Flags> bumps compile time from 16s to 3m. Let's leave it as-is for
+     * now and revisit if we get some flags that can be used in pairs. When it happens, review all
+     * uses of the flags field to include the "&" operator and not "==".
+     */
+    Flags flags = {};
 };
 
 /**
@@ -74,11 +121,11 @@
  * section in linux/netlink.h.
  */
 enum class MessageGenre {
-    UNKNOWN,
-    GET,
-    NEW,
-    DELETE,
-    ACK,
+    Unknown,
+    Get,
+    New,
+    Delete,
+    Ack,
 };
 
 /**
@@ -103,8 +150,15 @@
     MessageDetails getMessageDetails(nlmsgtype_t msgtype) const;
     virtual void dataToStream(std::stringstream& ss, const Buffer<nlmsghdr> hdr) const = 0;
 
+    /**
+     * Message tracking for stateful protocols (such as NETLINK_GENERIC).
+     *
+     * \param hdr Message to track
+     */
+    virtual void track(const Buffer<nlmsghdr> hdr);
+
     static MessageDetails getMessageDetails(
-            const std::optional<std::reference_wrapper<const MessageDescriptor>>& msgDescMaybe,
+            const std::optional<std::reference_wrapper<MessageDescriptor>>& msgDescMaybe,
             nlmsgtype_t msgtype);
 
   protected:
diff --git a/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.cpp b/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.cpp
index cd2e8c6..16208ab 100644
--- a/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.cpp
@@ -32,7 +32,7 @@
     return mName;
 }
 
-const std::optional<std::reference_wrapper<const MessageDescriptor>>
+const std::optional<std::reference_wrapper<MessageDescriptor>>
 NetlinkProtocol::getMessageDescriptor(nlmsgtype_t nlmsg_type) {
     if (mMessageDescrs.count(nlmsg_type) == 0) return std::nullopt;
     return *mMessageDescrs.find(nlmsg_type)->second;
@@ -41,7 +41,7 @@
 NetlinkProtocol::MessageDescriptorMap NetlinkProtocol::toMap(
         const NetlinkProtocol::MessageDescriptorList& descrs, int protocol) {
     MessageDescriptorMap map;
-    for (const auto& descr : descrs) {
+    for (auto& descr : descrs) {
         for (const auto& [mtype, mdet] : descr->getMessageDetailsMap()) {
             map.emplace(mtype, descr);
         }
diff --git a/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.h b/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.h
index c969547..b173b91 100644
--- a/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.h
+++ b/automotive/can/1.0/default/libnl++/protocols/NetlinkProtocol.h
@@ -40,17 +40,17 @@
 
     const std::string& getName() const;
 
-    virtual const std::optional<std::reference_wrapper<const MessageDescriptor>>
-    getMessageDescriptor(nlmsgtype_t nlmsg_type);
+    virtual const std::optional<std::reference_wrapper<MessageDescriptor>> getMessageDescriptor(
+            nlmsgtype_t nlmsg_type);
 
   protected:
-    typedef std::vector<std::shared_ptr<const MessageDescriptor>> MessageDescriptorList;
+    typedef std::vector<std::shared_ptr<MessageDescriptor>> MessageDescriptorList;
 
     NetlinkProtocol(int protocol, const std::string& name,
                     const MessageDescriptorList&& messageDescrs);
 
   private:
-    typedef std::map<nlmsgtype_t, std::shared_ptr<const MessageDescriptor>> MessageDescriptorMap;
+    typedef std::map<nlmsgtype_t, std::shared_ptr<MessageDescriptor>> MessageDescriptorMap;
 
     const int mProtocol;
     const std::string mName;
diff --git a/automotive/can/1.0/default/libnl++/protocols/common/Empty.cpp b/automotive/can/1.0/default/libnl++/protocols/common/Empty.cpp
index 8a672d3..4b509c9 100644
--- a/automotive/can/1.0/default/libnl++/protocols/common/Empty.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/common/Empty.cpp
@@ -20,9 +20,9 @@
 
 // clang-format off
 Empty::Empty() : MessageDefinition<char>("nlmsg", {
-    {NLMSG_NOOP, {"NOOP", MessageGenre::UNKNOWN}},
-    {NLMSG_DONE, {"DONE", MessageGenre::UNKNOWN}},
-    {NLMSG_OVERRUN, {"OVERRUN", MessageGenre::UNKNOWN}},
+    {NLMSG_NOOP, {"NOOP", MessageGenre::Unknown}},
+    {NLMSG_DONE, {"DONE", MessageGenre::Unknown}},
+    {NLMSG_OVERRUN, {"OVERRUN", MessageGenre::Unknown}},
 }) {}
 // clang-format on
 
diff --git a/automotive/can/1.0/default/libnl++/protocols/common/Error.cpp b/automotive/can/1.0/default/libnl++/protocols/common/Error.cpp
index 44708a3..77451ed 100644
--- a/automotive/can/1.0/default/libnl++/protocols/common/Error.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/common/Error.cpp
@@ -20,13 +20,15 @@
 
 #include <libnl++/printer.h>
 
+#include <map>
+
 namespace android::nl::protocols::base {
 
 using DataType = AttributeDefinition::DataType;
 
 // clang-format off
 Error::Error(int protocol) : MessageDefinition<nlmsgerr>("nlmsg", {
-    {NLMSG_ERROR, {"ERROR", MessageGenre::ACK}},
+    {NLMSG_ERROR, {"ERROR", MessageGenre::Ack}},
 }, {
     {NLMSGERR_ATTR_MSG, {"MSG", DataType::String}},
     {NLMSGERR_ATTR_OFFS, {"OFFS", DataType::Uint}},
@@ -34,9 +36,156 @@
 }), mProtocol(protocol) {}
 // clang-format on
 
+std::map<int, std::string> errnoNames{
+        {EPERM, "EPERM"},                      // Operation not permitted
+        {ENOENT, "ENOENT"},                    // No such file or directory
+        {ESRCH, "ESRCH"},                      // No such process
+        {EINTR, "EINTR"},                      // Interrupted system call
+        {EIO, "EIO"},                          // I/O error
+        {ENXIO, "ENXIO"},                      // No such device or address
+        {E2BIG, "E2BIG"},                      // Argument list too long
+        {ENOEXEC, "ENOEXEC"},                  // Exec format error
+        {EBADF, "EBADF"},                      // Bad file number
+        {ECHILD, "ECHILD"},                    // No child processes
+        {EAGAIN, "EAGAIN"},                    // Try again
+        {ENOMEM, "ENOMEM"},                    // Out of memory
+        {EACCES, "EACCES"},                    // Permission denied
+        {EFAULT, "EFAULT"},                    // Bad address
+        {ENOTBLK, "ENOTBLK"},                  // Block device required
+        {EBUSY, "EBUSY"},                      // Device or resource busy
+        {EEXIST, "EEXIST"},                    // File exists
+        {EXDEV, "EXDEV"},                      // Cross-device link
+        {ENODEV, "ENODEV"},                    // No such device
+        {ENOTDIR, "ENOTDIR"},                  // Not a directory
+        {EISDIR, "EISDIR"},                    // Is a directory
+        {EINVAL, "EINVAL"},                    // Invalid argument
+        {ENFILE, "ENFILE"},                    // File table overflow
+        {EMFILE, "EMFILE"},                    // Too many open files
+        {ENOTTY, "ENOTTY"},                    // Not a typewriter
+        {ETXTBSY, "ETXTBSY"},                  // Text file busy
+        {EFBIG, "EFBIG"},                      // File too large
+        {ENOSPC, "ENOSPC"},                    // No space left on device
+        {ESPIPE, "ESPIPE"},                    // Illegal seek
+        {EROFS, "EROFS"},                      // Read-only file system
+        {EMLINK, "EMLINK"},                    // Too many links
+        {EPIPE, "EPIPE"},                      // Broken pipe
+        {EDOM, "EDOM"},                        // Math argument out of domain of func
+        {ERANGE, "ERANGE"},                    // Math result not representable
+        {EDEADLK, "EDEADLK"},                  // Resource deadlock would occur
+        {ENAMETOOLONG, "ENAMETOOLONG"},        // File name too long
+        {ENOLCK, "ENOLCK"},                    // No record locks available
+        {ENOSYS, "ENOSYS"},                    // Invalid system call number
+        {ENOTEMPTY, "ENOTEMPTY"},              // Directory not empty
+        {ELOOP, "ELOOP"},                      // Too many symbolic links encountered
+        {ENOMSG, "ENOMSG"},                    // No message of desired type
+        {EIDRM, "EIDRM"},                      // Identifier removed
+        {ECHRNG, "ECHRNG"},                    // Channel number out of range
+        {EL2NSYNC, "EL2NSYNC"},                // Level 2 not synchronized
+        {EL3HLT, "EL3HLT"},                    // Level 3 halted
+        {EL3RST, "EL3RST"},                    // Level 3 reset
+        {ELNRNG, "ELNRNG"},                    // Link number out of range
+        {EUNATCH, "EUNATCH"},                  // Protocol driver not attached
+        {ENOCSI, "ENOCSI"},                    // No CSI structure available
+        {EL2HLT, "EL2HLT"},                    // Level 2 halted
+        {EBADE, "EBADE"},                      // Invalid exchange
+        {EBADR, "EBADR"},                      // Invalid request descriptor
+        {EXFULL, "EXFULL"},                    // Exchange full
+        {ENOANO, "ENOANO"},                    // No anode
+        {EBADRQC, "EBADRQC"},                  // Invalid request code
+        {EBADSLT, "EBADSLT"},                  // Invalid slot
+        {EBFONT, "EBFONT"},                    // Bad font file format
+        {ENOSTR, "ENOSTR"},                    // Device not a stream
+        {ENODATA, "ENODATA"},                  // No data available
+        {ETIME, "ETIME"},                      // Timer expired
+        {ENOSR, "ENOSR"},                      // Out of streams resources
+        {ENONET, "ENONET"},                    // Machine is not on the network
+        {ENOPKG, "ENOPKG"},                    // Package not installed
+        {EREMOTE, "EREMOTE"},                  // Object is remote
+        {ENOLINK, "ENOLINK"},                  // Link has been severed
+        {EADV, "EADV"},                        // Advertise error
+        {ESRMNT, "ESRMNT"},                    // Srmount error
+        {ECOMM, "ECOMM"},                      // Communication error on send
+        {EPROTO, "EPROTO"},                    // Protocol error
+        {EMULTIHOP, "EMULTIHOP"},              // Multihop attempted
+        {EDOTDOT, "EDOTDOT"},                  // RFS specific error
+        {EBADMSG, "EBADMSG"},                  // Not a data message
+        {EOVERFLOW, "EOVERFLOW"},              // Value too large for defined data type
+        {ENOTUNIQ, "ENOTUNIQ"},                // Name not unique on network
+        {EBADFD, "EBADFD"},                    // File descriptor in bad state
+        {EREMCHG, "EREMCHG"},                  // Remote address changed
+        {ELIBACC, "ELIBACC"},                  // Can not access a needed shared library
+        {ELIBBAD, "ELIBBAD"},                  // Accessing a corrupted shared library
+        {ELIBSCN, "ELIBSCN"},                  // .lib section in a.out corrupted
+        {ELIBMAX, "ELIBMAX"},                  // Attempting to link in too many shared libraries
+        {ELIBEXEC, "ELIBEXEC"},                // Cannot exec a shared library directly
+        {EILSEQ, "EILSEQ"},                    // Illegal byte sequence
+        {ERESTART, "ERESTART"},                // Interrupted system call should be restarted
+        {ESTRPIPE, "ESTRPIPE"},                // Streams pipe error
+        {EUSERS, "EUSERS"},                    // Too many users
+        {ENOTSOCK, "ENOTSOCK"},                // Socket operation on non-socket
+        {EDESTADDRREQ, "EDESTADDRREQ"},        // Destination address required
+        {EMSGSIZE, "EMSGSIZE"},                // Message too long
+        {EPROTOTYPE, "EPROTOTYPE"},            // Protocol wrong type for socket
+        {ENOPROTOOPT, "ENOPROTOOPT"},          // Protocol not available
+        {EPROTONOSUPPORT, "EPROTONOSUPPORT"},  // Protocol not supported
+        {ESOCKTNOSUPPORT, "ESOCKTNOSUPPORT"},  // Socket type not supported
+        {EOPNOTSUPP, "EOPNOTSUPP"},            // Operation not supported on transport endpoint
+        {EPFNOSUPPORT, "EPFNOSUPPORT"},        // Protocol family not supported
+        {EAFNOSUPPORT, "EAFNOSUPPORT"},        // Address family not supported by protocol
+        {EADDRINUSE, "EADDRINUSE"},            // Address already in use
+        {EADDRNOTAVAIL, "EADDRNOTAVAIL"},      // Cannot assign requested address
+        {ENETDOWN, "ENETDOWN"},                // Network is down
+        {ENETUNREACH, "ENETUNREACH"},          // Network is unreachable
+        {ENETRESET, "ENETRESET"},              // Network dropped connection because of reset
+        {ECONNABORTED, "ECONNABORTED"},        // Software caused connection abort
+        {ECONNRESET, "ECONNRESET"},            // Connection reset by peer
+        {ENOBUFS, "ENOBUFS"},                  // No buffer space available
+        {EISCONN, "EISCONN"},                  // Transport endpoint is already connected
+        {ENOTCONN, "ENOTCONN"},                // Transport endpoint is not connected
+        {ESHUTDOWN, "ESHUTDOWN"},              // Cannot send after transport endpoint shutdown
+        {ETOOMANYREFS, "ETOOMANYREFS"},        // Too many references: cannot splice
+        {ETIMEDOUT, "ETIMEDOUT"},              // Connection timed out
+        {ECONNREFUSED, "ECONNREFUSED"},        // Connection refused
+        {EHOSTDOWN, "EHOSTDOWN"},              // Host is down
+        {EHOSTUNREACH, "EHOSTUNREACH"},        // No route to host
+        {EALREADY, "EALREADY"},                // Operation already in progress
+        {EINPROGRESS, "EINPROGRESS"},          // Operation now in progress
+        {ESTALE, "ESTALE"},                    // Stale file handle
+        {EUCLEAN, "EUCLEAN"},                  // Structure needs cleaning
+        {ENOTNAM, "ENOTNAM"},                  // Not a XENIX named type file
+        {ENAVAIL, "ENAVAIL"},                  // No XENIX semaphores available
+        {EISNAM, "EISNAM"},                    // Is a named type file
+        {EREMOTEIO, "EREMOTEIO"},              // Remote I/O error
+        {EDQUOT, "EDQUOT"},                    // Quota exceeded
+        {ENOMEDIUM, "ENOMEDIUM"},              // No medium found
+        {EMEDIUMTYPE, "EMEDIUMTYPE"},          // Wrong medium type
+        {ECANCELED, "ECANCELED"},              // Operation Canceled
+        {ENOKEY, "ENOKEY"},                    // Required key not available
+        {EKEYEXPIRED, "EKEYEXPIRED"},          // Key has expired
+        {EKEYREVOKED, "EKEYREVOKED"},          // Key has been revoked
+        {EKEYREJECTED, "EKEYREJECTED"},        // Key was rejected by service
+        {EOWNERDEAD, "EOWNERDEAD"},            // Owner died
+        {ENOTRECOVERABLE, "ENOTRECOVERABLE"},  // State not recoverable
+        {ERFKILL, "ERFKILL"},                  // Operation not possible due to RF-kill
+        {EHWPOISON, "EHWPOISON"},              // Memory page has hardware error
+
+        // Duplicates: EWOULDBLOCK (Operation would block), EDEADLOCK
+};
+
 void Error::toStream(std::stringstream& ss, const nlmsgerr& data) const {
-    ss << "nlmsgerr{error=" << data.error
-       << ", msg=" << toString({&data.msg, sizeof(data.msg)}, mProtocol, false) << "}";
+    ss << "nlmsgerr{";
+    if (data.error == 0) {
+        ss << "ACK";
+    } else {
+        ss << "error=";
+        const auto nameIt = errnoNames.find(-data.error);
+        if (nameIt == errnoNames.end()) {
+            ss << data.error;
+        } else {
+            ss << nameIt->second;
+        }
+    }
+    ss << ", msg=" << toString({&data.msg, sizeof(data.msg)}, mProtocol, false) << "}";
 }
 
 }  // namespace android::nl::protocols::base
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.cpp b/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.cpp
index a3c6736..1e1ad12 100644
--- a/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.cpp
@@ -16,12 +16,19 @@
 
 #include "Ctrl.h"
 
+#include "families/Nl80211.h"
+
+#include <libnl++/Message.h>
+
 namespace android::nl::protocols::generic {
 
 using DataType = AttributeDefinition::DataType;
+using Flags = AttributeDefinition::Flags;
 
 // clang-format off
-Ctrl::Ctrl() : GenericMessageBase(GENL_ID_CTRL, "ID_CTRL", {
+Ctrl::Ctrl(Generic::FamilyRegister& familyRegister)
+    : GenericMessageBase(GENL_ID_CTRL, "ID_CTRL",
+{
     {CTRL_CMD_NEWFAMILY, "NEWFAMILY"},
     {CTRL_CMD_DELFAMILY, "DELFAMILY"},
     {CTRL_CMD_GETFAMILY, "GETFAMILY"},
@@ -33,7 +40,7 @@
     {CTRL_CMD_GETMCAST_GRP, "GETMCAST_GRP"},
 }, {
     {CTRL_ATTR_FAMILY_ID, {"FAMILY_ID", DataType::Uint}},
-    {CTRL_ATTR_FAMILY_NAME, {"FAMILY_NAME", DataType::String}},
+    {CTRL_ATTR_FAMILY_NAME, {"FAMILY_NAME", DataType::StringNul}},
     {CTRL_ATTR_VERSION, {"VERSION", DataType::Uint}},
     {CTRL_ATTR_HDRSIZE, {"HDRSIZE", DataType::Uint}},
     {CTRL_ATTR_MAXATTR, {"MAXATTR", DataType::Uint}},
@@ -42,14 +49,31 @@
             {CTRL_ATTR_OP_ID, {"ID", DataType::Uint}},
             {CTRL_ATTR_OP_FLAGS, {"FLAGS", DataType::Uint}},
         }}},
-    }}},
+    }, Flags::Verbose}},
     {CTRL_ATTR_MCAST_GROUPS, {"MCAST_GROUPS", DataType::Nested, AttributeMap{
         {std::nullopt, {"GRP", DataType::Nested, AttributeMap{
-            {CTRL_ATTR_MCAST_GRP_NAME, {"NAME", DataType::String}},
+            {CTRL_ATTR_MCAST_GRP_NAME, {"NAME", DataType::StringNul}},
             {CTRL_ATTR_MCAST_GRP_ID, {"ID", DataType::Uint}},
         }}},
     }}},
-}) {}
+}), mFamilyRegister(familyRegister) {}
 // clang-format on
 
+void Ctrl::track(const Buffer<nlmsghdr> hdr) {
+    const auto msgMaybe = Message<genlmsghdr>::parse(hdr, {GENL_ID_CTRL});
+    if (!msgMaybe.has_value()) return;
+    const auto msg = *msgMaybe;
+
+    if (msg->cmd != CTRL_CMD_NEWFAMILY) return;
+    const auto familyId = msg.attributes.get<uint16_t>(CTRL_ATTR_FAMILY_ID);
+    const auto familyName = msg.attributes.get<std::string>(CTRL_ATTR_FAMILY_NAME);
+
+    /* For now, we support just a single family. But if you add more, please define proper
+     * abstraction and not hardcode every name and class here.
+     */
+    if (familyName == "nl80211") {
+        mFamilyRegister[familyId] = std::make_shared<families::Nl80211>(familyId);
+    }
+}
+
 }  // namespace android::nl::protocols::generic
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.h b/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.h
index 6af87a8..b13df02 100644
--- a/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.h
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/Ctrl.h
@@ -16,13 +16,19 @@
 
 #pragma once
 
+#include "Generic.h"
 #include "GenericMessageBase.h"
 
 namespace android::nl::protocols::generic {
 
 class Ctrl : public GenericMessageBase {
   public:
-    Ctrl();
+    Ctrl(Generic::FamilyRegister& familyRegister);
+
+    void track(const Buffer<nlmsghdr> hdr) override;
+
+  private:
+    Generic::FamilyRegister& mFamilyRegister;
 };
 
 }  // namespace android::nl::protocols::generic
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/Generic.cpp b/automotive/can/1.0/default/libnl++/protocols/generic/Generic.cpp
index 1a24914..5e34a1f 100644
--- a/automotive/can/1.0/default/libnl++/protocols/generic/Generic.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/Generic.cpp
@@ -21,11 +21,12 @@
 
 namespace android::nl::protocols::generic {
 
-Generic::Generic() : NetlinkProtocol(NETLINK_GENERIC, "GENERIC", {std::make_shared<Ctrl>()}) {}
+Generic::Generic()
+    : NetlinkProtocol(NETLINK_GENERIC, "GENERIC", {std::make_shared<Ctrl>(mFamilyRegister)}) {}
 
-const std::optional<std::reference_wrapper<const MessageDescriptor>> Generic::getMessageDescriptor(
+const std::optional<std::reference_wrapper<MessageDescriptor>> Generic::getMessageDescriptor(
         nlmsgtype_t nlmsg_type) {
-    const auto desc = NetlinkProtocol::getMessageDescriptor(nlmsg_type);
+    auto desc = NetlinkProtocol::getMessageDescriptor(nlmsg_type);
     if (desc.has_value()) return desc;
 
     auto it = mFamilyRegister.find(nlmsg_type);
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/Generic.h b/automotive/can/1.0/default/libnl++/protocols/generic/Generic.h
index 593c92d..2cdd584 100644
--- a/automotive/can/1.0/default/libnl++/protocols/generic/Generic.h
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/Generic.h
@@ -25,13 +25,15 @@
  */
 class Generic : public NetlinkProtocol {
   public:
+    typedef std::map<nlmsgtype_t, std::shared_ptr<MessageDescriptor>> FamilyRegister;
+
     Generic();
 
-    const std::optional<std::reference_wrapper<const MessageDescriptor>> getMessageDescriptor(
+    const std::optional<std::reference_wrapper<MessageDescriptor>> getMessageDescriptor(
             nlmsgtype_t nlmsg_type);
 
   private:
-    std::map<nlmsgtype_t, std::shared_ptr<const MessageDescriptor>> mFamilyRegister;
+    FamilyRegister mFamilyRegister;
 };
 
 }  // namespace android::nl::protocols::generic
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.cpp b/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.cpp
index 134638e..b7b811b 100644
--- a/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/GenericMessageBase.cpp
@@ -22,16 +22,27 @@
         nlmsgtype_t msgtype, const std::string&& msgname,
         const std::initializer_list<GenericCommandNameMap::value_type> commandNames,
         const std::initializer_list<AttributeMap::value_type> attrTypes)
-    : MessageDefinition<genlmsghdr>(msgname, {{msgtype, {msgname, MessageGenre::UNKNOWN}}},
+    : MessageDefinition<genlmsghdr>(msgname, {{msgtype, {msgname, MessageGenre::Unknown}}},
                                     attrTypes),
       mCommandNames(commandNames) {}
 
 void GenericMessageBase::toStream(std::stringstream& ss, const genlmsghdr& data) const {
+    const auto commandNameIt = mCommandNames.find(data.cmd);
+    const auto commandName = (commandNameIt == mCommandNames.end())
+                                     ? std::nullopt
+                                     : std::optional<std::string>(commandNameIt->second);
+
+    if (commandName.has_value() && data.version == 1 && data.reserved == 0) {
+        // short version
+        ss << *commandName;
+        return;
+    }
+
     ss << "genlmsghdr{";
-    if (mCommandNames.count(data.cmd) == 0) {
+    if (commandName.has_value()) {
         ss << "cmd=" << unsigned(data.cmd);
     } else {
-        ss << "cmd=" << mCommandNames.find(data.cmd)->second;
+        ss << "cmd=" << *commandName;
     }
     ss << ", version=" << unsigned(data.version);
     if (data.reserved != 0) ss << ", reserved=" << data.reserved;
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/families/Nl80211.cpp b/automotive/can/1.0/default/libnl++/protocols/generic/families/Nl80211.cpp
new file mode 100644
index 0000000..23ec66f
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/families/Nl80211.cpp
@@ -0,0 +1,1009 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Nl80211.h"
+
+#include "../../structs.h"
+#include "common.h"
+
+#include <linux/nl80211.h>
+
+#include <iomanip>
+
+namespace android::nl::protocols::generic::families {
+
+/**
+ * Reduce verbosity of printed Information Elements.
+ */
+static constexpr bool kCompactIE = true;
+
+enum {
+    // broken compatibility in Aug 2020
+    NL80211_ATTR_CNTDWN_OFFS_BEACON = NL80211_ATTR_CSA_C_OFF_BEACON,
+    NL80211_ATTR_CNTDWN_OFFS_PRESP = NL80211_ATTR_CSA_C_OFF_PRESP,
+
+    // new fields not available in current Android
+    NL80211_ATTR_FILS_DISCOVERY = NL80211_ATTR_HE_6GHZ_CAPABILITY + 1,
+    NL80211_ATTR_UNSOL_BCAST_PROBE_RESP,
+    NL80211_ATTR_S1G_CAPABILITY,
+    NL80211_ATTR_S1G_CAPABILITY_MASK,
+
+    NL80211_FREQUENCY_ATTR_1MHZ = NL80211_FREQUENCY_ATTR_OFFSET + 1,
+    NL80211_FREQUENCY_ATTR_2MHZ,
+    NL80211_FREQUENCY_ATTR_4MHZ,
+    NL80211_FREQUENCY_ATTR_8MHZ,
+    NL80211_FREQUENCY_ATTR_16MHZ,
+};
+
+enum ieee80211_eid {
+    WLAN_EID_SSID = 0,
+};
+
+using DataType = AttributeDefinition::DataType;
+using Flags = AttributeDefinition::Flags;
+
+static void informationElementsToStream(std::stringstream& ss, const Buffer<nlattr> attr);
+static void nl80211_pattern_supportToStream(std::stringstream& ss, const Buffer<nlattr> attr);
+
+static const AttributeMap iftypes{
+        {NL80211_IFTYPE_UNSPECIFIED, {"UNSPECIFIED", DataType::Flag}},
+        {NL80211_IFTYPE_ADHOC, {"ADHOC", DataType::Flag}},
+        {NL80211_IFTYPE_STATION, {"STATION", DataType::Flag}},
+        {NL80211_IFTYPE_AP, {"AP", DataType::Flag}},
+        {NL80211_IFTYPE_AP_VLAN, {"AP_VLAN", DataType::Flag}},
+        {NL80211_IFTYPE_WDS, {"WDS", DataType::Flag}},
+        {NL80211_IFTYPE_MONITOR, {"MONITOR", DataType::Flag}},
+        {NL80211_IFTYPE_MESH_POINT, {"MESH_POINT", DataType::Flag}},
+        {NL80211_IFTYPE_P2P_CLIENT, {"P2P_CLIENT", DataType::Flag}},
+        {NL80211_IFTYPE_P2P_GO, {"P2P_GO", DataType::Flag}},
+        {NL80211_IFTYPE_P2P_DEVICE, {"P2P_DEVICE", DataType::Flag}},
+        {NL80211_IFTYPE_OCB, {"OCB", DataType::Flag}},
+        {NL80211_IFTYPE_NAN, {"NAN", DataType::Flag}},
+};
+
+// clang-format off
+Nl80211::Nl80211(nlmsgtype_t familyId) : GenericMessageBase(familyId, "nl80211", {
+    /* Script to generate the (initial) top-level list from linux/nl80211.h:
+     * sed -e 's/^  NL80211_CMD_\(.*\),$/    {NL80211_CMD_\1, "\1"},/g'
+     */
+    {NL80211_CMD_UNSPEC, "UNSPEC"},
+
+    {NL80211_CMD_GET_WIPHY, "GET_WIPHY"},
+    {NL80211_CMD_SET_WIPHY, "SET_WIPHY"},
+    {NL80211_CMD_NEW_WIPHY, "NEW_WIPHY"},
+    {NL80211_CMD_DEL_WIPHY, "DEL_WIPHY"},
+
+    {NL80211_CMD_GET_INTERFACE, "GET_INTERFACE"},
+    {NL80211_CMD_SET_INTERFACE, "SET_INTERFACE"},
+    {NL80211_CMD_NEW_INTERFACE, "NEW_INTERFACE"},
+    {NL80211_CMD_DEL_INTERFACE, "DEL_INTERFACE"},
+
+    {NL80211_CMD_GET_KEY, "GET_KEY"},
+    {NL80211_CMD_SET_KEY, "SET_KEY"},
+    {NL80211_CMD_NEW_KEY, "NEW_KEY"},
+    {NL80211_CMD_DEL_KEY, "DEL_KEY"},
+
+    {NL80211_CMD_GET_BEACON, "GET_BEACON"},
+    {NL80211_CMD_SET_BEACON, "SET_BEACON"},
+    {NL80211_CMD_START_AP, "START_AP"},
+    {NL80211_CMD_STOP_AP, "STOP_AP"},
+
+    {NL80211_CMD_GET_STATION, "GET_STATION"},
+    {NL80211_CMD_SET_STATION, "SET_STATION"},
+    {NL80211_CMD_NEW_STATION, "NEW_STATION"},
+    {NL80211_CMD_DEL_STATION, "DEL_STATION"},
+
+    {NL80211_CMD_GET_MPATH, "GET_MPATH"},
+    {NL80211_CMD_SET_MPATH, "SET_MPATH"},
+    {NL80211_CMD_NEW_MPATH, "NEW_MPATH"},
+    {NL80211_CMD_DEL_MPATH, "DEL_MPATH"},
+
+    {NL80211_CMD_SET_BSS, "SET_BSS"},
+
+    {NL80211_CMD_SET_REG, "SET_REG"},
+    {NL80211_CMD_REQ_SET_REG, "REQ_SET_REG"},
+
+    {NL80211_CMD_GET_MESH_CONFIG, "GET_MESH_CONFIG"},
+    {NL80211_CMD_SET_MESH_CONFIG, "SET_MESH_CONFIG"},
+
+    {NL80211_CMD_SET_MGMT_EXTRA_IE, "SET_MGMT_EXTRA_IE"},
+
+    {NL80211_CMD_GET_REG, "GET_REG"},
+
+    {NL80211_CMD_GET_SCAN, "GET_SCAN"},
+    {NL80211_CMD_TRIGGER_SCAN, "TRIGGER_SCAN"},
+    {NL80211_CMD_NEW_SCAN_RESULTS, "NEW_SCAN_RESULTS"},
+    {NL80211_CMD_SCAN_ABORTED, "SCAN_ABORTED"},
+
+    {NL80211_CMD_REG_CHANGE, "REG_CHANGE"},
+
+    {NL80211_CMD_AUTHENTICATE, "AUTHENTICATE"},
+    {NL80211_CMD_ASSOCIATE, "ASSOCIATE"},
+    {NL80211_CMD_DEAUTHENTICATE, "DEAUTHENTICATE"},
+    {NL80211_CMD_DISASSOCIATE, "DISASSOCIATE"},
+
+    {NL80211_CMD_MICHAEL_MIC_FAILURE, "MICHAEL_MIC_FAILURE"},
+
+    {NL80211_CMD_REG_BEACON_HINT, "REG_BEACON_HINT"},
+
+    {NL80211_CMD_JOIN_IBSS, "JOIN_IBSS"},
+    {NL80211_CMD_LEAVE_IBSS, "LEAVE_IBSS"},
+
+    {NL80211_CMD_TESTMODE, "TESTMODE"},
+
+    {NL80211_CMD_CONNECT, "CONNECT"},
+    {NL80211_CMD_ROAM, "ROAM"},
+    {NL80211_CMD_DISCONNECT, "DISCONNECT"},
+
+    {NL80211_CMD_SET_WIPHY_NETNS, "SET_WIPHY_NETNS"},
+
+    {NL80211_CMD_GET_SURVEY, "GET_SURVEY"},
+    {NL80211_CMD_NEW_SURVEY_RESULTS, "NEW_SURVEY_RESULTS"},
+
+    {NL80211_CMD_SET_PMKSA, "SET_PMKSA"},
+    {NL80211_CMD_DEL_PMKSA, "DEL_PMKSA"},
+    {NL80211_CMD_FLUSH_PMKSA, "FLUSH_PMKSA"},
+
+    {NL80211_CMD_REMAIN_ON_CHANNEL, "REMAIN_ON_CHANNEL"},
+    {NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, "CANCEL_REMAIN_ON_CHANNEL"},
+
+    {NL80211_CMD_SET_TX_BITRATE_MASK, "SET_TX_BITRATE_MASK"},
+
+    {NL80211_CMD_REGISTER_FRAME, "REGISTER_FRAME"},
+    {NL80211_CMD_FRAME, "FRAME"},
+    {NL80211_CMD_FRAME_TX_STATUS, "FRAME_TX_STATUS"},
+
+    {NL80211_CMD_SET_POWER_SAVE, "SET_POWER_SAVE"},
+    {NL80211_CMD_GET_POWER_SAVE, "GET_POWER_SAVE"},
+
+    {NL80211_CMD_SET_CQM, "SET_CQM"},
+    {NL80211_CMD_NOTIFY_CQM, "NOTIFY_CQM"},
+
+    {NL80211_CMD_SET_CHANNEL, "SET_CHANNEL"},
+    {NL80211_CMD_SET_WDS_PEER, "SET_WDS_PEER"},
+
+    {NL80211_CMD_FRAME_WAIT_CANCEL, "FRAME_WAIT_CANCEL"},
+
+    {NL80211_CMD_JOIN_MESH, "JOIN_MESH"},
+    {NL80211_CMD_LEAVE_MESH, "LEAVE_MESH"},
+
+    {NL80211_CMD_UNPROT_DEAUTHENTICATE, "UNPROT_DEAUTHENTICATE"},
+    {NL80211_CMD_UNPROT_DISASSOCIATE, "UNPROT_DISASSOCIATE"},
+
+    {NL80211_CMD_NEW_PEER_CANDIDATE, "NEW_PEER_CANDIDATE"},
+
+    {NL80211_CMD_GET_WOWLAN, "GET_WOWLAN"},
+    {NL80211_CMD_SET_WOWLAN, "SET_WOWLAN"},
+
+    {NL80211_CMD_START_SCHED_SCAN, "START_SCHED_SCAN"},
+    {NL80211_CMD_STOP_SCHED_SCAN, "STOP_SCHED_SCAN"},
+    {NL80211_CMD_SCHED_SCAN_RESULTS, "SCHED_SCAN_RESULTS"},
+    {NL80211_CMD_SCHED_SCAN_STOPPED, "SCHED_SCAN_STOPPED"},
+
+    {NL80211_CMD_SET_REKEY_OFFLOAD, "SET_REKEY_OFFLOAD"},
+
+    {NL80211_CMD_PMKSA_CANDIDATE, "PMKSA_CANDIDATE"},
+
+    {NL80211_CMD_TDLS_OPER, "TDLS_OPER"},
+    {NL80211_CMD_TDLS_MGMT, "TDLS_MGMT"},
+
+    {NL80211_CMD_UNEXPECTED_FRAME, "UNEXPECTED_FRAME"},
+
+    {NL80211_CMD_PROBE_CLIENT, "PROBE_CLIENT"},
+
+    {NL80211_CMD_REGISTER_BEACONS, "REGISTER_BEACONS"},
+
+    {NL80211_CMD_UNEXPECTED_4ADDR_FRAME, "UNEXPECTED_4ADDR_FRAME"},
+
+    {NL80211_CMD_SET_NOACK_MAP, "SET_NOACK_MAP"},
+
+    {NL80211_CMD_CH_SWITCH_NOTIFY, "CH_SWITCH_NOTIFY"},
+
+    {NL80211_CMD_START_P2P_DEVICE, "START_P2P_DEVICE"},
+    {NL80211_CMD_STOP_P2P_DEVICE, "STOP_P2P_DEVICE"},
+
+    {NL80211_CMD_CONN_FAILED, "CONN_FAILED"},
+
+    {NL80211_CMD_SET_MCAST_RATE, "SET_MCAST_RATE"},
+
+    {NL80211_CMD_SET_MAC_ACL, "SET_MAC_ACL"},
+
+    {NL80211_CMD_RADAR_DETECT, "RADAR_DETECT"},
+
+    {NL80211_CMD_GET_PROTOCOL_FEATURES, "GET_PROTOCOL_FEATURES"},
+
+    {NL80211_CMD_UPDATE_FT_IES, "UPDATE_FT_IES"},
+    {NL80211_CMD_FT_EVENT, "FT_EVENT"},
+
+    {NL80211_CMD_CRIT_PROTOCOL_START, "CRIT_PROTOCOL_START"},
+    {NL80211_CMD_CRIT_PROTOCOL_STOP, "CRIT_PROTOCOL_STOP"},
+
+    {NL80211_CMD_GET_COALESCE, "GET_COALESCE"},
+    {NL80211_CMD_SET_COALESCE, "SET_COALESCE"},
+
+    {NL80211_CMD_CHANNEL_SWITCH, "CHANNEL_SWITCH"},
+
+    {NL80211_CMD_VENDOR, "VENDOR"},
+
+    {NL80211_CMD_SET_QOS_MAP, "SET_QOS_MAP"},
+
+    {NL80211_CMD_ADD_TX_TS, "ADD_TX_TS"},
+    {NL80211_CMD_DEL_TX_TS, "DEL_TX_TS"},
+
+    {NL80211_CMD_GET_MPP, "GET_MPP"},
+
+    {NL80211_CMD_JOIN_OCB, "JOIN_OCB"},
+    {NL80211_CMD_LEAVE_OCB, "LEAVE_OCB"},
+
+    {NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, "CH_SWITCH_STARTED_NOTIFY"},
+
+    {NL80211_CMD_TDLS_CHANNEL_SWITCH, "TDLS_CHANNEL_SWITCH"},
+    {NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH, "TDLS_CANCEL_CHANNEL_SWITCH"},
+
+    {NL80211_CMD_WIPHY_REG_CHANGE, "WIPHY_REG_CHANGE"},
+
+    {NL80211_CMD_ABORT_SCAN, "ABORT_SCAN"},
+
+    {NL80211_CMD_START_NAN, "START_NAN"},
+    {NL80211_CMD_STOP_NAN, "STOP_NAN"},
+    {NL80211_CMD_ADD_NAN_FUNCTION, "ADD_NAN_FUNCTION"},
+    {NL80211_CMD_DEL_NAN_FUNCTION, "DEL_NAN_FUNCTION"},
+    {NL80211_CMD_CHANGE_NAN_CONFIG, "CHANGE_NAN_CONFIG"},
+    {NL80211_CMD_NAN_MATCH, "NAN_MATCH"},
+
+    {NL80211_CMD_SET_MULTICAST_TO_UNICAST, "SET_MULTICAST_TO_UNICAST"},
+
+    {NL80211_CMD_UPDATE_CONNECT_PARAMS, "UPDATE_CONNECT_PARAMS"},
+
+    {NL80211_CMD_SET_PMK, "SET_PMK"},
+    {NL80211_CMD_DEL_PMK, "DEL_PMK"},
+
+    {NL80211_CMD_PORT_AUTHORIZED, "PORT_AUTHORIZED"},
+
+    {NL80211_CMD_RELOAD_REGDB, "RELOAD_REGDB"},
+
+    {NL80211_CMD_EXTERNAL_AUTH, "EXTERNAL_AUTH"},
+
+    {NL80211_CMD_STA_OPMODE_CHANGED, "STA_OPMODE_CHANGED"},
+
+    {NL80211_CMD_CONTROL_PORT_FRAME, "CONTROL_PORT_FRAME"},
+
+    {NL80211_CMD_GET_FTM_RESPONDER_STATS, "GET_FTM_RESPONDER_STATS"},
+
+    {NL80211_CMD_PEER_MEASUREMENT_START, "PEER_MEASUREMENT_START"},
+    {NL80211_CMD_PEER_MEASUREMENT_RESULT, "PEER_MEASUREMENT_RESULT"},
+    {NL80211_CMD_PEER_MEASUREMENT_COMPLETE, "PEER_MEASUREMENT_COMPLETE"},
+
+    {NL80211_CMD_NOTIFY_RADAR, "NOTIFY_RADAR"},
+
+    {NL80211_CMD_UPDATE_OWE_INFO, "UPDATE_OWE_INFO"},
+
+    {NL80211_CMD_PROBE_MESH_LINK, "PROBE_MESH_LINK"},
+
+    {NL80211_CMD_SET_TID_CONFIG, "SET_TID_CONFIG"},
+
+    {NL80211_CMD_UNPROT_BEACON, "UNPROT_BEACON"},
+
+    {NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS, "CONTROL_PORT_FRAME_TX_STATUS"},
+}, {
+    /* Script to generate the (initial) top-level list from linux/nl80211.h:
+     * sed -e 's/^\tNL80211_ATTR_\(.*\),$/    {NL80211_ATTR_\1, {"\1"}},/g'
+     */
+    {NL80211_ATTR_UNSPEC, {"UNSPEC"}},
+
+    {NL80211_ATTR_WIPHY, {"WIPHY", DataType::Uint}},
+    {NL80211_ATTR_WIPHY_NAME, {"WIPHY_NAME", DataType::StringNul}},
+
+    {NL80211_ATTR_IFINDEX, {"IFINDEX", DataType::Uint}},
+    {NL80211_ATTR_IFNAME, {"IFNAME", DataType::StringNul}},
+    {NL80211_ATTR_IFTYPE, {"IFTYPE", DataType::Uint}},
+
+    {NL80211_ATTR_MAC, {"MAC", DataType::Raw}},
+
+    {NL80211_ATTR_KEY_DATA, {"KEY_DATA"}},
+    {NL80211_ATTR_KEY_IDX, {"KEY_IDX"}},
+    {NL80211_ATTR_KEY_CIPHER, {"KEY_CIPHER"}},
+    {NL80211_ATTR_KEY_SEQ, {"KEY_SEQ"}},
+    {NL80211_ATTR_KEY_DEFAULT, {"KEY_DEFAULT"}},
+
+    {NL80211_ATTR_BEACON_INTERVAL, {"BEACON_INTERVAL"}},
+    {NL80211_ATTR_DTIM_PERIOD, {"DTIM_PERIOD"}},
+    {NL80211_ATTR_BEACON_HEAD, {"BEACON_HEAD"}},
+    {NL80211_ATTR_BEACON_TAIL, {"BEACON_TAIL"}},
+
+    {NL80211_ATTR_STA_AID, {"STA_AID"}},
+    {NL80211_ATTR_STA_FLAGS, {"STA_FLAGS"}},
+    {NL80211_ATTR_STA_LISTEN_INTERVAL, {"STA_LISTEN_INTERVAL"}},
+    {NL80211_ATTR_STA_SUPPORTED_RATES, {"STA_SUPPORTED_RATES"}},
+    {NL80211_ATTR_STA_VLAN, {"STA_VLAN"}},
+    {NL80211_ATTR_STA_INFO, {"STA_INFO"}},
+
+    {NL80211_ATTR_WIPHY_BANDS, {"WIPHY_BANDS", DataType::Nested, AttributeMap{
+        {std::nullopt, {"BAND", DataType::Nested, AttributeMap{
+            {NL80211_BAND_ATTR_FREQS, {"FREQS", DataType::Nested, AttributeMap{
+                {std::nullopt, {"FQ", DataType::Nested, AttributeMap{
+                    {NL80211_FREQUENCY_ATTR_FREQ, {"FREQ", DataType::Uint}},
+                    {NL80211_FREQUENCY_ATTR_DISABLED, {"DISABLED", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_NO_IR, {"NO_IR", DataType::Flag}},
+                    {__NL80211_FREQUENCY_ATTR_NO_IBSS, {"_NO_IBSS", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_RADAR, {"RADAR", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_MAX_TX_POWER, {"MAX_TX_POWER", DataType::Uint}},
+                    {NL80211_FREQUENCY_ATTR_DFS_STATE, {"DFS_STATE", DataType::Uint}},
+                    {NL80211_FREQUENCY_ATTR_DFS_TIME, {"DFS_TIME", DataType::Uint}},
+                    {NL80211_FREQUENCY_ATTR_NO_HT40_MINUS, {"NO_HT40_MINUS", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_NO_HT40_PLUS, {"NO_HT40_PLUS", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_NO_80MHZ, {"NO_80MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_NO_160MHZ, {"NO_160MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, {"DFS_CAC_TIME", DataType::Uint}},
+                    {NL80211_FREQUENCY_ATTR_INDOOR_ONLY, {"INDOOR_ONLY", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_IR_CONCURRENT, {"IR_CONCURRENT", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_NO_20MHZ, {"NO_20MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_NO_10MHZ, {"NO_10MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_WMM, {"WMM"}},
+                    {NL80211_FREQUENCY_ATTR_NO_HE, {"NO_HE", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_OFFSET, {"OFFSET", DataType::Uint}},
+                    {NL80211_FREQUENCY_ATTR_1MHZ, {"1MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_2MHZ, {"2MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_4MHZ, {"4MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_8MHZ, {"8MHZ", DataType::Flag}},
+                    {NL80211_FREQUENCY_ATTR_16MHZ, {"16MHZ", DataType::Flag}},
+                }}},
+            }, Flags::Verbose}},
+            {NL80211_BAND_ATTR_RATES, {"RATES", DataType::Nested, AttributeMap{
+                {std::nullopt, {"RATE", DataType::Nested, AttributeMap{
+                    {NL80211_BITRATE_ATTR_RATE, {"RATE", DataType::Uint}},
+                    {NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE,
+                            {"2GHZ_SHORTPREAMBLE", DataType::Flag}},
+                }}},
+            }}},
+
+            {NL80211_BAND_ATTR_HT_MCS_SET, {"HT_MCS_SET"}},  // struct ieee80211_mcs_info
+            {NL80211_BAND_ATTR_HT_CAPA, {"HT_CAPA", DataType::Uint}},
+            {NL80211_BAND_ATTR_HT_AMPDU_FACTOR, {"HT_AMPDU_FACTOR", DataType::Uint}},
+            {NL80211_BAND_ATTR_HT_AMPDU_DENSITY, {"HT_AMPDU_DENSITY", DataType::Uint}},
+
+            {NL80211_BAND_ATTR_VHT_MCS_SET, {"VHT_MCS_SET"}},  // struct ieee80211_vht_mcs_info
+            {NL80211_BAND_ATTR_VHT_CAPA, {"VHT_CAPA", DataType::Uint}},
+            {NL80211_BAND_ATTR_IFTYPE_DATA, {"IFTYPE_DATA"}},
+
+            {NL80211_BAND_ATTR_EDMG_CHANNELS, {"EDMG_CHANNELS"}},
+            {NL80211_BAND_ATTR_EDMG_BW_CONFIG, {"EDMG_BW_CONFIG"}},
+        }}},
+    }, Flags::Verbose}},
+
+    {NL80211_ATTR_MNTR_FLAGS, {"MNTR_FLAGS"}},
+
+    {NL80211_ATTR_MESH_ID, {"MESH_ID"}},
+    {NL80211_ATTR_STA_PLINK_ACTION, {"STA_PLINK_ACTION"}},
+    {NL80211_ATTR_MPATH_NEXT_HOP, {"MPATH_NEXT_HOP"}},
+    {NL80211_ATTR_MPATH_INFO, {"MPATH_INFO"}},
+
+    {NL80211_ATTR_BSS_CTS_PROT, {"BSS_CTS_PROT"}},
+    {NL80211_ATTR_BSS_SHORT_PREAMBLE, {"BSS_SHORT_PREAMBLE"}},
+    {NL80211_ATTR_BSS_SHORT_SLOT_TIME, {"BSS_SHORT_SLOT_TIME"}},
+
+    {NL80211_ATTR_HT_CAPABILITY, {"HT_CAPABILITY"}},
+
+    {NL80211_ATTR_SUPPORTED_IFTYPES, {"SUPPORTED_IFTYPES", DataType::Nested, iftypes}},
+
+    {NL80211_ATTR_REG_ALPHA2, {"REG_ALPHA2"}},
+    {NL80211_ATTR_REG_RULES, {"REG_RULES"}},
+
+    {NL80211_ATTR_MESH_CONFIG, {"MESH_CONFIG"}},
+
+    {NL80211_ATTR_BSS_BASIC_RATES, {"BSS_BASIC_RATES"}},
+
+    {NL80211_ATTR_WIPHY_TXQ_PARAMS, {"WIPHY_TXQ_PARAMS"}},
+    {NL80211_ATTR_WIPHY_FREQ, {"WIPHY_FREQ"}},
+    {NL80211_ATTR_WIPHY_CHANNEL_TYPE, {"WIPHY_CHANNEL_TYPE"}},
+
+    {NL80211_ATTR_KEY_DEFAULT_MGMT, {"KEY_DEFAULT_MGMT"}},
+
+    {NL80211_ATTR_MGMT_SUBTYPE, {"MGMT_SUBTYPE"}},
+    {NL80211_ATTR_IE, {"IE"}},
+
+    {NL80211_ATTR_MAX_NUM_SCAN_SSIDS, {"MAX_NUM_SCAN_SSIDS", DataType::Uint}},
+
+    {NL80211_ATTR_SCAN_FREQUENCIES, {"SCAN_FREQUENCIES", DataType::Nested, AttributeMap{
+        {std::nullopt, {"FQ", DataType::Uint}},
+    }, Flags::Verbose}},
+    {NL80211_ATTR_SCAN_SSIDS, {"SCAN_SSIDS", DataType::Nested, AttributeMap{
+        {std::nullopt, {"SSID", DataType::String}},
+    }}},
+    {NL80211_ATTR_GENERATION, {"GENERATION", DataType::Uint}},
+    {NL80211_ATTR_BSS, {"BSS", DataType::Nested, AttributeMap{
+        {NL80211_BSS_BSSID, {"BSSID", DataType::Raw}},
+        {NL80211_BSS_FREQUENCY, {"FREQUENCY", DataType::Uint}},
+        {NL80211_BSS_TSF, {"TSF", DataType::Uint}},
+        {NL80211_BSS_BEACON_INTERVAL, {"BEACON_INTERVAL", DataType::Uint}},
+        {NL80211_BSS_CAPABILITY, {"CAPABILITY", DataType::Uint}},
+        {NL80211_BSS_INFORMATION_ELEMENTS, {"INFORMATION_ELEMENTS",
+                DataType::Struct, informationElementsToStream}},
+        {NL80211_BSS_SIGNAL_MBM, {"SIGNAL_MBM", DataType::Uint}},
+        {NL80211_BSS_SIGNAL_UNSPEC, {"SIGNAL_UNSPEC", DataType::Uint}},
+        {NL80211_BSS_STATUS, {"STATUS", DataType::Uint}},  // enum nl80211_bss_status
+        {NL80211_BSS_SEEN_MS_AGO, {"SEEN_MS_AGO", DataType::Uint}},
+        {NL80211_BSS_BEACON_IES, {"BEACON_IES", DataType::Struct, informationElementsToStream}},
+        {NL80211_BSS_CHAN_WIDTH, {"CHAN_WIDTH", DataType::Uint}},
+        {NL80211_BSS_BEACON_TSF, {"BEACON_TSF", DataType::Uint}},
+        {NL80211_BSS_PRESP_DATA, {"PRESP_DATA", DataType::Flag}},
+        {NL80211_BSS_LAST_SEEN_BOOTTIME, {"LAST_SEEN_BOOTTIME", DataType::Uint}},
+        {NL80211_BSS_PAD, {"PAD"}},
+        {NL80211_BSS_PARENT_TSF, {"PARENT_TSF"}},
+        {NL80211_BSS_PARENT_BSSID, {"PARENT_BSSID"}},
+        {NL80211_BSS_CHAIN_SIGNAL, {"CHAIN_SIGNAL", DataType::Nested, AttributeMap{
+            {std::nullopt, {"SIG", DataType::Uint}},
+        }}},
+        {NL80211_BSS_FREQUENCY_OFFSET, {"FREQUENCY_OFFSET"}},
+    }}},
+
+    {NL80211_ATTR_REG_INITIATOR, {"REG_INITIATOR"}},
+    {NL80211_ATTR_REG_TYPE, {"REG_TYPE"}},
+
+    {NL80211_ATTR_SUPPORTED_COMMANDS, {"SUPPORTED_COMMANDS", DataType::Nested,AttributeMap{
+        {std::nullopt, {"CMD", DataType::Uint}}, // enum nl80211_commands
+    }}},
+
+    {NL80211_ATTR_FRAME, {"FRAME"}},
+    {NL80211_ATTR_SSID, {"SSID"}},
+    {NL80211_ATTR_AUTH_TYPE, {"AUTH_TYPE"}},
+    {NL80211_ATTR_REASON_CODE, {"REASON_CODE"}},
+
+    {NL80211_ATTR_KEY_TYPE, {"KEY_TYPE"}},
+
+    {NL80211_ATTR_MAX_SCAN_IE_LEN, {"MAX_SCAN_IE_LEN", DataType::Uint}},
+    {NL80211_ATTR_CIPHER_SUITES, {"CIPHER_SUITES", DataType::Struct, arrayToStream<int32_t>}},
+
+    {NL80211_ATTR_FREQ_BEFORE, {"FREQ_BEFORE"}},
+    {NL80211_ATTR_FREQ_AFTER, {"FREQ_AFTER"}},
+
+    {NL80211_ATTR_FREQ_FIXED, {"FREQ_FIXED"}},
+
+    {NL80211_ATTR_WIPHY_RETRY_SHORT, {"WIPHY_RETRY_SHORT", DataType::Uint}},
+    {NL80211_ATTR_WIPHY_RETRY_LONG, {"WIPHY_RETRY_LONG", DataType::Uint}},
+    {NL80211_ATTR_WIPHY_FRAG_THRESHOLD, {"WIPHY_FRAG_THRESHOLD", DataType::Uint}},
+    {NL80211_ATTR_WIPHY_RTS_THRESHOLD, {"WIPHY_RTS_THRESHOLD", DataType::Uint}},
+
+    {NL80211_ATTR_TIMED_OUT, {"TIMED_OUT"}},
+
+    {NL80211_ATTR_USE_MFP, {"USE_MFP"}},
+
+    {NL80211_ATTR_STA_FLAGS2, {"STA_FLAGS2"}},
+
+    {NL80211_ATTR_CONTROL_PORT, {"CONTROL_PORT"}},
+
+    {NL80211_ATTR_TESTDATA, {"TESTDATA"}},
+
+    {NL80211_ATTR_PRIVACY, {"PRIVACY"}},
+
+    {NL80211_ATTR_DISCONNECTED_BY_AP, {"DISCONNECTED_BY_AP"}},
+    {NL80211_ATTR_STATUS_CODE, {"STATUS_CODE"}},
+
+    {NL80211_ATTR_CIPHER_SUITES_PAIRWISE, {"CIPHER_SUITES_PAIRWISE"}},
+    {NL80211_ATTR_CIPHER_SUITE_GROUP, {"CIPHER_SUITE_GROUP"}},
+    {NL80211_ATTR_WPA_VERSIONS, {"WPA_VERSIONS"}},
+    {NL80211_ATTR_AKM_SUITES, {"AKM_SUITES"}},
+
+    {NL80211_ATTR_REQ_IE, {"REQ_IE"}},
+    {NL80211_ATTR_RESP_IE, {"RESP_IE"}},
+
+    {NL80211_ATTR_PREV_BSSID, {"PREV_BSSID"}},
+
+    {NL80211_ATTR_KEY, {"KEY"}},
+    {NL80211_ATTR_KEYS, {"KEYS"}},
+
+    {NL80211_ATTR_PID, {"PID"}},
+
+    {NL80211_ATTR_4ADDR, {"4ADDR"}},
+
+    {NL80211_ATTR_SURVEY_INFO, {"SURVEY_INFO"}},
+
+    {NL80211_ATTR_PMKID, {"PMKID"}},
+    {NL80211_ATTR_MAX_NUM_PMKIDS, {"MAX_NUM_PMKIDS", DataType::Uint}},
+
+    {NL80211_ATTR_DURATION, {"DURATION"}},
+
+    {NL80211_ATTR_COOKIE, {"COOKIE"}},
+
+    {NL80211_ATTR_WIPHY_COVERAGE_CLASS, {"WIPHY_COVERAGE_CLASS", DataType::Uint}},
+
+    {NL80211_ATTR_TX_RATES, {"TX_RATES"}},
+
+    {NL80211_ATTR_FRAME_MATCH, {"FRAME_MATCH"}},
+
+    {NL80211_ATTR_ACK, {"ACK"}},
+
+    {NL80211_ATTR_PS_STATE, {"PS_STATE"}},
+
+    {NL80211_ATTR_CQM, {"CQM"}},
+
+    {NL80211_ATTR_LOCAL_STATE_CHANGE, {"LOCAL_STATE_CHANGE"}},
+
+    {NL80211_ATTR_AP_ISOLATE, {"AP_ISOLATE"}},
+
+    {NL80211_ATTR_WIPHY_TX_POWER_SETTING, {"WIPHY_TX_POWER_SETTING"}},
+    {NL80211_ATTR_WIPHY_TX_POWER_LEVEL, {"WIPHY_TX_POWER_LEVEL"}},
+
+    {NL80211_ATTR_TX_FRAME_TYPES, {"TX_FRAME_TYPES", DataType::Nested, AttributeMap{
+        {std::nullopt, {"TFT", DataType::Nested, AttributeMap{
+            {NL80211_ATTR_FRAME_TYPE, {"FRAME_TYPE", DataType::Uint}},
+        }}},
+    }, Flags::Verbose}},
+    {NL80211_ATTR_RX_FRAME_TYPES, {"RX_FRAME_TYPES", DataType::Nested, AttributeMap{
+        {std::nullopt, {"RFT", DataType::Nested, AttributeMap{
+            {NL80211_ATTR_FRAME_TYPE, {"FRAME_TYPE", DataType::Uint}},
+        }}},
+    }, Flags::Verbose}},
+
+    {NL80211_ATTR_FRAME_TYPE, {"FRAME_TYPE", DataType::Uint}},
+
+    {NL80211_ATTR_CONTROL_PORT_ETHERTYPE, {"CONTROL_PORT_ETHERTYPE"}},
+    {NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, {"CONTROL_PORT_NO_ENCRYPT"}},
+
+    {NL80211_ATTR_SUPPORT_IBSS_RSN, {"SUPPORT_IBSS_RSN"}},
+
+    {NL80211_ATTR_WIPHY_ANTENNA_TX, {"WIPHY_ANTENNA_TX"}},
+    {NL80211_ATTR_WIPHY_ANTENNA_RX, {"WIPHY_ANTENNA_RX"}},
+
+    {NL80211_ATTR_MCAST_RATE, {"MCAST_RATE"}},
+
+    {NL80211_ATTR_OFFCHANNEL_TX_OK, {"OFFCHANNEL_TX_OK", DataType::Flag}},
+
+    {NL80211_ATTR_BSS_HT_OPMODE, {"BSS_HT_OPMODE"}},
+
+    {NL80211_ATTR_KEY_DEFAULT_TYPES, {"KEY_DEFAULT_TYPES"}},
+
+    {NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
+            {"MAX_REMAIN_ON_CHANNEL_DURATION", DataType::Uint}},
+
+    {NL80211_ATTR_MESH_SETUP, {"MESH_SETUP"}},
+
+    {NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, {"WIPHY_ANTENNA_AVAIL_TX", DataType::Uint}},
+    {NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, {"WIPHY_ANTENNA_AVAIL_RX", DataType::Uint}},
+
+    {NL80211_ATTR_SUPPORT_MESH_AUTH, {"SUPPORT_MESH_AUTH"}},
+    {NL80211_ATTR_STA_PLINK_STATE, {"STA_PLINK_STATE"}},
+
+    {NL80211_ATTR_WOWLAN_TRIGGERS, {"WOWLAN_TRIGGERS"}},
+    {NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED,
+            {"WOWLAN_TRIGGERS_SUPPORTED", DataType::Nested, AttributeMap{
+        {NL80211_WOWLAN_TRIG_ANY, {"ANY", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_DISCONNECT, {"DISCONNECT", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_MAGIC_PKT, {"MAGIC_PKT", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_PKT_PATTERN,
+                {"PKT_PATTERN", DataType::Struct, nl80211_pattern_supportToStream}},
+        {NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED, {"GTK_REKEY_SUPPORTED", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE, {"GTK_REKEY_FAILURE", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, {"EAP_IDENT_REQUEST", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, {"4WAY_HANDSHAKE", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_RFKILL_RELEASE, {"RFKILL_RELEASE", DataType::Flag}},
+        {NL80211_WOWLAN_TRIG_TCP_CONNECTION, {"TCP_CONNECTION", DataType::Nested, AttributeMap{
+            {NL80211_WOWLAN_TCP_SRC_IPV4, {"SRC_IPV4"}},
+            {NL80211_WOWLAN_TCP_DST_IPV4, {"DST_IPV4"}},
+            {NL80211_WOWLAN_TCP_DST_MAC, {"DST_MAC"}},
+            {NL80211_WOWLAN_TCP_SRC_PORT, {"SRC_PORT", DataType::Uint}},
+            {NL80211_WOWLAN_TCP_DST_PORT, {"DST_PORT", DataType::Uint}},
+            {NL80211_WOWLAN_TCP_DATA_PAYLOAD, {"DATA_PAYLOAD"}},
+            {NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, {"DATA_PAYLOAD_SEQ"}},
+            {NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, {"DATA_PAYLOAD_TOKEN"}},
+            {NL80211_WOWLAN_TCP_DATA_INTERVAL, {"DATA_INTERVAL", DataType::Uint}},
+            {NL80211_WOWLAN_TCP_WAKE_PAYLOAD, {"WAKE_PAYLOAD"}},
+            {NL80211_WOWLAN_TCP_WAKE_MASK, {"WAKE_MASK"}},
+        }}},
+        {NL80211_WOWLAN_TRIG_NET_DETECT, {"NET_DETECT", DataType::Uint}},
+
+        /* Not in WOWLAN_TRIGGERS_SUPPORTED:
+         * - NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211
+         * - NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN
+         * - NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023
+         * - NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN
+         * - NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH
+         * - NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST
+         * - NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS
+         * - NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS
+         */
+    }}},
+
+    {NL80211_ATTR_SCHED_SCAN_INTERVAL, {"SCHED_SCAN_INTERVAL"}},
+
+    {NL80211_ATTR_INTERFACE_COMBINATIONS, {"INTERFACE_COMBINATIONS", DataType::Nested, AttributeMap{
+        {std::nullopt, {"IC", DataType::Nested, AttributeMap{
+            {NL80211_IFACE_COMB_UNSPEC, {"UNSPEC"}},
+            {NL80211_IFACE_COMB_LIMITS, {"LIMITS", DataType::Nested, AttributeMap{
+                {std::nullopt, {"LT", DataType::Nested, AttributeMap{
+                    {NL80211_IFACE_LIMIT_UNSPEC, {"UNSPEC"}},
+                    {NL80211_IFACE_LIMIT_MAX, {"MAX", DataType::Uint}},
+                    {NL80211_IFACE_LIMIT_TYPES, {"TYPES", DataType::Nested, iftypes}},
+                }}},
+            }}},
+            {NL80211_IFACE_COMB_MAXNUM, {"MAXNUM", DataType::Uint}},
+            {NL80211_IFACE_COMB_STA_AP_BI_MATCH, {"STA_AP_BI_MATCH", DataType::Flag}},
+            {NL80211_IFACE_COMB_NUM_CHANNELS, {"NUM_CHANNELS", DataType::Uint}},
+            {NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, {"RADAR_DETECT_WIDTHS", DataType::Uint}},
+            {NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, {"RADAR_DETECT_REGIONS", DataType::Uint}},
+            {NL80211_IFACE_COMB_BI_MIN_GCD, {"BI_MIN_GCD"}},
+        }}},
+    }, Flags::Verbose}},
+    {NL80211_ATTR_SOFTWARE_IFTYPES, {"SOFTWARE_IFTYPES", DataType::Nested, iftypes}},
+
+    {NL80211_ATTR_REKEY_DATA, {"REKEY_DATA"}},
+
+    {NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, {"MAX_NUM_SCHED_SCAN_SSIDS", DataType::Uint}},
+    {NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, {"MAX_SCHED_SCAN_IE_LEN", DataType::Uint}},
+
+    {NL80211_ATTR_SCAN_SUPP_RATES, {"SCAN_SUPP_RATES"}},
+
+    {NL80211_ATTR_HIDDEN_SSID, {"HIDDEN_SSID"}},
+
+    {NL80211_ATTR_IE_PROBE_RESP, {"IE_PROBE_RESP"}},
+    {NL80211_ATTR_IE_ASSOC_RESP, {"IE_ASSOC_RESP"}},
+
+    {NL80211_ATTR_STA_WME, {"STA_WME"}},
+    {NL80211_ATTR_SUPPORT_AP_UAPSD, {"SUPPORT_AP_UAPSD"}},
+
+    {NL80211_ATTR_ROAM_SUPPORT, {"ROAM_SUPPORT", DataType::Flag}},
+
+    {NL80211_ATTR_SCHED_SCAN_MATCH, {"SCHED_SCAN_MATCH"}},
+    {NL80211_ATTR_MAX_MATCH_SETS, {"MAX_MATCH_SETS", DataType::Uint}},
+
+    {NL80211_ATTR_PMKSA_CANDIDATE, {"PMKSA_CANDIDATE"}},
+
+    {NL80211_ATTR_TX_NO_CCK_RATE, {"TX_NO_CCK_RATE"}},
+
+    {NL80211_ATTR_TDLS_ACTION, {"TDLS_ACTION"}},
+    {NL80211_ATTR_TDLS_DIALOG_TOKEN, {"TDLS_DIALOG_TOKEN"}},
+    {NL80211_ATTR_TDLS_OPERATION, {"TDLS_OPERATION"}},
+    {NL80211_ATTR_TDLS_SUPPORT, {"TDLS_SUPPORT", DataType::Flag}},
+    {NL80211_ATTR_TDLS_EXTERNAL_SETUP, {"TDLS_EXTERNAL_SETUP", DataType::Flag}},
+
+    {NL80211_ATTR_DEVICE_AP_SME, {"DEVICE_AP_SME", DataType::Uint}},
+
+    {NL80211_ATTR_DONT_WAIT_FOR_ACK, {"DONT_WAIT_FOR_ACK"}},
+
+    {NL80211_ATTR_FEATURE_FLAGS, {"FEATURE_FLAGS", DataType::Uint}},
+
+    {NL80211_ATTR_PROBE_RESP_OFFLOAD, {"PROBE_RESP_OFFLOAD", DataType::Uint}},
+
+    {NL80211_ATTR_PROBE_RESP, {"PROBE_RESP"}},
+
+    {NL80211_ATTR_DFS_REGION, {"DFS_REGION"}},
+
+    {NL80211_ATTR_DISABLE_HT, {"DISABLE_HT"}},
+    {NL80211_ATTR_HT_CAPABILITY_MASK, {"HT_CAPABILITY_MASK"}},
+
+    {NL80211_ATTR_NOACK_MAP, {"NOACK_MAP"}},
+
+    {NL80211_ATTR_INACTIVITY_TIMEOUT, {"INACTIVITY_TIMEOUT"}},
+
+    {NL80211_ATTR_RX_SIGNAL_DBM, {"RX_SIGNAL_DBM"}},
+
+    {NL80211_ATTR_BG_SCAN_PERIOD, {"BG_SCAN_PERIOD"}},
+
+    {NL80211_ATTR_WDEV, {"WDEV", DataType::Uint}},
+
+    {NL80211_ATTR_USER_REG_HINT_TYPE, {"USER_REG_HINT_TYPE"}},
+
+    {NL80211_ATTR_CONN_FAILED_REASON, {"CONN_FAILED_REASON"}},
+
+    {NL80211_ATTR_AUTH_DATA, {"AUTH_DATA"}},
+
+    {NL80211_ATTR_VHT_CAPABILITY, {"VHT_CAPABILITY"}},
+
+    {NL80211_ATTR_SCAN_FLAGS, {"SCAN_FLAGS", DataType::Uint}},
+
+    {NL80211_ATTR_CHANNEL_WIDTH, {"CHANNEL_WIDTH"}},
+    {NL80211_ATTR_CENTER_FREQ1, {"CENTER_FREQ1"}},
+    {NL80211_ATTR_CENTER_FREQ2, {"CENTER_FREQ2"}},
+
+    {NL80211_ATTR_P2P_CTWINDOW, {"P2P_CTWINDOW"}},
+    {NL80211_ATTR_P2P_OPPPS, {"P2P_OPPPS"}},
+
+    {NL80211_ATTR_LOCAL_MESH_POWER_MODE, {"LOCAL_MESH_POWER_MODE"}},
+
+    {NL80211_ATTR_ACL_POLICY, {"ACL_POLICY"}},
+
+    {NL80211_ATTR_MAC_ADDRS, {"MAC_ADDRS"}},
+
+    {NL80211_ATTR_MAC_ACL_MAX, {"MAC_ACL_MAX", DataType::Uint}},
+
+    {NL80211_ATTR_RADAR_EVENT, {"RADAR_EVENT"}},
+
+    {NL80211_ATTR_EXT_CAPA, {"EXT_CAPA"}},
+    {NL80211_ATTR_EXT_CAPA_MASK, {"EXT_CAPA_MASK"}},
+
+    {NL80211_ATTR_STA_CAPABILITY, {"STA_CAPABILITY"}},
+    {NL80211_ATTR_STA_EXT_CAPABILITY, {"STA_EXT_CAPABILITY"}},
+
+    {NL80211_ATTR_PROTOCOL_FEATURES, {"PROTOCOL_FEATURES", DataType::Uint}},
+    {NL80211_ATTR_SPLIT_WIPHY_DUMP, {"SPLIT_WIPHY_DUMP", DataType::Flag}},
+
+    {NL80211_ATTR_DISABLE_VHT, {"DISABLE_VHT", DataType::Flag}},
+    {NL80211_ATTR_VHT_CAPABILITY_MASK, {"VHT_CAPABILITY_MASK"}},
+
+    {NL80211_ATTR_MDID, {"MDID"}},
+    {NL80211_ATTR_IE_RIC, {"IE_RIC"}},
+
+    {NL80211_ATTR_CRIT_PROT_ID, {"CRIT_PROT_ID"}},
+    {NL80211_ATTR_MAX_CRIT_PROT_DURATION, {"MAX_CRIT_PROT_DURATION"}},
+
+    {NL80211_ATTR_PEER_AID, {"PEER_AID"}},
+
+    {NL80211_ATTR_COALESCE_RULE, {"COALESCE_RULE"}},
+
+    {NL80211_ATTR_CH_SWITCH_COUNT, {"CH_SWITCH_COUNT"}},
+    {NL80211_ATTR_CH_SWITCH_BLOCK_TX, {"CH_SWITCH_BLOCK_TX"}},
+    {NL80211_ATTR_CSA_IES, {"CSA_IES"}},
+    {NL80211_ATTR_CNTDWN_OFFS_BEACON, {"CNTDWN_OFFS_BEACON"}},
+    {NL80211_ATTR_CNTDWN_OFFS_PRESP, {"CNTDWN_OFFS_PRESP"}},
+
+    {NL80211_ATTR_RXMGMT_FLAGS, {"RXMGMT_FLAGS"}},
+
+    {NL80211_ATTR_STA_SUPPORTED_CHANNELS, {"STA_SUPPORTED_CHANNELS"}},
+
+    {NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, {"STA_SUPPORTED_OPER_CLASSES"}},
+
+    {NL80211_ATTR_HANDLE_DFS, {"HANDLE_DFS"}},
+
+    {NL80211_ATTR_SUPPORT_5_MHZ, {"SUPPORT_5_MHZ"}},
+    {NL80211_ATTR_SUPPORT_10_MHZ, {"SUPPORT_10_MHZ"}},
+
+    {NL80211_ATTR_OPMODE_NOTIF, {"OPMODE_NOTIF"}},
+
+    {NL80211_ATTR_VENDOR_ID, {"VENDOR_ID"}},
+    {NL80211_ATTR_VENDOR_SUBCMD, {"VENDOR_SUBCMD"}},
+    {NL80211_ATTR_VENDOR_DATA, {"VENDOR_DATA", DataType::Raw, AttributeMap{}, Flags::Verbose}},
+    {NL80211_ATTR_VENDOR_EVENTS, {"VENDOR_EVENTS", DataType::Nested, AttributeMap{},
+            Flags::Verbose}},
+
+    {NL80211_ATTR_QOS_MAP, {"QOS_MAP"}},
+
+    {NL80211_ATTR_MAC_HINT, {"MAC_HINT"}},
+    {NL80211_ATTR_WIPHY_FREQ_HINT, {"WIPHY_FREQ_HINT"}},
+
+    {NL80211_ATTR_MAX_AP_ASSOC_STA, {"MAX_AP_ASSOC_STA"}},
+
+    {NL80211_ATTR_TDLS_PEER_CAPABILITY, {"TDLS_PEER_CAPABILITY"}},
+
+    {NL80211_ATTR_SOCKET_OWNER, {"SOCKET_OWNER"}},
+
+    {NL80211_ATTR_CSA_C_OFFSETS_TX, {"CSA_C_OFFSETS_TX"}},
+    {NL80211_ATTR_MAX_CSA_COUNTERS, {"MAX_CSA_COUNTERS"}},
+
+    {NL80211_ATTR_TDLS_INITIATOR, {"TDLS_INITIATOR"}},
+
+    {NL80211_ATTR_USE_RRM, {"USE_RRM"}},
+
+    {NL80211_ATTR_WIPHY_DYN_ACK, {"WIPHY_DYN_ACK"}},
+
+    {NL80211_ATTR_TSID, {"TSID"}},
+    {NL80211_ATTR_USER_PRIO, {"USER_PRIO"}},
+    {NL80211_ATTR_ADMITTED_TIME, {"ADMITTED_TIME"}},
+
+    {NL80211_ATTR_SMPS_MODE, {"SMPS_MODE"}},
+
+    {NL80211_ATTR_OPER_CLASS, {"OPER_CLASS"}},
+
+    {NL80211_ATTR_MAC_MASK, {"MAC_MASK"}},
+
+    {NL80211_ATTR_WIPHY_SELF_MANAGED_REG, {"WIPHY_SELF_MANAGED_REG"}},
+
+    {NL80211_ATTR_EXT_FEATURES, {"EXT_FEATURES"}},
+
+    {NL80211_ATTR_SURVEY_RADIO_STATS, {"SURVEY_RADIO_STATS"}},
+
+    {NL80211_ATTR_NETNS_FD, {"NETNS_FD"}},
+
+    {NL80211_ATTR_SCHED_SCAN_DELAY, {"SCHED_SCAN_DELAY"}},
+
+    {NL80211_ATTR_REG_INDOOR, {"REG_INDOOR"}},
+
+    {NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS, {"MAX_NUM_SCHED_SCAN_PLANS", DataType::Uint}},
+    {NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL, {"MAX_SCAN_PLAN_INTERVAL", DataType::Uint}},
+    {NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS, {"MAX_SCAN_PLAN_ITERATIONS", DataType::Uint}},
+    {NL80211_ATTR_SCHED_SCAN_PLANS, {"SCHED_SCAN_PLANS"}},
+
+    {NL80211_ATTR_PBSS, {"PBSS"}},
+
+    {NL80211_ATTR_BSS_SELECT, {"BSS_SELECT"}},
+
+    {NL80211_ATTR_STA_SUPPORT_P2P_PS, {"STA_SUPPORT_P2P_PS"}},
+
+    {NL80211_ATTR_PAD, {"PAD"}},
+
+    {NL80211_ATTR_IFTYPE_EXT_CAPA, {"IFTYPE_EXT_CAPA"}},
+
+    {NL80211_ATTR_MU_MIMO_GROUP_DATA, {"MU_MIMO_GROUP_DATA"}},
+    {NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR, {"MU_MIMO_FOLLOW_MAC_ADDR"}},
+
+    {NL80211_ATTR_SCAN_START_TIME_TSF, {"SCAN_START_TIME_TSF"}},
+    {NL80211_ATTR_SCAN_START_TIME_TSF_BSSID, {"SCAN_START_TIME_TSF_BSSID"}},
+    {NL80211_ATTR_MEASUREMENT_DURATION, {"MEASUREMENT_DURATION"}},
+    {NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY, {"MEASUREMENT_DURATION_MANDATORY"}},
+
+    {NL80211_ATTR_MESH_PEER_AID, {"MESH_PEER_AID"}},
+
+    {NL80211_ATTR_NAN_MASTER_PREF, {"NAN_MASTER_PREF"}},
+    {NL80211_ATTR_BANDS, {"BANDS"}},
+    {NL80211_ATTR_NAN_FUNC, {"NAN_FUNC"}},
+    {NL80211_ATTR_NAN_MATCH, {"NAN_MATCH"}},
+
+    {NL80211_ATTR_FILS_KEK, {"FILS_KEK"}},
+    {NL80211_ATTR_FILS_NONCES, {"FILS_NONCES"}},
+
+    {NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED, {"MULTICAST_TO_UNICAST_ENABLED"}},
+
+    {NL80211_ATTR_BSSID, {"BSSID"}},
+
+    {NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI, {"SCHED_SCAN_RELATIVE_RSSI"}},
+    {NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST, {"SCHED_SCAN_RSSI_ADJUST"}},
+
+    {NL80211_ATTR_TIMEOUT_REASON, {"TIMEOUT_REASON"}},
+
+    {NL80211_ATTR_FILS_ERP_USERNAME, {"FILS_ERP_USERNAME"}},
+    {NL80211_ATTR_FILS_ERP_REALM, {"FILS_ERP_REALM"}},
+    {NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, {"FILS_ERP_NEXT_SEQ_NUM"}},
+    {NL80211_ATTR_FILS_ERP_RRK, {"FILS_ERP_RRK"}},
+    {NL80211_ATTR_FILS_CACHE_ID, {"FILS_CACHE_ID"}},
+
+    {NL80211_ATTR_PMK, {"PMK"}},
+
+    {NL80211_ATTR_SCHED_SCAN_MULTI, {"SCHED_SCAN_MULTI"}},
+    {NL80211_ATTR_SCHED_SCAN_MAX_REQS, {"SCHED_SCAN_MAX_REQS"}},
+
+    {NL80211_ATTR_WANT_1X_4WAY_HS, {"WANT_1X_4WAY_HS"}},
+    {NL80211_ATTR_PMKR0_NAME, {"PMKR0_NAME"}},
+    {NL80211_ATTR_PORT_AUTHORIZED, {"PORT_AUTHORIZED"}},
+
+    {NL80211_ATTR_EXTERNAL_AUTH_ACTION, {"EXTERNAL_AUTH_ACTION"}},
+    {NL80211_ATTR_EXTERNAL_AUTH_SUPPORT, {"EXTERNAL_AUTH_SUPPORT"}},
+
+    {NL80211_ATTR_NSS, {"NSS"}},
+    {NL80211_ATTR_ACK_SIGNAL, {"ACK_SIGNAL"}},
+
+    {NL80211_ATTR_CONTROL_PORT_OVER_NL80211, {"CONTROL_PORT_OVER_NL80211"}},
+
+    {NL80211_ATTR_TXQ_STATS, {"TXQ_STATS"}},
+    {NL80211_ATTR_TXQ_LIMIT, {"TXQ_LIMIT"}},
+    {NL80211_ATTR_TXQ_MEMORY_LIMIT, {"TXQ_MEMORY_LIMIT"}},
+    {NL80211_ATTR_TXQ_QUANTUM, {"TXQ_QUANTUM"}},
+
+    {NL80211_ATTR_HE_CAPABILITY, {"HE_CAPABILITY"}},
+
+    {NL80211_ATTR_FTM_RESPONDER, {"FTM_RESPONDER"}},
+
+    {NL80211_ATTR_FTM_RESPONDER_STATS, {"FTM_RESPONDER_STATS"}},
+
+    {NL80211_ATTR_TIMEOUT, {"TIMEOUT"}},
+
+    {NL80211_ATTR_PEER_MEASUREMENTS, {"PEER_MEASUREMENTS"}},
+
+    {NL80211_ATTR_AIRTIME_WEIGHT, {"AIRTIME_WEIGHT"}},
+    {NL80211_ATTR_STA_TX_POWER_SETTING, {"STA_TX_POWER_SETTING"}},
+    {NL80211_ATTR_STA_TX_POWER, {"STA_TX_POWER"}},
+
+    {NL80211_ATTR_SAE_PASSWORD, {"SAE_PASSWORD"}},
+
+    {NL80211_ATTR_TWT_RESPONDER, {"TWT_RESPONDER"}},
+
+    {NL80211_ATTR_HE_OBSS_PD, {"HE_OBSS_PD"}},
+
+    {NL80211_ATTR_WIPHY_EDMG_CHANNELS, {"WIPHY_EDMG_CHANNELS"}},
+    {NL80211_ATTR_WIPHY_EDMG_BW_CONFIG, {"WIPHY_EDMG_BW_CONFIG"}},
+
+    {NL80211_ATTR_VLAN_ID, {"VLAN_ID"}},
+
+    {NL80211_ATTR_HE_BSS_COLOR, {"HE_BSS_COLOR"}},
+
+    {NL80211_ATTR_IFTYPE_AKM_SUITES, {"IFTYPE_AKM_SUITES"}},
+
+    {NL80211_ATTR_TID_CONFIG, {"TID_CONFIG"}},
+
+    {NL80211_ATTR_CONTROL_PORT_NO_PREAUTH, {"CONTROL_PORT_NO_PREAUTH"}},
+
+    {NL80211_ATTR_PMK_LIFETIME, {"PMK_LIFETIME"}},
+    {NL80211_ATTR_PMK_REAUTH_THRESHOLD, {"PMK_REAUTH_THRESHOLD"}},
+
+    {NL80211_ATTR_RECEIVE_MULTICAST, {"RECEIVE_MULTICAST"}},
+    {NL80211_ATTR_WIPHY_FREQ_OFFSET, {"WIPHY_FREQ_OFFSET"}},
+    {NL80211_ATTR_CENTER_FREQ1_OFFSET, {"CENTER_FREQ1_OFFSET"}},
+    {NL80211_ATTR_SCAN_FREQ_KHZ, {"SCAN_FREQ_KHZ"}},
+
+    {NL80211_ATTR_HE_6GHZ_CAPABILITY, {"HE_6GHZ_CAPABILITY"}},
+
+    {NL80211_ATTR_FILS_DISCOVERY, {"FILS_DISCOVERY"}},
+
+    {NL80211_ATTR_UNSOL_BCAST_PROBE_RESP, {"UNSOL_BCAST_PROBE_RESP"}},
+
+    {NL80211_ATTR_S1G_CAPABILITY, {"S1G_CAPABILITY"}},
+    {NL80211_ATTR_S1G_CAPABILITY_MASK, {"S1G_CAPABILITY_MASK"}},
+}) {}
+// clang-format on
+
+static void informationElementsToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
+    struct IEHeader {
+        uint8_t elementId;
+        uint8_t length;
+    } __attribute__((packed));
+    static_assert(sizeof(IEHeader) == 2);
+
+    const auto alldata = attr.data<uint8_t>();
+    const auto bytes = alldata.getRaw();
+
+    ss << '{';
+
+    if constexpr (kCompactIE) {
+        ss << "len=" << bytes.len() << ", ";
+        ss << "crc=" << std::hex << std::setw(4) << crc16(alldata) << std::dec << ", ";
+    }
+
+    bool first = true;
+    auto printComma = [&first, &ss]() {
+        // put separator at every but first entry
+        if (!first) ss << ", ";
+        first = false;
+    };
+
+    for (size_t offset = 0; offset < bytes.len();) {
+        const auto ptr = bytes.ptr() + offset;
+        const auto remainingLen = bytes.len() - offset;
+
+        // can we fit one more header?
+        if (sizeof(IEHeader) > remainingLen) break;
+        IEHeader ieHeader;
+        memcpy(&ieHeader, ptr, sizeof(IEHeader));
+        if (sizeof(IEHeader) + ieHeader.length > remainingLen) {
+            printComma();
+            ss << "ERR";
+            break;
+        }
+        offset += sizeof(IEHeader) + ieHeader.length;
+
+        const Buffer<uint8_t> data(ptr + sizeof(IEHeader), ieHeader.length);
+
+        if (ieHeader.elementId == WLAN_EID_SSID) {
+            printComma();
+
+            const auto str = data.getRaw();
+            const std::string ssid(reinterpret_cast<const char*>(str.ptr()), str.len());
+            ss << "SSID=\"" << printableOnly(ssid) << '"';
+
+            continue;
+        }
+
+        if constexpr (kCompactIE) continue;
+
+        // print entry ID:LENGTH/CRC16
+        printComma();
+        ss << (int)ieHeader.elementId << ':' << (int)ieHeader.length << '/';
+        ss << std::hex << std::setw(4) << crc16(data) << std::dec;
+    }
+    ss << '}';
+}
+
+static void nl80211_pattern_supportToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
+    const auto& [ok, data] = attr.data<nl80211_pattern_support>().getFirst();
+    if (!ok) {
+        ss << "invalid structure";
+        return;
+    }
+    ss << '{'                          //
+       << data.max_patterns << ','     //
+       << data.min_pattern_len << ','  //
+       << data.max_pattern_len << ','  //
+       << data.max_pkt_offset << '}';
+}
+
+}  // namespace android::nl::protocols::generic::families
diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/families/Nl80211.h b/automotive/can/1.0/default/libnl++/protocols/generic/families/Nl80211.h
new file mode 100644
index 0000000..8a9608c
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/protocols/generic/families/Nl80211.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "../GenericMessageBase.h"
+
+namespace android::nl::protocols::generic::families {
+
+class Nl80211 : public GenericMessageBase {
+  public:
+    Nl80211(nlmsgtype_t familyId);
+};
+
+}  // namespace android::nl::protocols::generic::families
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp b/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp
index 7db487a..9cc05da 100644
--- a/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp
+++ b/automotive/can/1.0/default/libnl++/protocols/route/Link.cpp
@@ -16,6 +16,7 @@
 
 #include "Link.h"
 
+#include "../structs.h"
 #include "structs.h"
 
 #include <net/if.h>
@@ -26,9 +27,9 @@
 
 // clang-format off
 Link::Link() : MessageDefinition<ifinfomsg>("link", {
-    {RTM_NEWLINK, {"NEWLINK", MessageGenre::NEW}},
-    {RTM_DELLINK, {"DELLINK", MessageGenre::DELETE}},
-    {RTM_GETLINK, {"GETLINK", MessageGenre::GET}},
+    {RTM_NEWLINK, {"NEWLINK", MessageGenre::New}},
+    {RTM_DELLINK, {"DELLINK", MessageGenre::Delete}},
+    {RTM_GETLINK, {"GETLINK", MessageGenre::Get}},
 }, {
     {IFLA_ADDRESS, {"ADDRESS"}},
     {IFLA_BROADCAST, {"BROADCAST"}},
diff --git a/automotive/can/1.0/default/libnl++/protocols/route/structs.h b/automotive/can/1.0/default/libnl++/protocols/route/structs.h
index b9d622a..fea2ce1 100644
--- a/automotive/can/1.0/default/libnl++/protocols/route/structs.h
+++ b/automotive/can/1.0/default/libnl++/protocols/route/structs.h
@@ -30,16 +30,6 @@
 // ifla_cacheinfo
 void ifla_cacheinfoToStream(std::stringstream& ss, const Buffer<nlattr> attr);
 
-template <typename T>
-void arrayToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
-    ss << '{';
-    for (const auto it : attr.data<T>().getRaw()) {
-        ss << it << ',';
-    }
-    ss.seekp(-1, std::ios_base::cur);
-    ss << '}';
-}
-
 // rtnl_link_stats or rtnl_link_stats64
 template <typename T>
 void statsToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
diff --git a/automotive/can/1.0/default/libnl++/protocols/structs.h b/automotive/can/1.0/default/libnl++/protocols/structs.h
new file mode 100644
index 0000000..44c17b8
--- /dev/null
+++ b/automotive/can/1.0/default/libnl++/protocols/structs.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sstream>
+
+namespace android::nl::protocols {
+
+template <typename T>
+void arrayToStream(std::stringstream& ss, const Buffer<nlattr> attr) {
+    ss << '{';
+    for (const auto it : attr.data<T>().getRaw()) {
+        ss << it << ',';
+    }
+    ss.seekp(-1, std::ios_base::cur);
+    ss << '}';
+}
+
+}  // namespace android::nl::protocols
diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index 8de9304..e56c2d1 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -305,11 +305,22 @@
             const auto id = 0xFFFFFFFF; // meaningless id
             hidl_vec<uint8_t> values;
             auto err = pCam->setExtendedInfo_1_1(id, values);
-            ASSERT_NE(EvsResult::INVALID_ARG, err);
+            if (isLogicalCam) {
+                // Logical camera device does not support setExtendedInfo
+                // method.
+                ASSERT_EQ(EvsResult::INVALID_ARG, err);
+            } else {
+                ASSERT_NE(EvsResult::INVALID_ARG, err);
+            }
 
-            pCam->getExtendedInfo_1_1(id, [](const auto& result, const auto& data) {
-                ASSERT_NE(EvsResult::INVALID_ARG, result);
-                ASSERT_EQ(0, data.size());
+
+            pCam->getExtendedInfo_1_1(id, [&isLogicalCam](const auto& result, const auto& data) {
+                if (isLogicalCam) {
+                    ASSERT_EQ(EvsResult::INVALID_ARG, result);
+                } else {
+                    ASSERT_NE(EvsResult::INVALID_ARG, result);
+                    ASSERT_EQ(0, data.size());
+                }
             });
 
             // Explicitly close the camera so resources are released right away
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 8d1693a..bbb48e1 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -88,6 +88,7 @@
     whole_static_libs: [
         "android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
         "android.hardware.automotive.vehicle@2.0-manager-lib",
+        "libqemu_pipe",
     ],
     shared_libs: [
         "libbase",
@@ -95,7 +96,6 @@
         "libprotobuf-cpp-lite",
     ],
     static_libs: [
-        "libqemu_pipe",
         "android.hardware.automotive.vehicle@2.0-libproto-native",
     ],
 }
@@ -210,6 +210,5 @@
         "android.hardware.automotive.vehicle@2.0-manager-lib",
         "android.hardware.automotive.vehicle@2.0-default-impl-lib",
         "android.hardware.automotive.vehicle@2.0-libproto-native",
-        "libqemu_pipe",
     ],
 }
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index 80a1831..8ef2b60 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -308,6 +308,19 @@
 
         {.config =
                  {
+                         .prop = toInt(VehicleProperty::SEAT_OCCUPANCY),
+                         .access = VehiclePropertyAccess::READ,
+                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+                         .areaConfigs = {VehicleAreaConfig{.areaId = (SEAT_1_LEFT)},
+                                         VehicleAreaConfig{.areaId = (SEAT_1_RIGHT)}},
+                 },
+         .initialAreaValues = {{SEAT_1_LEFT,
+                                {.int32Values = {(int)VehicleSeatOccupancyState::VACANT}}},
+                               {SEAT_1_RIGHT,
+                                {.int32Values = {(int)VehicleSeatOccupancyState::VACANT}}}}},
+
+        {.config =
+                 {
                          .prop = toInt(VehicleProperty::INFO_DRIVER_SEAT),
                          .access = VehiclePropertyAccess::READ,
                          .changeMode = VehiclePropertyChangeMode::STATIC,
@@ -417,7 +430,7 @@
                          .minSampleRate = 1.0f,
                          .maxSampleRate = 2.0f,
                  },
-         .initialValue = {.floatValues = {100.0f}}},  // units in meters
+         .initialValue = {.floatValues = {50000.0f}}},  // units in meters
 
         {.config =
                  {
diff --git a/biometrics/face/1.1/default/BiometricsFace.cpp b/biometrics/face/1.1/default/BiometricsFace.cpp
index 2143880..57b3a92 100644
--- a/biometrics/face/1.1/default/BiometricsFace.cpp
+++ b/biometrics/face/1.1/default/BiometricsFace.cpp
@@ -24,8 +24,8 @@
 constexpr uint64_t kDeviceId = 123;
 // Arbitrary value.
 constexpr uint64_t kAuthenticatorId = 987;
-// Arbitrary value.
-constexpr uint64_t kLockoutDuration = 555;
+// Not locked out.
+constexpr uint64_t kLockoutDuration = 0;
 
 BiometricsFace::BiometricsFace() : mRandom(std::mt19937::default_seed) {}
 
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/AcquiredInfo.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/AcquiredInfo.aidl
index 88c066c..a05fad9 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/AcquiredInfo.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/AcquiredInfo.aidl
@@ -42,4 +42,8 @@
   SENSOR_DIRTY = 21,
   VENDOR = 22,
   FIRST_FRAME_RECEIVED = 23,
+  DARK_GLASSES_DETECTED = 24,
+  FACE_COVERING_DETECTED = 25,
+  EYES_NOT_VISIBLE = 26,
+  MOUTH_NOT_VISIBLE = 27,
 }
diff --git a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISessionCallback.aidl b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISessionCallback.aidl
index 477ba5a..caf65ae 100644
--- a/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISessionCallback.aidl
+++ b/biometrics/face/aidl/aidl_api/android.hardware.biometrics.face/current/android/hardware/biometrics/face/ISessionCallback.aidl
@@ -19,8 +19,8 @@
 @VintfStability
 interface ISessionCallback {
   void onStateChanged(in int cookie, in android.hardware.biometrics.face.SessionState state);
-  void onChallengeGenerated(in int sensorId, in int userId, in long challenge);
-  void onChallengeRevoked(in int sensorId, in int userId, in long challenge);
+  void onChallengeGenerated(in long challenge);
+  void onChallengeRevoked(in long challenge);
   void onAcquired(in android.hardware.biometrics.face.AcquiredInfo info, in int vendorCode);
   void onError(in android.hardware.biometrics.face.Error error, in int vendorCode);
   void onEnrollmentProgress(in int enrollmentId, int remaining);
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/AcquiredInfo.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/AcquiredInfo.aidl
index 5897cdc..56a600f 100644
--- a/biometrics/face/aidl/android/hardware/biometrics/face/AcquiredInfo.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/AcquiredInfo.aidl
@@ -218,6 +218,30 @@
     /**
      * The first frame from the camera has been received.
      */
-    FIRST_FRAME_RECEIVED = 23
+    FIRST_FRAME_RECEIVED = 23,
+
+    /**
+     * Dark glasses detected. This can be useful for providing relevant feedback to the user and
+     * enabling an alternative authentication logic if the implementation supports it.
+     */
+    DARK_GLASSES_DETECTED = 24,
+
+    /**
+     * A face mask or face covering detected. This can be useful for providing relevant feedback to
+     * the user and enabling an alternative authentication logic if the implementation supports it.
+     */
+    FACE_COVERING_DETECTED = 25,
+
+    /**
+     * Either one or both eyes are not visible in the frame. Prefer to use DARK_GLASSES_DETECTED if
+     * the eyes are not visible due to dark glasses.
+     */
+    EYES_NOT_VISIBLE = 26,
+
+    /**
+     * The mouth is not visible in the frame. Prefer to use MASK_DETECTED if the mouth is not
+     * visible due to a mask.
+     */
+    MOUTH_NOT_VISIBLE = 27,
 }
 
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
index c7beae0..425b352 100644
--- a/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/ISession.aidl
@@ -292,16 +292,9 @@
      *
      * When invoked by the framework, the implementation must perform the following sequence of
      * events:
-     *   1) Verify the authenticity and integrity of the provided HAT. If this check fails, the HAL
-     *      must invoke ISessionCallback#onError with Error::UNABLE_TO_PROCESS and return to
-     *      SessionState::IDLING if no subsequent work is in the queue.
-     *   2) Verify that the timestamp provided within the HAT is relatively recent (e.g. on the
-     *      order of minutes, not hours). If this check fails, the HAL must invoke
-     *      ISessionCallback#onError with Error::UNABLE_TO_PROCESS and return to
-     *      SessionState::IDLING if no subsequent work is in the queue.
-     *   3) Update the authenticatorId with a new entropy-encoded random number
-     *   4) Persist the new authenticatorId to non-ephemeral storage
-     *   5) Notify the framework that the above is completed, via
+     *   1) Update the authenticatorId with a new entropy-encoded random number
+     *   2) Persist the new authenticatorId to non-ephemeral storage
+     *   3) Notify the framework that the above is completed, via
      *      ISessionCallback#onAuthenticatorInvalidated
      *
      * A practical use case of invalidation would be when the user adds a new enrollment to a sensor
diff --git a/biometrics/face/aidl/android/hardware/biometrics/face/ISessionCallback.aidl b/biometrics/face/aidl/android/hardware/biometrics/face/ISessionCallback.aidl
index 2608d5f..fd4a648 100644
--- a/biometrics/face/aidl/android/hardware/biometrics/face/ISessionCallback.aidl
+++ b/biometrics/face/aidl/android/hardware/biometrics/face/ISessionCallback.aidl
@@ -31,12 +31,12 @@
     /**
      * Notifies the framework when a challenge is successfully generated.
      */
-    void onChallengeGenerated(in int sensorId, in int userId, in long challenge);
+    void onChallengeGenerated(in long challenge);
 
     /**
      * Notifies the framework when a challenge has been revoked.
      */
-    void onChallengeRevoked(in int sensorId, in int userId, in long challenge);
+    void onChallengeRevoked(in long challenge);
 
     /**
      * This method must only be used to notify the framework during the following states:
diff --git a/biometrics/face/aidl/default/Face.cpp b/biometrics/face/aidl/default/Face.cpp
index 929c7e7..d3883d6 100644
--- a/biometrics/face/aidl/default/Face.cpp
+++ b/biometrics/face/aidl/default/Face.cpp
@@ -20,7 +20,7 @@
 namespace aidl::android::hardware::biometrics::face {
 
 const int kSensorId = 0;
-const common::SensorStrength kSensorStrength = common::SensorStrength::WEAK;
+const common::SensorStrength kSensorStrength = common::SensorStrength::STRONG;
 const int kMaxEnrollmentsPerUser = 5;
 const FaceSensorType kSensorType = FaceSensorType::RGB;
 const bool kHalControlsPreview = true;
diff --git a/biometrics/face/aidl/default/Session.cpp b/biometrics/face/aidl/default/Session.cpp
index 2b1d4a7..c5d7d23 100644
--- a/biometrics/face/aidl/default/Session.cpp
+++ b/biometrics/face/aidl/default/Session.cpp
@@ -21,17 +21,35 @@
 namespace aidl::android::hardware::biometrics::face {
 
 class CancellationSignal : public common::BnCancellationSignal {
+  private:
+    std::shared_ptr<ISessionCallback> cb_;
   public:
-    ndk::ScopedAStatus cancel() override { return ndk::ScopedAStatus::ok(); }
+    explicit CancellationSignal(std::shared_ptr<ISessionCallback> cb) : cb_(std::move(cb)) {}
+
+    ndk::ScopedAStatus cancel() override {
+        cb_->onError(Error::CANCELED, 0 /* vendorCode */);
+        cb_->onStateChanged(0, SessionState::IDLING);
+        return ndk::ScopedAStatus::ok();
+    }
 };
 
 Session::Session(std::shared_ptr<ISessionCallback> cb) : cb_(std::move(cb)) {}
 
 ndk::ScopedAStatus Session::generateChallenge(int32_t /*cookie*/, int32_t /*timeoutSec*/) {
+    if (cb_) {
+        cb_->onStateChanged(0, SessionState::GENERATING_CHALLENGE);
+        cb_->onChallengeGenerated(0);
+        cb_->onStateChanged(0, SessionState::IDLING);
+    }
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Session::revokeChallenge(int32_t /*cookie*/, int64_t /*challenge*/) {
+ndk::ScopedAStatus Session::revokeChallenge(int32_t /*cookie*/, int64_t challenge) {
+    if (cb_) {
+        cb_->onStateChanged(0, SessionState::REVOKING_CHALLENGE);
+        cb_->onChallengeRevoked(challenge);
+        cb_->onStateChanged(0, SessionState::IDLING);
+    }
     return ndk::ScopedAStatus::ok();
 }
 
@@ -47,7 +65,7 @@
     if (cb_) {
         cb_->onStateChanged(0, SessionState::AUTHENTICATING);
     }
-    *return_val = SharedRefBase::make<CancellationSignal>();
+    *return_val = SharedRefBase::make<CancellationSignal>(cb_);
     return ndk::ScopedAStatus::ok();
 }
 
@@ -57,15 +75,30 @@
 }
 
 ndk::ScopedAStatus Session::enumerateEnrollments(int32_t /*cookie*/) {
+    if (cb_) {
+        cb_->onStateChanged(0, SessionState::ENUMERATING_ENROLLMENTS);
+        cb_->onEnrollmentsEnumerated(std::vector<int32_t>());
+        cb_->onStateChanged(0, SessionState::IDLING);
+    }
     return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus Session::removeEnrollments(int32_t /*cookie*/,
                                               const std::vector<int32_t>& /*enrollmentIds*/) {
+    if (cb_) {
+      cb_->onStateChanged(0, SessionState::REMOVING_ENROLLMENTS);
+      cb_->onEnrollmentsRemoved(std::vector<int32_t>());
+      cb_->onStateChanged(0, SessionState::IDLING);
+    }
     return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus Session::getAuthenticatorId(int32_t /*cookie*/) {
+    if (cb_) {
+        cb_->onStateChanged(0, SessionState::GETTING_AUTHENTICATOR_ID);
+        cb_->onAuthenticatorIdRetrieved(0 /* authenticatorId */);
+        cb_->onStateChanged(0, SessionState::IDLING);
+    }
     return ndk::ScopedAStatus::ok();
 }
 
@@ -75,6 +108,11 @@
 
 ndk::ScopedAStatus Session::resetLockout(int32_t /*cookie*/,
                                          const keymaster::HardwareAuthToken& /*hat*/) {
+    if (cb_) {
+        cb_->onStateChanged(0, SessionState::RESETTING_LOCKOUT);
+        cb_->onLockoutCleared();
+        cb_->onStateChanged(0, SessionState::IDLING);
+    }
     return ndk::ScopedAStatus::ok();
 }
 }  // namespace aidl::android::hardware::biometrics::face
diff --git a/biometrics/face/aidl/vts/VtsHalBiometricsFaceTargetTest.cpp b/biometrics/face/aidl/vts/VtsHalBiometricsFaceTargetTest.cpp
index c1ba66b..5f10306 100644
--- a/biometrics/face/aidl/vts/VtsHalBiometricsFaceTargetTest.cpp
+++ b/biometrics/face/aidl/vts/VtsHalBiometricsFaceTargetTest.cpp
@@ -51,13 +51,11 @@
         return ndk::ScopedAStatus::ok();
     }
 
-    ndk::ScopedAStatus onChallengeGenerated(int32_t /*sensorId*/, int32_t /*userId*/,
-                                            int64_t /*challenge*/) override {
+    ndk::ScopedAStatus onChallengeGenerated(int64_t /*challenge*/) override {
         return ndk::ScopedAStatus::ok();
     }
 
-    ndk::ScopedAStatus onChallengeRevoked(int32_t /*sensorId*/, int32_t /*userId*/,
-                                          int64_t /*challenge*/) override {
+    ndk::ScopedAStatus onChallengeRevoked(int64_t /*challenge*/) override {
         return ndk::ScopedAStatus::ok();
     }
 
diff --git a/biometrics/fingerprint/2.1/default/README.md b/biometrics/fingerprint/2.1/default/README.md
new file mode 100644
index 0000000..c41664e
--- /dev/null
+++ b/biometrics/fingerprint/2.1/default/README.md
@@ -0,0 +1,6 @@
+## Default IBiometricsFingerprint@2.1 HAL ##
+---
+
+## Overview: ##
+
+Provides a default implementation that loads pre-HIDL HALs and exposes it to the framework.
\ No newline at end of file
diff --git a/biometrics/fingerprint/2.2/default/Android.bp b/biometrics/fingerprint/2.2/default/Android.bp
new file mode 100644
index 0000000..8931308
--- /dev/null
+++ b/biometrics/fingerprint/2.2/default/Android.bp
@@ -0,0 +1,22 @@
+cc_binary {
+    name: "android.hardware.biometrics.fingerprint@2.2-service.example",
+    defaults: ["hidl_defaults"],
+    init_rc: ["android.hardware.biometrics.fingerprint@2.2-service.rc"],
+    vintf_fragments: ["android.hardware.biometrics.fingerprint@2.2-service.xml"],
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: [
+        "BiometricsFingerprint.cpp",
+        "service.cpp",
+    ],
+
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libhidlbase",
+        "libhardware",
+        "libutils",
+        "android.hardware.biometrics.fingerprint@2.2",
+    ],
+
+}
diff --git a/biometrics/fingerprint/2.2/default/BiometricsFingerprint.cpp b/biometrics/fingerprint/2.2/default/BiometricsFingerprint.cpp
new file mode 100644
index 0000000..b07a17c
--- /dev/null
+++ b/biometrics/fingerprint/2.2/default/BiometricsFingerprint.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "android.hardware.biometrics.fingerprint@2.2-service"
+#define LOG_VERBOSE "android.hardware.biometrics.fingerprint@2.2-service"
+
+#include <hardware/hw_auth_token.h>
+
+#include <android/log.h>
+#include <hardware/hardware.h>
+#include <hardware/fingerprint.h>
+#include "BiometricsFingerprint.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+namespace android {
+namespace hardware {
+namespace biometrics {
+namespace fingerprint {
+namespace V2_2 {
+namespace implementation {
+
+using RequestStatus = android::hardware::biometrics::fingerprint::V2_1::RequestStatus;
+using FingerprintError = android::hardware::biometrics::fingerprint::V2_1::FingerprintError;
+
+constexpr uint64_t kDeviceId = 1;
+
+BiometricsFingerprint::BiometricsFingerprint() {
+
+}
+
+BiometricsFingerprint::~BiometricsFingerprint() {
+
+}
+
+Return<uint64_t> BiometricsFingerprint::setNotify(
+        const sp<IBiometricsFingerprintClientCallback>& clientCallback) {
+    mClientCallback = clientCallback;
+    return kDeviceId;
+}
+
+Return<uint64_t> BiometricsFingerprint::preEnroll()  {
+    // On a real implementation, this must be generated and stored in the TEE or its equivalent.
+    return rand();
+}
+
+Return<RequestStatus> BiometricsFingerprint::enroll(const hidl_array<uint8_t, 69>&  /* hat */,
+        uint32_t /* gid */, uint32_t /* timeoutSec */) {
+    // On a real implementation, the HAT must be checked in the TEE or its equivalent.
+    mClientCallback->onError(kDeviceId, FingerprintError::ERROR_UNABLE_TO_PROCESS,
+            0 /* vendorCode */);
+    return RequestStatus::SYS_OK;
+}
+
+Return<RequestStatus> BiometricsFingerprint::postEnroll() {
+    return RequestStatus::SYS_OK;
+}
+
+Return<uint64_t> BiometricsFingerprint::getAuthenticatorId() {
+    return 1;
+}
+
+Return<RequestStatus> BiometricsFingerprint::cancel() {
+    mClientCallback->onError(kDeviceId, FingerprintError::ERROR_CANCELED, 0 /* vendorCode */);
+    return RequestStatus::SYS_OK;
+}
+
+Return<RequestStatus> BiometricsFingerprint::enumerate()  {
+    mClientCallback->onEnumerate(kDeviceId, 0 /* fingerId */, 0 /* groupId */,
+            0 /* remaining */);
+    return RequestStatus::SYS_OK;
+}
+
+Return<RequestStatus> BiometricsFingerprint::remove(uint32_t gid, uint32_t fid) {
+    mClientCallback->onRemoved(kDeviceId, fid, gid, 0 /* remaining */);
+    return RequestStatus::SYS_OK;
+}
+
+Return<RequestStatus> BiometricsFingerprint::setActiveGroup(uint32_t /* gid */,
+        const hidl_string& storePath) {
+    // Return invalid for paths that the HAL is unable to write to.
+    std::string path = storePath.c_str();
+    if (path.compare("") == 0 || path.compare("/") == 0) {
+        return RequestStatus::SYS_EINVAL;
+    }
+    return RequestStatus::SYS_OK;
+}
+
+Return<RequestStatus> BiometricsFingerprint::authenticate(uint64_t /* operationId */,
+        uint32_t /* gid */) {
+    return RequestStatus::SYS_OK;
+}
+
+} // namespace implementation
+}  // namespace V2_2
+}  // namespace fingerprint
+}  // namespace biometrics
+}  // namespace hardware
+}  // namespace android
diff --git a/biometrics/fingerprint/2.2/default/BiometricsFingerprint.h b/biometrics/fingerprint/2.2/default/BiometricsFingerprint.h
new file mode 100644
index 0000000..a6861b3
--- /dev/null
+++ b/biometrics/fingerprint/2.2/default/BiometricsFingerprint.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_BIOMETRICS_FINGERPRINT_V2_2_BIOMETRICSFINGERPRINT_H
+#define ANDROID_HARDWARE_BIOMETRICS_FINGERPRINT_V2_2_BIOMETRICSFINGERPRINT_H
+
+#include <log/log.h>
+#include <android/log.h>
+#include <hardware/hardware.h>
+#include <hardware/fingerprint.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <android/hardware/biometrics/fingerprint/2.2/IBiometricsFingerprint.h>
+
+namespace android {
+namespace hardware {
+namespace biometrics {
+namespace fingerprint {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::hardware::biometrics::fingerprint::V2_2::IBiometricsFingerprint;
+using ::android::hardware::biometrics::fingerprint::V2_1::IBiometricsFingerprintClientCallback;
+using ::android::hardware::biometrics::fingerprint::V2_1::RequestStatus;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::sp;
+
+struct BiometricsFingerprint : public IBiometricsFingerprint {
+public:
+    BiometricsFingerprint();
+    ~BiometricsFingerprint();
+
+    // Methods from ::android::hardware::biometrics::fingerprint::V2_2::IBiometricsFingerprint follow.
+    Return<uint64_t> setNotify(const sp<IBiometricsFingerprintClientCallback>& clientCallback) override;
+    Return<uint64_t> preEnroll() override;
+    Return<RequestStatus> enroll(const hidl_array<uint8_t, 69>& hat, uint32_t gid, uint32_t timeoutSec) override;
+    Return<RequestStatus> postEnroll() override;
+    Return<uint64_t> getAuthenticatorId() override;
+    Return<RequestStatus> cancel() override;
+    Return<RequestStatus> enumerate() override;
+    Return<RequestStatus> remove(uint32_t gid, uint32_t fid) override;
+    Return<RequestStatus> setActiveGroup(uint32_t gid, const hidl_string& storePath) override;
+    Return<RequestStatus> authenticate(uint64_t operationId, uint32_t gid) override;
+
+private:
+    sp<IBiometricsFingerprintClientCallback> mClientCallback;
+
+};
+
+}  // namespace implementation
+}  // namespace V2_2
+}  // namespace fingerprint
+}  // namespace biometrics
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_BIOMETRICS_FINGERPRINT_V2_2_BIOMETRICSFINGERPRINT_H
diff --git a/biometrics/fingerprint/2.2/default/android.hardware.biometrics.fingerprint@2.2-service.rc b/biometrics/fingerprint/2.2/default/android.hardware.biometrics.fingerprint@2.2-service.rc
new file mode 100644
index 0000000..1b406b0
--- /dev/null
+++ b/biometrics/fingerprint/2.2/default/android.hardware.biometrics.fingerprint@2.2-service.rc
@@ -0,0 +1,4 @@
+service vendor.fps_hal /vendor/bin/hw/android.hardware.biometrics.fingerprint@2.2-service.example
+    class hal
+    user nobody
+    group nobody
diff --git a/biometrics/fingerprint/2.2/default/android.hardware.biometrics.fingerprint@2.2-service.xml b/biometrics/fingerprint/2.2/default/android.hardware.biometrics.fingerprint@2.2-service.xml
new file mode 100644
index 0000000..5e69a1e
--- /dev/null
+++ b/biometrics/fingerprint/2.2/default/android.hardware.biometrics.fingerprint@2.2-service.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.biometrics.fingerprint</name>
+        <transport>hwbinder</transport>
+        <version>2.2</version>
+        <interface>
+            <name>IBiometricsFingerprint</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/biometrics/fingerprint/2.2/default/service.cpp b/biometrics/fingerprint/2.2/default/service.cpp
new file mode 100644
index 0000000..5bc69a0
--- /dev/null
+++ b/biometrics/fingerprint/2.2/default/service.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.biometrics.fingerprint@2.2-service"
+
+#include <android/log.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <android/hardware/biometrics/fingerprint/2.2/IBiometricsFingerprint.h>
+#include <android/hardware/biometrics/fingerprint/2.2/types.h>
+#include "BiometricsFingerprint.h"
+
+using android::hardware::biometrics::fingerprint::V2_2::IBiometricsFingerprint;
+using android::hardware::biometrics::fingerprint::V2_2::implementation::BiometricsFingerprint;
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::sp;
+
+int main() {
+    android::sp<IBiometricsFingerprint> bio = new BiometricsFingerprint();
+
+    configureRpcThreadpool(1, true /*callerWillJoin*/);
+
+    if (::android::OK != bio->registerAsService()) {
+        return 1;
+    }
+
+    joinRpcThreadpool();
+
+    return 0; // should never get here
+}
diff --git a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISession.aidl b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISession.aidl
index 00a08ba..4df7981 100644
--- a/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISession.aidl
+++ b/biometrics/fingerprint/aidl/aidl_api/android.hardware.biometrics.fingerprint/current/android/hardware/biometrics/fingerprint/ISession.aidl
@@ -26,7 +26,7 @@
   void enumerateEnrollments(in int cookie);
   void removeEnrollments(in int cookie, in int[] enrollmentIds);
   void getAuthenticatorId(in int cookie);
-  void invalidateAuthenticatorId(in int cookie, in android.hardware.keymaster.HardwareAuthToken hat);
+  void invalidateAuthenticatorId(in int cookie);
   void resetLockout(in int cookie, in android.hardware.keymaster.HardwareAuthToken hat);
   void onPointerDown(in int pointerId, in int x, in int y, in float minor, in float major);
   void onPointerUp(in int pointerId);
diff --git a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISession.aidl b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISession.aidl
index da767be..09bd04d 100644
--- a/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISession.aidl
+++ b/biometrics/fingerprint/aidl/android/hardware/biometrics/fingerprint/ISession.aidl
@@ -301,16 +301,9 @@
      *
      * When invoked by the framework, the implementation must perform the following sequence of
      * events:
-     *   1) Verify the authenticity and integrity of the provided HAT. If this check fails, the HAL
-     *      must invoke ISessionCallback#onError with Error::UNABLE_TO_PROCESS and return to
-     *      SessionState::IDLING if no subsequent work is in the queue.
-     *   2) Verify that the timestamp provided within the HAT is relatively recent (e.g. on the
-     *      order of minutes, not hours). If this check fails, the HAL must invoke
-     *      ISessionCallback#onError with Error::UNABLE_TO_PROCESS and return to
-     *      SessionState::IDLING if no subsequent work is in the queue.
-     *   3) Update the authenticatorId with a new entropy-encoded random number
-     *   4) Persist the new authenticatorId to non-ephemeral storage
-     *   5) Notify the framework that the above is completed, via
+     *   1) Update the authenticatorId with a new entropy-encoded random number
+     *   2) Persist the new authenticatorId to non-ephemeral storage
+     *   3) Notify the framework that the above is completed, via
      *      ISessionCallback#onAuthenticatorInvalidated
      *
      * A practical use case of invalidation would be when the user adds a new enrollment to a sensor
@@ -321,9 +314,8 @@
      *
      * @param cookie An identifier used to track subsystem operations related to this call path. The
      *               client must guarantee that it is unique per ISession.
-     * @param hat HardwareAuthToken that must be validated before proceeding with this operation.
      */
-    void invalidateAuthenticatorId(in int cookie, in HardwareAuthToken hat);
+    void invalidateAuthenticatorId(in int cookie);
 
     /**
      * resetLockout:
diff --git a/biometrics/fingerprint/aidl/default/Fingerprint.cpp b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
index 18ce0c8..6eb35d9 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp
+++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
@@ -19,7 +19,7 @@
 
 namespace aidl::android::hardware::biometrics::fingerprint {
 
-const int kSensorId = 0;
+const int kSensorId = 1;
 const common::SensorStrength kSensorStrength = common::SensorStrength::STRONG;
 const int kMaxEnrollmentsPerUser = 5;
 const FingerprintSensorType kSensorType = FingerprintSensorType::REAR;
diff --git a/biometrics/fingerprint/aidl/default/Session.cpp b/biometrics/fingerprint/aidl/default/Session.cpp
index 96f1e56..bf08203 100644
--- a/biometrics/fingerprint/aidl/default/Session.cpp
+++ b/biometrics/fingerprint/aidl/default/Session.cpp
@@ -79,8 +79,7 @@
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus Session::invalidateAuthenticatorId(int32_t /*cookie*/,
-                                                      const keymaster::HardwareAuthToken& /*hat*/) {
+ndk::ScopedAStatus Session::invalidateAuthenticatorId(int32_t /*cookie*/) {
     return ndk::ScopedAStatus::ok();
 }
 
diff --git a/biometrics/fingerprint/aidl/default/Session.h b/biometrics/fingerprint/aidl/default/Session.h
index 05c570c..ed3ae3f 100644
--- a/biometrics/fingerprint/aidl/default/Session.h
+++ b/biometrics/fingerprint/aidl/default/Session.h
@@ -49,8 +49,7 @@
 
     ndk::ScopedAStatus getAuthenticatorId(int32_t cookie) override;
 
-    ndk::ScopedAStatus invalidateAuthenticatorId(int32_t cookie,
-                                                 const keymaster::HardwareAuthToken& hat) override;
+    ndk::ScopedAStatus invalidateAuthenticatorId(int32_t cookie) override;
 
     ndk::ScopedAStatus resetLockout(int32_t cookie,
                                     const keymaster::HardwareAuthToken& hat) override;
diff --git a/bluetooth/1.0/default/h4_protocol.cc b/bluetooth/1.0/default/h4_protocol.cc
index 8c24f76..43abbe4 100644
--- a/bluetooth/1.0/default/h4_protocol.cc
+++ b/bluetooth/1.0/default/h4_protocol.cc
@@ -90,6 +90,7 @@
     hci_packet_type_ = static_cast<HciPacketType>(buffer[0]);
     if (hci_packet_type_ != HCI_PACKET_TYPE_ACL_DATA &&
         hci_packet_type_ != HCI_PACKET_TYPE_SCO_DATA &&
+        hci_packet_type_ != HCI_PACKET_TYPE_ISO_DATA &&
         hci_packet_type_ != HCI_PACKET_TYPE_EVENT) {
       LOG_ALWAYS_FATAL("%s: Unimplemented packet type %d", __func__,
                        static_cast<int>(hci_packet_type_));
diff --git a/bluetooth/1.0/default/hci_internals.h b/bluetooth/1.0/default/hci_internals.h
index 24e944f..6f7ff90 100644
--- a/bluetooth/1.0/default/hci_internals.h
+++ b/bluetooth/1.0/default/hci_internals.h
@@ -44,6 +44,10 @@
 const size_t HCI_EVENT_PREAMBLE_SIZE = 2;
 const size_t HCI_LENGTH_OFFSET_EVT = 1;
 
+// 2 bytes for handle and flags, 2 byte for data length (Volume 4, Part E, 5.4.5)
+const size_t HCI_ISO_PREAMBLE_SIZE = 4;
+const size_t HCI_LENGTH_OFFSET_ISO = 2;
+
 const size_t HCI_PREAMBLE_SIZE_MAX = HCI_ACL_PREAMBLE_SIZE;
 
 // Event codes (Volume 2, Part E, 7.7.14)
diff --git a/bluetooth/1.0/default/hci_packetizer.cc b/bluetooth/1.0/default/hci_packetizer.cc
index 7cb3a11..78ce61d 100644
--- a/bluetooth/1.0/default/hci_packetizer.cc
+++ b/bluetooth/1.0/default/hci_packetizer.cc
@@ -26,17 +26,27 @@
 
 namespace {
 
-const size_t preamble_size_for_type[] = {
-    0, HCI_COMMAND_PREAMBLE_SIZE, HCI_ACL_PREAMBLE_SIZE, HCI_SCO_PREAMBLE_SIZE,
-    HCI_EVENT_PREAMBLE_SIZE};
-const size_t packet_length_offset_for_type[] = {
-    0, HCI_LENGTH_OFFSET_CMD, HCI_LENGTH_OFFSET_ACL, HCI_LENGTH_OFFSET_SCO,
-    HCI_LENGTH_OFFSET_EVT};
+const size_t preamble_size_for_type[] = {0,
+                                         HCI_COMMAND_PREAMBLE_SIZE,
+                                         HCI_ACL_PREAMBLE_SIZE,
+                                         HCI_SCO_PREAMBLE_SIZE,
+                                         HCI_EVENT_PREAMBLE_SIZE,
+                                         HCI_ISO_PREAMBLE_SIZE};
+const size_t packet_length_offset_for_type[] = {0,
+                                                HCI_LENGTH_OFFSET_CMD,
+                                                HCI_LENGTH_OFFSET_ACL,
+                                                HCI_LENGTH_OFFSET_SCO,
+                                                HCI_LENGTH_OFFSET_EVT,
+                                                HCI_LENGTH_OFFSET_ISO};
 
 size_t HciGetPacketLengthForType(HciPacketType type, const uint8_t* preamble) {
   size_t offset = packet_length_offset_for_type[type];
-  if (type != HCI_PACKET_TYPE_ACL_DATA) return preamble[offset];
-  return (((preamble[offset + 1]) << 8) | preamble[offset]);
+  if (type == HCI_PACKET_TYPE_ACL_DATA) {
+    return (((preamble[offset + 1]) << 8) | preamble[offset]);
+  } else if (type == HCI_PACKET_TYPE_ISO_DATA) {
+    return ((((preamble[offset + 1]) & 0x3f) << 8) | preamble[offset]);
+  }
+  return preamble[offset];
 }
 
 }  // namespace
diff --git a/bluetooth/1.0/default/test/h4_protocol_unittest.cc b/bluetooth/1.0/default/test/h4_protocol_unittest.cc
index 283243d..174861c 100644
--- a/bluetooth/1.0/default/test/h4_protocol_unittest.cc
+++ b/bluetooth/1.0/default/test/h4_protocol_unittest.cc
@@ -190,8 +190,10 @@
 
   void WriteAndExpectInboundIsoData(char* payload) {
     // h4 type[1] + handle[2] + size[1]
-    char preamble[4] = {HCI_PACKET_TYPE_ISO_DATA, 20, 17, 0};
-    preamble[3] = strlen(payload) & 0xFF;
+    char preamble[5] = {HCI_PACKET_TYPE_ISO_DATA, 19, 92, 0, 0};
+    int length = strlen(payload);
+    preamble[3] = length & 0xFF;
+    preamble[4] = (length >> 8) & 0x3F;
 
     ALOGD("%s writing", __func__);
     TEMP_FAILURE_RETRY(write(fake_uart_, preamble, sizeof(preamble)));
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
index 0f349a4..092038b 100644
--- a/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
@@ -55,12 +55,17 @@
     const V2_0::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
   AudioConfiguration audioConfig_2_1;
 
-  audioConfig_2_1.codecConfig() = audioConfig.codecConfig();
-  audioConfig_2_1.pcmConfig() = {
-      .sampleRate = static_cast<SampleRate>(audioConfig.pcmConfig().sampleRate),
-      .channelMode = audioConfig.pcmConfig().channelMode,
-      .bitsPerSample = audioConfig.pcmConfig().bitsPerSample,
-      .dataIntervalUs = 0};
+  if (audioConfig.getDiscriminator() ==
+      V2_0::AudioConfiguration::hidl_discriminator::pcmConfig) {
+    audioConfig_2_1.pcmConfig() = {
+        .sampleRate =
+            static_cast<SampleRate>(audioConfig.pcmConfig().sampleRate),
+        .channelMode = audioConfig.pcmConfig().channelMode,
+        .bitsPerSample = audioConfig.pcmConfig().bitsPerSample,
+        .dataIntervalUs = 0};
+  } else {
+    audioConfig_2_1.codecConfig() = audioConfig.codecConfig();
+  }
 
   return startSession_2_1(hostIf, audioConfig_2_1, _hidl_cb);
 }
diff --git a/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp b/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
index c0ec907..37d1281 100644
--- a/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
+++ b/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
@@ -1043,6 +1043,7 @@
     } else {
       EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
       EXPECT_FALSE(dataMQ.isHandleValid());
+      tempDataMQ.reset(nullptr);
     }
   };
   android::hardware::bluetooth::audio::V2_1::AudioConfiguration audio_config =
@@ -1064,6 +1065,8 @@
           ASSERT_TRUE(hidl_retval.isOk());
           if (is_codec_config_valid) {
             EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+          } else {
+            EXPECT_TRUE(tempDataMQ == nullptr);
           }
           EXPECT_TRUE(audio_provider_2_1_->endSession().isOk());
         }  // uint32_t (data interval in microseconds)
@@ -1132,6 +1135,7 @@
     } else {
       EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
       EXPECT_FALSE(dataMQ.isHandleValid());
+      tempDataMQ.reset(nullptr);
     }
   };
   android::hardware::bluetooth::audio::V2_1::AudioConfiguration audio_config =
diff --git a/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h b/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h
index 5468658..ac17d6d 100644
--- a/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h
+++ b/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h
@@ -32,6 +32,7 @@
   unsigned int GetNumberSlots();
   unsigned int GetCurrentSlot();
   bool MarkBootSuccessful();
+  unsigned int GetActiveBootSlot();
   bool SetActiveBootSlot(unsigned int slot);
   bool SetSlotAsUnbootable(unsigned int slot);
   bool SetSlotBootable(unsigned int slot);
diff --git a/boot/1.1/default/boot_control/libboot_control.cpp b/boot/1.1/default/boot_control/libboot_control.cpp
index 2c6ccaf..9387c32 100644
--- a/boot/1.1/default/boot_control/libboot_control.cpp
+++ b/boot/1.1/default/boot_control/libboot_control.cpp
@@ -261,6 +261,24 @@
   return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl);
 }
 
+unsigned int BootControl::GetActiveBootSlot() {
+  bootloader_control bootctrl;
+  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
+
+  // Use the current slot by default.
+  unsigned int active_boot_slot = current_slot_;
+  unsigned int max_priority = bootctrl.slot_info[current_slot_].priority;
+  // Find the slot with the highest priority.
+  for (unsigned int i = 0; i < num_slots_; ++i) {
+    if (bootctrl.slot_info[i].priority > max_priority) {
+      max_priority = bootctrl.slot_info[i].priority;
+      active_boot_slot = i;
+    }
+  }
+
+  return active_boot_slot;
+}
+
 bool BootControl::SetActiveBootSlot(unsigned int slot) {
   if (slot >= kMaxNumSlots || slot >= num_slots_) {
     // Invalid slot number.
diff --git a/boot/1.2/Android.bp b/boot/1.2/Android.bp
new file mode 100644
index 0000000..e51c5cd
--- /dev/null
+++ b/boot/1.2/Android.bp
@@ -0,0 +1,15 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.boot@1.2",
+    root: "android.hardware",
+    srcs: [
+        "IBootControl.hal",
+    ],
+    interfaces: [
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/boot/1.2/IBootControl.hal b/boot/1.2/IBootControl.hal
new file mode 100644
index 0000000..bb0ad13
--- /dev/null
+++ b/boot/1.2/IBootControl.hal
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.boot@1.2;
+
+import @1.0::IBootControl;
+import @1.0::Slot;
+import @1.1::IBootControl;
+
+interface IBootControl extends @1.1::IBootControl {
+
+  /**
+   * Returns the active slot to boot into on the next boot. If
+   * setActiveBootSlot() has been called, the getter function should return the
+   * same slot as the one provided in the last setActiveBootSlot() call.
+   * The returned value is always guaranteed to be strictly less than the
+   * value returned by getNumberSlots. Slots start at 0 and finish at
+   * getNumberSlots() - 1. For instance, a system with A/B must return 0 or 1.
+   */
+   getActiveBootSlot() generates (Slot slot);
+};
+
diff --git a/boot/1.2/default/Android.bp b/boot/1.2/default/Android.bp
new file mode 100644
index 0000000..c097667
--- /dev/null
+++ b/boot/1.2/default/Android.bp
@@ -0,0 +1,50 @@
+cc_library_shared {
+    name: "android.hardware.boot@1.2-impl",
+    stem: "android.hardware.boot@1.0-impl-1.2",
+    defaults: [
+        "hidl_defaults",
+        "libboot_control_defaults",
+    ],
+    relative_install_path: "hw",
+    vendor: true,
+    recovery_available: true,
+    srcs: ["BootControl.cpp"],
+
+    shared_libs: [
+        "liblog",
+        "libhidlbase",
+        "libhardware",
+        "libutils",
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
+        "android.hardware.boot@1.2",
+    ],
+    static_libs: [
+        "libboot_control",
+        "libfstab",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.boot@1.2-service",
+    defaults: ["hidl_defaults"],
+    relative_install_path: "hw",
+    vendor: true,
+    init_rc: ["android.hardware.boot@1.2-service.rc"],
+    srcs: ["service.cpp"],
+
+    vintf_fragments: [
+        "android.hardware.boot@1.2.xml",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libhardware",
+        "libhidlbase",
+        "libutils",
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
+        "android.hardware.boot@1.2",
+    ],
+
+}
diff --git a/boot/1.2/default/BootControl.cpp b/boot/1.2/default/BootControl.cpp
new file mode 100644
index 0000000..c0bf02f
--- /dev/null
+++ b/boot/1.2/default/BootControl.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "android.hardware.boot@1.2-impl"
+
+#include <memory>
+
+#include <log/log.h>
+
+#include "BootControl.h"
+
+namespace android {
+namespace hardware {
+namespace boot {
+namespace V1_2 {
+namespace implementation {
+
+using ::android::hardware::boot::V1_0::CommandResult;
+
+bool BootControl::Init() {
+    return impl_.Init();
+}
+
+// Methods from ::android::hardware::boot::V1_0::IBootControl.
+Return<uint32_t> BootControl::getNumberSlots() {
+    return impl_.GetNumberSlots();
+}
+
+Return<uint32_t> BootControl::getCurrentSlot() {
+    return impl_.GetCurrentSlot();
+}
+
+Return<void> BootControl::markBootSuccessful(markBootSuccessful_cb _hidl_cb) {
+    struct CommandResult cr;
+    if (impl_.MarkBootSuccessful()) {
+        cr.success = true;
+        cr.errMsg = "Success";
+    } else {
+        cr.success = false;
+        cr.errMsg = "Operation failed";
+    }
+    _hidl_cb(cr);
+    return Void();
+}
+
+Return<void> BootControl::setActiveBootSlot(uint32_t slot, setActiveBootSlot_cb _hidl_cb) {
+    struct CommandResult cr;
+    if (impl_.SetActiveBootSlot(slot)) {
+        cr.success = true;
+        cr.errMsg = "Success";
+    } else {
+        cr.success = false;
+        cr.errMsg = "Operation failed";
+    }
+    _hidl_cb(cr);
+    return Void();
+}
+
+Return<void> BootControl::setSlotAsUnbootable(uint32_t slot, setSlotAsUnbootable_cb _hidl_cb) {
+    struct CommandResult cr;
+    if (impl_.SetSlotAsUnbootable(slot)) {
+        cr.success = true;
+        cr.errMsg = "Success";
+    } else {
+        cr.success = false;
+        cr.errMsg = "Operation failed";
+    }
+    _hidl_cb(cr);
+    return Void();
+}
+
+Return<BoolResult> BootControl::isSlotBootable(uint32_t slot) {
+    if (!impl_.IsValidSlot(slot)) {
+        return BoolResult::INVALID_SLOT;
+    }
+    return impl_.IsSlotBootable(slot) ? BoolResult::TRUE : BoolResult::FALSE;
+}
+
+Return<BoolResult> BootControl::isSlotMarkedSuccessful(uint32_t slot) {
+    if (!impl_.IsValidSlot(slot)) {
+        return BoolResult::INVALID_SLOT;
+    }
+    return impl_.IsSlotMarkedSuccessful(slot) ? BoolResult::TRUE : BoolResult::FALSE;
+}
+
+Return<void> BootControl::getSuffix(uint32_t slot, getSuffix_cb _hidl_cb) {
+    hidl_string ans;
+    const char* suffix = impl_.GetSuffix(slot);
+    if (suffix) {
+        ans = suffix;
+    }
+    _hidl_cb(ans);
+    return Void();
+}
+
+// Methods from ::android::hardware::boot::V1_1::IBootControl.
+Return<bool> BootControl::setSnapshotMergeStatus(MergeStatus status) {
+    return impl_.SetSnapshotMergeStatus(status);
+}
+
+Return<MergeStatus> BootControl::getSnapshotMergeStatus() {
+    return impl_.GetSnapshotMergeStatus();
+}
+
+// Methods from ::android::hardware::boot::V1_2::IBootControl.
+Return<uint32_t> BootControl::getActiveBootSlot() {
+    return impl_.GetActiveBootSlot();
+}
+
+IBootControl* HIDL_FETCH_IBootControl(const char* /* hal */) {
+    auto module = std::make_unique<BootControl>();
+    if (!module->Init()) {
+        ALOGE("Could not initialize BootControl module");
+        return nullptr;
+    }
+    return module.release();
+}
+
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace boot
+}  // namespace hardware
+}  // namespace android
diff --git a/boot/1.2/default/BootControl.h b/boot/1.2/default/BootControl.h
new file mode 100644
index 0000000..5791699
--- /dev/null
+++ b/boot/1.2/default/BootControl.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/boot/1.2/IBootControl.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <libboot_control/libboot_control.h>
+
+namespace android {
+namespace hardware {
+namespace boot {
+namespace V1_2 {
+namespace implementation {
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::boot::V1_0::BoolResult;
+using ::android::hardware::boot::V1_1::MergeStatus;
+using ::android::hardware::boot::V1_2::IBootControl;
+
+class BootControl : public IBootControl {
+  public:
+    bool Init();
+
+    // Methods from ::android::hardware::boot::V1_0::IBootControl.
+    Return<uint32_t> getNumberSlots() override;
+    Return<uint32_t> getCurrentSlot() override;
+    Return<void> markBootSuccessful(markBootSuccessful_cb _hidl_cb) override;
+    Return<void> setActiveBootSlot(uint32_t slot, setActiveBootSlot_cb _hidl_cb) override;
+    Return<void> setSlotAsUnbootable(uint32_t slot, setSlotAsUnbootable_cb _hidl_cb) override;
+    Return<BoolResult> isSlotBootable(uint32_t slot) override;
+    Return<BoolResult> isSlotMarkedSuccessful(uint32_t slot) override;
+    Return<void> getSuffix(uint32_t slot, getSuffix_cb _hidl_cb) override;
+
+    // Methods from ::android::hardware::boot::V1_1::IBootControl.
+    Return<bool> setSnapshotMergeStatus(MergeStatus status) override;
+    Return<MergeStatus> getSnapshotMergeStatus() override;
+
+    // Methods from ::android::hardware::boot::V1_2::IBootControl.
+    Return<uint32_t> getActiveBootSlot() override;
+
+  private:
+    android::bootable::BootControl impl_;
+};
+
+extern "C" IBootControl* HIDL_FETCH_IBootControl(const char* name);
+
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace boot
+}  // namespace hardware
+}  // namespace android
diff --git a/boot/1.2/default/android.hardware.boot@1.2-service.rc b/boot/1.2/default/android.hardware.boot@1.2-service.rc
new file mode 100644
index 0000000..14926c0
--- /dev/null
+++ b/boot/1.2/default/android.hardware.boot@1.2-service.rc
@@ -0,0 +1,7 @@
+service vendor.boot-hal-1-2 /vendor/bin/hw/android.hardware.boot@1.2-service
+    interface android.hardware.boot@1.0::IBootControl default
+    interface android.hardware.boot@1.1::IBootControl default
+    interface android.hardware.boot@1.2::IBootControl default
+    class early_hal
+    user root
+    group root
diff --git a/boot/1.2/default/android.hardware.boot@1.2.xml b/boot/1.2/default/android.hardware.boot@1.2.xml
new file mode 100644
index 0000000..ba91e8f
--- /dev/null
+++ b/boot/1.2/default/android.hardware.boot@1.2.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.boot</name>
+        <transport>hwbinder</transport>
+        <fqname>@1.2::IBootControl/default</fqname>
+    </hal>
+</manifest>
diff --git a/boot/1.2/default/service.cpp b/boot/1.2/default/service.cpp
new file mode 100644
index 0000000..3053957
--- /dev/null
+++ b/boot/1.2/default/service.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "android.hardware.boot@1.2-service"
+
+#include <android/hardware/boot/1.2/IBootControl.h>
+#include <hidl/LegacySupport.h>
+
+using android::hardware::defaultPassthroughServiceImplementation;
+using IBootControl_V1_0 = android::hardware::boot::V1_0::IBootControl;
+using IBootControl_V1_2 = android::hardware::boot::V1_2::IBootControl;
+
+int main(int /* argc */, char* /* argv */[]) {
+    return defaultPassthroughServiceImplementation<IBootControl_V1_0, IBootControl_V1_2>();
+}
diff --git a/boot/1.2/vts/functional/Android.bp b/boot/1.2/vts/functional/Android.bp
new file mode 100644
index 0000000..a7f5ccb
--- /dev/null
+++ b/boot/1.2/vts/functional/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "VtsHalBootV1_2TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalBootV1_2TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
+        "android.hardware.boot@1.2",
+        "libgmock",
+    ],
+    test_suites: [
+        "device-tests",
+        "vts",
+    ],
+}
diff --git a/boot/1.2/vts/functional/VtsHalBootV1_2TargetTest.cpp b/boot/1.2/vts/functional/VtsHalBootV1_2TargetTest.cpp
new file mode 100644
index 0000000..9df23c3
--- /dev/null
+++ b/boot/1.2/vts/functional/VtsHalBootV1_2TargetTest.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "boot_hidl_hal_test"
+
+#include <android-base/logging.h>
+#include <android/hardware/boot/1.2/IBootControl.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include <unistd.h>
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::boot::V1_0::CommandResult;
+using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::boot::V1_2::IBootControl;
+
+class BootHidlTest : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        boot = IBootControl::getService(GetParam());
+        ASSERT_NE(boot, nullptr);
+
+        LOG(INFO) << "Test is remote " << boot->isRemote();
+    }
+
+    sp<IBootControl> boot;
+};
+
+auto generate_callback(CommandResult* dest) {
+    return [=](CommandResult cr) { *dest = cr; };
+}
+
+TEST_P(BootHidlTest, GetActiveBootSlot) {
+    Slot curSlot = boot->getCurrentSlot();
+    Slot otherSlot = curSlot ? 0 : 1;
+
+    // Set the active slot, then check if the getter returns the correct slot.
+    CommandResult cr;
+    Return<void> result = boot->setActiveBootSlot(otherSlot, generate_callback(&cr));
+    EXPECT_TRUE(result.isOk());
+    Slot activeSlot = boot->getActiveBootSlot();
+    EXPECT_EQ(otherSlot, activeSlot);
+
+    result = boot->setActiveBootSlot(curSlot, generate_callback(&cr));
+    EXPECT_TRUE(result.isOk());
+    activeSlot = boot->getActiveBootSlot();
+    EXPECT_EQ(curSlot, activeSlot);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BootHidlTest);
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, BootHidlTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBootControl::descriptor)),
+        android::hardware::PrintInstanceNameToString);
diff --git a/camera/common/1.0/default/CameraModule.cpp b/camera/common/1.0/default/CameraModule.cpp
index 86f26e4..27e74f1 100644
--- a/camera/common/1.0/default/CameraModule.cpp
+++ b/camera/common/1.0/default/CameraModule.cpp
@@ -529,24 +529,29 @@
 }
 
 void CameraModule::removeCamera(int cameraId) {
-    std::unordered_set<std::string> physicalIds;
-    camera_metadata_t *metadata = const_cast<camera_metadata_t*>(
-            mCameraInfoMap.valueFor(cameraId).static_camera_characteristics);
-    common::V1_0::helper::CameraMetadata hidlMetadata(metadata);
+    // Skip HAL1 devices which isn't cached in mCameraInfoMap and don't advertise
+    // static_camera_characteristics
+    if (getDeviceVersion(cameraId) >= CAMERA_DEVICE_API_VERSION_3_0) {
+        std::unordered_set<std::string> physicalIds;
+        camera_metadata_t *metadata = const_cast<camera_metadata_t*>(
+                mCameraInfoMap.valueFor(cameraId).static_camera_characteristics);
+        common::V1_0::helper::CameraMetadata hidlMetadata(metadata);
 
-    if (isLogicalMultiCamera(hidlMetadata, &physicalIds)) {
-        for (const auto& id : physicalIds) {
-            int idInt = std::stoi(id);
-            if (mPhysicalCameraInfoMap.indexOfKey(idInt) >= 0) {
-                free_camera_metadata(mPhysicalCameraInfoMap[idInt]);
-                mPhysicalCameraInfoMap.removeItem(idInt);
-            } else {
-                ALOGE("%s: Cannot find corresponding static metadata for physical id %s",
-                        __FUNCTION__, id.c_str());
+        if (isLogicalMultiCamera(hidlMetadata, &physicalIds)) {
+            for (const auto& id : physicalIds) {
+                int idInt = std::stoi(id);
+                if (mPhysicalCameraInfoMap.indexOfKey(idInt) >= 0) {
+                    free_camera_metadata(mPhysicalCameraInfoMap[idInt]);
+                    mPhysicalCameraInfoMap.removeItem(idInt);
+                } else {
+                    ALOGE("%s: Cannot find corresponding static metadata for physical id %s",
+                            __FUNCTION__, id.c_str());
+                }
             }
         }
+        free_camera_metadata(metadata);
     }
-    free_camera_metadata(metadata);
+
     mCameraInfoMap.removeItem(cameraId);
     mDeviceVersionMap.removeItem(cameraId);
 }
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index aa5c48f..9e2b077 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -1209,7 +1209,12 @@
             return notify;
         }
 
-        if (physicalCameraMetadata.size() != request->expectedPhysicalResults.size()) {
+        // Physical device results are only expected in the last/final
+        // partial result notification.
+        bool expectPhysicalResults = !(request->usePartialResult &&
+                (results.partialResult < request->numPartialResults));
+        if (expectPhysicalResults &&
+                (physicalCameraMetadata.size() != request->expectedPhysicalResults.size())) {
             ALOGE("%s: Frame %d: Returned physical metadata count %zu "
                     "must be equal to expected count %zu", __func__, frameNumber,
                     physicalCameraMetadata.size(), request->expectedPhysicalResults.size());
@@ -1260,9 +1265,27 @@
             ADD_FAILURE();
             return notify;
         }
-        request->collectedResult.append(
-                reinterpret_cast<const camera_metadata_t*>(
-                    resultMetadata.data()));
+
+        // Verify no duplicate tags between partial results
+        const camera_metadata_t* partialMetadata =
+                reinterpret_cast<const camera_metadata_t*>(resultMetadata.data());
+        const camera_metadata_t* collectedMetadata = request->collectedResult.getAndLock();
+        camera_metadata_ro_entry_t searchEntry, foundEntry;
+        for (size_t i = 0; i < get_camera_metadata_size(partialMetadata); i++) {
+            if (0 != get_camera_metadata_ro_entry(partialMetadata, i, &searchEntry)) {
+                ADD_FAILURE();
+                request->collectedResult.unlock(collectedMetadata);
+                return notify;
+            }
+            if (-ENOENT !=
+                find_camera_metadata_ro_entry(collectedMetadata, searchEntry.tag, &foundEntry)) {
+                ADD_FAILURE();
+                request->collectedResult.unlock(collectedMetadata);
+                return notify;
+            }
+        }
+        request->collectedResult.unlock(collectedMetadata);
+        request->collectedResult.append(partialMetadata);
 
         isPartialResult =
             (results.partialResult < request->numPartialResults);
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 57f390c..b16ae40 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -134,7 +134,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.boot</name>
-        <version>1.1</version>
+        <version>1.2</version>
         <interface>
             <name>IBootControl</name>
             <instance>default</instance>
@@ -319,6 +319,14 @@
         </interface>
     </hal>
     <hal format="aidl" optional="true">
+        <name>android.hardware.security.keymint</name>
+        <interface>
+            <name>IKeyMintDevice</name>
+            <instance>default</instance>
+            <instance>strongbox</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
         <name>android.hardware.light</name>
         <interface>
             <name>ILights</name>
@@ -422,6 +430,14 @@
         </interface>
     </hal>
     <hal format="hidl" optional="true">
+        <name>android.hardware.radio.config</name>
+        <version>1.3</version>
+        <interface>
+            <name>IRadioConfig</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
         <name>android.hardware.renderscript</name>
         <version>1.0</version>
         <interface>
@@ -521,7 +537,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.usb.gadget</name>
-        <version>1.0-1</version>
+        <version>1.0-2</version>
         <interface>
             <name>IUsbGadget</name>
             <instance>default</instance>
@@ -534,6 +550,13 @@
             <instance>default</instance>
         </interface>
     </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.vibrator</name>
+        <interface>
+            <name>IVibratorManager</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.vr</name>
         <version>1.0</version>
diff --git a/current.txt b/current.txt
index 7ef176b..8623fc0 100644
--- a/current.txt
+++ b/current.txt
@@ -769,10 +769,18 @@
 # ABI preserving changes to HALs during Android S
 1ca372cd67d197df099e87616a613ba6ede6552638a603e18f86c8834302c3d1 android.hardware.gnss@1.0::IGnssMeasurementCallback
 6a271e493907e8ba20912e42771bd0d99ae45431a851d5675ef9496d02510a34 android.hardware.gnss@1.1::IGnssMeasurementCallback
+2c331a9605f3a08d9c1e0a36169ca57758bc43c11a78ef3f3730509885e52c15 android.hardware.graphics.composer@2.4::IComposerClient
 3da3ce039247872d95c6bd48621dbfdfa1c2d2a91a90f257862f87ee2bc46300 android.hardware.health@2.1::types
+9679f27a42f75781c8993ef163ed92808a1928de186639834841d0b8e326e63d android.hardware.gatekeeper@1.0::IGatekeeper
+9c4eb603d7b9ad675a14edb6180681c5a78da5c6bdc7755853912c974a21f7e5 android.hardware.gnss@1.0::IAGnssCallback
+40456eb90ea88b62d18ad3fbf1da8917981cd55ac04ce69c8e058d49ff5beff4 android.hardware.keymaster@3.0::IKeymasterDevice
+6017b4f2481feb0fffceae81c62bc372c898998b2d8fe69fbd39859d3a315e5e android.hardware.keymaster@4.0::IKeymasterDevice
+dabe23dde7c9e3ad65c61def7392f186d7efe7f4216f9b6f9cf0863745b1a9f4 android.hardware.keymaster@4.1::IKeymasterDevice
 cd84ab19c590e0e73dd2307b591a3093ee18147ef95e6d5418644463a6620076 android.hardware.neuralnetworks@1.2::IDevice
 9625e85f56515ad2cf87b6a1847906db669f746ea4ab02cd3d4ca25abc9b0109 android.hardware.neuralnetworks@1.2::types
 9e758e208d14f7256e0885d6d8ad0b61121b21d8c313864f981727ae55bffd16 android.hardware.neuralnetworks@1.3::types
+e8c86c69c438da8d1549856c1bb3e2d1b8da52722f8235ff49a30f2cce91742c android.hardware.soundtrigger@2.1::ISoundTriggerHwCallback
+b9fbb6e2e061ed0960939d48b785e9700210add1f13ed32ecd688d0f1ca20ef7 android.hardware.renderscript@1.0::types
 0f53d70e1eadf8d987766db4bf6ae2048004682168f4cab118da576787def3fa android.hardware.radio@1.0::types
 38d65fb20c60a5b823298560fc0825457ecdc49603a4b4e94bf81511790737da android.hardware.radio@1.4::types
 954c334efd80e8869b66d1ce5fe2755712d96ba4b3c38d415739c330af5fb4cb android.hardware.radio@1.5::types
diff --git a/gatekeeper/1.0/Android.bp b/gatekeeper/1.0/Android.bp
index 28fd5b6..f5cb8e4 100644
--- a/gatekeeper/1.0/Android.bp
+++ b/gatekeeper/1.0/Android.bp
@@ -10,5 +10,5 @@
     interfaces: [
         "android.hidl.base@1.0",
     ],
-    gen_java: true,
+    gen_java: false,
 }
diff --git a/gatekeeper/1.0/IGatekeeper.hal b/gatekeeper/1.0/IGatekeeper.hal
index 59dd7d1..84e8e06 100644
--- a/gatekeeper/1.0/IGatekeeper.hal
+++ b/gatekeeper/1.0/IGatekeeper.hal
@@ -15,6 +15,7 @@
  */
 package android.hardware.gatekeeper@1.0;
 
+@SensitiveData
 interface IGatekeeper {
 
 /**
diff --git a/gnss/1.0/IAGnssCallback.hal b/gnss/1.0/IAGnssCallback.hal
index 81f1689..11a6a5d 100644
--- a/gnss/1.0/IAGnssCallback.hal
+++ b/gnss/1.0/IAGnssCallback.hal
@@ -42,7 +42,6 @@
     /**
      * Represents the status of AGNSS augmented to support IPv4.
      */
-    @export(name="", value_prefix="GPS_")
     struct AGnssStatusIpV4 {
         AGnssType type;
         AGnssStatusValue status;
diff --git a/gnss/1.1/default/Android.bp b/gnss/1.1/default/Android.bp
index 9c498d5..ef43a34 100644
--- a/gnss/1.1/default/Android.bp
+++ b/gnss/1.1/default/Android.bp
@@ -18,6 +18,7 @@
         "android.hardware.gnss@2.0",
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@1.0",
+        "android.hardware.gnss-ndk_platform",
     ],
     static_libs: [
         "android.hardware.gnss@common-default-lib",
diff --git a/gnss/1.1/default/GnssDebug.cpp b/gnss/1.1/default/GnssDebug.cpp
index 252f4e6..39efcd2 100644
--- a/gnss/1.1/default/GnssDebug.cpp
+++ b/gnss/1.1/default/GnssDebug.cpp
@@ -37,8 +37,8 @@
             .latitudeDegrees = gMockLatitudeDegrees,
             .longitudeDegrees = gMockLongitudeDegrees,
             .altitudeMeters = gMockAltitudeMeters,
-            .speedMetersPerSec = kMockSpeedMetersPerSec,
-            .bearingDegrees = kMockBearingDegrees,
+            .speedMetersPerSec = gMockSpeedMetersPerSec,
+            .bearingDegrees = gMockBearingDegrees,
             .horizontalAccuracyMeters = kMockHorizontalAccuracyMeters,
             .verticalAccuracyMeters = kMockVerticalAccuracyMeters,
             .speedAccuracyMetersPerSecond = kMockSpeedAccuracyMetersPerSecond,
diff --git a/gnss/2.0/default/Android.bp b/gnss/2.0/default/Android.bp
index 37de55d..7da462f 100644
--- a/gnss/2.0/default/Android.bp
+++ b/gnss/2.0/default/Android.bp
@@ -29,7 +29,7 @@
         "GnssMeasurement.cpp",
         "GnssMeasurementCorrections.cpp",
         "GnssVisibilityControl.cpp",
-        "service.cpp"
+        "service.cpp",
     ],
     shared_libs: [
         "libhidlbase",
@@ -39,8 +39,9 @@
         "android.hardware.gnss.visibility_control@1.0",
         "android.hardware.gnss@2.1",
         "android.hardware.gnss@2.0",
-        "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
+        "android.hardware.gnss@1.0",
+        "android.hardware.gnss-ndk_platform",
     ],
     static_libs: [
         "android.hardware.gnss@common-default-lib",
diff --git a/gnss/2.1/default/Android.bp b/gnss/2.1/default/Android.bp
index 7739f90..63e5013 100644
--- a/gnss/2.1/default/Android.bp
+++ b/gnss/2.1/default/Android.bp
@@ -34,6 +34,7 @@
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@2.0",
+        "android.hardware.gnss-ndk_platform",
     ],
     static_libs: [
         "android.hardware.gnss@common-default-lib",
diff --git a/gnss/2.1/vts/functional/gnss_hal_test.h b/gnss/2.1/vts/functional/gnss_hal_test.h
index 7950670..2bcecf4 100644
--- a/gnss/2.1/vts/functional/gnss_hal_test.h
+++ b/gnss/2.1/vts/functional/gnss_hal_test.h
@@ -19,10 +19,9 @@
 #include <android/hardware/gnss/2.1/IGnss.h>
 #include "v2_1/gnss_hal_test_template.h"
 
-using android::hardware::gnss::V2_1::IGnss;
-
 // The main test class for GNSS HAL.
-class GnssHalTest : public GnssHalTestTemplate<IGnss> {
+class GnssHalTest : public android::hardware::gnss::common::GnssHalTestTemplate<
+                            android::hardware::gnss::V2_1::IGnss> {
   public:
     /**
      * IsGnssHalVersion_2_1:
diff --git a/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
index 7afd49c..deb80e8 100644
--- a/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
@@ -27,6 +27,9 @@
 
 using android::hardware::gnss::common::Utils;
 
+using android::hardware::gnss::V2_1::IGnssAntennaInfo;
+using android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
+
 using IGnssMeasurement_2_1 = android::hardware::gnss::V2_1::IGnssMeasurement;
 using IGnssMeasurement_2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
 using IGnssMeasurement_1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
diff --git a/gnss/3.0/default/Android.bp b/gnss/3.0/default/Android.bp
index 2b33b32..bb3c467 100644
--- a/gnss/3.0/default/Android.bp
+++ b/gnss/3.0/default/Android.bp
@@ -36,6 +36,7 @@
         "android.hardware.gnss@3.0",
         "android.hardware.gnss.measurement_corrections@1.1",
         "android.hardware.gnss.measurement_corrections@1.0",
+        "android.hardware.gnss-ndk_platform",
     ],
     static_libs: [
         "android.hardware.gnss@common-default-lib",
diff --git a/gnss/3.0/vts/functional/gnss_hal_test.h b/gnss/3.0/vts/functional/gnss_hal_test.h
index 387214e..be6d38c 100644
--- a/gnss/3.0/vts/functional/gnss_hal_test.h
+++ b/gnss/3.0/vts/functional/gnss_hal_test.h
@@ -19,7 +19,6 @@
 #include <android/hardware/gnss/3.0/IGnss.h>
 #include "v2_1/gnss_hal_test_template.h"
 
-using android::hardware::gnss::V3_0::IGnss;
-
 // The main test class for GNSS HAL.
-class GnssHalTest : public GnssHalTestTemplate<IGnss> {};
\ No newline at end of file
+class GnssHalTest : public android::hardware::gnss::common::GnssHalTestTemplate<
+                            android::hardware::gnss::V3_0::IGnss> {};
\ No newline at end of file
diff --git a/gnss/aidl/TEST_MAPPING b/gnss/aidl/TEST_MAPPING
new file mode 100644
index 0000000..c21c3e4
--- /dev/null
+++ b/gnss/aidl/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "VtsHalGnssTargetTest"
+    }
+  ]
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/ElapsedRealtime.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/ElapsedRealtime.aidl
new file mode 100644
index 0000000..a0e8de4
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/ElapsedRealtime.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+parcelable ElapsedRealtime {
+  int flags;
+  long timestampNs;
+  double timeUncertaintyNs;
+  const int HAS_TIMESTAMP_NS = 1;
+  const int HAS_TIME_UNCERTAINTY_NS = 2;
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssClock.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssClock.aidl
new file mode 100644
index 0000000..42b940e
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssClock.aidl
@@ -0,0 +1,39 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+parcelable GnssClock {
+  int gnssClockFlags;
+  int leapSecond;
+  long timeNs;
+  double timeUncertaintyNs;
+  long fullBiasNs;
+  double biasNs;
+  double biasUncertaintyNs;
+  double driftNsps;
+  double driftUncertaintyNsps;
+  int hwClockDiscontinuityCount;
+  android.hardware.gnss.GnssSignalType referenceSignalTypeForIsb;
+  const int HAS_LEAP_SECOND = 1;
+  const int HAS_TIME_UNCERTAINTY = 2;
+  const int HAS_FULL_BIAS = 4;
+  const int HAS_BIAS = 8;
+  const int HAS_BIAS_UNCERTAINTY = 16;
+  const int HAS_DRIFT = 32;
+  const int HAS_DRIFT_UNCERTAINTY = 64;
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssData.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssData.aidl
new file mode 100644
index 0000000..7ffabd2
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssData.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+parcelable GnssData {
+  android.hardware.gnss.GnssMeasurement[] measurements;
+  android.hardware.gnss.GnssClock clock;
+  android.hardware.gnss.ElapsedRealtime elapsedRealtime;
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMeasurement.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMeasurement.aidl
new file mode 100644
index 0000000..7d15855
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMeasurement.aidl
@@ -0,0 +1,79 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+parcelable GnssMeasurement {
+  int flags;
+  int svid;
+  android.hardware.gnss.GnssSignalType signalType;
+  double timeOffsetNs;
+  int state;
+  long receivedSvTimeInNs;
+  long receivedSvTimeUncertaintyInNs;
+  double antennaCN0DbHz;
+  double basebandCN0DbHz;
+  double pseudorangeRateMps;
+  double pseudorangeRateUncertaintyMps;
+  int accumulatedDeltaRangeState;
+  double accumulatedDeltaRangeM;
+  double accumulatedDeltaRangeUncertaintyM;
+  float carrierFrequencyHz;
+  long carrierCycles;
+  double carrierPhase;
+  double carrierPhaseUncertainty;
+  android.hardware.gnss.GnssMultipathIndicator multipathIndicator;
+  double snrDb;
+  double agcLevelDb;
+  double fullInterSignalBiasNs;
+  double fullInterSignalBiasUncertaintyNs;
+  double satelliteInterSignalBiasNs;
+  double satelliteInterSignalBiasUncertaintyNs;
+  const int HAS_SNR = 1;
+  const int HAS_CARRIER_FREQUENCY = 512;
+  const int HAS_CARRIER_CYCLES = 1024;
+  const int HAS_CARRIER_PHASE = 2048;
+  const int HAS_CARRIER_PHASE_UNCERTAINTY = 4096;
+  const int HAS_AUTOMATIC_GAIN_CONTROL = 8192;
+  const int HAS_FULL_ISB = 65536;
+  const int HAS_FULL_ISB_UNCERTAINTY = 131072;
+  const int HAS_SATELLITE_ISB = 262144;
+  const int HAS_SATELLITE_ISB_UNCERTAINTY = 524288;
+  const int STATE_UNKNOWN = 0;
+  const int STATE_CODE_LOCK = 1;
+  const int STATE_BIT_SYNC = 2;
+  const int STATE_SUBFRAME_SYNC = 4;
+  const int STATE_TOW_DECODED = 8;
+  const int STATE_MSEC_AMBIGUOUS = 16;
+  const int STATE_SYMBOL_SYNC = 32;
+  const int STATE_GLO_STRING_SYNC = 64;
+  const int STATE_GLO_TOD_DECODED = 128;
+  const int STATE_BDS_D2_BIT_SYNC = 256;
+  const int STATE_BDS_D2_SUBFRAME_SYNC = 512;
+  const int STATE_GAL_E1BC_CODE_LOCK = 1024;
+  const int STATE_GAL_E1C_2ND_CODE_LOCK = 2048;
+  const int STATE_GAL_E1B_PAGE_SYNC = 4096;
+  const int STATE_SBAS_SYNC = 8192;
+  const int STATE_TOW_KNOWN = 16384;
+  const int STATE_GLO_TOD_KNOWN = 32768;
+  const int STATE_2ND_CODE_LOCK = 65536;
+  const int ADR_STATE_UNKNOWN = 0;
+  const int ADR_STATE_VALID = 1;
+  const int ADR_STATE_RESET = 2;
+  const int ADR_STATE_CYCLE_SLIP = 4;
+  const int ADR_STATE_HALF_CYCLE_RESOLVED = 8;
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMultipathIndicator.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMultipathIndicator.aidl
new file mode 100644
index 0000000..75ca3af
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssMultipathIndicator.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@Backing(type="int") @VintfStability
+enum GnssMultipathIndicator {
+  UNKNOWN = 0,
+  PRESENT = 1,
+  NOT_PRESENT = 2,
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssPowerStats.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssPowerStats.aidl
new file mode 100644
index 0000000..d385fd4
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssPowerStats.aidl
@@ -0,0 +1,28 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+parcelable GnssPowerStats {
+  android.hardware.gnss.ElapsedRealtime elapsedRealtime;
+  double totalEnergyMilliJoule;
+  double singlebandTrackingModeEnergyMilliJoule;
+  double multibandTrackingModeEnergyMilliJoule;
+  double singlebandAcquisitionModeEnergyMilliJoule;
+  double multibandAcquisitionModeEnergyMilliJoule;
+  double[] otherModesEnergyMilliJoule;
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssSignalType.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssSignalType.aidl
new file mode 100644
index 0000000..3e66c4a
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/GnssSignalType.aidl
@@ -0,0 +1,40 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+parcelable GnssSignalType {
+  android.hardware.gnss.GnssConstellationType constellation;
+  double carrierFrequencyHz;
+  String codeType;
+  const String CODE_TYPE_A = "A";
+  const String CODE_TYPE_B = "B";
+  const String CODE_TYPE_C = "C";
+  const String CODE_TYPE_D = "D";
+  const String CODE_TYPE_I = "I";
+  const String CODE_TYPE_L = "L";
+  const String CODE_TYPE_M = "M";
+  const String CODE_TYPE_N = "N";
+  const String CODE_TYPE_P = "P";
+  const String CODE_TYPE_Q = "Q";
+  const String CODE_TYPE_S = "S";
+  const String CODE_TYPE_W = "W";
+  const String CODE_TYPE_X = "X";
+  const String CODE_TYPE_Y = "Y";
+  const String CODE_TYPE_Z = "Z";
+  const String CODE_TYPE_UNKNOWN = "UNKNOWN";
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl
index 146577e..10ac150 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl
@@ -22,5 +22,7 @@
   void close();
   android.hardware.gnss.IGnssPsds getExtensionPsds();
   android.hardware.gnss.IGnssConfiguration getExtensionGnssConfiguration();
+  android.hardware.gnss.IGnssMeasurementInterface getExtensionGnssMeasurement();
+  android.hardware.gnss.IGnssPowerIndication getExtensionGnssPowerIndication();
   const int ERROR_INVALID_ARGUMENT = 1;
 }
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementCallback.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementCallback.aidl
new file mode 100644
index 0000000..e05e9b9
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementCallback.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+interface IGnssMeasurementCallback {
+  void gnssMeasurementCb(in android.hardware.gnss.GnssData data);
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementInterface.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementInterface.aidl
new file mode 100644
index 0000000..9576205
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssMeasurementInterface.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+interface IGnssMeasurementInterface {
+  void setCallback(in android.hardware.gnss.IGnssMeasurementCallback callback, in boolean enableFullTracking);
+  void close();
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndication.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndication.aidl
new file mode 100644
index 0000000..843489e
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndication.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+interface IGnssPowerIndication {
+  void setCallback(in android.hardware.gnss.IGnssPowerIndicationCallback callback);
+  oneway void requestGnssPowerStats();
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndicationCallback.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndicationCallback.aidl
new file mode 100644
index 0000000..5281d29
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssPowerIndicationCallback.aidl
@@ -0,0 +1,29 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+interface IGnssPowerIndicationCallback {
+  void setCapabilitiesCb(in int capabilities);
+  oneway void gnssPowerStatsCb(in android.hardware.gnss.GnssPowerStats gnssPowerStats);
+  const int CAPABILITY_TOTAL = 1;
+  const int CAPABILITY_SINGLEBAND_TRACKING = 2;
+  const int CAPABILITY_MULTIBAND_TRACKING = 4;
+  const int CAPABILITY_SINGLEBAND_ACQUISITION = 8;
+  const int CAPABILITY_MULTIBAND_ACQUISITION = 16;
+  const int CAPABILITY_OTHER_MODES = 32;
+}
diff --git a/gnss/aidl/android/hardware/gnss/ElapsedRealtime.aidl b/gnss/aidl/android/hardware/gnss/ElapsedRealtime.aidl
new file mode 100644
index 0000000..67d090e
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/ElapsedRealtime.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss;
+
+/**
+ * Cumulative GNSS power statistics since boot.
+ */
+@VintfStability
+parcelable ElapsedRealtime {
+
+    /** Bit mask indicating a valid timestampNs is stored in the ElapsedRealtime parcelable. */
+    const int HAS_TIMESTAMP_NS = 1 << 0;
+
+    /**
+     * Bit mask indicating a valid timeUncertaintyNs is stored in the ElapsedRealtime parcelable.
+     */
+    const int HAS_TIME_UNCERTAINTY_NS = 1 << 1;
+
+    /**
+     * A bit field of flags indicating the validity of each field in this data structure.
+     *
+     * The bit masks are the constants with prefix HAS_.
+     *
+     * Fields may have invalid information in them, if not marked as valid by the corresponding bit
+     * in flags.
+     */
+    int flags;
+
+    /**
+     * Estimate of the elapsed time since boot value for the corresponding event in nanoseconds.
+     */
+    long timestampNs;
+
+    /**
+     * Estimate of the relative precision of the alignment of this SystemClock timestamp, with the
+     * reported measurements in nanoseconds (68% confidence).
+     */
+    double timeUncertaintyNs;
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/GnssClock.aidl b/gnss/aidl/android/hardware/gnss/GnssClock.aidl
new file mode 100644
index 0000000..f416e08
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/GnssClock.aidl
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.GnssSignalType;
+
+/**
+ * Represents an estimate of the GNSS clock time.
+ */
+@VintfStability
+parcelable GnssClock {
+    /** Bit mask indicating a valid 'leap second' is stored in the GnssClock. */
+    const int HAS_LEAP_SECOND        = 1 << 0;
+    /** Bit mask indicating a valid 'time uncertainty' is stored in the GnssClock. */
+    const int HAS_TIME_UNCERTAINTY   = 1 << 1;
+    /** Bit mask indicating a valid 'full bias' is stored in the GnssClock. */
+    const int HAS_FULL_BIAS          = 1 << 2;
+    /** Bit mask indicating a valid 'bias' is stored in the GnssClock. */
+    const int HAS_BIAS               = 1 << 3;
+    /** Bit mask indicating a valid 'bias uncertainty' is stored in the GnssClock. */
+    const int HAS_BIAS_UNCERTAINTY   = 1 << 4;
+    /** Bit mask indicating a valid 'drift' is stored in the GnssClock. */
+    const int HAS_DRIFT              = 1 << 5;
+    /** Bit mask indicating a valid 'drift uncertainty' is stored in the GnssClock. */
+    const int HAS_DRIFT_UNCERTAINTY  = 1 << 6;
+
+    /**
+     * A bitfield of flags indicating the validity of the fields in this data
+     * structure.
+     *
+     * The bit masks are the constants with perfix HAS_.
+     *
+     * Fields for which there is no corresponding flag must be filled in
+     * with a valid value.  For convenience, these are marked as mandatory.
+     *
+     * Others fields may have invalid information in them, if not marked as
+     * valid by the corresponding bit in gnssClockFlags.
+     */
+    int gnssClockFlags;
+
+    /**
+     * Leap second data.
+     * The sign of the value is defined by the following equation:
+     *      utcTimeNs = timeNs - (fullBiasNs + biasNs) - leapSecond *
+     *      1,000,000,000
+     *
+     * If this data is available, gnssClockFlags must contain
+     * HAS_LEAP_SECOND.
+     */
+    int leapSecond;
+
+    /**
+     * The GNSS receiver internal clock value. This is the local hardware clock
+     * value.
+     *
+     * For local hardware clock, this value is expected to be monotonically
+     * increasing while the hardware clock remains powered on. (For the case of a
+     * HW clock that is not continuously on, see the
+     * hwClockDiscontinuityCount field). The receiver's estimate of GNSS time
+     * can be derived by subtracting the sum of fullBiasNs and biasNs (when
+     * available) from this value.
+     *
+     * This GNSS time must be the best estimate of current GNSS time
+     * that GNSS receiver can achieve.
+     *
+     * Sub-nanosecond accuracy can be provided by means of the 'biasNs' field.
+     * The value contains the timeUncertaintyNs in it.
+     *
+     * This value is mandatory.
+     */
+    long timeNs;
+
+    /**
+     * 1-Sigma uncertainty associated with the clock's time in nanoseconds.
+     * The uncertainty is represented as an absolute (single sided) value.
+     *
+     * If the data is available, gnssClockFlags must contain
+     * HAS_TIME_UNCERTAINTY. Ths value is ideally zero, as the time
+     * 'latched' by timeNs is defined as the reference clock vs. which all
+     * other times (and corresponding uncertainties) are measured.
+     */
+    double timeUncertaintyNs;
+
+    /**
+     * The difference between hardware clock ('time' field) inside GNSS receiver
+     * and the true GPS time since 0000Z, January 6, 1980, in nanoseconds.
+     *
+     * The sign of the value is defined by the following equation:
+     *      local estimate of GPS time = timeNs - (fullBiasNs + biasNs)
+     *
+     * If receiver has computed time for a non-GPS constellation, the time offset of
+     * that constellation versus GPS time must be applied to fill this value.
+     *
+     * The error estimate for the sum of this and the biasNs is the biasUncertaintyNs.
+     *
+     * If the data is available gnssClockFlags must contain HAS_FULL_BIAS.
+     *
+     * This value is mandatory if the receiver has estimated GPS time.
+     */
+    long fullBiasNs;
+
+    /**
+     * Sub-nanosecond bias - used with fullBiasNS, see fullBiasNs for details.
+     *
+     * The error estimate for the sum of this and the fullBiasNs is the
+     * biasUncertaintyNs.
+     *
+     * If the data is available gnssClockFlags must contain HAS_BIAS.
+     *
+     * This value is mandatory if the receiver has estimated GPS time.
+     */
+    double biasNs;
+
+    /**
+     * 1-Sigma uncertainty associated with the local estimate of GNSS time (clock
+     * bias) in nanoseconds. The uncertainty is represented as an absolute
+     * (single sided) value.
+     *
+     * The caller is responsible for using this uncertainty (it can be very
+     * large before the GPS time has been fully resolved.)
+     *
+     * If the data is available gnssClockFlags must contain HAS_BIAS_UNCERTAINTY.
+     *
+     * This value is mandatory if the receiver has estimated GPS time.
+     */
+    double biasUncertaintyNs;
+
+    /**
+     * The clock's drift in nanoseconds (per second).
+     *
+     * A positive value means that the frequency is higher than the nominal
+     * frequency, and that the (fullBiasNs + biasNs) is growing more positive
+     * over time.
+     *
+     * If the data is available gnssClockFlags must contain HAS_DRIFT.
+     *
+     * This value is mandatory if the receiver has estimated GPS time.
+     */
+    double driftNsps;
+
+    /**
+     * 1-Sigma uncertainty associated with the clock's drift in nanoseconds (per
+     * second).
+     * The uncertainty is represented as an absolute (single sided) value.
+     *
+     * If the data is available gnssClockFlags must contain HAS_DRIFT_UNCERTAINTY.
+     *
+     * This value is mandatory if the receiver has estimated GPS time.
+     */
+    double driftUncertaintyNsps;
+
+    /**
+     * This field must be incremented, when there are discontinuities in the
+     * hardware clock.
+     *
+     * A "discontinuity" is meant to cover the case of a switch from one source
+     * of clock to another.  A single free-running crystal oscillator (XO)
+     * will generally not have any discontinuities, and this can be set and
+     * left at 0.
+     *
+     * If, however, the timeNs value (HW clock) is derived from a composite of
+     * sources, that is not as smooth as a typical XO, or is otherwise stopped &
+     * restarted, then this value shall be incremented each time a discontinuity
+     * occurs.  (E.g. this value can start at zero at device boot-up and
+     * increment each time there is a change in clock continuity. In the
+     * unlikely event that this value reaches full scale, rollover (not
+     * clamping) is required, such that this value continues to change, during
+     * subsequent discontinuity events.)
+     *
+     * While this number stays the same, between GnssClock reports, it can be
+     * safely assumed that the timeNs value has been running continuously, e.g.
+     * derived from a single, high quality clock (XO like, or better, that is
+     * typically used during continuous GNSS signal sampling.)
+     *
+     * It is expected, esp. during periods where there are few GNSS signals
+     * available, that the HW clock be discontinuity-free as long as possible,
+     * as this avoids the need to use (waste) a GNSS measurement to fully
+     * re-solve for the GNSS clock bias and drift, when using the accompanying
+     * measurements, from consecutive GnssData reports.
+     *
+     * This value is mandatory.
+     */
+    int hwClockDiscontinuityCount;
+
+    /**
+     * Reference GNSS signal type for inter-signal bias.
+     */
+    GnssSignalType referenceSignalTypeForIsb;
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/GnssData.aidl b/gnss/aidl/android/hardware/gnss/GnssData.aidl
new file mode 100644
index 0000000..ed30c98
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/GnssData.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.ElapsedRealtime;
+import android.hardware.gnss.GnssClock;
+import android.hardware.gnss.GnssMeasurement;
+
+/**
+ * Represents a reading of GNSS measurements. For devices launched in Android Q or newer, it is
+ * mandatory that these be provided, on request, when the GNSS receiver is searching/tracking
+ * signals.
+ *
+ * - Reporting of GNSS constellation measurements is mandatory.
+ * - Reporting of all tracked constellations are encouraged.
+ */
+@VintfStability
+parcelable GnssData {
+    /** The array of measurements. */
+    GnssMeasurement[] measurements;
+
+    /** The GNSS clock time reading. */
+    GnssClock clock;
+
+    /**
+     * Timing information of the GNSS data synchronized with SystemClock.elapsedRealtimeNanos()
+     * clock.
+     */
+    ElapsedRealtime elapsedRealtime;
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl b/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl
new file mode 100644
index 0000000..fae862b
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/GnssMeasurement.aidl
@@ -0,0 +1,634 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.GnssConstellationType;
+import android.hardware.gnss.GnssSignalType;
+import android.hardware.gnss.GnssMultipathIndicator;
+
+/**
+ * Represents a GNSS Measurement, it contains raw and computed information.
+ *
+ * All signal measurement information (e.g. svTime, pseudorangeRate, multipathIndicator) reported in
+ * this struct must be based on GNSS signal measurements only. You must not synthesize measurements
+ * by calculating or reporting expected measurements based on known or estimated position, velocity,
+ * or time.
+ */
+@VintfStability
+parcelable GnssMeasurement {
+    /** Bit mask indicating a valid 'snr' is stored in the GnssMeasurement. */
+    const int HAS_SNR                        = 1 << 0;
+    /** Bit mask indicating a valid 'carrier frequency' is stored in the GnssMeasurement. */
+    const int HAS_CARRIER_FREQUENCY          = 1 << 9;
+    /** Bit mask indicating a valid 'carrier cycles' is stored in the GnssMeasurement. */
+    const int HAS_CARRIER_CYCLES             = 1 << 10;
+    /** Bit mask indicating a valid 'carrier phase' is stored in the GnssMeasurement. */
+    const int HAS_CARRIER_PHASE              = 1 << 11;
+    /** Bit mask indicating a valid 'carrier phase uncertainty' is stored in the GnssMeasurement. */
+    const int HAS_CARRIER_PHASE_UNCERTAINTY  = 1 << 12;
+    /** Bit mask indicating a valid automatic gain control is stored in the GnssMeasurement. */
+    const int HAS_AUTOMATIC_GAIN_CONTROL     = 1 << 13;
+    /** Bit mask indicating a valid full inter-signal bias is stored in the GnssMeasurement. */
+    const int HAS_FULL_ISB                   = 1 << 16;
+    /**
+     * Bit mask indicating a valid full inter-signal bias uncertainty is stored in the
+     * GnssMeasurement.
+     */
+    const int HAS_FULL_ISB_UNCERTAINTY       = 1 << 17;
+    /**
+     * Bit mask indicating a valid satellite inter-signal bias is stored in the GnssMeasurement.
+     */
+    const int HAS_SATELLITE_ISB              = 1 << 18;
+    /**
+     * Bit mask indicating a valid satellite inter-signal bias uncertainty is stored in the
+     * GnssMeasurement.
+     */
+    const int HAS_SATELLITE_ISB_UNCERTAINTY  = 1 << 19;
+
+    /**
+     * A bitfield of flags indicating the validity of the fields in this GnssMeasurement. The bit
+     * masks are defined in the constants with prefix HAS_*
+     *
+     * Fields for which there is no corresponding flag must be filled in with a valid value.  For
+     * convenience, these are marked as mandatory.
+     *
+     * Others fields may have invalid information in them, if not marked as valid by the
+     * corresponding bit in flags.
+     */
+    int flags;
+
+    /**
+     * Satellite vehicle ID number, as defined in GnssSvInfo::svid
+     *
+     * This value is mandatory.
+     */
+    int svid;
+
+    /**
+     * Defines the constellation of the given SV.
+     *
+     * This value is mandatory.
+     */
+    GnssSignalType signalType;
+
+    /**
+     * Time offset at which the measurement was taken in nanoseconds.
+     * The reference receiver's time is specified by GnssData::clock::timeNs.
+     *
+     * The sign of timeOffsetNs is given by the following equation:
+     *      measurement time = GnssClock::timeNs + timeOffsetNs
+     *
+     * It provides an individual time-stamp for the measurement, and allows
+     * sub-nanosecond accuracy. It may be zero if all measurements are
+     * aligned to a common time.
+     *
+     * This value is mandatory.
+     */
+    double timeOffsetNs;
+
+    /**
+     * Flags indicating the GNSS measurement state.
+     *
+     * The expected behavior here is for GNSS HAL to set all the flags that apply. For example, if
+     * the state for a satellite is only C/A code locked and bit synchronized, and there is still
+     * millisecond ambiguity, the state must be set as:
+     *
+     * STATE_CODE_LOCK | STATE_BIT_SYNC |  STATE_MSEC_AMBIGUOUS
+     *
+     * If GNSS is still searching for a satellite, the corresponding state must be set to
+     * STATE_UNKNOWN(0).
+     *
+     * The received satellite time is relative to the beginning of the system week for all
+     * constellations except for Glonass where it is relative to the beginning of the Glonass system
+     * day.
+     *
+     * The table below indicates the valid range of the received GNSS satellite time.  These ranges
+     * depend on the constellation and code being tracked and the state of the tracking algorithms
+     * given by the getState method. If the state flag is set, then the valid measurement range is
+     * zero to the value in the table. The state flag with the widest range indicates the range of
+     * the received GNSS satellite time value.
+     *
+     * +---------------------------+--------------------+-----+-----------+--------------------+------+
+     * |                           |GPS/QZSS	        |GLNS |BDS        |GAL                 |SBAS  |
+     * +---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |State Flag	               |L1    |L5I   |L5Q   |L1OF |B1I   |B1I |E1B   |E1C   |E5AQ  |L1    |
+     * |                           |C/A	  |      |      |     |(D1)  |(D2)|      |      |      |C/A   |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_UNKNOWN              |0	  |0	 |0	    |0	  |0	 |0   |0     |0     |0	   |0     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_CODE_LOCK            |1ms   |1 ms  |1 ms  |1 ms |1 ms  |1 ms|-     |-     |1 ms  |1 ms  |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_SYMBOL_SYNC          |20ms  |10 ms |1 ms  |10 ms|20 ms |2 ms|4 ms  |4 ms  |1 ms  |2 ms  |
+     * |                           |(opt.)|	     |(opt.)|     |(opt.)|    |(opt.)|(opt.)|(opt.)|      |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_BIT_SYNC             |20 ms |20 ms |1 ms  |20 ms|20 ms |-   |8 ms  |-     |1 ms  |4 ms  |
+     * |                           |      |      |(opt.)|     |      |    |      |      |(opt.)|      |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_SUBFRAME_SYNC        |6s    |6s    |-     |2 s  |6 s   |-   |-     |-     |100 ms|-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_TOW_DECODED          |1 week|-     |-     |1 day|1 week|-   |1 week|-     |-     |1 week|
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_TOW_KNOWN            |1 week|-     |-     |1 day|1 week|-   |1 week|-     |-     |1 week|
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_GLO_STRING_SYNC      |-     |-     |-     |2 s  |-     |-   |-     |-     |-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_GLO_TOD_DECODED      |-     |-     |-     |1 day|-     |-   |-     |-     |-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_GLO_TOD_KNOWN        |-     |-     |-     |1 day|-     |-   |-     |-     |-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_BDS_D2_BIT_SYNC      |-     |-     |-     |-    |-     |2 ms|-     |-     |-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_BDS_D2_SUBFRAME_SYNC |-     |-     |-     |-    |-     |600 |-     |-     |-     |-     |
+     * |                           |      |      |      |     |      |ms  |      |      |      |      |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_GAL_E1BC_CODE_LOCK   |-     |-     |-     |-    |-     |-   |4 ms  |4 ms  |-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_GAL_E1C_2ND_CODE_LOCK|-     |-     |-     |-    |-     |-   |-     |100 ms|-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_2ND_CODE_LOCK        |-     |10 ms |20 ms |-    |-     |-	  |-     |100 ms|100 ms|-     |
+     * |                           |      |(opt.)|      |     |      |    |      |(opt.)|      |      |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_GAL_E1B_PAGE_SYNC    |-     |-     |-     |-    |-     |-   |2 s   |-     |-     |-     |
+     * |---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     * |STATE_SBAS_SYNC            |-     |-     |-     |-    |-     |-   |-     |-     |-     |1s    |
+     * +---------------------------+------+------+------+-----+------+----+------+------+------+------+
+     *
+     * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
+     * been determined from other sources. If TOW decoded is set then TOW Known must also be set.
+     *
+     * Note well: if there is any ambiguity in integer millisecond, STATE_MSEC_AMBIGUOUS must be
+     * set accordingly, in the 'state' field.  This value must be populated if 'state' !=
+     * STATE_UNKNOWN.
+     *
+     * Note on optional flags:
+     *   - For L1 C/A and B1I, STATE_SYMBOL_SYNC is optional since the symbol length is the
+     *     same as the bit length.
+     *   - For L5Q and E5aQ, STATE_BIT_SYNC and STATE_SYMBOL_SYNC are optional since they are
+     *     implied by STATE_CODE_LOCK.
+     *   - STATE_2ND_CODE_LOCK for L5I is optional since it is implied by STATE_SYMBOL_SYNC.
+     *   - STATE_2ND_CODE_LOCK for E1C is optional since it is implied by
+     *     STATE_GAL_E1C_2ND_CODE_LOCK.
+     *   - For E1B and E1C, STATE_SYMBOL_SYNC is optional, because it is implied by
+     *     STATE_GAL_E1BC_CODE_LOCK.
+     */
+    const int STATE_UNKNOWN                = 0;
+    const int STATE_CODE_LOCK              = 1 << 0;
+    const int STATE_BIT_SYNC               = 1 << 1;
+    const int STATE_SUBFRAME_SYNC          = 1 << 2;
+    const int STATE_TOW_DECODED            = 1 << 3;
+    const int STATE_MSEC_AMBIGUOUS         = 1 << 4;
+    const int STATE_SYMBOL_SYNC            = 1 << 5;
+    const int STATE_GLO_STRING_SYNC        = 1 << 6;
+    const int STATE_GLO_TOD_DECODED        = 1 << 7;
+    const int STATE_BDS_D2_BIT_SYNC        = 1 << 8;
+    const int STATE_BDS_D2_SUBFRAME_SYNC   = 1 << 9;
+    const int STATE_GAL_E1BC_CODE_LOCK     = 1 << 10;
+    const int STATE_GAL_E1C_2ND_CODE_LOCK  = 1 << 11;
+    const int STATE_GAL_E1B_PAGE_SYNC      = 1 << 12;
+    const int STATE_SBAS_SYNC              = 1 << 13;
+    const int STATE_TOW_KNOWN              = 1 << 14;
+    const int STATE_GLO_TOD_KNOWN          = 1 << 15;
+    const int STATE_2ND_CODE_LOCK          = 1 << 16;
+
+    /**
+     * A bitfield of flags indicating the GnssMeasurementState per satellite sync state. It
+     * represents the current sync state for the associated satellite.
+     *
+     * Based on the sync state, the 'received GNSS tow' field must be interpreted accordingly.
+     *
+     * The bit masks are defined in the constants with prefix STATE_.
+     *
+     * This value is mandatory.
+     */
+    int state;
+
+    /**
+     * The received GNSS Time-of-Week at the measurement time, in nanoseconds.
+     * For GNSS & QZSS, this is the received GNSS Time-of-Week at the
+     * measurement time, in nanoseconds. The value is relative to the
+     * beginning of the current GNSS week.
+     *
+     * Given the highest sync state that can be achieved, per each satellite,
+     * valid range for this field can be:
+     * Searching       : [ 0       ] : STATE_UNKNOWN
+     * C/A code lock   : [ 0 1ms   ] : STATE_CODE_LOCK set
+     * Bit sync        : [ 0 20ms  ] : STATE_BIT_SYNC set
+     * Subframe sync   : [ 0  6s   ] : STATE_SUBFRAME_SYNC set
+     * TOW decoded     : [ 0 1week ] : STATE_TOW_DECODED set
+     * TOW Known       : [ 0 1week ] : STATE_TOW_KNOWN set
+     *
+     * Note: TOW Known refers to the case where TOW is possibly not decoded
+     * over the air but has been determined from other sources. If TOW
+     * decoded is set then TOW Known must also be set.
+     *
+     * Note: If there is any ambiguity in integer millisecond,
+     * STATE_MSEC_AMBIGUOUS must be set accordingly, in the
+     * 'state' field.
+     *
+     * This value must be populated if 'state' != STATE_UNKNOWN.
+     *
+     * For Glonass, this is the received Glonass time of day, at the
+     * measurement time in nanoseconds.
+     *
+     * Given the highest sync state that can be achieved, per each satellite,
+     * valid range for this field can be:
+     * Searching           : [ 0       ] : STATE_UNKNOWN set
+     * C/A code lock       : [ 0   1ms ] : STATE_CODE_LOCK set
+     * Symbol sync         : [ 0  10ms ] : STATE_SYMBOL_SYNC set
+     * Bit sync            : [ 0  20ms ] : STATE_BIT_SYNC set
+     * String sync         : [ 0    2s ] : STATE_GLO_STRING_SYNC set
+     * Time of day decoded : [ 0  1day ] : STATE_GLO_TOD_DECODED set
+     * Time of day known   : [ 0  1day ] : STATE_GLO_TOD_KNOWN set
+     *
+     * Note: Time of day known refers to the case where it is possibly not
+     * decoded over the air but has been determined from other sources. If
+     * Time of day decoded is set then Time of day known must also be set.
+     *
+     * For Beidou, this is the received Beidou time of week,
+     * at the measurement time in nanoseconds.
+     *
+     * Given the highest sync state that can be achieved, per each satellite,
+     * valid range for this field can be:
+     * Searching            : [ 0       ] : STATE_UNKNOWN set.
+     * C/A code lock        : [ 0   1ms ] : STATE_CODE_LOCK set.
+     * Bit sync (D2)        : [ 0   2ms ] : STATE_BDS_D2_BIT_SYNC set.
+     * Bit sync (D1)        : [ 0  20ms ] : STATE_BIT_SYNC set.
+     * Subframe (D2)        : [ 0  0.6s ] : STATE_BDS_D2_SUBFRAME_SYNC set.
+     * Subframe (D1)        : [ 0    6s ] : STATE_SUBFRAME_SYNC set.
+     * Time of week decoded : [ 0 1week ] : STATE_TOW_DECODED set.
+     * Time of week known   : [ 0 1week ] : STATE_TOW_KNOWN set
+     *
+     * Note: TOW Known refers to the case where TOW is possibly not decoded
+     * over the air but has been determined from other sources. If TOW
+     * decoded is set then TOW Known must also be set.
+     *
+     * For Galileo, this is the received Galileo time of week,
+     * at the measurement time in nanoseconds.
+     *
+     * E1BC code lock       : [ 0  4ms ] : STATE_GAL_E1BC_CODE_LOCK set.
+     * E1C 2nd code lock    : [ 0 100ms] : STATE_GAL_E1C_2ND_CODE_LOCK set.
+     * E1B page             : [ 0   2s ] : STATE_GAL_E1B_PAGE_SYNC set.
+     * Time of week decoded : [ 0 1week] : STATE_TOW_DECODED is set.
+     * Time of week known   : [ 0 1week] : STATE_TOW_KNOWN set
+     *
+     * Note: TOW Known refers to the case where TOW is possibly not decoded
+     * over the air but has been determined from other sources. If TOW
+     * decoded is set then TOW Known must also be set.
+     *
+     * For SBAS, this is received SBAS time, at the measurement time in
+     * nanoseconds.
+     *
+     * Given the highest sync state that can be achieved, per each satellite,
+     * valid range for this field can be:
+     * Searching    : [ 0     ] : STATE_UNKNOWN
+     * C/A code lock: [ 0 1ms ] : STATE_CODE_LOCK is set
+     * Symbol sync  : [ 0 2ms ] : STATE_SYMBOL_SYNC is set
+     * Message      : [ 0  1s ] : STATE_SBAS_SYNC is set
+     */
+    long receivedSvTimeInNs;
+
+    /**
+     * 1-Sigma uncertainty of the Received GNSS Time-of-Week in nanoseconds.
+     *
+     * This value must be populated if 'state' != STATE_UNKNOWN.
+     */
+    long receivedSvTimeUncertaintyInNs;
+
+    /**
+     * Carrier-to-noise density in dB-Hz, typically in the range [0, 63].
+     * It contains the measured C/N0 value for the signal at the antenna port.
+     *
+     * If a signal has separate components (e.g. Pilot and Data channels) and
+     * the receiver only processes one of the components, then the reported
+     * antennaCN0DbHz reflects only the component that is processed.
+     *
+     * This value is mandatory.
+     */
+    double antennaCN0DbHz;
+
+    /**
+     * Baseband Carrier-to-noise density in dB-Hz, typically in the range [0, 63]. It contains the
+     * measured C/N0 value for the signal measured at the baseband.
+     *
+     * This is typically a few dB weaker than the value estimated for C/N0 at the antenna port,
+     * which is reported in cN0DbHz.
+     *
+     * If a signal has separate components (e.g. Pilot and Data channels) and the receiver only
+     * processes one of the components, then the reported basebandCN0DbHz reflects only the
+     * component that is processed.
+     *
+     * This value is mandatory.
+     */
+    double basebandCN0DbHz;
+
+    /**
+     * Pseudorange rate at the timestamp in m/s. The correction of a given
+     * Pseudorange Rate value includes corrections for receiver and satellite
+     * clock frequency errors. Ensure that this field is independent (see
+     * comment at top of GnssMeasurement struct.)
+     *
+     * It is mandatory to provide the 'uncorrected' 'pseudorange rate', and
+     * provide GnssClock's 'drift' field as well. When providing the
+     * uncorrected pseudorange rate, do not apply the corrections described above.)
+     *
+     * The value includes the 'pseudorange rate uncertainty' in it.
+     * A positive 'uncorrected' value indicates that the SV is moving away from
+     * the receiver.
+     *
+     * The sign of the 'uncorrected' 'pseudorange rate' and its relation to the
+     * sign of 'doppler shift' is given by the equation:
+     *      pseudorange rate = -k * doppler shift   (where k is a constant)
+     *
+     * This must be the most accurate pseudorange rate available, based on
+     * fresh signal measurements from this channel.
+     *
+     * It is mandatory that this value be provided at typical carrier phase PRR
+     * quality (few cm/sec per second of uncertainty, or better) - when signals
+     * are sufficiently strong & stable, e.g. signals from a GNSS simulator at >=
+     * 35 dB-Hz.
+     */
+    double pseudorangeRateMps;
+
+    /**
+     * 1-Sigma uncertainty of the pseudorangeRateMps.
+     * The uncertainty is represented as an absolute (single sided) value.
+     *
+     * This value is mandatory.
+     */
+    double pseudorangeRateUncertaintyMps;
+
+
+    /**
+     * Flags indicating the Accumulated Delta Range's states.
+     *
+     * See the table below for a detailed interpretation of each state.
+     *
+     * +---------------------+-------------------+-----------------------------+
+     * | ADR_STATE           | Time of relevance | Interpretation              |
+     * +---------------------+-------------------+-----------------------------+
+     * | UNKNOWN             | ADR(t)            | No valid carrier phase      |
+     * |                     |                   | information is available    |
+     * |                     |                   | at time t.                  |
+     * +---------------------+-------------------+-----------------------------+
+     * | VALID               | ADR(t)            | Valid carrier phase         |
+     * |                     |                   | information is available    |
+     * |                     |                   | at time t. This indicates   |
+     * |                     |                   | that this measurement can   |
+     * |                     |                   | be used as a reference for  |
+     * |                     |                   | future measurements.        |
+     * |                     |                   | However, to compare it to   |
+     * |                     |                   | previous measurements to    |
+     * |                     |                   | compute delta range,        |
+     * |                     |                   | other bits should be        |
+     * |                     |                   | checked. Specifically, it   |
+     * |                     |                   | can be used for delta range |
+     * |                     |                   | computation if it is valid  |
+     * |                     |                   | and has no reset or cycle   |
+     * |                     |                   | slip at this epoch i.e.     |
+     * |                     |                   | if VALID_BIT == 1 &&        |
+     * |                     |                   | CYCLE_SLIP_BIT == 0 &&      |
+     * |                     |                   | RESET_BIT == 0.             |
+     * +---------------------+-------------------+-----------------------------+
+     * | RESET               | ADR(t) - ADR(t-1) | Carrier phase accumulation  |
+     * |                     |                   | has been restarted between  |
+     * |                     |                   | current time t and previous |
+     * |                     |                   | time t-1. This indicates    |
+     * |                     |                   | that this measurement can   |
+     * |                     |                   | be used as a reference for  |
+     * |                     |                   | future measurements, but it |
+     * |                     |                   | should not be compared to   |
+     * |                     |                   | previous measurements to    |
+     * |                     |                   | compute delta range.        |
+     * +---------------------+-------------------+-----------------------------+
+     * | CYCLE_SLIP          | ADR(t) - ADR(t-1) | Cycle slip(s) have been     |
+     * |                     |                   | detected between the        |
+     * |                     |                   | current time t and previous |
+     * |                     |                   | time t-1. This indicates    |
+     * |                     |                   | that this measurement can   |
+     * |                     |                   | be used as a reference for  |
+     * |                     |                   | future measurements.        |
+     * |                     |                   | Clients can use a           |
+     * |                     |                   | measurement with a cycle    |
+     * |                     |                   | slip to compute delta range |
+     * |                     |                   | against previous            |
+     * |                     |                   | measurements at their own   |
+     * |                     |                   | risk.                       |
+     * +---------------------+-------------------+-----------------------------+
+     * | HALF_CYCLE_RESOLVED | ADR(t)            | Half cycle ambiguity is     |
+     * |                     |                   | resolved at time t.         |
+     * +---------------------+-------------------+-----------------------------+
+     */
+    const int ADR_STATE_UNKNOWN = 0;
+    const int ADR_STATE_VALID = 1 << 0;
+    const int ADR_STATE_RESET = 1 << 1;
+    const int ADR_STATE_CYCLE_SLIP = 1 << 2;
+    const int ADR_STATE_HALF_CYCLE_RESOLVED = 1 << 3;
+
+    /**
+     * A bitfield of flags indicating the accumulated delta range's state. It indicates whether ADR
+     * is reset or there is a cycle slip(indicating loss of lock).
+     *
+     * The bit masks are defined in constants with prefix ADR_STATE_.
+     *
+     * This value is mandatory.
+     */
+    int accumulatedDeltaRangeState;
+
+    /**
+     * Accumulated delta range since the last channel reset in meters.
+     * A positive value indicates that the SV is moving away from the receiver.
+     *
+     * The sign of the 'accumulated delta range' and its relation to the sign of
+     * 'carrier phase' is given by the equation:
+     * accumulated delta range = -k * carrier phase (where k is a constant)
+     *
+     * This value must be populated if 'accumulated delta range state' !=
+     * ADR_STATE_UNKNOWN.
+     * However, it is expected that the data is only accurate when:
+     *      'accumulated delta range state' == ADR_STATE_VALID.
+     *
+     * The alignment of the phase measurement will not be  adjusted by the receiver so the in-phase
+     * and quadrature phase components will have a quarter cycle offset as they do when transmitted
+     * from the satellites. If the measurement is from a combination of the in-phase and quadrature
+     * phase components, then the alignment of the phase measurement will be aligned to the in-phase
+     * component.
+     */
+    double accumulatedDeltaRangeM;
+
+    /**
+     * 1-Sigma uncertainty of the accumulated delta range in meters.
+     * This value must be populated if 'accumulated delta range state' !=
+     * ADR_STATE_UNKNOWN.
+     */
+    double accumulatedDeltaRangeUncertaintyM;
+
+    /**
+     * Carrier frequency of the signal tracked, for example it can be the
+     * GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz, L5 =
+     * 1176.45 MHz, varying GLO channels, etc. If the field is not set, it
+     * is the primary common use central frequency, e.g. L1 = 1575.45 MHz
+     * for GPS.
+     *
+     * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same
+     * time, two raw measurement structs must be reported for this same
+     * satellite, in one of the measurement structs, all the values related
+     * to L1 must be filled, and in the other all of the values related to
+     * L5 must be filled.
+     *
+     * If the data is available, gnssMeasurementFlags must contain
+     * HAS_CARRIER_FREQUENCY.
+     */
+    float carrierFrequencyHz;
+
+    /**
+     * The number of full carrier cycles between the satellite and the
+     * receiver. The reference frequency is given by the field
+     * 'carrierFrequencyHz'. Indications of possible cycle slips and
+     * resets in the accumulation of this value can be inferred from the
+     * accumulatedDeltaRangeState flags.
+     *
+     * If the data is available, gnssMeasurementFlags must contain
+     * HAS_CARRIER_CYCLES.
+     */
+    long carrierCycles;
+
+    /**
+     * The RF phase detected by the receiver, in the range [0.0, 1.0].
+     * This is usually the fractional part of the complete carrier phase
+     * measurement.
+     *
+     * The reference frequency is given by the field 'carrierFrequencyHz'.
+     * The value contains the 'carrier-phase uncertainty' in it.
+     *
+     * If the data is available, gnssMeasurementFlags must contain
+     * HAS_CARRIER_PHASE.
+     */
+    double carrierPhase;
+
+    /**
+     * 1-Sigma uncertainty of the carrier-phase.
+     * If the data is available, gnssMeasurementFlags must contain
+     * HAS_CARRIER_PHASE_UNCERTAINTY.
+     */
+    double carrierPhaseUncertainty;
+
+    /**
+     * An enumeration that indicates the 'multipath' state of the event.
+     *
+     * The multipath Indicator is intended to report the presence of overlapping
+     * signals that manifest as distorted correlation peaks.
+     *
+     * - if there is a distorted correlation peak shape, report that multipath
+     *   is MULTIPATH_INDICATOR_PRESENT.
+     * - if there is no distorted correlation peak shape, report
+     *   MULTIPATH_INDICATOR_NOT_PRESENT
+     * - if signals are too weak to discern this information, report
+     *   MULTIPATH_INDICATOR_UNKNOWN
+     *
+     * Example: when doing the standardized overlapping Multipath Performance
+     * test (3GPP TS 34.171) the Multipath indicator must report
+     * MULTIPATH_INDICATOR_PRESENT for those signals that are tracked, and
+     * contain multipath, and MULTIPATH_INDICATOR_NOT_PRESENT for those
+     * signals that are tracked and do not contain multipath.
+     */
+    GnssMultipathIndicator multipathIndicator;
+
+    /**
+     * Signal-to-noise ratio at correlator output in dB.
+     * If the data is available, GnssMeasurementFlags must contain HAS_SNR.
+     * This is the power ratio of the "correlation peak height above the
+     * observed noise floor" to "the noise RMS".
+     */
+    double snrDb;
+
+    /**
+     * Automatic gain control (AGC) level. AGC acts as a variable gain
+     * amplifier adjusting the power of the incoming signal. The AGC level
+     * may be used to indicate potential interference. When AGC is at a
+     * nominal level, this value must be set as 0. Higher gain (and/or lower
+     * input power) must be output as a positive number. Hence in cases of
+     * strong jamming, in the band of this signal, this value must go more
+     * negative.
+     *
+     * Note: Different hardware designs (e.g. antenna, pre-amplification, or
+     * other RF HW components) may also affect the typical output of this
+     * value on any given hardware design in an open sky test - the
+     * important aspect of this output is that changes in this value are
+     * indicative of changes on input signal power in the frequency band for
+     * this measurement.
+     */
+    double agcLevelDb;
+
+    /**
+     * The full inter-signal bias (ISB) in nanoseconds.
+     *
+     * This value is the sum of the estimated receiver-side and the space-segment-side inter-system
+     * bias, inter-frequency bias and inter-code bias, including
+     *
+     * - Receiver inter-constellation bias (with respect to the constellation in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Receiver inter-frequency bias (with respect to the carrier frequency in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Receiver inter-code bias (with respect to the code type in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPS-UTC Time Offset (TauGps), BDS-GLO
+     *   Time Offset (BGTO)) (with respect to the constellation in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Group delay (e.g., Total Group Delay (TGD))
+     * - Satellite inter-frequency bias (GLO only) (with respect to the carrier frequency in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Satellite inter-code bias (e.g., Differential Code Bias (DCB)) (with respect to the code
+     *   type in GnssClock.referenceSignalTypeForIsb)
+     *
+     * If a component of the above is already compensated in the provided
+     * GnssMeasurement.receivedSvTimeInNs, then it must not be included in the reported full ISB.
+     *
+     * The value does not include the inter-frequency Ionospheric bias.
+     *
+     * The full ISB of GnssClock.referenceSignalTypeForIsb is defined to be 0.0 nanoseconds.
+     */
+    double fullInterSignalBiasNs;
+
+    /**
+     * 1-sigma uncertainty associated with the full inter-signal bias in nanoseconds.
+     */
+    double fullInterSignalBiasUncertaintyNs;
+
+    /**
+     * The satellite inter-signal bias in nanoseconds.
+     *
+     * This value is the sum of the space-segment-side inter-system bias, inter-frequency bias
+     * and inter-code bias, including
+     *
+     * - Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPS-UTC Time Offset (TauGps), BDS-GLO
+     *   Time Offset (BGTO)) (with respect to the constellation in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Group delay (e.g., Total Group Delay (TGD))
+     * - Satellite inter-frequency bias (GLO only) (with respect to the carrier frequency in
+     *   GnssClock.referenceSignalTypeForIsb)
+     * - Satellite inter-code bias (e.g., Differential Code Bias (DCB)) (with respect to the code
+     *   type in GnssClock.referenceSignalTypeForIsb)
+     *
+     * The satellite ISB of GnssClock.referenceSignalTypeForIsb is defined to be 0.0 nanoseconds.
+     */
+    double satelliteInterSignalBiasNs;
+
+    /**
+     * 1-sigma uncertainty associated with the satellite inter-signal bias in nanoseconds.
+     */
+    double satelliteInterSignalBiasUncertaintyNs;
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/GnssMultipathIndicator.aidl b/gnss/aidl/android/hardware/gnss/GnssMultipathIndicator.aidl
new file mode 100644
index 0000000..ec1ce62
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/GnssMultipathIndicator.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss;
+
+/**
+ * Enumeration of available values for the GNSS Measurement's multipath
+ * indicator.
+ */
+@VintfStability
+@Backing(type="int")
+enum GnssMultipathIndicator {
+    /** The indicator is not available or unknown. */
+    UNKNOWN      = 0,
+    /** The measurement is indicated to be affected by multipath. */
+    PRESENT      = 1,
+    /** The measurement is indicated to be not affected by multipath. */
+    NOT_PRESENT = 2,
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/GnssPowerStats.aidl b/gnss/aidl/android/hardware/gnss/GnssPowerStats.aidl
new file mode 100644
index 0000000..2bea44d
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/GnssPowerStats.aidl
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.ElapsedRealtime;
+
+/**
+ * Cumulative GNSS power statistics since boot.
+ */
+@VintfStability
+parcelable GnssPowerStats {
+    /**
+     * Timing information of the GnssPowerStats synchronized with SystemClock.elapsedRealtimeNanos()
+     * clock.
+     */
+    ElapsedRealtime elapsedRealtime;
+
+    /**
+     * Total GNSS energy consumption in milli-joules (mWatt-seconds).
+     */
+    double totalEnergyMilliJoule;
+
+    /**
+     * Total energy consumption in milli-joules (mWatt-seconds) for which the GNSS engine is
+     * tracking signals of a single frequency band.
+     */
+    double singlebandTrackingModeEnergyMilliJoule;
+
+    /**
+     * Total energy consumption in milli-joules (mWatt-seconds) for which the GNSS engine is
+     * tracking signals of multiple frequency bands.
+     */
+    double multibandTrackingModeEnergyMilliJoule;
+
+    /**
+     * Total energy consumption in milli-joules (mWatt-seconds) for which the GNSS engine is
+     * acquiring signals of a single frequency band.
+     */
+    double singlebandAcquisitionModeEnergyMilliJoule;
+
+    /**
+     * Total energy consumption in milli-joules (mWatt-seconds) for which the GNSS engine is
+     * acquiring signals of multiple frequency bands.
+     */
+    double multibandAcquisitionModeEnergyMilliJoule;
+
+    /**
+     * Total energy consumption in milli-joules (mWatt-seconds) for which the GNSS engine is
+     * operating in each of the vendor-specific power modes.
+     */
+    double[] otherModesEnergyMilliJoule;
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl b/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl
new file mode 100644
index 0000000..a284fed
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/GnssSignalType.aidl
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.GnssConstellationType;
+
+/**
+ * Represents a GNSS signal type.
+ */
+@VintfStability
+parcelable GnssSignalType {
+    /**
+     * Constellation type of the SV that transmits the signal.
+     */
+    GnssConstellationType constellation;
+
+    /**
+     * Carrier frequency of the signal tracked, for example it can be the
+     * GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz, L5 =
+     * 1176.45 MHz, varying GLO channels, etc. If the field is not set, it
+     * is the primary common use central frequency, e.g. L1 = 1575.45 MHz
+     * for GPS.
+     *
+     * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same
+     * time, two raw measurement structs must be reported for this same
+     * satellite, in one of the measurement structs, all the values related
+     * to L1 must be filled, and in the other all of the values related to
+     * L5 must be filled.
+     */
+    double carrierFrequencyHz;
+
+    /**
+     * GNSS signal code type "A" representing GALILEO E1A, GALILEO E6A, IRNSS L5A, IRNSS SA.
+     */
+    const String CODE_TYPE_A = "A";
+
+    /**
+     * GNSS signal code type "B" representing GALILEO E1B, GALILEO E6B, IRNSS L5B, IRNSS SB.
+     */
+    const String CODE_TYPE_B = "B";
+
+    /**
+     * GNSS signal code type "C" representing GPS L1 C/A,  GPS L2 C/A, GLONASS G1 C/A,
+     * GLONASS G2 C/A, GALILEO E1C, GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, IRNSS L5C.
+     */
+    const String CODE_TYPE_C = "C";
+
+    /**
+     * GNSS signal code type "D" representing BDS B1C D.
+     */
+    const String CODE_TYPE_D = "D";
+
+    /**
+     * GNSS signal code type "I" representing GPS L5 I, GLONASS G3 I, GALILEO E5a I, GALILEO E5b I,
+     * GALILEO E5a+b I, SBAS L5 I, QZSS L5 I, BDS B1 I, BDS B2 I, BDS B3 I.
+     */
+    const String CODE_TYPE_I = "I";
+
+    /**
+     * GNSS signal code type "L" representing GPS L1C (P), GPS L2C (L), QZSS L1C (P), QZSS L2C (L),
+     * LEX(6) L.
+     */
+    const String CODE_TYPE_L = "L";
+
+    /**
+     * GNSS signal code type "M" representing GPS L1M, GPS L2M.
+     */
+    const String CODE_TYPE_M = "M";
+
+    /**
+     * GNSS signal code type "N" representing GPS L1 codeless, GPS L2 codeless.
+     */
+    const String CODE_TYPE_N = "N";
+
+    /**
+     * GNSS signal code type "P" representing GPS L1P, GPS L2P, GLONASS G1P, GLONASS G2P, BDS B1C P.
+     */
+    const String CODE_TYPE_P = "P";
+
+    /**
+     * GNSS signal code type "Q" representing GPS L5 Q, GLONASS G3 Q, GALILEO E5a Q, GALILEO E5b Q,
+     * GALILEO E5a+b Q, SBAS L5 Q, QZSS L5 Q, BDS B1 Q, BDS B2 Q, BDS B3 Q.
+     */
+    const String CODE_TYPE_Q = "Q";
+
+    /**
+     * GNSS signal code type "S" represents GPS L1C (D), GPS L2C (M), QZSS L1C (D), QZSS L2C (M),
+     * LEX(6) S.
+     */
+    const String CODE_TYPE_S = "S";
+
+    /**
+     * GNSS signal code type "W" representing GPS L1 Z-tracking, GPS L2 Z-tracking.
+     */
+    const String CODE_TYPE_W = "W";
+
+    /**
+     * GNSS signal code type "X" representing GPS L1C (D+P), GPS L2C (M+L), GPS L5 (I+Q),
+     * GLONASS G3 (I+Q), GALILEO E1 (B+C), GALILEO E5a (I+Q), GALILEO E5b (I+Q), GALILEO E5a+b(I+Q),
+     * GALILEO E6 (B+C), SBAS L5 (I+Q), QZSS L1C (D+P), QZSS L2C (M+L), QZSS L5 (I+Q),
+     * LEX(6) (S+L), BDS B1 (I+Q), BDS B1C (D+P), BDS B2 (I+Q), BDS B3 (I+Q), IRNSS L5 (B+C).
+     */
+    const String CODE_TYPE_X = "X";
+
+    /**
+     * GNSS signal code type "Y" representing GPS L1Y, GPS L2Y.
+     */
+    const String CODE_TYPE_Y = "Y";
+
+    /**
+     * GNSS signal code type "Z" representing GALILEO E1 (A+B+C), GALILEO E6 (A+B+C), QZSS L1-SAIF.
+     */
+    const String CODE_TYPE_Z = "Z";
+
+    /**
+     * GNSS signal code type "UNKNOWN" representing the GNSS Measurement's code type is unknown.
+     */
+    const String CODE_TYPE_UNKNOWN = "UNKNOWN";
+
+    /**
+     * The type of code that is currently being tracked in the GNSS signal.
+     *
+     * For high precision applications the type of code being tracked needs to be considered
+     * in-order to properly apply code specific corrections to the pseudorange measurements.
+     *
+     * The value is one of the constant Strings with prefix CODE_TYPE_ defined in this parcelable.
+     *
+     * This is used to specify the observation descriptor defined in GNSS Observation Data File
+     * Header Section Description in the RINEX standard (Version 3.XX). In RINEX Version 3.03,
+     * in Appendix Table A2 Attributes are listed as uppercase letters (for instance, "A" for
+     * "A channel"). In the future, if for instance a code "G" was added in the official RINEX
+     * standard, "G" could be specified here.
+     */
+    String codeType;
+}
diff --git a/gnss/aidl/android/hardware/gnss/IGnss.aidl b/gnss/aidl/android/hardware/gnss/IGnss.aidl
index 24632aa..c815e2d 100644
--- a/gnss/aidl/android/hardware/gnss/IGnss.aidl
+++ b/gnss/aidl/android/hardware/gnss/IGnss.aidl
@@ -17,8 +17,10 @@
 package android.hardware.gnss;
 
 import android.hardware.gnss.IGnssCallback;
-import android.hardware.gnss.IGnssPsds;
 import android.hardware.gnss.IGnssConfiguration;
+import android.hardware.gnss.IGnssMeasurementInterface;
+import android.hardware.gnss.IGnssPowerIndication;
+import android.hardware.gnss.IGnssPsds;
 
 /**
  * Represents the standard GNSS (Global Navigation Satellite System) interface.
@@ -65,6 +67,8 @@
     /**
      * This method returns the IGnssPsds interface.
      *
+     * This method must return non-null.
+     *
      * @return Handle to the IGnssPsds interface.
      */
     IGnssPsds getExtensionPsds();
@@ -72,7 +76,27 @@
     /**
      * This method returns the IGnssConfiguration interface.
      *
+     * This method must return non-null.
+     *
      * @return Handle to the IGnssConfiguration interface.
      */
     IGnssConfiguration getExtensionGnssConfiguration();
-}
\ No newline at end of file
+
+    /**
+     * This methods returns the IGnssMeasurementInterface interface.
+     *
+     * This method must return non-null.
+     *
+     * @return Handle to the IGnssMeasurementInterface interface.
+     */
+    IGnssMeasurementInterface getExtensionGnssMeasurement();
+
+    /**
+     * This method returns the IGnssPowerIndication interface.
+     *
+     * This method must return non-null.
+     *
+     * @return Handle to the IGnssPowerIndication interface.
+     */
+    IGnssPowerIndication getExtensionGnssPowerIndication();
+}
diff --git a/gnss/aidl/android/hardware/gnss/IGnssMeasurementCallback.aidl b/gnss/aidl/android/hardware/gnss/IGnssMeasurementCallback.aidl
new file mode 100644
index 0000000..328cf2a
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/IGnssMeasurementCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.GnssData;
+
+/**
+ * The callback interface to report GNSS Measurement from the HAL.
+ */
+@VintfStability
+interface IGnssMeasurementCallback {
+    /**
+     * Callback for the hal to pass a GnssData structure back to the client.
+     *
+     * @param data Contains a reading of GNSS measurements.
+     */
+    void gnssMeasurementCb(in GnssData data);
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/IGnssMeasurementInterface.aidl b/gnss/aidl/android/hardware/gnss/IGnssMeasurementInterface.aidl
new file mode 100644
index 0000000..fdeebde
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/IGnssMeasurementInterface.aidl
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.IGnssMeasurementCallback;
+
+/**
+ * Extended interface for GNSS Measurement support.
+ */
+@VintfStability
+interface IGnssMeasurementInterface {
+    /**
+     * Initializes the interface and registers the callback routines with the HAL. After a
+     * successful call to 'setCallback' the HAL must begin to provide updates at an average
+     * output rate of 1Hz (occasional intra-measurement time offsets in the range from 0-2000msec
+     * can be tolerated.)
+     *
+     * @param callback Handle to GnssMeasurement callback interface.
+     * @param enableFullTracking If true, GNSS chipset must switch off duty cycling. In such mode
+     *     no clock discontinuities are expected and, when supported, carrier phase should be
+     *     continuous in good signal conditions. All non-blocklisted, healthy constellations,
+     *     satellites and frequency bands that the chipset supports must be reported in this mode.
+     *     The GNSS chipset is allowed to consume more power in this mode. If false, API must
+     *     optimize power via duty cycling, constellations and frequency limits, etc.
+     *
+     * @return initRet Returns SUCCESS if successful. Returns ERROR_ALREADY_INIT if a callback has
+     *     already been registered without a corresponding call to 'close'. Returns ERROR_GENERIC
+     *     for any other error. The HAL must not generate any other updates upon returning this
+     *     error code.
+     */
+    void setCallback(in IGnssMeasurementCallback callback, in boolean enableFullTracking);
+
+    /**
+     * Stops updates from the HAL, and unregisters the callback routines. After a call to close(),
+     * the previously registered callbacks must be considered invalid by the HAL.
+     *
+     * If close() is invoked without a previous setCallback, this function must perform
+     * no work.
+     */
+    void close();
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/IGnssPowerIndication.aidl b/gnss/aidl/android/hardware/gnss/IGnssPowerIndication.aidl
new file mode 100644
index 0000000..93fdadc
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/IGnssPowerIndication.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.IGnssPowerIndicationCallback;
+
+/**
+ * Extended interface for GNSS Power Indication support.
+ */
+@VintfStability
+interface IGnssPowerIndication {
+
+    /**
+     * Opens the IGnssPowerIndication interface and provides the callback routines to the HAL.
+     *
+     * @param callback Callback interface for IGnssPowerIndication.
+     */
+    void setCallback(in IGnssPowerIndicationCallback callback);
+
+    /**
+     * Requests the GNSS power statistics. One request call corresponds to one response callback
+     * from IGnssPowerIndicationCallback.gnssPowerStatsCb().
+     */
+    oneway void requestGnssPowerStats();
+}
\ No newline at end of file
diff --git a/gnss/aidl/android/hardware/gnss/IGnssPowerIndicationCallback.aidl b/gnss/aidl/android/hardware/gnss/IGnssPowerIndicationCallback.aidl
new file mode 100644
index 0000000..4474c0c
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/IGnssPowerIndicationCallback.aidl
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.GnssPowerStats;
+
+/**
+ * The callback interface to report GNSS Power Indication from the HAL.
+ */
+@VintfStability
+interface IGnssPowerIndicationCallback {
+
+    /** Capability bit mask indicating GNSS supports totalEnergyMilliJoule. */
+    const int CAPABILITY_TOTAL = 1 << 0;
+
+    /** Capability bit mask indicating GNSS supports singlebandTrackingModeEnergyMilliJoule. */
+    const int CAPABILITY_SINGLEBAND_TRACKING = 1 << 1;
+
+    /** Capability bit mask indicating GNSS supports multibandTrackingModeEnergyMilliJoule. */
+    const int CAPABILITY_MULTIBAND_TRACKING = 1 << 2;
+
+    /** Capability bit mask indicating GNSS supports singlebandAcquisitionModeEnergyMilliJoule. */
+    const int CAPABILITY_SINGLEBAND_ACQUISITION = 1 << 3;
+
+    /** Capability bit mask indicating GNSS supports multibandAcquisitionModeEnergyMilliJoule. */
+    const int CAPABILITY_MULTIBAND_ACQUISITION = 1 << 4;
+
+    /** Capability bit mask indicating GNSS supports otherModesEnergyMilliJoule. */
+    const int CAPABILITY_OTHER_MODES = 1 << 5;
+
+    /**
+     * Callback to inform framework the Power Indication specific capabilities of the GNSS HAL
+     * implementation.
+     *
+     * The GNSS HAL must call this method immediately after the framework opens the
+     * IGnssPowerIndication interface.
+     *
+     * @param capabilities Bitmask of CAPABILITY_* specifying the supported GNSS Power Indication
+     * capabilities.
+     */
+    void setCapabilitiesCb(in int capabilities);
+
+    /**
+     * Callback for the HAL to pass a GnssPowerStats structure back to the client.
+     *
+     * @param gnssPowerStats GNSS power statistics since boot.
+     */
+    oneway void gnssPowerStatsCb(in GnssPowerStats gnssPowerStats);
+}
\ No newline at end of file
diff --git a/gnss/aidl/default/Android.bp b/gnss/aidl/default/Android.bp
index 1fe43c3..6694ce6 100644
--- a/gnss/aidl/default/Android.bp
+++ b/gnss/aidl/default/Android.bp
@@ -47,8 +47,10 @@
     srcs: [
         "Gnss.cpp",
         "GnssHidlHal.cpp",
+        "GnssPowerIndication.cpp",
         "GnssPsds.cpp",
         "GnssConfiguration.cpp",
+        "GnssMeasurementInterface.cpp",
         "service.cpp",
     ],
     static_libs: [
diff --git a/gnss/aidl/default/Gnss.cpp b/gnss/aidl/default/Gnss.cpp
index b4d92fd..02bad60 100644
--- a/gnss/aidl/default/Gnss.cpp
+++ b/gnss/aidl/default/Gnss.cpp
@@ -19,6 +19,8 @@
 #include "Gnss.h"
 #include <log/log.h>
 #include "GnssConfiguration.h"
+#include "GnssMeasurementInterface.h"
+#include "GnssPowerIndication.h"
 #include "GnssPsds.h"
 
 namespace aidl::android::hardware::gnss {
@@ -65,4 +67,20 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus Gnss::getExtensionGnssPowerIndication(
+        std::shared_ptr<IGnssPowerIndication>* iGnssPowerIndication) {
+    ALOGD("Gnss::getExtensionGnssPowerIndication");
+
+    *iGnssPowerIndication = SharedRefBase::make<GnssPowerIndication>();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Gnss::getExtensionGnssMeasurement(
+        std::shared_ptr<IGnssMeasurementInterface>* iGnssMeasurement) {
+    ALOGD("Gnss::getExtensionGnssMeasurement");
+
+    *iGnssMeasurement = SharedRefBase::make<GnssMeasurementInterface>();
+    return ndk::ScopedAStatus::ok();
+}
+
 }  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/Gnss.h b/gnss/aidl/default/Gnss.h
index 61d7cf7..bccc7f2 100644
--- a/gnss/aidl/default/Gnss.h
+++ b/gnss/aidl/default/Gnss.h
@@ -18,6 +18,8 @@
 
 #include <aidl/android/hardware/gnss/BnGnss.h>
 #include <aidl/android/hardware/gnss/BnGnssConfiguration.h>
+#include <aidl/android/hardware/gnss/BnGnssMeasurementInterface.h>
+#include <aidl/android/hardware/gnss/BnGnssPowerIndication.h>
 #include <aidl/android/hardware/gnss/BnGnssPsds.h>
 #include "GnssConfiguration.h"
 
@@ -30,6 +32,10 @@
     ndk::ScopedAStatus getExtensionPsds(std::shared_ptr<IGnssPsds>* iGnssPsds) override;
     ndk::ScopedAStatus getExtensionGnssConfiguration(
             std::shared_ptr<IGnssConfiguration>* iGnssConfiguration) override;
+    ndk::ScopedAStatus getExtensionGnssPowerIndication(
+            std::shared_ptr<IGnssPowerIndication>* iGnssPowerIndication) override;
+    ndk::ScopedAStatus getExtensionGnssMeasurement(
+            std::shared_ptr<IGnssMeasurementInterface>* iGnssMeasurement) override;
 
     std::shared_ptr<GnssConfiguration> mGnssConfiguration;
 
diff --git a/gnss/aidl/default/GnssConfiguration.h b/gnss/aidl/default/GnssConfiguration.h
index 25fa16e..491733c 100644
--- a/gnss/aidl/default/GnssConfiguration.h
+++ b/gnss/aidl/default/GnssConfiguration.h
@@ -24,8 +24,6 @@
 
 namespace aidl::android::hardware::gnss {
 
-using android::hardware::gnss::GnssConstellationType;
-
 struct BlocklistedSourceHash {
     inline int operator()(const BlocklistedSource& source) const {
         return int(source.constellation) * 1000 + int(source.svid);
@@ -42,7 +40,8 @@
 using std::vector;
 using BlocklistedSourceSet =
         std::unordered_set<BlocklistedSource, BlocklistedSourceHash, BlocklistedSourceEqual>;
-using BlocklistedConstellationSet = std::unordered_set<GnssConstellationType>;
+using BlocklistedConstellationSet =
+        std::unordered_set<android::hardware::gnss::GnssConstellationType>;
 
 struct GnssConfiguration : public BnGnssConfiguration {
   public:
diff --git a/gnss/aidl/default/GnssHidlHal.cpp b/gnss/aidl/default/GnssHidlHal.cpp
index 11fc806..9529ec9 100644
--- a/gnss/aidl/default/GnssHidlHal.cpp
+++ b/gnss/aidl/default/GnssHidlHal.cpp
@@ -17,11 +17,10 @@
 #define LOG_TAG "GnssHidlHal"
 
 #include "GnssHidlHal.h"
-//#include <android/hardware/gnss/1.0/IGnssCallback.h>
 
 namespace aidl::android::hardware::gnss {
 
-namespace V1_0 = ::android::hardware::gnss::V1_0;
+using GnssSvInfo = ::android::hardware::gnss::V2_1::IGnssCallback::GnssSvInfo;
 
 GnssHidlHal::GnssHidlHal(const std::shared_ptr<Gnss>& gnssAidl) : mGnssAidl(gnssAidl) {
     Gnss* iGnss = mGnssAidl.get();
@@ -45,8 +44,8 @@
         if (mGnssConfigurationAidl->isBlocklistedV2_1(gnssSvInfoList[i])) {
             ALOGD("Blocklisted constellation: %d, svid: %d",
                   (int)gnssSvInfoList[i].v2_0.constellation, gnssSvInfoList[i].v2_0.v1_0.svid);
-            gnssSvInfoList[i].v2_0.v1_0.svFlag &=
-                    ~static_cast<uint8_t>(V1_0::IGnssCallback::GnssSvFlags::USED_IN_FIX);
+            gnssSvInfoList[i].v2_0.v1_0.svFlag &= ~static_cast<uint8_t>(
+                    ::android::hardware::gnss::V1_0::IGnssCallback::GnssSvFlags::USED_IN_FIX);
         }
     }
     return gnssSvInfoList;
diff --git a/gnss/aidl/default/GnssHidlHal.h b/gnss/aidl/default/GnssHidlHal.h
index 50aad3a..93a79a1 100644
--- a/gnss/aidl/default/GnssHidlHal.h
+++ b/gnss/aidl/default/GnssHidlHal.h
@@ -22,16 +22,16 @@
 
 namespace aidl::android::hardware::gnss {
 
-using ::android::hardware::gnss::common::implementation::GnssTemplate;
-using GnssSvInfo = ::android::hardware::gnss::V2_1::IGnssCallback::GnssSvInfo;
-
-class GnssHidlHal : public GnssTemplate<::android::hardware::gnss::V2_1::IGnss> {
+class GnssHidlHal : public ::android::hardware::gnss::common::implementation::GnssTemplate<
+                            ::android::hardware::gnss::V2_1::IGnss> {
   public:
     GnssHidlHal(const std::shared_ptr<Gnss>& gnssAidl);
 
   private:
-    hidl_vec<GnssSvInfo> filterBlocklistedSatellitesV2_1(
-            hidl_vec<GnssSvInfo> gnssSvInfoList) override;
+    hidl_vec<::android::hardware::gnss::V2_1::IGnssCallback::GnssSvInfo>
+    filterBlocklistedSatellitesV2_1(
+            hidl_vec<::android::hardware::gnss::V2_1::IGnssCallback::GnssSvInfo> gnssSvInfoList)
+            override;
 
     std::shared_ptr<Gnss> mGnssAidl;
     std::shared_ptr<GnssConfiguration> mGnssConfigurationAidl;
diff --git a/gnss/aidl/default/GnssMeasurementInterface.cpp b/gnss/aidl/default/GnssMeasurementInterface.cpp
new file mode 100644
index 0000000..d726d95
--- /dev/null
+++ b/gnss/aidl/default/GnssMeasurementInterface.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GnssMeasIfaceAidl"
+
+#include "GnssMeasurementInterface.h"
+#include <aidl/android/hardware/gnss/BnGnss.h>
+#include <log/log.h>
+#include "Utils.h"
+
+namespace aidl::android::hardware::gnss {
+
+using Utils = ::android::hardware::gnss::common::Utils;
+
+std::shared_ptr<IGnssMeasurementCallback> GnssMeasurementInterface::sCallback = nullptr;
+
+GnssMeasurementInterface::GnssMeasurementInterface() : mMinIntervalMillis(1000) {}
+
+GnssMeasurementInterface::~GnssMeasurementInterface() {
+    stop();
+}
+
+ndk::ScopedAStatus GnssMeasurementInterface::setCallback(
+        const std::shared_ptr<IGnssMeasurementCallback>& callback, const bool enableFullTracking) {
+    ALOGD("setCallback: enableFullTracking: %d", (int)enableFullTracking);
+    std::unique_lock<std::mutex> lock(mMutex);
+    sCallback = callback;
+
+    if (mIsActive) {
+        ALOGW("GnssMeasurement callback already set. Resetting the callback...");
+        stop();
+    }
+    start();
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus GnssMeasurementInterface::close() {
+    ALOGD("close");
+    stop();
+    std::unique_lock<std::mutex> lock(mMutex);
+    sCallback = nullptr;
+    return ndk::ScopedAStatus::ok();
+}
+
+void GnssMeasurementInterface::start() {
+    ALOGD("start");
+    mIsActive = true;
+    mThread = std::thread([this]() {
+        while (mIsActive == true) {
+            auto measurement = Utils::getMockMeasurement();
+            this->reportMeasurement(measurement);
+
+            std::this_thread::sleep_for(std::chrono::milliseconds(mMinIntervalMillis));
+        }
+    });
+}
+
+void GnssMeasurementInterface::stop() {
+    ALOGD("stop");
+    mIsActive = false;
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+
+void GnssMeasurementInterface::reportMeasurement(const GnssData& data) {
+    ALOGD("reportMeasurement()");
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (sCallback == nullptr) {
+        ALOGE("%s: GnssMeasurement::sCallback is null.", __func__);
+        return;
+    }
+    sCallback->gnssMeasurementCb(data);
+}
+
+}  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/GnssMeasurementInterface.h b/gnss/aidl/default/GnssMeasurementInterface.h
new file mode 100644
index 0000000..69cd871
--- /dev/null
+++ b/gnss/aidl/default/GnssMeasurementInterface.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/gnss/BnGnssMeasurementCallback.h>
+#include <aidl/android/hardware/gnss/BnGnssMeasurementInterface.h>
+#include <atomic>
+#include <mutex>
+#include <thread>
+
+namespace aidl::android::hardware::gnss {
+
+struct GnssMeasurementInterface : public BnGnssMeasurementInterface {
+  public:
+    GnssMeasurementInterface();
+    ~GnssMeasurementInterface();
+    ndk::ScopedAStatus setCallback(const std::shared_ptr<IGnssMeasurementCallback>& callback,
+                                   const bool enableFullTracking) override;
+    ndk::ScopedAStatus close() override;
+
+  private:
+    void start();
+    void stop();
+    void reportMeasurement(const GnssData&);
+
+    std::atomic<long> mMinIntervalMillis;
+    std::atomic<bool> mIsActive;
+    std::thread mThread;
+
+    // Guarded by mMutex
+    static std::shared_ptr<IGnssMeasurementCallback> sCallback;
+
+    // Synchronization lock for sCallback
+    mutable std::mutex mMutex;
+};
+
+}  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/GnssPowerIndication.cpp b/gnss/aidl/default/GnssPowerIndication.cpp
new file mode 100644
index 0000000..429cc8c
--- /dev/null
+++ b/gnss/aidl/default/GnssPowerIndication.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GnssPowerIndicationAidl"
+
+#include "GnssPowerIndication.h"
+#include <aidl/android/hardware/gnss/BnGnss.h>
+#include <log/log.h>
+#include <utils/SystemClock.h>
+
+namespace aidl::android::hardware::gnss {
+
+std::shared_ptr<IGnssPowerIndicationCallback> GnssPowerIndication::sCallback = nullptr;
+
+ndk::ScopedAStatus GnssPowerIndication::setCallback(
+        const std::shared_ptr<IGnssPowerIndicationCallback>& callback) {
+    ALOGD("setCallback");
+    std::unique_lock<std::mutex> lock(mMutex);
+    sCallback = callback;
+    sCallback->setCapabilitiesCb(IGnssPowerIndicationCallback::CAPABILITY_TOTAL |
+                                 IGnssPowerIndicationCallback::CAPABILITY_SINGLEBAND_TRACKING |
+                                 IGnssPowerIndicationCallback::CAPABILITY_MULTIBAND_TRACKING |
+                                 IGnssPowerIndicationCallback::CAPABILITY_SINGLEBAND_ACQUISITION |
+                                 IGnssPowerIndicationCallback::CAPABILITY_MULTIBAND_ACQUISITION |
+                                 IGnssPowerIndicationCallback::CAPABILITY_OTHER_MODES);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus GnssPowerIndication::requestGnssPowerStats() {
+    ALOGD("requestGnssPowerStats");
+    std::unique_lock<std::mutex> lock(mMutex);
+
+    ElapsedRealtime elapsedRealtime = {
+            .flags = ElapsedRealtime::HAS_TIMESTAMP_NS | ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS,
+            .timestampNs = ::android::elapsedRealtimeNano(),
+            .timeUncertaintyNs = 1000,
+    };
+    GnssPowerStats gnssPowerStats = {
+            .elapsedRealtime = elapsedRealtime,
+            .totalEnergyMilliJoule = 1.59975e+3,
+            .singlebandTrackingModeEnergyMilliJoule = 1.2342e+3,
+            .multibandTrackingModeEnergyMilliJoule = 3.653e+2,
+            .otherModesEnergyMilliJoule = {1.232e+2, 3.234e+3},
+    };
+    sCallback->gnssPowerStatsCb(gnssPowerStats);
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/GnssPowerIndication.h b/gnss/aidl/default/GnssPowerIndication.h
new file mode 100644
index 0000000..e32fd72
--- /dev/null
+++ b/gnss/aidl/default/GnssPowerIndication.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/gnss/BnGnssPowerIndication.h>
+
+namespace aidl::android::hardware::gnss {
+
+struct GnssPowerIndication : public BnGnssPowerIndication {
+  public:
+    ndk::ScopedAStatus setCallback(
+            const std::shared_ptr<IGnssPowerIndicationCallback>& callback) override;
+    ndk::ScopedAStatus requestGnssPowerStats() override;
+
+  private:
+    // Guarded by mMutex
+    static std::shared_ptr<IGnssPowerIndicationCallback> sCallback;
+
+    // Synchronization lock for sCallback
+    mutable std::mutex mMutex;
+};
+
+}  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/vts/Android.bp b/gnss/aidl/vts/Android.bp
index 48a0e21..c10e809 100644
--- a/gnss/aidl/vts/Android.bp
+++ b/gnss/aidl/vts/Android.bp
@@ -22,6 +22,8 @@
         "gnss_hal_test.cpp",
         "gnss_hal_test_cases.cpp",
         "GnssCallbackAidl.cpp",
+        "GnssMeasurementCallbackAidl.cpp",
+        "GnssPowerIndicationCallback.cpp",
         "VtsHalGnssTargetTest.cpp",
     ],
     shared_libs: [
diff --git a/gnss/aidl/vts/GnssMeasurementCallbackAidl.cpp b/gnss/aidl/vts/GnssMeasurementCallbackAidl.cpp
new file mode 100644
index 0000000..c4fad7f
--- /dev/null
+++ b/gnss/aidl/vts/GnssMeasurementCallbackAidl.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GnssMeasurementCallbackAidl"
+
+#include "GnssMeasurementCallbackAidl.h"
+#include <inttypes.h>
+#include <log/log.h>
+
+using android::hardware::gnss::GnssData;
+
+android::binder::Status GnssMeasurementCallbackAidl::gnssMeasurementCb(const GnssData& gnssData) {
+    ALOGI("gnssMeasurementCb");
+    ALOGI("elapsedRealtime: flags = %d, timestampNs: %" PRId64 ", timeUncertaintyNs=%lf",
+          gnssData.elapsedRealtime.flags, gnssData.elapsedRealtime.timestampNs,
+          gnssData.elapsedRealtime.timeUncertaintyNs);
+    for (const auto& measurement : gnssData.measurements) {
+        ALOGI("measurement.receivedSvTimeInNs=%" PRId64, measurement.receivedSvTimeInNs);
+    }
+    gnss_data_cbq_.store(gnssData);
+    return android::binder::Status::ok();
+}
diff --git a/gnss/aidl/vts/GnssMeasurementCallbackAidl.h b/gnss/aidl/vts/GnssMeasurementCallbackAidl.h
new file mode 100644
index 0000000..f6c79cf
--- /dev/null
+++ b/gnss/aidl/vts/GnssMeasurementCallbackAidl.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/gnss/BnGnssMeasurementCallback.h>
+#include "GnssCallbackEventQueue.h"
+
+/** Implementation for IGnssMeasurementCallback. */
+class GnssMeasurementCallbackAidl : public android::hardware::gnss::BnGnssMeasurementCallback {
+  public:
+    GnssMeasurementCallbackAidl() : gnss_data_cbq_("gnss_data") {}
+    ~GnssMeasurementCallbackAidl() {}
+
+    android::binder::Status gnssMeasurementCb(
+            const android::hardware::gnss::GnssData& gnssData) override;
+
+    android::hardware::gnss::common::GnssCallbackEventQueue<android::hardware::gnss::GnssData>
+            gnss_data_cbq_;
+};
diff --git a/gnss/aidl/vts/GnssPowerIndicationCallback.cpp b/gnss/aidl/vts/GnssPowerIndicationCallback.cpp
new file mode 100644
index 0000000..1cbfc20
--- /dev/null
+++ b/gnss/aidl/vts/GnssPowerIndicationCallback.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GnssPwrIndCallback"
+
+#include "GnssPowerIndicationCallback.h"
+#include <log/log.h>
+
+using android::hardware::gnss::GnssPowerStats;
+
+android::binder::Status GnssPowerIndicationCallback::setCapabilitiesCb(const int capabilities) {
+    ALOGI("Capabilities received %d", capabilities);
+    capabilities_cbq_.store(capabilities);
+    return android::binder::Status::ok();
+}
+
+android::binder::Status GnssPowerIndicationCallback::gnssPowerStatsCb(
+        const GnssPowerStats& gnssPowerStats) {
+    ALOGI("gnssPowerStatsCb");
+    ALOGI("elapsedRealtime: %ld, totalEnergyMilliJoule: %f",
+          (long)gnssPowerStats.elapsedRealtime.timestampNs, gnssPowerStats.totalEnergyMilliJoule);
+    ALOGI("singlebandTrackingModeEnergyMilliJoule: %f, multibandTrackingModeEnergyMilliJoule: %f",
+          gnssPowerStats.singlebandTrackingModeEnergyMilliJoule,
+          gnssPowerStats.multibandTrackingModeEnergyMilliJoule);
+    ALOGI("singlebandAcquisitionModeEnergyMilliJoule: %f, "
+          "multibandAcquisitionModeEnergyMilliJoule: %f",
+          gnssPowerStats.singlebandAcquisitionModeEnergyMilliJoule,
+          gnssPowerStats.multibandAcquisitionModeEnergyMilliJoule);
+    for (const auto& otherModeEnergyMilliJoule : gnssPowerStats.otherModesEnergyMilliJoule) {
+        ALOGI("otherModeEnergyMilliJoule: %f", otherModeEnergyMilliJoule);
+    }
+    gnss_power_stats_cbq_.store(gnssPowerStats);
+    return android::binder::Status::ok();
+}
diff --git a/gnss/aidl/vts/GnssPowerIndicationCallback.h b/gnss/aidl/vts/GnssPowerIndicationCallback.h
new file mode 100644
index 0000000..3416f17
--- /dev/null
+++ b/gnss/aidl/vts/GnssPowerIndicationCallback.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/gnss/BnGnssPowerIndicationCallback.h>
+#include <android/hardware/gnss/GnssPowerStats.h>
+#include "GnssCallbackEventQueue.h"
+
+/** Implementation for IGnssPowerIndicationCallback. */
+class GnssPowerIndicationCallback : public android::hardware::gnss::BnGnssPowerIndicationCallback {
+  public:
+    GnssPowerIndicationCallback()
+        : capabilities_cbq_("capabilities"),
+          gnss_power_stats_cbq_("gnss_power_stats") {}
+    ~GnssPowerIndicationCallback() {}
+
+    android::binder::Status setCapabilitiesCb(const int capabilities) override;
+    android::binder::Status gnssPowerStatsCb(
+            const android::hardware::gnss::GnssPowerStats& gnssPowerStats) override;
+
+    android::hardware::gnss::common::GnssCallbackEventQueue<int> capabilities_cbq_;
+    int last_capabilities_;
+    android::hardware::gnss::common::GnssCallbackEventQueue<android::hardware::gnss::GnssPowerStats>
+            gnss_power_stats_cbq_;
+    android::hardware::gnss::GnssPowerStats last_gnss_power_stats_;
+};
diff --git a/gnss/aidl/vts/gnss_hal_test.h b/gnss/aidl/vts/gnss_hal_test.h
index eb5301e..f72f7fe 100644
--- a/gnss/aidl/vts/gnss_hal_test.h
+++ b/gnss/aidl/vts/gnss_hal_test.h
@@ -36,7 +36,7 @@
 using android::hardware::gnss::IGnssConfiguration;
 
 // The main test class for GNSS HAL.
-class GnssHalTest : public GnssHalTestTemplate<IGnss_V2_1> {
+class GnssHalTest : public android::hardware::gnss::common::GnssHalTestTemplate<IGnss_V2_1> {
   public:
     GnssHalTest(){};
     ~GnssHalTest(){};
diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp
index 2b8a447..857c742 100644
--- a/gnss/aidl/vts/gnss_hal_test_cases.cpp
+++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp
@@ -16,16 +16,30 @@
 
 #define LOG_TAG "GnssHalTestCases"
 
+#include <android/hardware/gnss/IGnss.h>
+#include <android/hardware/gnss/IGnssMeasurementCallback.h>
+#include <android/hardware/gnss/IGnssMeasurementInterface.h>
+#include <android/hardware/gnss/IGnssPowerIndication.h>
 #include <android/hardware/gnss/IGnssPsds.h>
+#include "GnssMeasurementCallbackAidl.h"
+#include "GnssPowerIndicationCallback.h"
 #include "gnss_hal_test.h"
 
 using android::sp;
 using android::hardware::gnss::BlocklistedSource;
-using GnssConstellationTypeAidl = android::hardware::gnss::GnssConstellationType;
+using android::hardware::gnss::ElapsedRealtime;
+using android::hardware::gnss::GnssClock;
+using android::hardware::gnss::GnssMeasurement;
+using android::hardware::gnss::IGnss;
 using android::hardware::gnss::IGnssConfiguration;
+using android::hardware::gnss::IGnssMeasurementCallback;
+using android::hardware::gnss::IGnssMeasurementInterface;
+using android::hardware::gnss::IGnssPowerIndication;
 using android::hardware::gnss::IGnssPsds;
 using android::hardware::gnss::PsdsType;
 
+using GnssConstellationTypeAidl = android::hardware::gnss::GnssConstellationType;
+
 /*
  * SetupTeardownCreateCleanup:
  * Requests the gnss HAL then calls cleanup
@@ -37,7 +51,7 @@
 /*
  * TestPsdsExtension:
  * 1. Gets the PsdsExtension and verifies that it returns a non-null extension.
- * 2. Injects empty PSDS data and verifies that it returns false.
+ * 2. Injects empty PSDS data and verifies that it returns an error.
  */
 TEST_P(GnssHalTest, TestPsdsExtension) {
     sp<IGnssPsds> iGnssPsds;
@@ -46,7 +60,91 @@
     ASSERT_TRUE(iGnssPsds != nullptr);
 
     status = iGnssPsds->injectPsdsData(PsdsType::LONG_TERM, std::vector<uint8_t>());
+    ASSERT_FALSE(status.isOk());
+}
+
+/*
+ * TestGnssMeasurementExtension:
+ * 1. Gets the GnssMeasurementExtension and verifies that it returns a non-null extension.
+ * 2. Sets a GnssMeasurementCallback, waits for a measurement, and verifies fields are valid.
+ */
+TEST_P(GnssHalTest, TestGnssMeasurementExtension) {
+    const int kFirstGnssMeasurementTimeoutSeconds = 10;
+    sp<IGnssMeasurementInterface> iGnssMeasurement;
+    auto status = aidl_gnss_hal_->getExtensionGnssMeasurement(&iGnssMeasurement);
     ASSERT_TRUE(status.isOk());
+    ASSERT_TRUE(iGnssMeasurement != nullptr);
+
+    auto callback = sp<GnssMeasurementCallbackAidl>::make();
+    status = iGnssMeasurement->setCallback(callback, /* enableFullTracking= */ true);
+    ASSERT_TRUE(status.isOk());
+
+    android::hardware::gnss::GnssData lastMeasurement;
+    ASSERT_TRUE(callback->gnss_data_cbq_.retrieve(lastMeasurement,
+                                                  kFirstGnssMeasurementTimeoutSeconds));
+    EXPECT_EQ(callback->gnss_data_cbq_.calledCount(), 1);
+    ASSERT_TRUE(lastMeasurement.measurements.size() > 0);
+
+    // Validity check GnssData fields
+    ASSERT_TRUE(
+            lastMeasurement.elapsedRealtime.flags >= 0 &&
+            lastMeasurement.elapsedRealtime.flags <=
+                    (ElapsedRealtime::HAS_TIMESTAMP_NS | ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS));
+    if (lastMeasurement.elapsedRealtime.flags & ElapsedRealtime::HAS_TIMESTAMP_NS) {
+        ASSERT_TRUE(lastMeasurement.elapsedRealtime.timestampNs > 0);
+    }
+    if (lastMeasurement.elapsedRealtime.flags & ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS) {
+        ASSERT_TRUE(lastMeasurement.elapsedRealtime.timeUncertaintyNs > 0);
+    }
+    ASSERT_TRUE(lastMeasurement.clock.gnssClockFlags >= 0 &&
+                lastMeasurement.clock.gnssClockFlags <=
+                        (GnssClock::HAS_LEAP_SECOND | GnssClock::HAS_TIME_UNCERTAINTY |
+                         GnssClock::HAS_FULL_BIAS | GnssClock::HAS_BIAS |
+                         GnssClock::HAS_BIAS_UNCERTAINTY | GnssClock::HAS_DRIFT |
+                         GnssClock::HAS_DRIFT_UNCERTAINTY));
+    for (const auto& measurement : lastMeasurement.measurements) {
+        ASSERT_TRUE(
+                measurement.flags >= 0 &&
+                measurement.flags <=
+                        (GnssMeasurement::HAS_SNR | GnssMeasurement::HAS_CARRIER_FREQUENCY |
+                         GnssMeasurement::HAS_CARRIER_CYCLES | GnssMeasurement::HAS_CARRIER_PHASE |
+                         GnssMeasurement::HAS_CARRIER_PHASE_UNCERTAINTY |
+                         GnssMeasurement::HAS_AUTOMATIC_GAIN_CONTROL |
+                         GnssMeasurement::HAS_FULL_ISB | GnssMeasurement::HAS_FULL_ISB_UNCERTAINTY |
+                         GnssMeasurement::HAS_SATELLITE_ISB |
+                         GnssMeasurement::HAS_SATELLITE_ISB_UNCERTAINTY));
+    }
+
+    status = iGnssMeasurement->close();
+    ASSERT_TRUE(status.isOk());
+}
+
+/*
+ * TestGnssPowerIndication
+ * 1. Gets the GnssPowerIndicationExtension.
+ * 2. Sets a GnssPowerIndicationCallback.
+ * 3.
+ */
+TEST_P(GnssHalTest, TestGnssPowerIndication) {
+    sp<IGnssPowerIndication> iGnssPowerIndication;
+    auto status = aidl_gnss_hal_->getExtensionGnssPowerIndication(&iGnssPowerIndication);
+    ASSERT_TRUE(status.isOk());
+    ASSERT_TRUE(iGnssPowerIndication != nullptr);
+
+    auto gnssPowerIndicationCallback = sp<GnssPowerIndicationCallback>::make();
+    status = iGnssPowerIndication->setCallback(gnssPowerIndicationCallback);
+    ASSERT_TRUE(status.isOk());
+
+    const int kTimeoutSec = 2;
+    EXPECT_TRUE(gnssPowerIndicationCallback->capabilities_cbq_.retrieve(
+            gnssPowerIndicationCallback->last_capabilities_, kTimeoutSec));
+
+    EXPECT_EQ(gnssPowerIndicationCallback->capabilities_cbq_.calledCount(), 1);
+
+    iGnssPowerIndication->requestGnssPowerStats();
+    EXPECT_TRUE(gnssPowerIndicationCallback->gnss_power_stats_cbq_.retrieve(
+            gnssPowerIndicationCallback->last_gnss_power_stats_, kTimeoutSec));
+    EXPECT_EQ(gnssPowerIndicationCallback->gnss_power_stats_cbq_.calledCount(), 1);
 }
 
 /*
diff --git a/gnss/common/utils/default/Android.bp b/gnss/common/utils/default/Android.bp
index 730de4b..be1d532 100644
--- a/gnss/common/utils/default/Android.bp
+++ b/gnss/common/utils/default/Android.bp
@@ -42,5 +42,6 @@
         "android.hardware.gnss@2.1",
         "android.hardware.gnss.measurement_corrections@1.1",
         "android.hardware.gnss.measurement_corrections@1.0",
+        "android.hardware.gnss-ndk_platform",
     ],
 }
diff --git a/gnss/common/utils/default/MockLocation.cpp b/gnss/common/utils/default/MockLocation.cpp
index 2d8e7c5..c90075f 100644
--- a/gnss/common/utils/default/MockLocation.cpp
+++ b/gnss/common/utils/default/MockLocation.cpp
@@ -21,5 +21,7 @@
 float gMockLatitudeDegrees{37.4219999};
 float gMockLongitudeDegrees{-122.0840575};
 float gMockAltitudeMeters{1.60062531};
+float gMockBearingDegrees{0};
+float gMockSpeedMetersPerSec{0};
 
 }  // namespace android::hardware::gnss::common
diff --git a/gnss/common/utils/default/Utils.cpp b/gnss/common/utils/default/Utils.cpp
index fa83634..dd932d4 100644
--- a/gnss/common/utils/default/Utils.cpp
+++ b/gnss/common/utils/default/Utils.cpp
@@ -17,6 +17,7 @@
 #include <Constants.h>
 #include <MockLocation.h>
 #include <Utils.h>
+#include <aidl/android/hardware/gnss/BnGnss.h>
 #include <utils/SystemClock.h>
 
 namespace android {
@@ -24,16 +25,28 @@
 namespace gnss {
 namespace common {
 
+using IGnss = aidl::android::hardware::gnss::IGnss;
+using IGnssMeasurementCallback = aidl::android::hardware::gnss::IGnssMeasurementCallback;
+using GnssMeasurement = aidl::android::hardware::gnss::GnssMeasurement;
 using GnssSvFlags = V1_0::IGnssCallback::GnssSvFlags;
 using GnssMeasurementFlagsV1_0 = V1_0::IGnssMeasurementCallback::GnssMeasurementFlags;
 using GnssMeasurementFlagsV2_1 = V2_1::IGnssMeasurementCallback::GnssMeasurementFlags;
 using GnssMeasurementStateV2_0 = V2_0::IGnssMeasurementCallback::GnssMeasurementState;
-using ElapsedRealtime = V2_0::ElapsedRealtime;
+using ElapsedRealtime = aidl::android::hardware::gnss::ElapsedRealtime;
 using ElapsedRealtimeFlags = V2_0::ElapsedRealtimeFlags;
 using GnssConstellationTypeV2_0 = V2_0::GnssConstellationType;
 using IGnssMeasurementCallbackV2_0 = V2_0::IGnssMeasurementCallback;
 using GnssSignalType = V2_1::GnssSignalType;
 
+using GnssDataV2_0 = V2_0::IGnssMeasurementCallback::GnssData;
+using GnssDataV2_1 = V2_1::IGnssMeasurementCallback::GnssData;
+using GnssSvInfoV1_0 = V1_0::IGnssCallback::GnssSvInfo;
+using GnssSvInfoV2_0 = V2_0::IGnssCallback::GnssSvInfo;
+using GnssSvInfoV2_1 = V2_1::IGnssCallback::GnssSvInfo;
+using GnssAntennaInfo = ::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo;
+using Row = V2_1::IGnssAntennaInfoCallback::Row;
+using Coord = V2_1::IGnssAntennaInfoCallback::Coord;
+
 GnssDataV2_1 Utils::getMockMeasurementV2_1() {
     GnssDataV2_0 gnssDataV2_0 = Utils::getMockMeasurementV2_0();
     V2_1::IGnssMeasurementCallback::GnssMeasurement gnssMeasurementV2_1 = {
@@ -110,7 +123,7 @@
                                                        .driftUncertaintyNsps = 310.64968328491528,
                                                        .hwClockDiscontinuityCount = 1};
 
-    ElapsedRealtime timestamp = {
+    V2_0::ElapsedRealtime timestamp = {
             .flags = ElapsedRealtimeFlags::HAS_TIMESTAMP_NS |
                      ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS,
             .timestampNs = static_cast<uint64_t>(::android::elapsedRealtimeNano()),
@@ -124,6 +137,52 @@
     return gnssData;
 }
 
+aidl::android::hardware::gnss::GnssData Utils::getMockMeasurement() {
+    aidl::android::hardware::gnss::GnssSignalType signalType = {
+            .constellation = aidl::android::hardware::gnss::GnssConstellationType::GLONASS,
+            .carrierFrequencyHz = 1.59975e+09,
+            .codeType = aidl::android::hardware::gnss::GnssSignalType::CODE_TYPE_C,
+    };
+    aidl::android::hardware::gnss::GnssMeasurement measurement = {
+            .flags = GnssMeasurement::HAS_CARRIER_FREQUENCY,
+            .svid = 6,
+            .signalType = signalType,
+            .timeOffsetNs = 0.0,
+            .receivedSvTimeInNs = 8195997131077,
+            .receivedSvTimeUncertaintyInNs = 15,
+            .antennaCN0DbHz = 30.0,
+            .pseudorangeRateMps = -484.13739013671875,
+            .pseudorangeRateUncertaintyMps = 1.0379999876022339,
+            .accumulatedDeltaRangeState = GnssMeasurement::ADR_STATE_UNKNOWN,
+            .accumulatedDeltaRangeM = 0.0,
+            .accumulatedDeltaRangeUncertaintyM = 0.0,
+            .multipathIndicator = aidl::android::hardware::gnss::GnssMultipathIndicator::UNKNOWN,
+            .state = GnssMeasurement::STATE_CODE_LOCK | GnssMeasurement::STATE_BIT_SYNC |
+                     GnssMeasurement::STATE_SUBFRAME_SYNC | GnssMeasurement::STATE_TOW_DECODED |
+                     GnssMeasurement::STATE_GLO_STRING_SYNC |
+                     GnssMeasurement::STATE_GLO_TOD_DECODED};
+
+    aidl::android::hardware::gnss::GnssClock clock = {.timeNs = 2713545000000,
+                                                      .fullBiasNs = -1226701900521857520,
+                                                      .biasNs = 0.59689998626708984,
+                                                      .biasUncertaintyNs = 47514.989972114563,
+                                                      .driftNsps = -51.757811607455452,
+                                                      .driftUncertaintyNsps = 310.64968328491528,
+                                                      .hwClockDiscontinuityCount = 1};
+
+    ElapsedRealtime timestamp = {
+            .flags = ElapsedRealtime::HAS_TIMESTAMP_NS | ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS,
+            .timestampNs = ::android::elapsedRealtimeNano(),
+            // This is an hardcoded value indicating a 1ms of uncertainty between the two clocks.
+            // In an actual implementation provide an estimate of the synchronization uncertainty
+            // or don't set the field.
+            .timeUncertaintyNs = 1000000};
+
+    aidl::android::hardware::gnss::GnssData gnssData = {
+            .measurements = {measurement}, .clock = clock, .elapsedRealtime = timestamp};
+    return gnssData;
+}
+
 V2_0::GnssLocation Utils::getMockLocationV2_0() {
     const V2_0::ElapsedRealtime timestamp = {
             .flags = V2_0::ElapsedRealtimeFlags::HAS_TIMESTAMP_NS |
@@ -145,8 +204,8 @@
             .latitudeDegrees = gMockLatitudeDegrees,
             .longitudeDegrees = gMockLongitudeDegrees,
             .altitudeMeters = gMockAltitudeMeters,
-            .speedMetersPerSec = kMockSpeedMetersPerSec,
-            .bearingDegrees = kMockBearingDegrees,
+            .speedMetersPerSec = gMockSpeedMetersPerSec,
+            .bearingDegrees = gMockBearingDegrees,
             .horizontalAccuracyMeters = kMockHorizontalAccuracyMeters,
             .verticalAccuracyMeters = kMockVerticalAccuracyMeters,
             .speedAccuracyMetersPerSecond = kMockSpeedAccuracyMetersPerSecond,
diff --git a/gnss/common/utils/default/include/Constants.h b/gnss/common/utils/default/include/Constants.h
index ad4e0eb..a290ed2 100644
--- a/gnss/common/utils/default/include/Constants.h
+++ b/gnss/common/utils/default/include/Constants.h
@@ -24,8 +24,6 @@
 namespace gnss {
 namespace common {
 
-const float kMockSpeedMetersPerSec = 0;
-const float kMockBearingDegrees = 0;
 const float kMockHorizontalAccuracyMeters = 5;
 const float kMockVerticalAccuracyMeters = 5;
 const float kMockSpeedAccuracyMetersPerSecond = 1;
diff --git a/gnss/common/utils/default/include/MockLocation.h b/gnss/common/utils/default/include/MockLocation.h
index cd8cb5d..0bfdd1a 100644
--- a/gnss/common/utils/default/include/MockLocation.h
+++ b/gnss/common/utils/default/include/MockLocation.h
@@ -27,6 +27,8 @@
 extern float gMockLatitudeDegrees;
 extern float gMockLongitudeDegrees;
 extern float gMockAltitudeMeters;
+extern float gMockBearingDegrees;
+extern float gMockSpeedMetersPerSec;
 
 }  // namespace common
 }  // namespace gnss
diff --git a/gnss/common/utils/default/include/NmeaFixInfo.h b/gnss/common/utils/default/include/NmeaFixInfo.h
index fb2c1a4..06eae7e 100644
--- a/gnss/common/utils/default/include/NmeaFixInfo.h
+++ b/gnss/common/utils/default/include/NmeaFixInfo.h
@@ -26,11 +26,6 @@
 namespace hardware {
 namespace gnss {
 namespace common {
-using ::android::sp;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
 
 constexpr char GPGA_RECORD_TAG[] = "$GPGGA";
 constexpr char GPRMC_RECORD_TAG[] = "$GPRMC";
diff --git a/gnss/common/utils/default/include/Utils.h b/gnss/common/utils/default/include/Utils.h
index d9ad5a5..0ca1b00 100644
--- a/gnss/common/utils/default/include/Utils.h
+++ b/gnss/common/utils/default/include/Utils.h
@@ -17,6 +17,7 @@
 #ifndef android_hardware_gnss_common_default_Utils_H_
 #define android_hardware_gnss_common_default_Utils_H_
 
+#include <aidl/android/hardware/gnss/BnGnssMeasurementInterface.h>
 #include <android/hardware/gnss/1.0/IGnss.h>
 #include <android/hardware/gnss/2.0/IGnss.h>
 #include <android/hardware/gnss/2.1/IGnss.h>
@@ -28,28 +29,22 @@
 namespace gnss {
 namespace common {
 
-using GnssDataV2_0 = V2_0::IGnssMeasurementCallback::GnssData;
-using GnssDataV2_1 = V2_1::IGnssMeasurementCallback::GnssData;
-using GnssSvInfoV1_0 = V1_0::IGnssCallback::GnssSvInfo;
-using GnssSvInfoV2_0 = V2_0::IGnssCallback::GnssSvInfo;
-using GnssSvInfoV2_1 = V2_1::IGnssCallback::GnssSvInfo;
-using GnssAntennaInfo = ::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo;
-using Row = ::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::Row;
-using Coord = ::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::Coord;
-
 struct Utils {
-    static GnssDataV2_0 getMockMeasurementV2_0();
-    static GnssDataV2_1 getMockMeasurementV2_1();
+    static aidl::android::hardware::gnss::GnssData getMockMeasurement();
+    static V2_0::IGnssMeasurementCallback::GnssData getMockMeasurementV2_0();
+    static V2_1::IGnssMeasurementCallback::GnssData getMockMeasurementV2_1();
     static V2_0::GnssLocation getMockLocationV2_0();
     static V1_0::GnssLocation getMockLocationV1_0();
-    static hidl_vec<GnssSvInfoV2_1> getMockSvInfoListV2_1();
-    static GnssSvInfoV2_1 getMockSvInfoV2_1(GnssSvInfoV2_0 gnssSvInfoV2_0, float basebandCN0DbHz);
-    static GnssSvInfoV2_0 getMockSvInfoV2_0(GnssSvInfoV1_0 gnssSvInfoV1_0,
-                                            V2_0::GnssConstellationType type);
-    static GnssSvInfoV1_0 getMockSvInfoV1_0(int16_t svid, V1_0::GnssConstellationType type,
-                                            float cN0DbHz, float elevationDegrees,
-                                            float azimuthDegrees);
-    static hidl_vec<GnssAntennaInfo> getMockAntennaInfos();
+    static hidl_vec<V2_1::IGnssCallback::GnssSvInfo> getMockSvInfoListV2_1();
+    static V2_1::IGnssCallback::GnssSvInfo getMockSvInfoV2_1(
+            V2_0::IGnssCallback::GnssSvInfo gnssSvInfoV2_0, float basebandCN0DbHz);
+    static V2_0::IGnssCallback::GnssSvInfo getMockSvInfoV2_0(
+            V1_0::IGnssCallback::GnssSvInfo gnssSvInfoV1_0, V2_0::GnssConstellationType type);
+    static V1_0::IGnssCallback::GnssSvInfo getMockSvInfoV1_0(int16_t svid,
+                                                             V1_0::GnssConstellationType type,
+                                                             float cN0DbHz, float elevationDegrees,
+                                                             float azimuthDegrees);
+    static hidl_vec<V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo> getMockAntennaInfos();
 };
 
 }  // namespace common
diff --git a/gnss/common/utils/default/include/v2_1/GnssAntennaInfo.h b/gnss/common/utils/default/include/v2_1/GnssAntennaInfo.h
index e74ff54..a232499 100644
--- a/gnss/common/utils/default/include/v2_1/GnssAntennaInfo.h
+++ b/gnss/common/utils/default/include/v2_1/GnssAntennaInfo.h
@@ -23,29 +23,25 @@
 
 namespace android::hardware::gnss::V2_1::implementation {
 
-using ::android::sp;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using IGnssAntennaInfo = ::android::hardware::gnss::V2_1::IGnssAntennaInfo;
-using IGnssAntennaInfoCallback = ::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
-
-struct GnssAntennaInfo : public IGnssAntennaInfo {
+struct GnssAntennaInfo : public ::android::hardware::gnss::V2_1::IGnssAntennaInfo {
     GnssAntennaInfo();
     ~GnssAntennaInfo();
 
     // Methods from ::android::hardware::gnss::V2_1::IGnssAntennaInfo follow.
     Return<GnssAntennaInfoStatus> setCallback(
-            const sp<IGnssAntennaInfoCallback>& callback) override;
+            const sp<::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback>& callback) override;
     Return<void> close() override;
 
   private:
     void start();
     void stop();
     void reportAntennaInfo(
-            const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& antennaInfo) const;
+            const hidl_vec<
+                    ::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>&
+                    antennaInfo) const;
 
     // Guarded by mMutex
-    static sp<IGnssAntennaInfoCallback> sCallback;
+    static sp<::android::hardware::gnss::V2_1::IGnssAntennaInfoCallback> sCallback;
 
     std::atomic<long> mMinIntervalMillis;
     std::atomic<bool> mIsActive;
diff --git a/gnss/common/utils/default/include/v2_1/GnssConfiguration.h b/gnss/common/utils/default/include/v2_1/GnssConfiguration.h
index 8463a5c..2cfb38f 100644
--- a/gnss/common/utils/default/include/v2_1/GnssConfiguration.h
+++ b/gnss/common/utils/default/include/v2_1/GnssConfiguration.h
@@ -25,35 +25,27 @@
 
 namespace android::hardware::gnss::V2_1::implementation {
 
-using ::android::sp;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
-using BlacklistedSourceV2_1 =
-        ::android::hardware::gnss::V2_1::IGnssConfiguration::BlacklistedSource;
-using GnssConstellationTypeV2_0 = V2_0::GnssConstellationType;
-using GnssSvInfoV2_1 = V2_1::IGnssCallback::GnssSvInfo;
-
 struct BlacklistedSourceHashV2_1 {
-    inline int operator()(const BlacklistedSourceV2_1& source) const {
+    inline int operator()(
+            const ::android::hardware::gnss::V2_1::IGnssConfiguration::BlacklistedSource& source)
+            const {
         return int(source.constellation) * 1000 + int(source.svid);
     }
 };
 
 struct BlacklistedSourceEqualV2_1 {
-    inline bool operator()(const BlacklistedSourceV2_1& s1, const BlacklistedSourceV2_1& s2) const {
+    inline bool operator()(
+            const ::android::hardware::gnss::V2_1::IGnssConfiguration::BlacklistedSource& s1,
+            const ::android::hardware::gnss::V2_1::IGnssConfiguration::BlacklistedSource& s2)
+            const {
         return (s1.constellation == s2.constellation) && (s1.svid == s2.svid);
     }
 };
 
 using BlacklistedSourceSetV2_1 =
-        std::unordered_set<BlacklistedSourceV2_1, BlacklistedSourceHashV2_1,
-                           BlacklistedSourceEqualV2_1>;
-using BlacklistedConstellationSetV2_1 = std::unordered_set<GnssConstellationTypeV2_0>;
+        std::unordered_set<::android::hardware::gnss::V2_1::IGnssConfiguration::BlacklistedSource,
+                           BlacklistedSourceHashV2_1, BlacklistedSourceEqualV2_1>;
+using BlacklistedConstellationSetV2_1 = std::unordered_set<V2_0::GnssConstellationType>;
 
 struct GnssConfiguration : public IGnssConfiguration {
     // Methods from ::android::hardware::gnss::V1_0::IGnssConfiguration follow.
@@ -78,7 +70,7 @@
     Return<bool> setBlacklist_2_1(
             const hidl_vec<V2_1::IGnssConfiguration::BlacklistedSource>& blacklist) override;
 
-    Return<bool> isBlacklistedV2_1(const GnssSvInfoV2_1& gnssSvInfo) const;
+    Return<bool> isBlacklistedV2_1(const V2_1::IGnssCallback::GnssSvInfo& gnssSvInfo) const;
 
   private:
     mutable std::recursive_mutex mMutex;
diff --git a/gnss/common/utils/default/include/v2_1/GnssDebug.h b/gnss/common/utils/default/include/v2_1/GnssDebug.h
index 8580989..481de59 100644
--- a/gnss/common/utils/default/include/v2_1/GnssDebug.h
+++ b/gnss/common/utils/default/include/v2_1/GnssDebug.h
@@ -21,15 +21,8 @@
 
 namespace android::hardware::gnss::V1_1::implementation {
 
-using ::android::sp;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using V1_0::IGnssDebug;
-
 /* Interface for GNSS Debug support. */
-struct GnssDebug : public IGnssDebug {
+struct GnssDebug : public V1_0::IGnssDebug {
     /*
      * Methods from ::android::hardware::gnss::V1_0::IGnssDebug follow.
      * These declarations were generated from IGnssDebug.hal.
diff --git a/gnss/common/utils/default/include/v2_1/GnssMeasurement.h b/gnss/common/utils/default/include/v2_1/GnssMeasurement.h
index 1d1fc9d..db8407b 100644
--- a/gnss/common/utils/default/include/v2_1/GnssMeasurement.h
+++ b/gnss/common/utils/default/include/v2_1/GnssMeasurement.h
@@ -25,17 +25,6 @@
 
 namespace android::hardware::gnss::V2_1::implementation {
 
-using GnssDataV2_1 = V2_1::IGnssMeasurementCallback::GnssData;
-using GnssDataV2_0 = V2_0::IGnssMeasurementCallback::GnssData;
-
-using ::android::sp;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
 struct GnssMeasurement : public IGnssMeasurement {
     GnssMeasurement();
     ~GnssMeasurement();
@@ -59,8 +48,8 @@
   private:
     void start();
     void stop();
-    void reportMeasurement(const GnssDataV2_0&);
-    void reportMeasurement(const GnssDataV2_1&);
+    void reportMeasurement(const V2_0::IGnssMeasurementCallback::GnssData&);
+    void reportMeasurement(const V2_1::IGnssMeasurementCallback::GnssData&);
 
     // Guarded by mMutex
     static sp<V2_1::IGnssMeasurementCallback> sCallback_2_1;
diff --git a/gnss/common/utils/default/include/v2_1/GnssMeasurementCorrections.h b/gnss/common/utils/default/include/v2_1/GnssMeasurementCorrections.h
index eaa7659..54045ad 100644
--- a/gnss/common/utils/default/include/v2_1/GnssMeasurementCorrections.h
+++ b/gnss/common/utils/default/include/v2_1/GnssMeasurementCorrections.h
@@ -22,9 +22,6 @@
 
 namespace android::hardware::gnss::measurement_corrections::V1_1::implementation {
 
-using ::android::sp;
-using ::android::hardware::Return;
-
 struct GnssMeasurementCorrections : public IMeasurementCorrections {
     GnssMeasurementCorrections();
     ~GnssMeasurementCorrections();
diff --git a/gnss/common/utils/default/include/v2_1/GnssTemplate.h b/gnss/common/utils/default/include/v2_1/GnssTemplate.h
index cbf3933..4d4ec93 100644
--- a/gnss/common/utils/default/include/v2_1/GnssTemplate.h
+++ b/gnss/common/utils/default/include/v2_1/GnssTemplate.h
@@ -39,16 +39,6 @@
 
 namespace android::hardware::gnss::common::implementation {
 
-using GnssSvInfo = V2_1::IGnssCallback::GnssSvInfo;
-
-using common::NmeaFixInfo;
-using common::Utils;
-using measurement_corrections::V1_1::implementation::GnssMeasurementCorrections;
-
-using V2_1::implementation::GnssAntennaInfo;
-using V2_1::implementation::GnssConfiguration;
-using V2_1::implementation::GnssMeasurement;
-
 constexpr int INPUT_BUFFER_SIZE = 128;
 constexpr char CMD_GET_LOCATION[] = "CMD_GET_LOCATION";
 constexpr char GNSS_PATH[] = "/dev/gnss0";
@@ -120,7 +110,7 @@
     std::unique_ptr<V2_0::GnssLocation> getLocationFromHW();
     void reportLocation(const V2_0::GnssLocation&) const;
     void reportLocation(const V1_0::GnssLocation&) const;
-    void reportSvStatus(const hidl_vec<GnssSvInfo>&) const;
+    void reportSvStatus(const hidl_vec<V2_1::IGnssCallback::GnssSvInfo>&) const;
 
     Return<void> help(const hidl_handle& fd);
     Return<void> setLocation(const hidl_handle& fd, const hidl_vec<hidl_string>& options);
@@ -131,15 +121,15 @@
     static sp<V1_0::IGnssCallback> sGnssCallback_1_0;
 
     std::atomic<long> mMinIntervalMs;
-    sp<GnssConfiguration> mGnssConfiguration;
+    sp<V2_1::implementation::GnssConfiguration> mGnssConfiguration;
     std::atomic<bool> mIsActive;
     std::atomic<bool> mHardwareModeChecked;
     std::atomic<int> mGnssFd;
     std::thread mThread;
 
     mutable std::mutex mMutex;
-    virtual hidl_vec<GnssSvInfo> filterBlocklistedSatellitesV2_1(
-            hidl_vec<GnssSvInfo> gnssSvInfoList);
+    virtual hidl_vec<V2_1::IGnssCallback::GnssSvInfo> filterBlocklistedSatellitesV2_1(
+            hidl_vec<V2_1::IGnssCallback::GnssSvInfo> gnssSvInfoList);
 };
 
 template <class T_IGnss>
@@ -154,7 +144,7 @@
 template <class T_IGnss>
 GnssTemplate<T_IGnss>::GnssTemplate()
     : mMinIntervalMs(1000),
-      mGnssConfiguration{new GnssConfiguration()},
+      mGnssConfiguration{new V2_1::implementation::GnssConfiguration()},
       mHardwareModeChecked(false),
       mGnssFd(-1) {}
 
@@ -237,8 +227,8 @@
 }
 
 template <class T_IGnss>
-hidl_vec<GnssSvInfo> GnssTemplate<T_IGnss>::filterBlocklistedSatellitesV2_1(
-        hidl_vec<GnssSvInfo> gnssSvInfoList) {
+hidl_vec<V2_1::IGnssCallback::GnssSvInfo> GnssTemplate<T_IGnss>::filterBlocklistedSatellitesV2_1(
+        hidl_vec<V2_1::IGnssCallback::GnssSvInfo> gnssSvInfoList) {
     ALOGD("filterBlocklistedSatellitesV2_1");
     for (uint32_t i = 0; i < gnssSvInfoList.size(); i++) {
         if (mGnssConfiguration->isBlacklistedV2_1(gnssSvInfoList[i])) {
@@ -348,7 +338,7 @@
 template <class T_IGnss>
 Return<sp<V1_0::IGnssMeasurement>> GnssTemplate<T_IGnss>::getExtensionGnssMeasurement() {
     ALOGD("Gnss::getExtensionGnssMeasurement");
-    return new GnssMeasurement();
+    return new V2_1::implementation::GnssMeasurement();
 }
 
 template <class T_IGnss>
@@ -501,14 +491,14 @@
 template <class T_IGnss>
 Return<sp<V2_0::IGnssMeasurement>> GnssTemplate<T_IGnss>::getExtensionGnssMeasurement_2_0() {
     ALOGD("Gnss::getExtensionGnssMeasurement_2_0");
-    return new GnssMeasurement();
+    return new V2_1::implementation::GnssMeasurement();
 }
 
 template <class T_IGnss>
 Return<sp<measurement_corrections::V1_0::IMeasurementCorrections>>
 GnssTemplate<T_IGnss>::getExtensionMeasurementCorrections() {
     ALOGD("Gnss::getExtensionMeasurementCorrections()");
-    return new GnssMeasurementCorrections();
+    return new measurement_corrections::V1_1::implementation::GnssMeasurementCorrections();
 }
 
 template <class T_IGnss>
@@ -569,7 +559,7 @@
 template <class T_IGnss>
 Return<sp<V2_1::IGnssMeasurement>> GnssTemplate<T_IGnss>::getExtensionGnssMeasurement_2_1() {
     ALOGD("Gnss::getExtensionGnssMeasurement_2_1");
-    return new GnssMeasurement();
+    return new V2_1::implementation::GnssMeasurement();
 }
 
 template <class T_IGnss>
@@ -582,17 +572,18 @@
 Return<sp<measurement_corrections::V1_1::IMeasurementCorrections>>
 GnssTemplate<T_IGnss>::getExtensionMeasurementCorrections_1_1() {
     ALOGD("Gnss::getExtensionMeasurementCorrections_1_1()");
-    return new GnssMeasurementCorrections();
+    return new measurement_corrections::V1_1::implementation::GnssMeasurementCorrections();
 }
 
 template <class T_IGnss>
 Return<sp<V2_1::IGnssAntennaInfo>> GnssTemplate<T_IGnss>::getExtensionGnssAntennaInfo() {
     ALOGD("Gnss::getExtensionGnssAntennaInfo");
-    return new GnssAntennaInfo();
+    return new V2_1::implementation::GnssAntennaInfo();
 }
 
 template <class T_IGnss>
-void GnssTemplate<T_IGnss>::reportSvStatus(const hidl_vec<GnssSvInfo>& svInfoList) const {
+void GnssTemplate<T_IGnss>::reportSvStatus(
+        const hidl_vec<V2_1::IGnssCallback::GnssSvInfo>& svInfoList) const {
     std::unique_lock<std::mutex> lock(mMutex);
     // TODO(skz): update this to call 2_0 callback if non-null
     if (sGnssCallback_2_1 == nullptr) {
@@ -651,6 +642,8 @@
     auto lat = gMockLatitudeDegrees;
     auto lon = gMockLongitudeDegrees;
     auto ele = gMockAltitudeMeters;
+    auto bea = gMockBearingDegrees;
+    auto spd = gMockSpeedMetersPerSec;
 
     for (size_t i = 1; i < options.size(); ++i) {
         std::string option = options[i];
@@ -663,6 +656,12 @@
         } else if (option.rfind("ele=", 0) == 0) {
             option = option.substr(4);
             ele = stof(option);
+        } else if (option.rfind("bea=", 0) == 0) {
+            option = option.substr(4);
+            bea = stof(option);
+        } else if (option.rfind("spd=", 0) == 0) {
+            option = option.substr(4);
+            spd = stof(option);
         } else {
             dprintf(fd->data[0], "unsupported location argument: %s\n", option.c_str());
         }
@@ -671,9 +670,12 @@
     gMockLatitudeDegrees = lat;
     gMockLongitudeDegrees = lon;
     gMockAltitudeMeters = ele;
+    gMockBearingDegrees = bea;
+    gMockSpeedMetersPerSec = spd;
 
-    dprintf(fd->data[0], "mock location updated to lat=%f lon=%f ele=%f\n", gMockLatitudeDegrees,
-            gMockLongitudeDegrees, gMockAltitudeMeters);
+    dprintf(fd->data[0], "mock location updated to lat=%f lon=%f ele=%f bea=%f spd=%f\n",
+            gMockLatitudeDegrees, gMockLongitudeDegrees, gMockAltitudeMeters, gMockBearingDegrees,
+            gMockSpeedMetersPerSec);
 
     return Void();
 }
@@ -682,7 +684,7 @@
 Return<void> GnssTemplate<T_IGnss>::help(const hidl_handle& fd) {
     dprintf(fd->data[0],
             "invalid option for Gnss HAL; valid options are:\n"
-            "location lat=.. lon=.. ele=..\n");
+            "location [lat=..] [lon=..] [ele=..] [bea=..] [spd=..]\n");
     return Void();
 }
 
diff --git a/gnss/common/utils/default/v2_1/GnssConfiguration.cpp b/gnss/common/utils/default/v2_1/GnssConfiguration.cpp
index 8b30701..be974bc 100644
--- a/gnss/common/utils/default/v2_1/GnssConfiguration.cpp
+++ b/gnss/common/utils/default/v2_1/GnssConfiguration.cpp
@@ -21,6 +21,9 @@
 
 namespace android::hardware::gnss::V2_1::implementation {
 
+using GnssSvInfoV2_1 = V2_1::IGnssCallback::GnssSvInfo;
+using BlacklistedSourceV2_1 = V2_1::IGnssConfiguration::BlacklistedSource;
+
 // Methods from ::android::hardware::gnss::V1_0::IGnssConfiguration follow.
 Return<bool> GnssConfiguration::setSuplEs(bool enable) {
     ALOGD("setSuplEs enable: %d", enable);
@@ -69,7 +72,7 @@
 
 // Methods from ::android::hardware::gnss::V2_1::IGnssConfiguration follow.
 Return<bool> GnssConfiguration::setBlacklist_2_1(
-        const hidl_vec<V2_1::IGnssConfiguration::BlacklistedSource>& sourceList) {
+        const hidl_vec<BlacklistedSourceV2_1>& sourceList) {
     std::unique_lock<std::recursive_mutex> lock(mMutex);
     mBlacklistedConstellationSet.clear();
     mBlacklistedSourceSet.clear();
diff --git a/gnss/common/utils/default/v2_1/GnssDebug.cpp b/gnss/common/utils/default/v2_1/GnssDebug.cpp
index d78b0b6..537a90c 100644
--- a/gnss/common/utils/default/v2_1/GnssDebug.cpp
+++ b/gnss/common/utils/default/v2_1/GnssDebug.cpp
@@ -33,8 +33,8 @@
             .latitudeDegrees = gMockLatitudeDegrees,
             .longitudeDegrees = gMockLongitudeDegrees,
             .altitudeMeters = gMockAltitudeMeters,
-            .speedMetersPerSec = kMockSpeedMetersPerSec,
-            .bearingDegrees = kMockBearingDegrees,
+            .speedMetersPerSec = gMockSpeedMetersPerSec,
+            .bearingDegrees = gMockBearingDegrees,
             .horizontalAccuracyMeters = kMockHorizontalAccuracyMeters,
             .verticalAccuracyMeters = kMockVerticalAccuracyMeters,
             .speedAccuracyMetersPerSecond = kMockSpeedAccuracyMetersPerSecond,
diff --git a/gnss/common/utils/default/v2_1/GnssMeasurement.cpp b/gnss/common/utils/default/v2_1/GnssMeasurement.cpp
index 7d3a002..887cb5a 100644
--- a/gnss/common/utils/default/v2_1/GnssMeasurement.cpp
+++ b/gnss/common/utils/default/v2_1/GnssMeasurement.cpp
@@ -114,7 +114,7 @@
     }
 }
 
-void GnssMeasurement::reportMeasurement(const GnssDataV2_0& data) {
+void GnssMeasurement::reportMeasurement(const V2_0::IGnssMeasurementCallback::GnssData& data) {
     ALOGD("reportMeasurement()");
     std::unique_lock<std::mutex> lock(mMutex);
     if (sCallback_2_0 == nullptr) {
@@ -127,7 +127,7 @@
     }
 }
 
-void GnssMeasurement::reportMeasurement(const GnssDataV2_1& data) {
+void GnssMeasurement::reportMeasurement(const V2_1::IGnssMeasurementCallback::GnssData& data) {
     ALOGD("reportMeasurement()");
     std::unique_lock<std::mutex> lock(mMutex);
     if (sCallback_2_1 == nullptr) {
diff --git a/gnss/common/utils/vts/include/v2_1/gnss_hal_test_template.h b/gnss/common/utils/vts/include/v2_1/gnss_hal_test_template.h
index 1439158..fec3503 100644
--- a/gnss/common/utils/vts/include/v2_1/gnss_hal_test_template.h
+++ b/gnss/common/utils/vts/include/v2_1/gnss_hal_test_template.h
@@ -23,36 +23,10 @@
 #include <gtest/gtest.h>
 #include <chrono>
 
-using ::android::hardware::gnss::common::Utils;
-
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::Void;
-
-using android::hardware::gnss::common::GnssCallback;
-using android::hardware::gnss::common::GnssCallbackEventQueue;
-using android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrectionsCallback;
-using android::hardware::gnss::V1_0::GnssLocationFlags;
-using android::hardware::gnss::V2_0::GnssConstellationType;
-using android::hardware::gnss::V2_1::IGnssAntennaInfo;
-using android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
-
-using GnssLocation_1_0 = android::hardware::gnss::V1_0::GnssLocation;
-using GnssLocation_2_0 = android::hardware::gnss::V2_0::GnssLocation;
-
-using IGnssCallback_1_0 = android::hardware::gnss::V1_0::IGnssCallback;
-using IGnssCallback_2_0 = android::hardware::gnss::V2_0::IGnssCallback;
-using IGnssCallback_2_1 = android::hardware::gnss::V2_1::IGnssCallback;
-
-using IGnssMeasurementCallback_1_0 = android::hardware::gnss::V1_0::IGnssMeasurementCallback;
-using IGnssMeasurementCallback_1_1 = android::hardware::gnss::V1_1::IGnssMeasurementCallback;
-using IGnssMeasurementCallback_2_0 = android::hardware::gnss::V2_0::IGnssMeasurementCallback;
-using IGnssMeasurementCallback_2_1 = android::hardware::gnss::V2_1::IGnssMeasurementCallback;
-
-using android::sp;
-
 #define TIMEOUT_SEC 2  // for basic commands/responses
 
+namespace android::hardware::gnss::common {
+
 // The main test class for GNSS HAL.
 template <class T_IGnss>
 class GnssHalTestTemplate : public testing::TestWithParam<std::string> {
@@ -62,34 +36,37 @@
     virtual void TearDown() override;
 
     /* Callback class for GnssMeasurement. */
-    class GnssMeasurementCallback : public IGnssMeasurementCallback_2_1 {
+    class GnssMeasurementCallback : public V2_1::IGnssMeasurementCallback {
       public:
-        GnssCallbackEventQueue<IGnssMeasurementCallback_2_1::GnssData> measurement_cbq_;
+        GnssCallbackEventQueue<V2_1::IGnssMeasurementCallback::GnssData> measurement_cbq_;
 
         GnssMeasurementCallback() : measurement_cbq_("measurement"){};
         virtual ~GnssMeasurementCallback() = default;
 
         // Methods from V1_0::IGnssMeasurementCallback follow.
-        Return<void> GnssMeasurementCb(const IGnssMeasurementCallback_1_0::GnssData&) override {
+        Return<void> GnssMeasurementCb(const V1_0::IGnssMeasurementCallback::GnssData&) override {
             return Void();
         }
 
         // Methods from V1_1::IGnssMeasurementCallback follow.
-        Return<void> gnssMeasurementCb(const IGnssMeasurementCallback_1_1::GnssData&) override {
+        Return<void> gnssMeasurementCb(const V1_1::IGnssMeasurementCallback::GnssData&) override {
             return Void();
         }
 
         // Methods from V2_0::IGnssMeasurementCallback follow.
-        Return<void> gnssMeasurementCb_2_0(const IGnssMeasurementCallback_2_0::GnssData&) override {
+        Return<void> gnssMeasurementCb_2_0(
+                const V2_0::IGnssMeasurementCallback::GnssData&) override {
             return Void();
         }
 
         // Methods from V2_1::IGnssMeasurementCallback follow.
-        Return<void> gnssMeasurementCb_2_1(const IGnssMeasurementCallback_2_1::GnssData&) override;
+        Return<void> gnssMeasurementCb_2_1(
+                const V2_1::IGnssMeasurementCallback::GnssData&) override;
     };
 
     /* Callback class for GnssMeasurementCorrections. */
-    class GnssMeasurementCorrectionsCallback : public IMeasurementCorrectionsCallback {
+    class GnssMeasurementCorrectionsCallback
+        : public measurement_corrections::V1_0::IMeasurementCorrectionsCallback {
       public:
         uint32_t last_capabilities_;
         GnssCallbackEventQueue<uint32_t> capabilities_cbq_;
@@ -102,9 +79,9 @@
     };
 
     /* Callback class for GnssAntennaInfo. */
-    class GnssAntennaInfoCallback : public IGnssAntennaInfoCallback {
+    class GnssAntennaInfoCallback : public V2_1::IGnssAntennaInfoCallback {
       public:
-        GnssCallbackEventQueue<hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>>
+        GnssCallbackEventQueue<hidl_vec<V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>>
                 antenna_info_cbq_;
 
         GnssAntennaInfoCallback() : antenna_info_cbq_("info"){};
@@ -112,7 +89,7 @@
 
         // Methods from V2_1::GnssAntennaInfoCallback follow.
         Return<void> gnssAntennaInfoCb(
-                const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
+                const hidl_vec<V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
     };
 
     /*
@@ -138,7 +115,7 @@
      *
      *   check_speed: true if speed related fields are also verified.
      */
-    void CheckLocation(const GnssLocation_2_0& location, const bool check_speed);
+    void CheckLocation(const V2_0::GnssLocation& location, const bool check_speed);
 
     /*
      * StartAndCheckLocations:
@@ -170,7 +147,7 @@
      * Note that location is not stopped in this method. The client should call
      * StopAndClearLocations() after the call.
      */
-    GnssConstellationType startLocationAndGetNonGpsConstellation(
+    V2_0::GnssConstellationType startLocationAndGetNonGpsConstellation(
             const int locations_to_await, const int gnss_sv_info_list_timeout);
 
     sp<T_IGnss> gnss_hal_;      // GNSS HAL to call into
@@ -283,7 +260,7 @@
 }
 
 template <class T_IGnss>
-void GnssHalTestTemplate<T_IGnss>::CheckLocation(const GnssLocation_2_0& location,
+void GnssHalTestTemplate<T_IGnss>::CheckLocation(const V2_0::GnssLocation& location,
                                                  bool check_speed) {
     const bool check_more_accuracies =
             (gnss_cb_->info_cbq_.calledCount() > 0 && gnss_cb_->last_info_.yearOfHw >= 2017);
@@ -315,7 +292,7 @@
 }
 
 template <class T_IGnss>
-GnssConstellationType GnssHalTestTemplate<T_IGnss>::startLocationAndGetNonGpsConstellation(
+V2_0::GnssConstellationType GnssHalTestTemplate<T_IGnss>::startLocationAndGetNonGpsConstellation(
         const int locations_to_await, const int gnss_sv_info_list_timeout) {
     gnss_cb_->location_cbq_.reset();
     StartAndCheckLocations(locations_to_await);
@@ -328,29 +305,29 @@
           sv_info_list_cbq_size, locations_to_await, location_called_count);
 
     // Find first non-GPS constellation to blacklist
-    GnssConstellationType constellation_to_blacklist = GnssConstellationType::UNKNOWN;
+    V2_0::GnssConstellationType constellation_to_blacklist = V2_0::GnssConstellationType::UNKNOWN;
     for (int i = 0; i < sv_info_list_cbq_size; ++i) {
-        hidl_vec<IGnssCallback_2_1::GnssSvInfo> sv_info_vec;
+        hidl_vec<V2_1::IGnssCallback::GnssSvInfo> sv_info_vec;
         gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_vec, gnss_sv_info_list_timeout);
         for (uint32_t iSv = 0; iSv < sv_info_vec.size(); iSv++) {
             const auto& gnss_sv = sv_info_vec[iSv];
-            if ((gnss_sv.v2_0.v1_0.svFlag & IGnssCallback_1_0::GnssSvFlags::USED_IN_FIX) &&
-                (gnss_sv.v2_0.constellation != GnssConstellationType::UNKNOWN) &&
-                (gnss_sv.v2_0.constellation != GnssConstellationType::GPS)) {
+            if ((gnss_sv.v2_0.v1_0.svFlag & V1_0::IGnssCallback::GnssSvFlags::USED_IN_FIX) &&
+                (gnss_sv.v2_0.constellation != V2_0::GnssConstellationType::UNKNOWN) &&
+                (gnss_sv.v2_0.constellation != V2_0::GnssConstellationType::GPS)) {
                 // found a non-GPS constellation
                 constellation_to_blacklist = gnss_sv.v2_0.constellation;
                 break;
             }
         }
-        if (constellation_to_blacklist != GnssConstellationType::UNKNOWN) {
+        if (constellation_to_blacklist != V2_0::GnssConstellationType::UNKNOWN) {
             break;
         }
     }
 
-    if (constellation_to_blacklist == GnssConstellationType::UNKNOWN) {
+    if (constellation_to_blacklist == V2_0::GnssConstellationType::UNKNOWN) {
         ALOGI("No non-GPS constellations found, constellation blacklist test less effective.");
         // Proceed functionally to blacklist something.
-        constellation_to_blacklist = GnssConstellationType::GLONASS;
+        constellation_to_blacklist = V2_0::GnssConstellationType::GLONASS;
     }
 
     return constellation_to_blacklist;
@@ -358,7 +335,7 @@
 
 template <class T_IGnss>
 Return<void> GnssHalTestTemplate<T_IGnss>::GnssMeasurementCallback::gnssMeasurementCb_2_1(
-        const IGnssMeasurementCallback_2_1::GnssData& data) {
+        const V2_1::IGnssMeasurementCallback::GnssData& data) {
     ALOGD("GnssMeasurement v2.1 received. Size = %d", (int)data.measurements.size());
     measurement_cbq_.store(data);
     return Void();
@@ -374,8 +351,10 @@
 
 template <class T_IGnss>
 Return<void> GnssHalTestTemplate<T_IGnss>::GnssAntennaInfoCallback::gnssAntennaInfoCb(
-        const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+        const hidl_vec<V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
     ALOGD("GnssAntennaInfo v2.1 received. Size = %d", (int)gnssAntennaInfos.size());
     antenna_info_cbq_.store(gnssAntennaInfos);
     return Void();
 }
+
+}  // namespace android::hardware::gnss::common
diff --git a/graphics/composer/2.1/utils/resources/Android.bp b/graphics/composer/2.1/utils/resources/Android.bp
index dc20eae..d77bff0 100644
--- a/graphics/composer/2.1/utils/resources/Android.bp
+++ b/graphics/composer/2.1/utils/resources/Android.bp
@@ -15,6 +15,7 @@
 
 cc_library {
     name: "android.hardware.graphics.composer@2.1-resources",
+    system_ext_specific: true,
     defaults: ["hidl_defaults"],
     vendor_available: true,
     shared_libs: [
diff --git a/graphics/composer/2.1/vts/functional/Android.bp b/graphics/composer/2.1/vts/functional/Android.bp
index e137afb..9e703d8 100644
--- a/graphics/composer/2.1/vts/functional/Android.bp
+++ b/graphics/composer/2.1/vts/functional/Android.bp
@@ -21,6 +21,7 @@
 
     // TODO(b/64437680): Assume these libs are always available on the device.
     shared_libs: [
+        "libbase",
         "libfmq",
         "libsync",
         "android.hardware.graphics.mapper@2.0",
diff --git a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
index 4fee560..f0250c0 100644
--- a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
+++ b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "graphics_composer_hidl_hal_test"
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <composer-vts/2.1/ComposerVts.h>
 #include <composer-vts/2.1/GraphicsComposerCallback.h>
 #include <composer-vts/2.1/TestCommandReader.h>
@@ -1102,3 +1103,15 @@
 }  // namespace graphics
 }  // namespace hardware
 }  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    using namespace std::chrono_literals;
+    if (!android::base::WaitForProperty("init.svc.surfaceflinger", "stopped", 10s)) {
+        ALOGE("Failed to stop init.svc.surfaceflinger");
+        return -1;
+    }
+
+    return RUN_ALL_TESTS();
+}
diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp
index 16e9138..6aa836c 100644
--- a/graphics/composer/2.2/vts/functional/Android.bp
+++ b/graphics/composer/2.2/vts/functional/Android.bp
@@ -31,6 +31,7 @@
         "libEGL",
         "libGLESv1_CM",
         "libGLESv2",
+        "libbase",
         "libfmq",
         "libgui",
         "libhidlbase",
diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
index 4d7df1c..31ec885 100644
--- a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
+++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "graphics_composer_hidl_hal_test@2.2"
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <composer-vts/2.1/GraphicsComposerCallback.h>
 #include <composer-vts/2.1/TestCommandReader.h>
@@ -700,3 +701,15 @@
 }  // namespace graphics
 }  // namespace hardware
 }  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    using namespace std::chrono_literals;
+    if (!android::base::WaitForProperty("init.svc.surfaceflinger", "stopped", 10s)) {
+        ALOGE("Failed to stop init.svc.surfaceflinger");
+        return -1;
+    }
+
+    return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/graphics/composer/2.3/vts/functional/Android.bp b/graphics/composer/2.3/vts/functional/Android.bp
index 1ab6b3b..1cbb60e 100644
--- a/graphics/composer/2.3/vts/functional/Android.bp
+++ b/graphics/composer/2.3/vts/functional/Android.bp
@@ -21,6 +21,7 @@
 
     // TODO(b/64437680): Assume these libs are always available on the device.
     shared_libs: [
+        "libbase",
         "libfmq",
         "libhidlbase",
         "libsync",
diff --git a/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp b/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
index a4c1b63..8b42654 100644
--- a/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
+++ b/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
 #include <composer-vts/2.1/GraphicsComposerCallback.h>
@@ -630,3 +631,15 @@
 }  // namespace graphics
 }  // namespace hardware
 }  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    using namespace std::chrono_literals;
+    if (!android::base::WaitForProperty("init.svc.surfaceflinger", "stopped", 10s)) {
+        ALOGE("Failed to stop init.svc.surfaceflinger");
+        return -1;
+    }
+
+    return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/graphics/composer/2.4/IComposerClient.hal b/graphics/composer/2.4/IComposerClient.hal
index 9e3cf0e..1a59bbd 100644
--- a/graphics/composer/2.4/IComposerClient.hal
+++ b/graphics/composer/2.4/IComposerClient.hal
@@ -34,7 +34,9 @@
         /**
          * The configuration group ID (as int32_t) this config is associated to.
          * Switching between configurations within the same group may be done seamlessly
-         * in some conditions via setActiveConfigWithConstraints.
+         * in some conditions via setActiveConfigWithConstraints. Configurations which
+         * share the same config group are similar in all attributes except for the
+         * vsync period.
          */
         CONFIG_GROUP = 7,
     };
diff --git a/graphics/composer/2.4/vts/functional/Android.bp b/graphics/composer/2.4/vts/functional/Android.bp
index d0209b7..cab549c 100644
--- a/graphics/composer/2.4/vts/functional/Android.bp
+++ b/graphics/composer/2.4/vts/functional/Android.bp
@@ -21,6 +21,7 @@
 
     // TODO(b/64437680): Assume these libs are always available on the device.
     shared_libs: [
+        "libbase",
         "libfmq",
         "libsync",
         "android.hardware.graphics.mapper@2.0",
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index e6ecf93..d8312a2 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -21,6 +21,7 @@
 #include <thread>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
 #include <composer-vts/2.4/ComposerVts.h>
@@ -57,6 +58,25 @@
 using ContentType = IComposerClient::ContentType;
 using DisplayCapability = IComposerClient::DisplayCapability;
 
+class VtsDisplay {
+  public:
+    VtsDisplay(Display display, int32_t displayWidth, int32_t displayHeight)
+        : mDisplay(display), mDisplayWidth(displayWidth), mDisplayHeight(displayHeight) {}
+
+    Display get() const { return mDisplay; }
+
+    IComposerClient::FRect getCrop() const {
+        return {0, 0, static_cast<float>(mDisplayWidth), static_cast<float>(mDisplayHeight)};
+    }
+
+    IComposerClient::Rect getFrameRect() const { return {0, 0, mDisplayWidth, mDisplayHeight}; }
+
+  private:
+    const Display mDisplay;
+    const int32_t mDisplayWidth;
+    const int32_t mDisplayHeight;
+};
+
 class GraphicsComposerHidlTest : public ::testing::TestWithParam<std::string> {
   protected:
     void SetUp() override {
@@ -67,15 +87,19 @@
         mComposerCallback = new GraphicsComposerCallback;
         mComposerClient->registerCallback_2_4(mComposerCallback);
 
-        // assume the first display is primary and is never removed
-        mPrimaryDisplay = waitForFirstDisplay();
+        // assume the first displays are built-in and are never removed
+        mDisplays = waitForDisplays();
 
         mInvalidDisplayId = GetInvalidDisplayId();
 
         // explicitly disable vsync
-        mComposerClient->setVsyncEnabled(mPrimaryDisplay, false);
+        for (const auto& display : mDisplays) {
+            mComposerClient->setVsyncEnabled(display.get(), false);
+        }
         mComposerCallback->setVsyncAllowed(false);
 
+        ASSERT_NO_FATAL_FAILURE(mGralloc = std::make_unique<Gralloc>());
+
         mWriter = std::make_unique<CommandWriterBase>(1024);
         mReader = std::make_unique<TestCommandReader>();
     }
@@ -83,6 +107,7 @@
     void TearDown() override {
         ASSERT_EQ(0, mReader->mErrors.size());
         ASSERT_EQ(0, mReader->mCompositionChanges.size());
+
         if (mComposerCallback != nullptr) {
             EXPECT_EQ(0, mComposerCallback->getInvalidHotplugCount());
             EXPECT_EQ(0, mComposerCallback->getInvalidRefreshCount());
@@ -97,10 +122,10 @@
     // display.  Currently assuming that a device will never have close to
     // std::numeric_limit<uint64_t>::max() displays registered while running tests
     Display GetInvalidDisplayId() {
-        std::vector<Display> validDisplays = mComposerCallback->getDisplays();
         uint64_t id = std::numeric_limits<uint64_t>::max();
         while (id > 0) {
-            if (std::find(validDisplays.begin(), validDisplays.end(), id) == validDisplays.end()) {
+            if (std::none_of(mDisplays.begin(), mDisplays.end(),
+                             [&](const VtsDisplay& display) { return id == display.get(); })) {
                 return id;
             }
             id--;
@@ -127,6 +152,30 @@
 
     void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }
 
+    const native_handle_t* allocate() {
+        return mGralloc->allocate(
+                /*width*/ 64, /*height*/ 64, /*layerCount*/ 1,
+                static_cast<common::V1_1::PixelFormat>(PixelFormat::RGBA_8888),
+                static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN));
+    }
+
+    struct TestParameters {
+        nsecs_t delayForChange;
+        bool refreshMiss;
+    };
+
+    void Test_setActiveConfigWithConstraints(const TestParameters& params);
+
+    void sendRefreshFrame(const VtsDisplay& display, const VsyncPeriodChangeTimeline*);
+
+    void waitForVsyncPeriodChange(Display display, const VsyncPeriodChangeTimeline& timeline,
+                                  int64_t desiredTimeNanos, int64_t oldPeriodNanos,
+                                  int64_t newPeriodNanos);
+
+    std::unique_ptr<ComposerClient> mComposerClient;
+    std::vector<VtsDisplay> mDisplays;
+    Display mInvalidDisplayId;
+
     void forEachTwoConfigs(Display display, std::function<void(Config, Config)> func) {
         const auto displayConfigs = mComposerClient->getDisplayConfigs(display);
         for (const Config config1 : displayConfigs) {
@@ -138,88 +187,44 @@
         }
     }
 
-    // use the slot count usually set by SF
-    static constexpr uint32_t kBufferSlotCount = 64;
-
     void Test_setContentType(const ContentType& contentType, const char* contentTypeStr);
     void Test_setContentTypeForDisplay(const Display& display,
                                        const std::vector<ContentType>& capabilities,
                                        const ContentType& contentType, const char* contentTypeStr);
 
-    std::unique_ptr<Composer> mComposer;
-    std::unique_ptr<ComposerClient> mComposerClient;
-    sp<GraphicsComposerCallback> mComposerCallback;
-    // the first display and is assumed never to be removed
-    Display mPrimaryDisplay;
-    Display mInvalidDisplayId;
-    std::unique_ptr<CommandWriterBase> mWriter;
-    std::unique_ptr<TestCommandReader> mReader;
-
   private:
-    Display waitForFirstDisplay() {
+    // use the slot count usually set by SF
+    static constexpr uint32_t kBufferSlotCount = 64;
+
+    std::vector<VtsDisplay> waitForDisplays() {
         while (true) {
+            // Sleep for a small period of time to allow all built-in displays
+            // to post hotplug events
+            std::this_thread::sleep_for(5ms);
             std::vector<Display> displays = mComposerCallback->getDisplays();
             if (displays.empty()) {
-                usleep(5 * 1000);
                 continue;
             }
 
-            return displays[0];
+            std::vector<VtsDisplay> vtsDisplays;
+            vtsDisplays.reserve(displays.size());
+            for (Display display : displays) {
+                const Config activeConfig = mComposerClient->getActiveConfig(display);
+                const int32_t displayWidth = mComposerClient->getDisplayAttribute_2_4(
+                        display, activeConfig, IComposerClient::Attribute::WIDTH);
+                const int32_t displayHeight = mComposerClient->getDisplayAttribute_2_4(
+                        display, activeConfig, IComposerClient::Attribute::HEIGHT);
+                vtsDisplays.emplace_back(VtsDisplay{display, displayWidth, displayHeight});
+            }
+
+            return vtsDisplays;
         }
     }
-};
 
-// Tests for IComposerClient::Command.
-class GraphicsComposerHidlCommandTest : public GraphicsComposerHidlTest {
-  protected:
-    void SetUp() override {
-        ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::SetUp());
-
-        ASSERT_NO_FATAL_FAILURE(mGralloc = std::make_unique<Gralloc>());
-
-        const Config activeConfig = mComposerClient->getActiveConfig(mPrimaryDisplay);
-        mDisplayWidth = mComposerClient->getDisplayAttribute_2_4(mPrimaryDisplay, activeConfig,
-                                                                 IComposerClient::Attribute::WIDTH);
-        mDisplayHeight = mComposerClient->getDisplayAttribute_2_4(
-                mPrimaryDisplay, activeConfig, IComposerClient::Attribute::HEIGHT);
-
-        mWriter = std::make_unique<CommandWriterBase>(1024);
-        mReader = std::make_unique<TestCommandReader>();
-    }
-
-    void TearDown() override {
-        ASSERT_EQ(0, mReader->mErrors.size());
-        ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::TearDown());
-    }
-
-    const native_handle_t* allocate() {
-        return mGralloc->allocate(
-                /*width*/ 64, /*height*/ 64, /*layerCount*/ 1,
-                static_cast<common::V1_1::PixelFormat>(PixelFormat::RGBA_8888),
-                static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN));
-    }
-
-    void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }
-
-    struct TestParameters {
-        nsecs_t delayForChange;
-        bool refreshMiss;
-    };
-
-    void Test_setActiveConfigWithConstraints(const TestParameters& params);
-
-    void sendRefreshFrame(const VsyncPeriodChangeTimeline*);
-
-    void waitForVsyncPeriodChange(Display display, const VsyncPeriodChangeTimeline& timeline,
-                                  int64_t desiredTimeNanos, int64_t oldPeriodNanos,
-                                  int64_t newPeriodNanos);
-
+    std::unique_ptr<Composer> mComposer;
     std::unique_ptr<CommandWriterBase> mWriter;
     std::unique_ptr<TestCommandReader> mReader;
-    int32_t mDisplayWidth;
-    int32_t mDisplayHeight;
-
-  private:
+    sp<GraphicsComposerCallback> mComposerCallback;
     std::unique_ptr<Gralloc> mGralloc;
 };
 
@@ -230,9 +235,10 @@
 }
 
 TEST_P(GraphicsComposerHidlTest, getDisplayCapabilities) {
-    for (Display display : mComposerCallback->getDisplays()) {
+    for (const auto& display : mDisplays) {
         std::vector<IComposerClient::DisplayCapability> capabilities;
-        EXPECT_EQ(Error::NONE, mComposerClient->getDisplayCapabilities(display, &capabilities));
+        EXPECT_EQ(Error::NONE,
+                  mComposerClient->getDisplayCapabilities(display.get(), &capabilities));
     }
 }
 
@@ -241,38 +247,40 @@
     EXPECT_EQ(Error::BAD_DISPLAY,
               mComposerClient->getDisplayConnectionType(mInvalidDisplayId, &type));
 
-    for (Display display : mComposerCallback->getDisplays()) {
-        EXPECT_EQ(Error::NONE, mComposerClient->getDisplayConnectionType(display, &type));
+    for (const auto& display : mDisplays) {
+        EXPECT_EQ(Error::NONE, mComposerClient->getDisplayConnectionType(display.get(), &type));
     }
 }
 
 TEST_P(GraphicsComposerHidlTest, GetDisplayAttribute_2_4) {
-    std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay);
-    for (auto config : configs) {
-        const std::array<IComposerClient::Attribute, 4> requiredAttributes = {{
-                IComposerClient::Attribute::WIDTH,
-                IComposerClient::Attribute::HEIGHT,
-                IComposerClient::Attribute::VSYNC_PERIOD,
-                IComposerClient::Attribute::CONFIG_GROUP,
-        }};
-        for (auto attribute : requiredAttributes) {
-            mComposerClient->getRaw()->getDisplayAttribute_2_4(
-                    mPrimaryDisplay, config, attribute,
-                    [&](const auto& tmpError, const auto& value) {
-                        EXPECT_EQ(Error::NONE, tmpError);
-                        EXPECT_NE(-1, value);
-                    });
-        }
+    for (const auto& display : mDisplays) {
+        std::vector<Config> configs = mComposerClient->getDisplayConfigs(display.get());
+        for (auto config : configs) {
+            const std::array<IComposerClient::Attribute, 4> requiredAttributes = {{
+                    IComposerClient::Attribute::WIDTH,
+                    IComposerClient::Attribute::HEIGHT,
+                    IComposerClient::Attribute::VSYNC_PERIOD,
+                    IComposerClient::Attribute::CONFIG_GROUP,
+            }};
+            for (auto attribute : requiredAttributes) {
+                mComposerClient->getRaw()->getDisplayAttribute_2_4(
+                        display.get(), config, attribute,
+                        [&](const auto& tmpError, const auto& value) {
+                            EXPECT_EQ(Error::NONE, tmpError);
+                            EXPECT_NE(-1, value);
+                        });
+            }
 
-        const std::array<IComposerClient::Attribute, 2> optionalAttributes = {{
-                IComposerClient::Attribute::DPI_X,
-                IComposerClient::Attribute::DPI_Y,
-        }};
-        for (auto attribute : optionalAttributes) {
-            mComposerClient->getRaw()->getDisplayAttribute_2_4(
-                    mPrimaryDisplay, config, attribute, [&](const auto& tmpError, const auto&) {
-                        EXPECT_TRUE(tmpError == Error::NONE || tmpError == Error::UNSUPPORTED);
-                    });
+            const std::array<IComposerClient::Attribute, 2> optionalAttributes = {{
+                    IComposerClient::Attribute::DPI_X,
+                    IComposerClient::Attribute::DPI_Y,
+            }};
+            for (auto attribute : optionalAttributes) {
+                mComposerClient->getRaw()->getDisplayAttribute_2_4(
+                        display.get(), config, attribute, [&](const auto& tmpError, const auto&) {
+                            EXPECT_TRUE(tmpError == Error::NONE || tmpError == Error::UNSUPPORTED);
+                        });
+            }
         }
     }
 }
@@ -283,11 +291,12 @@
               mComposerClient->getDisplayVsyncPeriod(mInvalidDisplayId, &vsyncPeriodNanos));
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, getDisplayVsyncPeriod) {
-    for (Display display : mComposerCallback->getDisplays()) {
-        for (Config config : mComposerClient->getDisplayConfigs(display)) {
+TEST_P(GraphicsComposerHidlTest, getDisplayVsyncPeriod) {
+    for (const auto& display : mDisplays) {
+        for (Config config : mComposerClient->getDisplayConfigs(display.get())) {
             VsyncPeriodNanos expectedVsyncPeriodNanos = mComposerClient->getDisplayAttribute_2_4(
-                    display, config, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
+                    display.get(), config,
+                    IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
 
             VsyncPeriodChangeTimeline timeline;
             IComposerClient::VsyncPeriodChangeConstraints constraints;
@@ -295,12 +304,12 @@
             constraints.desiredTimeNanos = systemTime();
             constraints.seamlessRequired = false;
             EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigWithConstraints(
-                                           display, config, constraints, &timeline));
+                                           display.get(), config, constraints, &timeline));
 
             if (timeline.refreshRequired) {
-                sendRefreshFrame(&timeline);
+                sendRefreshFrame(display, &timeline);
             }
-            waitForVsyncPeriodChange(display, timeline, constraints.desiredTimeNanos, 0,
+            waitForVsyncPeriodChange(display.get(), timeline, constraints.desiredTimeNanos, 0,
                                      expectedVsyncPeriodNanos);
 
             VsyncPeriodNanos vsyncPeriodNanos;
@@ -309,7 +318,7 @@
                 std::this_thread::sleep_for(10ms);
                 vsyncPeriodNanos = 0;
                 EXPECT_EQ(Error::NONE,
-                          mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+                          mComposerClient->getDisplayVsyncPeriod(display.get(), &vsyncPeriodNanos));
                 --retryCount;
             } while (vsyncPeriodNanos != expectedVsyncPeriodNanos && retryCount > 0);
 
@@ -322,7 +331,7 @@
                 timeout *= 2;
                 vsyncPeriodNanos = 0;
                 EXPECT_EQ(Error::NONE,
-                          mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+                          mComposerClient->getDisplayVsyncPeriod(display.get(), &vsyncPeriodNanos));
                 EXPECT_EQ(vsyncPeriodNanos, expectedVsyncPeriodNanos);
             }
         }
@@ -347,31 +356,34 @@
     constraints.seamlessRequired = false;
     constraints.desiredTimeNanos = systemTime();
 
-    for (Display display : mComposerCallback->getDisplays()) {
-        Config invalidConfigId = GetInvalidConfigId(display);
-        EXPECT_EQ(Error::BAD_CONFIG, mComposerClient->setActiveConfigWithConstraints(
-                                             display, invalidConfigId, constraints, &timeline));
+    for (const auto& display : mDisplays) {
+        Config invalidConfigId = GetInvalidConfigId(display.get());
+        EXPECT_EQ(Error::BAD_CONFIG,
+                  mComposerClient->setActiveConfigWithConstraints(display.get(), invalidConfigId,
+                                                                  constraints, &timeline));
     }
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, setActiveConfigWithConstraints_SeamlessNotAllowed) {
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_SeamlessNotAllowed) {
     VsyncPeriodChangeTimeline timeline;
     IComposerClient::VsyncPeriodChangeConstraints constraints;
 
     constraints.seamlessRequired = true;
     constraints.desiredTimeNanos = systemTime();
 
-    for (Display display : mComposerCallback->getDisplays()) {
-        forEachTwoConfigs(display, [&](Config config1, Config config2) {
+    for (const auto& display : mDisplays) {
+        forEachTwoConfigs(display.get(), [&](Config config1, Config config2) {
             const auto configGroup1 = mComposerClient->getDisplayAttribute_2_4(
-                    display, config1, IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
+                    display.get(), config1,
+                    IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
             const auto configGroup2 = mComposerClient->getDisplayAttribute_2_4(
-                    display, config2, IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
+                    display.get(), config2,
+                    IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
             if (configGroup1 != configGroup2) {
-                mComposerClient->setActiveConfig(display, config1);
-                sendRefreshFrame(nullptr);
+                mComposerClient->setActiveConfig(display.get(), config1);
+                sendRefreshFrame(display, nullptr);
                 EXPECT_EQ(Error::SEAMLESS_NOT_ALLOWED,
-                          mComposerClient->setActiveConfigWithConstraints(display, config2,
+                          mComposerClient->setActiveConfigWithConstraints(display.get(), config2,
                                                                           constraints, &timeline));
             }
         });
@@ -382,7 +394,8 @@
     return std::chrono::time_point<std::chrono::steady_clock>(std::chrono::nanoseconds(time));
 }
 
-void GraphicsComposerHidlCommandTest::sendRefreshFrame(const VsyncPeriodChangeTimeline* timeline) {
+void GraphicsComposerHidlTest::sendRefreshFrame(const VtsDisplay& display,
+                                                const VsyncPeriodChangeTimeline* timeline) {
     if (timeline != nullptr) {
         // Refresh time should be before newVsyncAppliedTimeNanos
         EXPECT_LT(timeline->refreshTimeNanos, timeline->newVsyncAppliedTimeNanos);
@@ -390,29 +403,25 @@
         std::this_thread::sleep_until(toTimePoint(timeline->refreshTimeNanos));
     }
 
-    mWriter->selectDisplay(mPrimaryDisplay);
-    mComposerClient->setPowerMode(mPrimaryDisplay, V2_1::IComposerClient::PowerMode::ON);
-    mComposerClient->setColorMode_2_3(mPrimaryDisplay, ColorMode::NATIVE,
-                                      RenderIntent::COLORIMETRIC);
+    mWriter->selectDisplay(display.get());
+    mComposerClient->setPowerMode(display.get(), V2_1::IComposerClient::PowerMode::ON);
+    mComposerClient->setColorMode_2_3(display.get(), ColorMode::NATIVE, RenderIntent::COLORIMETRIC);
 
     auto handle = allocate();
     ASSERT_NE(nullptr, handle);
 
-    IComposerClient::Rect displayFrame{0, 0, mDisplayWidth, mDisplayHeight};
-
     Layer layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));
+    ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(display.get(), kBufferSlotCount));
     mWriter->selectLayer(layer);
     mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
-    mWriter->setLayerDisplayFrame(displayFrame);
+    mWriter->setLayerDisplayFrame(display.getFrameRect());
     mWriter->setLayerPlaneAlpha(1);
-    mWriter->setLayerSourceCrop({0, 0, (float)mDisplayWidth, (float)mDisplayHeight});
+    mWriter->setLayerSourceCrop(display.getCrop());
     mWriter->setLayerTransform(static_cast<Transform>(0));
-    mWriter->setLayerVisibleRegion(std::vector<IComposerClient::Rect>(1, displayFrame));
+    mWriter->setLayerVisibleRegion(std::vector<IComposerClient::Rect>(1, display.getFrameRect()));
     mWriter->setLayerZOrder(10);
     mWriter->setLayerBlendMode(IComposerClient::BlendMode::NONE);
-    mWriter->setLayerSurfaceDamage(std::vector<IComposerClient::Rect>(1, displayFrame));
+    mWriter->setLayerSurfaceDamage(std::vector<IComposerClient::Rect>(1, display.getFrameRect()));
     mWriter->setLayerBuffer(0, handle, -1);
     mWriter->setLayerDataspace(Dataspace::UNKNOWN);
 
@@ -440,9 +449,11 @@
     execute();
 }
 
-void GraphicsComposerHidlCommandTest::waitForVsyncPeriodChange(
-        Display display, const VsyncPeriodChangeTimeline& timeline, int64_t desiredTimeNanos,
-        int64_t oldPeriodNanos, int64_t newPeriodNanos) {
+void GraphicsComposerHidlTest::waitForVsyncPeriodChange(Display display,
+                                                        const VsyncPeriodChangeTimeline& timeline,
+                                                        int64_t desiredTimeNanos,
+                                                        int64_t oldPeriodNanos,
+                                                        int64_t newPeriodNanos) {
     const auto CHANGE_DEADLINE = toTimePoint(timeline.newVsyncAppliedTimeNanos) + 100ms;
     while (std::chrono::steady_clock::now() <= CHANGE_DEADLINE) {
         VsyncPeriodNanos vsyncPeriodNanos;
@@ -456,17 +467,18 @@
     }
 }
 
-void GraphicsComposerHidlCommandTest::Test_setActiveConfigWithConstraints(
-        const TestParameters& params) {
-    for (Display display : mComposerCallback->getDisplays()) {
-        forEachTwoConfigs(display, [&](Config config1, Config config2) {
-            mComposerClient->setActiveConfig(display, config1);
-            sendRefreshFrame(nullptr);
+void GraphicsComposerHidlTest::Test_setActiveConfigWithConstraints(const TestParameters& params) {
+    for (const auto& display : mDisplays) {
+        forEachTwoConfigs(display.get(), [&](Config config1, Config config2) {
+            mComposerClient->setActiveConfig(display.get(), config1);
+            sendRefreshFrame(display, nullptr);
 
             int32_t vsyncPeriod1 = mComposerClient->getDisplayAttribute_2_4(
-                    display, config1, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
+                    display.get(), config1,
+                    IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
             int32_t vsyncPeriod2 = mComposerClient->getDisplayAttribute_2_4(
-                    display, config2, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
+                    display.get(), config2,
+                    IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
 
             if (vsyncPeriod1 == vsyncPeriod2) {
                 return;  // continue
@@ -477,7 +489,7 @@
                     .desiredTimeNanos = systemTime() + params.delayForChange,
                     .seamlessRequired = false};
             EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigWithConstraints(
-                                           display, config2, constraints, &timeline));
+                                           display.get(), config2, constraints, &timeline));
 
             EXPECT_TRUE(timeline.newVsyncAppliedTimeNanos >= constraints.desiredTimeNanos);
             // Refresh rate should change within a reasonable time
@@ -491,10 +503,10 @@
                     // callback
                     std::this_thread::sleep_until(toTimePoint(timeline.refreshTimeNanos) + 100ms);
                 }
-                sendRefreshFrame(&timeline);
+                sendRefreshFrame(display, &timeline);
             }
-            waitForVsyncPeriodChange(display, timeline, constraints.desiredTimeNanos, vsyncPeriod1,
-                                     vsyncPeriod2);
+            waitForVsyncPeriodChange(display.get(), timeline, constraints.desiredTimeNanos,
+                                     vsyncPeriod1, vsyncPeriod2);
 
             // At this point the refresh rate should have changed already, however in rare
             // cases the implementation might have missed the deadline. In this case a new
@@ -506,30 +518,30 @@
 
             if (newTimeline.has_value()) {
                 if (newTimeline->refreshRequired) {
-                    sendRefreshFrame(&newTimeline.value());
+                    sendRefreshFrame(display, &newTimeline.value());
                 }
-                waitForVsyncPeriodChange(display, newTimeline.value(), constraints.desiredTimeNanos,
-                                         vsyncPeriod1, vsyncPeriod2);
+                waitForVsyncPeriodChange(display.get(), newTimeline.value(),
+                                         constraints.desiredTimeNanos, vsyncPeriod1, vsyncPeriod2);
             }
 
             VsyncPeriodNanos vsyncPeriodNanos;
             EXPECT_EQ(Error::NONE,
-                      mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+                      mComposerClient->getDisplayVsyncPeriod(display.get(), &vsyncPeriodNanos));
             EXPECT_EQ(vsyncPeriodNanos, vsyncPeriod2);
         });
     }
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, setActiveConfigWithConstraints) {
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints) {
     Test_setActiveConfigWithConstraints({.delayForChange = 0, .refreshMiss = false});
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, setActiveConfigWithConstraints_Delayed) {
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_Delayed) {
     Test_setActiveConfigWithConstraints({.delayForChange = 300'000'000,  // 300ms
                                          .refreshMiss = false});
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, setActiveConfigWithConstraints_MissRefresh) {
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_MissRefresh) {
     Test_setActiveConfigWithConstraints({.delayForChange = 0, .refreshMiss = true});
 }
 
@@ -539,9 +551,9 @@
 }
 
 TEST_P(GraphicsComposerHidlTest, setAutoLowLatencyMode) {
-    for (Display display : mComposerCallback->getDisplays()) {
+    for (const auto& display : mDisplays) {
         std::vector<DisplayCapability> capabilities;
-        const auto error = mComposerClient->getDisplayCapabilities(display, &capabilities);
+        const auto error = mComposerClient->getDisplayCapabilities(display.get(), &capabilities);
         EXPECT_EQ(Error::NONE, error);
 
         const bool allmSupport =
@@ -550,16 +562,16 @@
 
         if (!allmSupport) {
             EXPECT_EQ(Error::UNSUPPORTED,
-                      mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, true));
+                      mComposerClient->setAutoLowLatencyMode(display.get(), true));
             EXPECT_EQ(Error::UNSUPPORTED,
-                      mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, false));
+                      mComposerClient->setAutoLowLatencyMode(display.get(), false));
             GTEST_SUCCEED() << "Auto Low Latency Mode is not supported on display "
-                            << std::to_string(display) << ", skipping test";
+                            << std::to_string(display.get()) << ", skipping test";
             return;
         }
 
-        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, true));
-        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, false));
+        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(display.get(), true));
+        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(display.get(), false));
     }
 }
 
@@ -572,10 +584,10 @@
 
 TEST_P(GraphicsComposerHidlTest, getSupportedContentTypes) {
     std::vector<ContentType> supportedContentTypes;
-    for (Display display : mComposerCallback->getDisplays()) {
+    for (const auto& display : mDisplays) {
         supportedContentTypes.clear();
         const auto error =
-                mComposerClient->getSupportedContentTypes(display, &supportedContentTypes);
+                mComposerClient->getSupportedContentTypes(display.get(), &supportedContentTypes);
         const bool noneSupported =
                 std::find(supportedContentTypes.begin(), supportedContentTypes.end(),
                           ContentType::NONE) != supportedContentTypes.end();
@@ -585,8 +597,8 @@
 }
 
 TEST_P(GraphicsComposerHidlTest, setContentTypeNoneAlwaysAccepted) {
-    for (Display display : mComposerCallback->getDisplays()) {
-        const auto error = mComposerClient->setContentType(display, ContentType::NONE);
+    for (const auto& display : mDisplays) {
+        const auto error = mComposerClient->setContentType(display.get(), ContentType::NONE);
         EXPECT_NE(Error::UNSUPPORTED, error);
     }
 }
@@ -618,13 +630,14 @@
 
 void GraphicsComposerHidlTest::Test_setContentType(const ContentType& contentType,
                                                    const char* contentTypeStr) {
-    for (Display display : mComposerCallback->getDisplays()) {
+    for (const auto& display : mDisplays) {
         std::vector<ContentType> supportedContentTypes;
         const auto error =
-                mComposerClient->getSupportedContentTypes(display, &supportedContentTypes);
+                mComposerClient->getSupportedContentTypes(display.get(), &supportedContentTypes);
         EXPECT_EQ(Error::NONE, error);
 
-        Test_setContentTypeForDisplay(display, supportedContentTypes, contentType, contentTypeStr);
+        Test_setContentTypeForDisplay(display.get(), supportedContentTypes, contentType,
+                                      contentTypeStr);
     }
 }
 
@@ -650,13 +663,7 @@
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)),
         android::hardware::PrintInstanceNameToString);
 
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsComposerHidlCommandTest);
-INSTANTIATE_TEST_SUITE_P(
-        PerInstance, GraphicsComposerHidlCommandTest,
-        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)),
-        android::hardware::PrintInstanceNameToString);
-
-TEST_P(GraphicsComposerHidlCommandTest, getLayerGenericMetadataKeys) {
+TEST_P(GraphicsComposerHidlTest, getLayerGenericMetadataKeys) {
     std::vector<IComposerClient::LayerGenericMetadataKey> keys;
     mComposerClient->getLayerGenericMetadataKeys(&keys);
 
@@ -685,3 +692,15 @@
 }  // namespace graphics
 }  // namespace hardware
 }  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    using namespace std::chrono_literals;
+    if (!android::base::WaitForProperty("init.svc.surfaceflinger", "stopped", 10s)) {
+        ALOGE("Failed to stop init.svc.surfaceflinger");
+        return -1;
+    }
+
+    return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
index 730b601..702334d 100644
--- a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
@@ -55,7 +55,7 @@
      * This method may only be called once per instance. If called more than once, STATUS_FAILED
      * will be returned.
      *
-     * @return the unencrypted key-pair in PKCS#8 format.
+     * @return the private key, in DER format as specified in RFC 5915.
      */
     byte[] createEphemeralKeyPair();
 
@@ -88,10 +88,10 @@
      * The setRequestedNamespaces() and setVerificationToken() methods will be called before
      * this method is called.
      *
-     * This method be called after createEphemeralKeyPair(), setReaderEphemeralPublicKey(),
-     * createAuthChallenge() and before startRetrieveEntry(). This method call is followed by
-     * multiple calls of startRetrieveEntryValue(), retrieveEntryValue(), and finally
-     * finishRetrieval().
+     * This method is called after createEphemeralKeyPair(), setReaderEphemeralPublicKey(),
+     * createAuthChallenge() (note that those calls are optional) and before startRetrieveEntry().
+     * This method call is followed by multiple calls of startRetrieveEntryValue(),
+     * retrieveEntryValue(), and finally finishRetrieval().
      *
      * It is permissible to perform data retrievals multiple times using the same instance (e.g.
      * startRetrieval(), then multiple calls of startRetrieveEntryValue(), retrieveEntryValue(),
@@ -343,12 +343,13 @@
      *
      *  - signature: must be set to ECDSA.
      *
-     *  - subject: CN shall be set to "Android Identity Credential Authentication Key".
+     *  - subject: CN shall be set to "Android Identity Credential Authentication Key". (fixed
+     *    value: same on all certs)
      *
-     *  - issuer: shall be set to "credentialStoreName (credentialStoreAuthorName)" using the
-     *    values returned in HardwareInformation.
+     *  - issuer: CN shall be set to "Android Identity Credential Key". (fixed value:
+     *    same on all certs)
      *
-     *  - validity: should be from current time and one year in the future.
+     *  - validity: should be from current time and one year in the future (365 days).
      *
      *  - subjectPublicKeyInfo: must contain attested public key.
      *
diff --git a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
index 297fd1d..c48cb66 100644
--- a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -37,12 +37,12 @@
      *
      *  - signature: must be set to ECDSA.
      *
-     *  - subject: CN shall be set to "Android Identity Credential Key".
+     *  - subject: CN shall be set to "Android Identity Credential Key". (fixed value:
+     *    same on all certs)
      *
-     *  - issuer: shall be set to "credentialStoreName (credentialStoreAuthorName)" using the
-     *    values returned in HardwareInformation.
+     *  - issuer: Same as the subject field of the batch attestation key.
      *
-     *  - validity: should be from current time and expire at the same time as the
+     *  - validity: Should be set to current time and expire at the same time as the
      *    attestation batch certificate used.
      *
      *  - subjectPublicKeyInfo: must contain attested public key.
@@ -55,19 +55,14 @@
      *
      *  - The attestationSecurityLevel field must be set to either Software (0),
      *    TrustedEnvironment (1), or StrongBox (2) depending on how attestation is
-     *    implemented. Only the default AOSP implementation of this HAL may use
-     *    value 0 (additionally, this implementation must not be used on production
-     *    devices).
+     *    implemented.
      *
-     *  - The keymasterVersion field in the attestation extension must be set to (10*major + minor)
-     *    where major and minor are the Identity Credential interface major and minor versions.
-     *    Specifically for this version of the interface (1.0) this value is 10.
+     *  - The keymasterVersion field in the attestation extension must be set to the.
+     *    same value as used for Android Keystore keys.
      *
      *  - The keymasterSecurityLevel field in the attestation extension must be set to
      *    either Software (0), TrustedEnvironment (1), or StrongBox (2) depending on how
-     *    the Trusted Application backing the HAL implementation is implemented. Only
-     *    the default AOSP implementation of this HAL may use value 0 (additionally, this
-     *    implementation must not be used on production devices)
+     *    the Trusted Application backing the HAL implementation is implemented.
      *
      *  - The attestationChallenge field must be set to the passed-in challenge.
      *
@@ -81,7 +76,8 @@
      *
      *    - Tag::IDENTITY_CREDENTIAL_KEY which indicates that the key is an Identity
      *      Credential key (which can only sign/MAC very specific messages) and not an Android
-     *      Keystore key (which can be used to sign/MAC anything).
+     *      Keystore key (which can be used to sign/MAC anything). This must not be set
+     *      for test credentials.
      *
      *    - Tag::PURPOSE must be set to SIGN
      *
@@ -95,10 +91,13 @@
      *
      *    - Tag::EC_CURVE must be set to P_256
      *
-     * Additional authorizations may be needed in the softwareEnforced and teeEnforced
-     * fields - the above is not an exhaustive list. Specifically, authorizations containing
-     * information about the root of trust, OS version, verified boot state, and so on should
-     * be included.
+     *    - Tag::ROOT_OF_TRUST must be set
+     *
+     *    - Tag::OS_VERSION and Tag::OS_PATCHLEVEL must be set
+     *
+     * Additional authorizations may be appear in the softwareEnforced and teeEnforced
+     * fields. For example if the device has a boot or vendor partitions, then BOOT_PATCHLEVEL
+     * and VENDOR_PATCHLEVEL should be set.
      *
      * Since the chain is required to be generated using Keymaster Attestation, the returned
      * certificate chain has the following properties:
@@ -112,8 +111,8 @@
      * As with any user of attestation, the Issuing Authority (as a relying party) wishing
      * to issue a credential to a device using these APIs, must carefully examine the
      * returned certificate chain for all of the above (and more). In particular, the Issuing
-     * Authority should check the root of trust, verified boot state, patch level,
-     * application id, etc.
+     * Authority should check the root of trust (which include verified boot state), patch level,
+     * attestation application id, etc.
      *
      * This all depends on the needs of the Issuing Authority and the kind of credential but
      * in general an Issuing Authority should never issue a credential to a device without
diff --git a/identity/aidl/default/IdentityCredential.cpp b/identity/aidl/default/IdentityCredential.cpp
index 87d9a93..dfcd4f5 100644
--- a/identity/aidl/default/IdentityCredential.cpp
+++ b/identity/aidl/default/IdentityCredential.cpp
@@ -272,6 +272,7 @@
         const HardwareAuthToken& authToken, const vector<uint8_t>& itemsRequest,
         const vector<uint8_t>& signingKeyBlob, const vector<uint8_t>& sessionTranscript,
         const vector<uint8_t>& readerSignature, const vector<int32_t>& requestCounts) {
+    std::unique_ptr<cppbor::Item> sessionTranscriptItem;
     if (sessionTranscript.size() > 0) {
         auto [item, _, message] = cppbor::parse(sessionTranscript);
         if (item == nullptr) {
@@ -279,7 +280,7 @@
                     IIdentityCredentialStore::STATUS_INVALID_DATA,
                     "SessionTranscript contains invalid CBOR"));
         }
-        sessionTranscriptItem_ = std::move(item);
+        sessionTranscriptItem = std::move(item);
     }
     if (numStartRetrievalCalls_ > 0) {
         if (sessionTranscript_ != sessionTranscript) {
@@ -319,7 +320,7 @@
         vector<uint8_t> encodedReaderAuthentication =
                 cppbor::Array()
                         .add("ReaderAuthentication")
-                        .add(sessionTranscriptItem_->clone())
+                        .add(std::move(sessionTranscriptItem))
                         .add(cppbor::Semantic(24, itemsRequestBytes))
                         .encode();
         vector<uint8_t> encodedReaderAuthenticationBytes =
@@ -776,13 +777,6 @@
     optional<vector<uint8_t>> mac;
     if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
         readerPublicKey_.size() > 0) {
-        cppbor::Array array;
-        array.add("DeviceAuthentication");
-        array.add(sessionTranscriptItem_->clone());
-        array.add(docType_);
-        array.add(cppbor::Semantic(24, encodedDeviceNameSpaces));
-        vector<uint8_t> deviceAuthenticationBytes = cppbor::Semantic(24, array.encode()).encode();
-
         vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
         optional<vector<uint8_t>> signingKey =
                 support::decryptAes128Gcm(storageKey_, signingKeyBlob_, docTypeAsBlob);
@@ -792,31 +786,15 @@
                     "Error decrypting signingKeyBlob"));
         }
 
-        optional<vector<uint8_t>> sharedSecret =
-                support::ecdh(readerPublicKey_, signingKey.value());
-        if (!sharedSecret) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_FAILED, "Error doing ECDH"));
-        }
-
-        // Mix-in SessionTranscriptBytes
         vector<uint8_t> sessionTranscriptBytes = cppbor::Semantic(24, sessionTranscript_).encode();
-        vector<uint8_t> sharedSecretWithSessionTranscriptBytes = sharedSecret.value();
-        std::copy(sessionTranscriptBytes.begin(), sessionTranscriptBytes.end(),
-                  std::back_inserter(sharedSecretWithSessionTranscriptBytes));
-
-        vector<uint8_t> salt = {0x00};
-        vector<uint8_t> info = {};
-        optional<vector<uint8_t>> derivedKey =
-                support::hkdf(sharedSecretWithSessionTranscriptBytes, salt, info, 32);
-        if (!derivedKey) {
+        optional<vector<uint8_t>> eMacKey =
+                support::calcEMacKey(signingKey.value(), readerPublicKey_, sessionTranscriptBytes);
+        if (!eMacKey) {
             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_FAILED,
-                    "Error deriving key from shared secret"));
+                    IIdentityCredentialStore::STATUS_FAILED, "Error calculating EMacKey"));
         }
-
-        mac = support::coseMac0(derivedKey.value(), {},      // payload
-                                deviceAuthenticationBytes);  // detached content
+        mac = support::calcMac(sessionTranscript_, docType_, encodedDeviceNameSpaces,
+                               eMacKey.value());
         if (!mac) {
             return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                     IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));
@@ -830,9 +808,9 @@
 
 ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
         vector<uint8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
-    string serialDecimal = "0";  // TODO: set serial to something unique
-    string issuer = "Android Open Source Project";
-    string subject = "Android IdentityCredential Reference Implementation";
+    string serialDecimal = "1";
+    string issuer = "Android Identity Credential Key";
+    string subject = "Android Identity Credential Authentication Key";
     time_t validityNotBefore = time(nullptr);
     time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
 
diff --git a/identity/aidl/default/IdentityCredential.h b/identity/aidl/default/IdentityCredential.h
index a82531d..a8a6409 100644
--- a/identity/aidl/default/IdentityCredential.h
+++ b/identity/aidl/default/IdentityCredential.h
@@ -103,7 +103,6 @@
     map<int32_t, int> profileIdToAccessCheckResult_;
     vector<uint8_t> signingKeyBlob_;
     vector<uint8_t> sessionTranscript_;
-    std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
     vector<uint8_t> itemsRequest_;
     vector<int32_t> requestCountsRemaining_;
     map<string, set<string>> requestedNameSpacesAndNames_;
diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp
index fea289b..141b4de 100644
--- a/identity/aidl/default/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/WritableIdentityCredential.cpp
@@ -74,7 +74,7 @@
     vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
 
     optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> keyAttestationPair =
-            support::createEcKeyPairAndAttestation(challenge, appId);
+            support::createEcKeyPairAndAttestation(challenge, appId, testCredential_);
     if (!keyAttestationPair) {
         LOG(ERROR) << "Error creating credentialKey and attestation";
         return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index c1f44e7..03966de 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -9,7 +9,6 @@
         "VtsIWritableIdentityCredentialTests.cpp",
         "VtsIdentityTestUtils.cpp",
         "VtsAttestationTests.cpp",
-        "VtsAttestationParserSupport.cpp",
         "UserAuthTests.cpp",
         "ReaderAuthTests.cpp",
     ],
@@ -20,13 +19,14 @@
     static_libs: [
         "libcppbor",
         "libkeymaster_portable",
-        "libsoft_attestation_cert",
         "libpuresoftkeymasterdevice",
         "android.hardware.keymaster@4.0",
         "android.hardware.identity-support-lib",
         "android.hardware.identity-cpp",
         "android.hardware.keymaster-cpp",
         "android.hardware.keymaster-ndk_platform",
+        "libkeymaster4support",
+        "libkeymaster4_1support",
     ],
     test_suites: [
         "general-tests",
diff --git a/identity/aidl/vts/VtsAttestationParserSupport.cpp b/identity/aidl/vts/VtsAttestationParserSupport.cpp
deleted file mode 100644
index 71fe733..0000000
--- a/identity/aidl/vts/VtsAttestationParserSupport.cpp
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright 2019, 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.
- */
-
-#include "VtsAttestationParserSupport.h"
-
-#include <aidl/Gtest.h>
-#include <map>
-
-namespace android::hardware::identity::test_utils {
-
-using std::endl;
-using std::map;
-using std::optional;
-using std::string;
-using std::vector;
-
-using ::android::sp;
-using ::android::String16;
-using ::android::binder::Status;
-
-using ::keymaster::ASN1_OBJECT_Ptr;
-using ::keymaster::AuthorizationSet;
-using ::keymaster::EVP_PKEY_Ptr;
-using ::keymaster::kAttestionRecordOid;
-using ::keymaster::TAG_ATTESTATION_APPLICATION_ID;
-using ::keymaster::TAG_IDENTITY_CREDENTIAL_KEY;
-using ::keymaster::TAG_INCLUDE_UNIQUE_ID;
-using ::keymaster::TypedTag;
-using ::keymaster::X509_Ptr;
-
-using support::certificateChainSplit;
-
-optional<keymaster_cert_chain_t> AttestationCertificateParser::certificateChainToKeymasterChain(
-        const vector<Certificate>& certificates) {
-    if (certificates.size() <= 0) {
-        return {};
-    }
-
-    keymaster_cert_chain_t kCert;
-    kCert.entry_count = certificates.size();
-    kCert.entries = (keymaster_blob_t*)malloc(sizeof(keymaster_blob_t) * kCert.entry_count);
-
-    int index = 0;
-    for (const auto& c : certificates) {
-        kCert.entries[index].data_length = c.encodedCertificate.size();
-        uint8_t* data = (uint8_t*)malloc(c.encodedCertificate.size());
-
-        memcpy(data, c.encodedCertificate.data(), c.encodedCertificate.size());
-        kCert.entries[index].data = (const uint8_t*)data;
-        index++;
-    }
-
-    return kCert;
-}
-
-bool AttestationCertificateParser::parse() {
-    optional<keymaster_cert_chain_t> cert_chain = certificateChainToKeymasterChain(origCertChain_);
-    if (!cert_chain) {
-        return false;
-    }
-
-    if (cert_chain.value().entry_count < 3) {
-        return false;
-    }
-
-    if (!verifyChain(cert_chain.value())) {
-        return false;
-    }
-
-    if (!verifyAttestationRecord(cert_chain.value().entries[0])) {
-        return false;
-    }
-
-    keymaster_free_cert_chain(&cert_chain.value());
-    return true;
-}
-
-ASN1_OCTET_STRING* AttestationCertificateParser::getAttestationRecord(X509* certificate) {
-    ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1));
-    if (!oid.get()) return nullptr;
-
-    int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1);
-    if (location == -1) return nullptr;
-
-    X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location);
-    if (!attest_rec_ext) return nullptr;
-
-    ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
-    return attest_rec;
-}
-
-X509* AttestationCertificateParser::parseCertBlob(const keymaster_blob_t& blob) {
-    const uint8_t* p = blob.data;
-    return d2i_X509(nullptr, &p, blob.data_length);
-}
-
-bool AttestationCertificateParser::verifyAttestationRecord(
-        const keymaster_blob_t& attestation_cert) {
-    X509_Ptr cert(parseCertBlob(attestation_cert));
-    if (!cert.get()) {
-        return false;
-    }
-
-    ASN1_OCTET_STRING* attest_rec = getAttestationRecord(cert.get());
-    if (!attest_rec) {
-        return false;
-    }
-
-    keymaster_blob_t att_unique_id = {};
-    keymaster_blob_t att_challenge;
-    keymaster_error_t ret = parse_attestation_record(
-            attest_rec->data, attest_rec->length, &att_attestation_version_,
-            &att_attestation_security_level_, &att_keymaster_version_,
-            &att_keymaster_security_level_, &att_challenge, &att_sw_enforced_, &att_hw_enforced_,
-            &att_unique_id);
-    if (ret) {
-        return false;
-    }
-
-    att_challenge_.assign(att_challenge.data, att_challenge.data + att_challenge.data_length);
-    return true;
-}
-
-uint32_t AttestationCertificateParser::getKeymasterVersion() {
-    return att_keymaster_version_;
-}
-
-uint32_t AttestationCertificateParser::getAttestationVersion() {
-    return att_attestation_version_;
-}
-
-vector<uint8_t> AttestationCertificateParser::getAttestationChallenge() {
-    return att_challenge_;
-}
-
-keymaster_security_level_t AttestationCertificateParser::getKeymasterSecurityLevel() {
-    return att_keymaster_security_level_;
-}
-
-keymaster_security_level_t AttestationCertificateParser::getAttestationSecurityLevel() {
-    return att_attestation_security_level_;
-}
-
-// Verify the Attestation certificates are correctly chained.
-bool AttestationCertificateParser::verifyChain(const keymaster_cert_chain_t& chain) {
-    for (size_t i = 0; i < chain.entry_count - 1; ++i) {
-        keymaster_blob_t& key_cert_blob = chain.entries[i];
-        keymaster_blob_t& signing_cert_blob = chain.entries[i + 1];
-
-        X509_Ptr key_cert(parseCertBlob(key_cert_blob));
-        X509_Ptr signing_cert(parseCertBlob(signing_cert_blob));
-        if (!key_cert.get() || !signing_cert.get()) {
-            return false;
-        }
-
-        EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
-        if (!signing_pubkey.get()) return false;
-
-        if (X509_verify(key_cert.get(), signing_pubkey.get()) != 1) {
-            return false;
-        }
-
-        if (i + 1 == chain.entry_count - 1) {
-            // Last entry is self-signed.
-            if (X509_verify(signing_cert.get(), signing_pubkey.get()) != 1) {
-                return false;
-            }
-        }
-    }
-
-    return true;
-}
-
-}  // namespace android::hardware::identity::test_utils
diff --git a/identity/aidl/vts/VtsAttestationParserSupport.h b/identity/aidl/vts/VtsAttestationParserSupport.h
deleted file mode 100644
index 7c7e1b6..0000000
--- a/identity/aidl/vts/VtsAttestationParserSupport.h
+++ /dev/null
@@ -1,122 +0,0 @@
-
-/*
- * Copyright 2019, 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.
- */
-
-#ifndef VTS_ATTESTATION_PARSER_SUPPORT_H
-#define VTS_ATTESTATION_PARSER_SUPPORT_H
-
-//#include <aidl/Gtest.h>
-#include <android/hardware/identity/IIdentityCredentialStore.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-#include <android/hardware/keymaster/4.0/types.h>
-#include <hardware/keymaster_defs.h>
-#include <keymaster/android_keymaster_utils.h>
-#include <keymaster/authorization_set.h>
-#include <keymaster/contexts/pure_soft_keymaster_context.h>
-#include <keymaster/contexts/soft_attestation_cert.h>
-#include <keymaster/keymaster_tags.h>
-#include <keymaster/km_openssl/attestation_utils.h>
-#include <vector>
-
-namespace android::hardware::identity::test_utils {
-
-using ::std::optional;
-using ::std::string;
-using ::std::vector;
-
-using ::keymaster::AuthorizationSet;
-using ::keymaster::TypedTag;
-
-class AttestationCertificateParser {
-  public:
-    AttestationCertificateParser(const vector<Certificate>& certChain)
-        : origCertChain_(certChain) {}
-
-    bool parse();
-
-    uint32_t getKeymasterVersion();
-    uint32_t getAttestationVersion();
-    vector<uint8_t> getAttestationChallenge();
-    keymaster_security_level_t getKeymasterSecurityLevel();
-    keymaster_security_level_t getAttestationSecurityLevel();
-
-    template <keymaster_tag_t Tag>
-    bool getSwEnforcedBool(TypedTag<KM_BOOL, Tag> tag) {
-        if (att_sw_enforced_.GetTagValue(tag)) {
-            return true;
-        }
-
-        return false;
-    }
-
-    template <keymaster_tag_t Tag>
-    bool getHwEnforcedBool(TypedTag<KM_BOOL, Tag> tag) {
-        if (att_hw_enforced_.GetTagValue(tag)) {
-            return true;
-        }
-
-        return false;
-    }
-
-    template <keymaster_tag_t Tag>
-    optional<vector<uint8_t>> getHwEnforcedBlob(TypedTag<KM_BYTES, Tag> tag) {
-        keymaster_blob_t blob;
-        if (att_hw_enforced_.GetTagValue(tag, &blob)) {
-            return {};
-        }
-
-        vector<uint8_t> ret(blob.data, blob.data + blob.data_length);
-        return ret;
-    }
-
-    template <keymaster_tag_t Tag>
-    optional<vector<uint8_t>> getSwEnforcedBlob(TypedTag<KM_BYTES, Tag> tag) {
-        keymaster_blob_t blob;
-        if (!att_sw_enforced_.GetTagValue(tag, &blob)) {
-            return {};
-        }
-
-        vector<uint8_t> ret(blob.data, blob.data + blob.data_length);
-        return ret;
-    }
-
-  private:
-    // Helper functions.
-    bool verifyChain(const keymaster_cert_chain_t& chain);
-
-    ASN1_OCTET_STRING* getAttestationRecord(X509* certificate);
-
-    X509* parseCertBlob(const keymaster_blob_t& blob);
-
-    bool verifyAttestationRecord(const keymaster_blob_t& attestation_cert);
-
-    optional<keymaster_cert_chain_t> certificateChainToKeymasterChain(
-            const vector<Certificate>& certificates);
-
-    // Private variables.
-    vector<Certificate> origCertChain_;
-    AuthorizationSet att_sw_enforced_;
-    AuthorizationSet att_hw_enforced_;
-    uint32_t att_attestation_version_;
-    uint32_t att_keymaster_version_;
-    keymaster_security_level_t att_attestation_security_level_;
-    keymaster_security_level_t att_keymaster_security_level_;
-    vector<uint8_t> att_challenge_;
-};
-
-}  // namespace android::hardware::identity::test_utils
-
-#endif  // VTS_ATTESTATION_PARSER_SUPPORT_H
diff --git a/identity/aidl/vts/VtsAttestationTests.cpp b/identity/aidl/vts/VtsAttestationTests.cpp
index 673d08b..5529853 100644
--- a/identity/aidl/vts/VtsAttestationTests.cpp
+++ b/identity/aidl/vts/VtsAttestationTests.cpp
@@ -29,7 +29,6 @@
 #include <future>
 #include <map>
 
-#include "VtsAttestationParserSupport.h"
 #include "VtsIdentityTestUtils.h"
 
 namespace android::hardware::identity {
@@ -44,7 +43,6 @@
 using ::android::String16;
 using ::android::binder::Status;
 
-using test_utils::AttestationCertificateParser;
 using test_utils::setupWritableCredential;
 using test_utils::validateAttestationCertificate;
 
@@ -61,38 +59,12 @@
     sp<IIdentityCredentialStore> credentialStore_;
 };
 
-TEST_P(VtsAttestationTests, verifyAttestationWithNonemptyChallengeEmptyId) {
-    Status result;
-
-    HardwareInformation hwInfo;
-    ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
-    sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_));
-
-    string challenge = "NotSoRandomChallenge";
-    vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
-    vector<Certificate> attestationCertificate;
-    vector<uint8_t> attestationApplicationId = {};
-
-    result = writableCredential->getAttestationCertificate(
-            attestationApplicationId, attestationChallenge, &attestationCertificate);
-
-    ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
-                               << endl;
-
-    EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge,
-                                               attestationApplicationId, hwInfo));
-}
-
 TEST_P(VtsAttestationTests, verifyAttestationWithNonemptyChallengeNonemptyId) {
     Status result;
 
-    HardwareInformation hwInfo;
-    ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_,
+                                        false /* testCredential */));
 
     string challenge = "NotSoRandomChallenge1NotSoRandomChallenge1NotSoRandomChallenge1";
     vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
@@ -106,18 +78,16 @@
     ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
                                << endl;
 
-    EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge,
-                                               attestationApplicationId, hwInfo));
+    validateAttestationCertificate(attestationCertificate, attestationChallenge,
+                                   attestationApplicationId, false);
 }
 
 TEST_P(VtsAttestationTests, verifyAttestationWithVeryShortChallengeAndId) {
     Status result;
 
-    HardwareInformation hwInfo;
-    ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_,
+                                        false /* testCredential */));
 
     string challenge = "c";
     vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
@@ -131,8 +101,8 @@
     ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
                                << endl;
 
-    EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge,
-                                               attestationApplicationId, hwInfo));
+    validateAttestationCertificate(attestationCertificate, attestationChallenge,
+                                   attestationApplicationId, false);
 }
 
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VtsAttestationTests);
diff --git a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
index 500b79f..cdecb97 100644
--- a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
+++ b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
@@ -174,16 +174,17 @@
 
     string cborPretty;
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    true /* testCredential */));
 
     string challenge = "attestationChallenge";
-    test_utils::AttestationData attData(writableCredential, challenge, {});
+    test_utils::AttestationData attData(writableCredential, challenge,
+                                        {1} /* atteestationApplicationId */);
     ASSERT_TRUE(attData.result.isOk())
             << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
 
-    EXPECT_TRUE(validateAttestationCertificate(attData.attestationCertificate,
-                                               attData.attestationChallenge,
-                                               attData.attestationApplicationId, hwInfo));
+    validateAttestationCertificate(attData.attestationCertificate, attData.attestationChallenge,
+                                   attData.attestationApplicationId, true);
 
     // This is kinda of a hack but we need to give the size of
     // ProofOfProvisioning that we'll expect to receive.
@@ -368,6 +369,7 @@
     optional<vector<uint8_t>> signingPubKey =
             support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
     EXPECT_TRUE(signingPubKey);
+    test_utils::verifyAuthKeyCertificate(signingKeyCertificate.encodedCertificate);
 
     // Since we're using a test-credential we know storageKey meaning we can get the
     // private key. Do this, derive the public key from it, and check this matches what
@@ -418,9 +420,9 @@
     }
 
     vector<uint8_t> mac;
-    vector<uint8_t> deviceNameSpacesBytes;
-    ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesBytes).isOk());
-    cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
+    vector<uint8_t> deviceNameSpacesEncoded;
+    ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
+    cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
     ASSERT_EQ(
             "{\n"
             "  'PersonalData' : {\n"
@@ -435,37 +437,19 @@
             "  },\n"
             "}",
             cborPretty);
-    // The data that is MACed is ["DeviceAuthentication", sessionTranscript, docType,
-    // deviceNameSpacesBytes] so build up that structure
-    cppbor::Array deviceAuthentication;
-    deviceAuthentication.add("DeviceAuthentication");
-    deviceAuthentication.add(sessionTranscript.clone());
 
     string docType = "org.iso.18013-5.2019.mdl";
-    deviceAuthentication.add(docType);
-    deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
-    vector<uint8_t> deviceAuthenticationBytes =
-            cppbor::Semantic(24, deviceAuthentication.encode()).encode();
-    // Derive the key used for MACing.
     optional<vector<uint8_t>> readerEphemeralPrivateKey =
             support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
-    optional<vector<uint8_t>> sharedSecret =
-            support::ecdh(signingPubKey.value(), readerEphemeralPrivateKey.value());
-    ASSERT_TRUE(sharedSecret);
-    // Mix-in SessionTranscriptBytes
-    vector<uint8_t> sessionTranscriptBytes =
-            cppbor::Semantic(24, sessionTranscript.encode()).encode();
-    vector<uint8_t> sharedSecretWithSessionTranscriptBytes = sharedSecret.value();
-    std::copy(sessionTranscriptBytes.begin(), sessionTranscriptBytes.end(),
-              std::back_inserter(sharedSecretWithSessionTranscriptBytes));
-    vector<uint8_t> salt = {0x00};
-    vector<uint8_t> info = {};
-    optional<vector<uint8_t>> derivedKey =
-            support::hkdf(sharedSecretWithSessionTranscriptBytes, salt, info, 32);
-    ASSERT_TRUE(derivedKey);
+    optional<vector<uint8_t>> eMacKey = support::calcEMacKey(
+            readerEphemeralPrivateKey.value(),                           // Private Key
+            signingPubKey.value(),                                       // Public Key
+            cppbor::Semantic(24, sessionTranscript.encode()).encode());  // SessionTranscriptBytes
     optional<vector<uint8_t>> calculatedMac =
-            support::coseMac0(derivedKey.value(), {},      // payload
-                              deviceAuthenticationBytes);  // detached content
+            support::calcMac(sessionTranscript.encode(),  // SessionTranscript
+                             docType,                     // DocType
+                             deviceNameSpacesEncoded,     // DeviceNamespaces
+                             eMacKey.value());            // EMacKey
     ASSERT_TRUE(calculatedMac);
     EXPECT_EQ(mac, calculatedMac);
 
@@ -480,18 +464,14 @@
                                 signingKeyBlob, sessionTranscriptEncoded, {},  // readerSignature,
                                 testEntriesEntryCounts)
                         .isOk());
-    ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesBytes).isOk());
-    cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
+    ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
+    cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
     ASSERT_EQ("{}", cborPretty);
     // Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
-    deviceAuthentication = cppbor::Array();
-    deviceAuthentication.add("DeviceAuthentication");
-    deviceAuthentication.add(sessionTranscript.clone());
-    deviceAuthentication.add(docType);
-    deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
-    deviceAuthenticationBytes = cppbor::Semantic(24, deviceAuthentication.encode()).encode();
-    calculatedMac = support::coseMac0(derivedKey.value(), {},      // payload
-                                      deviceAuthenticationBytes);  // detached content
+    calculatedMac = support::calcMac(sessionTranscript.encode(),  // SessionTranscript
+                                     docType,                     // DocType
+                                     deviceNameSpacesEncoded,     // DeviceNamespaces
+                                     eMacKey.value());            // EMacKey
     ASSERT_TRUE(calculatedMac);
     EXPECT_EQ(mac, calculatedMac);
 
@@ -506,18 +486,14 @@
                                 signingKeyBlob, sessionTranscriptEncoded, {},  // readerSignature,
                                 testEntriesEntryCounts)
                         .isOk());
-    ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesBytes).isOk());
-    cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
+    ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
+    cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
     ASSERT_EQ("{}", cborPretty);
     // Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
-    deviceAuthentication = cppbor::Array();
-    deviceAuthentication.add("DeviceAuthentication");
-    deviceAuthentication.add(sessionTranscript.clone());
-    deviceAuthentication.add(docType);
-    deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
-    deviceAuthenticationBytes = cppbor::Semantic(24, deviceAuthentication.encode()).encode();
-    calculatedMac = support::coseMac0(derivedKey.value(), {},      // payload
-                                      deviceAuthenticationBytes);  // detached content
+    calculatedMac = support::calcMac(sessionTranscript.encode(),  // SessionTranscript
+                                     docType,                     // DocType
+                                     deviceNameSpacesEncoded,     // DeviceNamespaces
+                                     eMacKey.value());            // EMacKey
     ASSERT_TRUE(calculatedMac);
     EXPECT_EQ(mac, calculatedMac);
 }
diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
index 8c35952..56e17ba 100644
--- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
+++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
@@ -61,7 +61,8 @@
     ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
 
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     vector<uint8_t> attestationChallenge;
     vector<Certificate> attestationCertificate;
@@ -82,12 +83,13 @@
     ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
 
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     string challenge = "NotSoRandomChallenge1NotSoRandomChallenge1NotSoRandomChallenge1";
     vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
     vector<Certificate> attestationCertificate;
-    vector<uint8_t> attestationApplicationId = {};
+    vector<uint8_t> attestationApplicationId = {1};
 
     result = writableCredential->getAttestationCertificate(
             attestationApplicationId, attestationChallenge, &attestationCertificate);
@@ -95,27 +97,27 @@
     EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
                                << endl;
 
-    EXPECT_TRUE(test_utils::validateAttestationCertificate(
-            attestationCertificate, attestationChallenge, attestationApplicationId, hwInfo));
+    test_utils::validateAttestationCertificate(attestationCertificate, attestationChallenge,
+                                               attestationApplicationId, false);
 }
 
 TEST_P(IdentityCredentialTests, verifyAttestationDoubleCallFails) {
     Status result;
 
-    HardwareInformation hwInfo;
-    ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     string challenge = "NotSoRandomChallenge1";
-    test_utils::AttestationData attData(writableCredential, challenge, {});
-    ASSERT_TRUE(test_utils::validateAttestationCertificate(
-            attData.attestationCertificate, attData.attestationChallenge,
-            attData.attestationApplicationId, hwInfo));
+    test_utils::AttestationData attData(writableCredential, challenge,
+                                        {1} /* atteestationApplicationId */);
+    test_utils::validateAttestationCertificate(attData.attestationCertificate,
+                                               attData.attestationChallenge,
+                                               attData.attestationApplicationId, false);
 
     string challenge2 = "NotSoRandomChallenge2";
-    test_utils::AttestationData attData2(writableCredential, challenge2, {});
+    test_utils::AttestationData attData2(writableCredential, challenge2,
+                                         {} /* atteestationApplicationId */);
     EXPECT_FALSE(attData2.result.isOk()) << attData2.result.exceptionCode() << "; "
                                          << attData2.result.exceptionMessage() << endl;
     EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, attData2.result.exceptionCode());
@@ -125,7 +127,8 @@
 TEST_P(IdentityCredentialTests, verifyStartPersonalization) {
     Status result;
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     // First call should go through
     const vector<int32_t> entryCounts = {2, 4};
@@ -147,7 +150,8 @@
 TEST_P(IdentityCredentialTests, verifyStartPersonalizationMin) {
     Status result;
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     // Verify minimal number of profile count and entry count
     const vector<int32_t> entryCounts = {1, 1};
@@ -160,7 +164,8 @@
 TEST_P(IdentityCredentialTests, verifyStartPersonalizationOne) {
     Status result;
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     // Verify minimal number of profile count and entry count
     const vector<int32_t> entryCounts = {1};
@@ -173,7 +178,8 @@
 TEST_P(IdentityCredentialTests, verifyStartPersonalizationLarge) {
     Status result;
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     // Verify set a large number of profile count and entry count is ok
     const vector<int32_t> entryCounts = {3000};
@@ -186,7 +192,8 @@
 TEST_P(IdentityCredentialTests, verifyProfileNumberMismatchShouldFail) {
     Status result;
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     // Enter mismatched entry and profile numbers
     const vector<int32_t> entryCounts = {5, 6};
@@ -224,7 +231,8 @@
 TEST_P(IdentityCredentialTests, verifyDuplicateProfileId) {
     Status result;
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     const vector<int32_t> entryCounts = {3, 6};
     writableCredential->setExpectedProofOfProvisioningSize(123456);
@@ -283,10 +291,12 @@
     ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
 
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     string challenge = "NotSoRandomChallenge1";
-    test_utils::AttestationData attData(writableCredential, challenge, {});
+    test_utils::AttestationData attData(writableCredential, challenge,
+                                        {} /* atteestationApplicationId */);
     EXPECT_TRUE(attData.result.isOk())
             << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
 
@@ -294,7 +304,7 @@
     ASSERT_TRUE(readerCertificate1);
 
     const vector<int32_t> entryCounts = {1u};
-    size_t expectedPoPSize = 186 + readerCertificate1.value().size();
+    size_t expectedPoPSize = 185 + readerCertificate1.value().size();
     // OK to fail, not available in v1 HAL
     writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize);
     result = writableCredential->startPersonalization(1, entryCounts);
@@ -308,7 +318,7 @@
     ASSERT_TRUE(secureProfiles);
 
     const vector<test_utils::TestEntryData> testEntries1 = {
-            {"Name Space", "Last name", string("Turing"), vector<int32_t>{0, 1}},
+            {"Name Space", "Last name", string("Turing"), vector<int32_t>{1}},
     };
 
     map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
@@ -347,11 +357,11 @@
             "      {\n"
             "        'name' : 'Last name',\n"
             "        'value' : 'Turing',\n"
-            "        'accessControlProfiles' : [0, 1, ],\n"
+            "        'accessControlProfiles' : [1, ],\n"
             "      },\n"
             "    ],\n"
             "  },\n"
-            "  true,\n"
+            "  false,\n"
             "]",
             cborPretty);
 
@@ -370,10 +380,12 @@
     ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
 
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     string challenge = "NotSoRandomChallenge";
-    test_utils::AttestationData attData(writableCredential, challenge, {});
+    test_utils::AttestationData attData(writableCredential, challenge,
+                                        {} /* atteestationApplicationId */);
     EXPECT_TRUE(attData.result.isOk())
             << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
 
@@ -510,7 +522,7 @@
             "      },\n"
             "    ],\n"
             "  },\n"
-            "  true,\n"
+            "  false,\n"
             "]",
             cborPretty);
 
@@ -529,10 +541,12 @@
     ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
 
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     string challenge = "NotSoRandomChallenge";
-    test_utils::AttestationData attData(writableCredential, challenge, {});
+    test_utils::AttestationData attData(writableCredential, challenge,
+                                        {} /* atteestationApplicationId */);
     ASSERT_TRUE(attData.result.isOk())
             << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
 
@@ -591,10 +605,12 @@
     ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
 
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     string challenge = "NotSoRandomChallenge";
-    test_utils::AttestationData attData(writableCredential, challenge, {});
+    test_utils::AttestationData attData(writableCredential, challenge,
+                                        {} /* atteestationApplicationId */);
     ASSERT_TRUE(attData.result.isOk())
             << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
 
@@ -667,7 +683,8 @@
 
 TEST_P(IdentityCredentialTests, verifyAccessControlProfileIdOutOfRange) {
     sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    false /* testCredential */));
 
     const vector<int32_t> entryCounts = {1};
     writableCredential->setExpectedProofOfProvisioningSize(123456);
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.cpp b/identity/aidl/vts/VtsIdentityTestUtils.cpp
index b6ed80f..3b10651 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.cpp
+++ b/identity/aidl/vts/VtsIdentityTestUtils.cpp
@@ -14,13 +14,17 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "VtsIdentityTestUtils"
+
 #include "VtsIdentityTestUtils.h"
 
 #include <aidl/Gtest.h>
+#include <android-base/logging.h>
+#include <keymaster/km_openssl/openssl_utils.h>
+#include <keymasterV4_1/attestation_record.h>
+#include <charconv>
 #include <map>
 
-#include "VtsAttestationParserSupport.h"
-
 namespace android::hardware::identity::test_utils {
 
 using std::endl;
@@ -32,15 +36,15 @@
 using ::android::sp;
 using ::android::String16;
 using ::android::binder::Status;
+using ::keymaster::X509_Ptr;
 
 bool setupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
-                             sp<IIdentityCredentialStore>& credentialStore) {
+                             sp<IIdentityCredentialStore>& credentialStore, bool testCredential) {
     if (credentialStore == nullptr) {
         return false;
     }
 
     string docType = "org.iso.18013-5.2019.mdl";
-    bool testCredential = true;
     Status result = credentialStore->createCredential(docType, testCredential, &writableCredential);
 
     if (result.isOk() && writableCredential != nullptr) {
@@ -178,63 +182,269 @@
     }
 }
 
-bool validateAttestationCertificate(const vector<Certificate>& inputCertificates,
-                                    const vector<uint8_t>& expectedChallenge,
-                                    const vector<uint8_t>& expectedAppId,
-                                    const HardwareInformation& hwInfo) {
-    AttestationCertificateParser certParser_(inputCertificates);
-    bool ret = certParser_.parse();
-    EXPECT_TRUE(ret);
-    if (!ret) {
+string x509NameToRfc2253String(X509_NAME* name) {
+    char* buf;
+    size_t bufSize;
+    BIO* bio;
+
+    bio = BIO_new(BIO_s_mem());
+    X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253);
+    bufSize = BIO_get_mem_data(bio, &buf);
+    string ret = string(buf, bufSize);
+    BIO_free(bio);
+
+    return ret;
+}
+
+int parseDigits(const char** s, int numDigits) {
+    int result;
+    auto [_, ec] = std::from_chars(*s, *s + numDigits, result);
+    if (ec != std::errc()) {
+        LOG(ERROR) << "Error parsing " << numDigits << " digits "
+                   << " from " << s;
+        return 0;
+    }
+    *s += numDigits;
+    return result;
+}
+
+bool parseAsn1Time(const ASN1_TIME* asn1Time, time_t* outTime) {
+    struct tm tm;
+
+    memset(&tm, '\0', sizeof(tm));
+    const char* timeStr = (const char*)asn1Time->data;
+    const char* s = timeStr;
+    if (asn1Time->type == V_ASN1_UTCTIME) {
+        tm.tm_year = parseDigits(&s, 2);
+        if (tm.tm_year < 70) {
+            tm.tm_year += 100;
+        }
+    } else if (asn1Time->type == V_ASN1_GENERALIZEDTIME) {
+        tm.tm_year = parseDigits(&s, 4) - 1900;
+        tm.tm_year -= 1900;
+    } else {
+        LOG(ERROR) << "Unsupported ASN1_TIME type " << asn1Time->type;
+        return false;
+    }
+    tm.tm_mon = parseDigits(&s, 2) - 1;
+    tm.tm_mday = parseDigits(&s, 2);
+    tm.tm_hour = parseDigits(&s, 2);
+    tm.tm_min = parseDigits(&s, 2);
+    tm.tm_sec = parseDigits(&s, 2);
+    // This may need to be updated if someone create certificates using +/- instead of Z.
+    //
+    if (*s != 'Z') {
+        LOG(ERROR) << "Expected Z in string '" << timeStr << "' at offset " << (s - timeStr);
         return false;
     }
 
-    // As per the IC HAL, the version of the Identity
-    // Credential HAL is 1.0 - and this is encoded as major*10 + minor. This field is used by
-    // Keymaster which is known to report integers less than or equal to 4 (for KM up to 4.0)
-    // and integers greater or equal than 41 (for KM starting with 4.1).
-    //
-    // Since we won't get to version 4.0 of the IC HAL for a while, let's also check that a KM
-    // version isn't errornously returned.
-    EXPECT_LE(10, certParser_.getKeymasterVersion());
-    EXPECT_GT(40, certParser_.getKeymasterVersion());
-    EXPECT_LE(3, certParser_.getAttestationVersion());
-
-    // Verify the app id matches to whatever we set it to be.
-    optional<vector<uint8_t>> appId =
-            certParser_.getSwEnforcedBlob(::keymaster::TAG_ATTESTATION_APPLICATION_ID);
-    if (appId) {
-        EXPECT_EQ(expectedAppId.size(), appId.value().size());
-        EXPECT_EQ(0, memcmp(expectedAppId.data(), appId.value().data(), expectedAppId.size()));
-    } else {
-        // app id not found
-        EXPECT_EQ(0, expectedAppId.size());
+    time_t t = timegm(&tm);
+    if (t == -1) {
+        LOG(ERROR) << "Error converting broken-down time to time_t";
+        return false;
     }
-
-    EXPECT_TRUE(certParser_.getHwEnforcedBool(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY));
-    EXPECT_FALSE(certParser_.getHwEnforcedBool(::keymaster::TAG_INCLUDE_UNIQUE_ID));
-
-    // Verify the challenge always matches in size and data of what is passed
-    // in.
-    vector<uint8_t> attChallenge = certParser_.getAttestationChallenge();
-    EXPECT_EQ(expectedChallenge.size(), attChallenge.size());
-    EXPECT_EQ(0, memcmp(expectedChallenge.data(), attChallenge.data(), expectedChallenge.size()));
-
-    // Ensure the attestation conveys that it's implemented in secure hardware (with carve-out
-    // for the reference implementation which cannot be implemented in secure hardware).
-    if (hwInfo.credentialStoreName == "Identity Credential Reference Implementation" &&
-        hwInfo.credentialStoreAuthorName == "Google") {
-        EXPECT_LE(KM_SECURITY_LEVEL_SOFTWARE, certParser_.getKeymasterSecurityLevel());
-        EXPECT_LE(KM_SECURITY_LEVEL_SOFTWARE, certParser_.getAttestationSecurityLevel());
-
-    } else {
-        // Actual devices should use TrustedEnvironment or StrongBox.
-        EXPECT_LE(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT, certParser_.getKeymasterSecurityLevel());
-        EXPECT_LE(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT, certParser_.getAttestationSecurityLevel());
-    }
+    *outTime = t;
     return true;
 }
 
+void validateAttestationCertificate(const vector<Certificate>& credentialKeyCertChain,
+                                    const vector<uint8_t>& expectedChallenge,
+                                    const vector<uint8_t>& expectedAppId, bool isTestCredential) {
+    ASSERT_GE(credentialKeyCertChain.size(), 2);
+
+    vector<uint8_t> certBytes = credentialKeyCertChain[0].encodedCertificate;
+    const uint8_t* certData = certBytes.data();
+    X509_Ptr cert = X509_Ptr(d2i_X509(nullptr, &certData, certBytes.size()));
+
+    vector<uint8_t> batchCertBytes = credentialKeyCertChain[1].encodedCertificate;
+    const uint8_t* batchCertData = batchCertBytes.data();
+    X509_Ptr batchCert = X509_Ptr(d2i_X509(nullptr, &batchCertData, batchCertBytes.size()));
+
+    // First get some values from the batch certificate which is checked
+    // against the top-level certificate (subject, notAfter)
+    //
+
+    X509_NAME* batchSubject = X509_get_subject_name(batchCert.get());
+    ASSERT_NE(nullptr, batchSubject);
+    time_t batchNotAfter;
+    ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(batchCert.get()), &batchNotAfter));
+
+    // Check all the requirements from IWritableIdentityCredential::getAttestationCertificate()...
+    //
+
+    //  - version: INTEGER 2 (means v3 certificate).
+    EXPECT_EQ(2, X509_get_version(cert.get()));
+
+    //  - serialNumber: INTEGER 1 (fixed value: same on all certs).
+    EXPECT_EQ(1, ASN1_INTEGER_get(X509_get_serialNumber(cert.get())));
+
+    //  - signature: must be set to ECDSA.
+    EXPECT_EQ(NID_ecdsa_with_SHA256, X509_get_signature_nid(cert.get()));
+
+    //  - subject: CN shall be set to "Android Identity Credential Key". (fixed value:
+    //    same on all certs)
+    X509_NAME* subject = X509_get_subject_name(cert.get());
+    ASSERT_NE(nullptr, subject);
+    EXPECT_EQ("CN=Android Identity Credential Key", x509NameToRfc2253String(subject));
+
+    //  - issuer: Same as the subject field of the batch attestation key.
+    X509_NAME* issuer = X509_get_issuer_name(cert.get());
+    ASSERT_NE(nullptr, issuer);
+    EXPECT_EQ(x509NameToRfc2253String(batchSubject), x509NameToRfc2253String(issuer));
+
+    //  - validity: Should be from current time and expire at the same time as the
+    //    attestation batch certificate used.
+    //
+    //  Allow for 10 seconds drift to account for the time drift between Secure HW
+    //  and this environment plus the difference between when the certificate was
+    //  created and until now
+    //
+    time_t notBefore;
+    ASSERT_TRUE(parseAsn1Time(X509_get0_notBefore(cert.get()), &notBefore));
+    uint64_t now = time(nullptr);
+    int64_t diffSecs = now - notBefore;
+    int64_t allowDriftSecs = 10;
+    EXPECT_LE(-allowDriftSecs, diffSecs);
+    EXPECT_GE(allowDriftSecs, diffSecs);
+
+    time_t notAfter;
+    ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(cert.get()), &notAfter));
+    EXPECT_EQ(notAfter, batchNotAfter);
+
+    auto [err, attRec] = keymaster::V4_1::parse_attestation_record(certBytes);
+    ASSERT_EQ(keymaster::V4_1::ErrorCode::OK, err);
+
+    //  - subjectPublicKeyInfo: must contain attested public key.
+
+    //  - The attestationVersion field in the attestation extension must be at least 3.
+    EXPECT_GE(attRec.attestation_version, 3);
+
+    //  - The attestationSecurityLevel field must be set to either Software (0),
+    //    TrustedEnvironment (1), or StrongBox (2) depending on how attestation is
+    //    implemented.
+    EXPECT_GE(attRec.attestation_security_level,
+              keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+    //  - The keymasterVersion field in the attestation extension must be set to the.
+    //    same value as used for Android Keystore keys.
+    //
+    // Nothing to check here...
+
+    //  - The keymasterSecurityLevel field in the attestation extension must be set to
+    //    either Software (0), TrustedEnvironment (1), or StrongBox (2) depending on how
+    //    the Trusted Application backing the HAL implementation is implemented.
+    EXPECT_GE(attRec.keymaster_security_level, keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+    //  - The attestationChallenge field must be set to the passed-in challenge.
+    EXPECT_EQ(expectedChallenge.size(), attRec.attestation_challenge.size());
+    EXPECT_TRUE(memcmp(expectedChallenge.data(), attRec.attestation_challenge.data(),
+                       attRec.attestation_challenge.size()) == 0);
+
+    //  - The uniqueId field must be empty.
+    EXPECT_EQ(attRec.unique_id.size(), 0);
+
+    //  - The softwareEnforced field in the attestation extension must include
+    //    Tag::ATTESTATION_APPLICATION_ID which must be set to the bytes of the passed-in
+    //    attestationApplicationId.
+    EXPECT_TRUE(attRec.software_enforced.Contains(keymaster::V4_0::TAG_ATTESTATION_APPLICATION_ID,
+                                                  expectedAppId));
+
+    //  - The teeEnforced field in the attestation extension must include
+    //
+    //    - Tag::IDENTITY_CREDENTIAL_KEY which indicates that the key is an Identity
+    //      Credential key (which can only sign/MAC very specific messages) and not an Android
+    //      Keystore key (which can be used to sign/MAC anything). This must not be set
+    //      for test credentials.
+    bool hasIcKeyTag =
+            attRec.hardware_enforced.Contains(static_cast<android::hardware::keymaster::V4_0::Tag>(
+                    keymaster::V4_1::Tag::IDENTITY_CREDENTIAL_KEY));
+    if (isTestCredential) {
+        EXPECT_FALSE(hasIcKeyTag);
+    } else {
+        EXPECT_TRUE(hasIcKeyTag);
+    }
+
+    //    - Tag::PURPOSE must be set to SIGN
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_PURPOSE,
+                                                  keymaster::V4_0::KeyPurpose::SIGN));
+
+    //    - Tag::KEY_SIZE must be set to the appropriate key size, in bits (e.g. 256)
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_KEY_SIZE, 256));
+
+    //    - Tag::ALGORITHM must be set to EC
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_ALGORITHM,
+                                                  keymaster::V4_0::Algorithm::EC));
+
+    //    - Tag::NO_AUTH_REQUIRED must be set
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_NO_AUTH_REQUIRED));
+
+    //    - Tag::DIGEST must be include SHA_2_256
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_DIGEST,
+                                                  keymaster::V4_0::Digest::SHA_2_256));
+
+    //    - Tag::EC_CURVE must be set to P_256
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_EC_CURVE,
+                                                  keymaster::V4_0::EcCurve::P_256));
+
+    //    - Tag::ROOT_OF_TRUST must be set
+    //
+    EXPECT_GE(attRec.root_of_trust.security_level,
+              keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+    //    - Tag::OS_VERSION and Tag::OS_PATCHLEVEL must be set
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_OS_VERSION));
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_OS_PATCHLEVEL));
+
+    // TODO: we could retrieve osVersion and osPatchLevel from Android itself and compare it
+    // with what was reported in the certificate.
+}
+
+void verifyAuthKeyCertificate(const vector<uint8_t>& authKeyCertChain) {
+    const uint8_t* data = authKeyCertChain.data();
+    auto cert = X509_Ptr(d2i_X509(nullptr, &data, authKeyCertChain.size()));
+
+    //  - version: INTEGER 2 (means v3 certificate).
+    EXPECT_EQ(X509_get_version(cert.get()), 2);
+
+    //  - serialNumber: INTEGER 1 (fixed value: same on all certs).
+    EXPECT_EQ(ASN1_INTEGER_get(X509_get_serialNumber(cert.get())), 1);
+
+    //  - signature: must be set to ECDSA.
+    EXPECT_EQ(X509_get_signature_nid(cert.get()), NID_ecdsa_with_SHA256);
+
+    //  - subject: CN shall be set to "Android Identity Credential Authentication Key". (fixed
+    //    value: same on all certs)
+    X509_NAME* subject = X509_get_subject_name(cert.get());
+    ASSERT_NE(subject, nullptr);
+    EXPECT_EQ(x509NameToRfc2253String(subject),
+              "CN=Android Identity Credential Authentication Key");
+
+    //  - issuer: CN shall be set to "Android Identity Credential Key". (fixed value:
+    //    same on all certs)
+    X509_NAME* issuer = X509_get_issuer_name(cert.get());
+    ASSERT_NE(issuer, nullptr);
+    EXPECT_EQ(x509NameToRfc2253String(issuer), "CN=Android Identity Credential Key");
+
+    //  - subjectPublicKeyInfo: must contain attested public key.
+
+    //  - validity: should be from current time and one year in the future (365 days).
+    time_t notBefore, notAfter;
+    ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(cert.get()), &notAfter));
+    ASSERT_TRUE(parseAsn1Time(X509_get0_notBefore(cert.get()), &notBefore));
+
+    //  Allow for 10 seconds drift to account for the time drift between Secure HW
+    //  and this environment plus the difference between when the certificate was
+    //  created and until now
+    //
+    uint64_t now = time(nullptr);
+    int64_t diffSecs = now - notBefore;
+    int64_t allowDriftSecs = 10;
+    EXPECT_LE(-allowDriftSecs, diffSecs);
+    EXPECT_GE(allowDriftSecs, diffSecs);
+    constexpr uint64_t kSecsInOneYear = 365 * 24 * 60 * 60;
+    EXPECT_EQ(notBefore + kSecsInOneYear, notAfter);
+}
+
 vector<RequestNamespace> buildRequestNamespaces(const vector<TestEntryData> entries) {
     vector<RequestNamespace> ret;
     RequestNamespace curNs;
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.h b/identity/aidl/vts/VtsIdentityTestUtils.h
index 673b736..85c24f8 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.h
+++ b/identity/aidl/vts/VtsIdentityTestUtils.h
@@ -34,8 +34,8 @@
 
 struct AttestationData {
     AttestationData(sp<IWritableIdentityCredential>& writableCredential, string challenge,
-                    vector<uint8_t> applicationId)
-        : attestationApplicationId(applicationId) {
+                    vector<uint8_t> attestationAppId)
+        : attestationApplicationId(attestationAppId) {
         // ASSERT_NE(writableCredential, nullptr);
 
         if (!challenge.empty()) {
@@ -94,7 +94,7 @@
 };
 
 bool setupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
-                             sp<IIdentityCredentialStore>& credentialStore);
+                             sp<IIdentityCredentialStore>& credentialStore, bool testCredential);
 
 optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal);
 
@@ -111,13 +111,17 @@
 
 void setImageData(vector<uint8_t>& image);
 
-bool validateAttestationCertificate(const vector<Certificate>& inputCertificates,
+void validateAttestationCertificate(const vector<Certificate>& credentialKeyCertChain,
                                     const vector<uint8_t>& expectedChallenge,
-                                    const vector<uint8_t>& expectedAppId,
-                                    const HardwareInformation& hwInfo);
+                                    const vector<uint8_t>& expectedAppId, bool isTestCredential);
 
 vector<RequestNamespace> buildRequestNamespaces(const vector<TestEntryData> entries);
 
+// Verifies that the X.509 certificate for a just created authentication key
+// is valid.
+//
+void verifyAuthKeyCertificate(const vector<uint8_t>& authKeyCertChain);
+
 }  // namespace android::hardware::identity::test_utils
 
 #endif  // VTS_IDENTITY_TEST_UTILS_H
diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
index f7ec7c5..3aa5bb6 100644
--- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
+++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
@@ -35,6 +35,9 @@
 using ::std::vector;
 using ::std::pair;
 
+// The semantic tag for a bstr which includes Encoded CBOR (RFC 7049, section 2.4)
+const int kSemanticTagEncodedCbor = 24;
+
 // ---------------------------------------------------------------------------
 // Miscellaneous utilities.
 // ---------------------------------------------------------------------------
@@ -108,45 +111,47 @@
 // ---------------------------------------------------------------------------
 // EC crypto functionality / abstraction (only supports P-256).
 // ---------------------------------------------------------------------------
+
 // Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the
-// PKCS#8 encoded key-pair.  Also generates an attestation
-// certificate using the |challenge| and |applicationId|, and returns the generated
-// certificate in X.509 certificate chain format.
+// DER encoded private key.  Also generates an attestation using the |challenge|
+// and |applicationId|, and returns the generated certificate chain.
 //
-// The attestation time fields used will be the current time, and expires in one year.
+// The notBeffore field will be the current time and the notAfter will be the same
+// same time as the batch certificate.
 //
 // The first parameter of the return value is the keyPair generated, second return in
 // the pair is the attestation certificate generated.
-optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
-        const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId);
-
-// Like createEcKeyPairAndAttestation() but allows you to choose the public key.
 //
+optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
+        const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
+        bool isTestCredential);
+
+// (TODO: remove when no longer used by 3rd party.)
 optional<vector<vector<uint8_t>>> createAttestationForEcPublicKey(
         const vector<uint8_t>& publicKey, const vector<uint8_t>& challenge,
         const vector<uint8_t>& applicationId);
 
 // Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the
-// PKCS#8 encoded key-pair.
+// private key in DER format (as specified in RFC 5915).
 //
 optional<vector<uint8_t>> createEcKeyPair();
 
-// For an EC key |keyPair| encoded in PKCS#8 format, extracts the public key in
+// For an EC key |keyPair| encoded in DER format, extracts the public key in
 // uncompressed point form.
 //
 optional<vector<uint8_t>> ecKeyPairGetPublicKey(const vector<uint8_t>& keyPair);
 
-// For an EC key |keyPair| encoded in PKCS#8 format, extracts the private key as
+// For an EC key |keyPair| encoded in DER format, extracts the private key as
 // an EC uncompressed key.
 //
 optional<vector<uint8_t>> ecKeyPairGetPrivateKey(const vector<uint8_t>& keyPair);
 
-// Creates a PKCS#8 encoded key-pair from a private key (which must be uncompressed,
-// e.g. 32 bytes). The public key is derived from the given private key..
+// Creates a DER encoded representation from a private key (which must be uncompressed,
+// e.g. 32 bytes).
 //
 optional<vector<uint8_t>> ecPrivateKeyToKeyPair(const vector<uint8_t>& privateKey);
 
-// For an EC key |keyPair| encoded in PKCS#8 format, creates a PKCS#12 structure
+// For an EC key |keyPair| encoded in DER format, creates a PKCS#12 structure
 // with the key-pair (not using a password to encrypt the data). The public key
 // in the created structure is included as a certificate, using the given fields
 // |serialDecimal|, |issuer|, |subject|, |validityNotBefore|, and
@@ -209,6 +214,13 @@
 //
 optional<pair<size_t, size_t>> certificateFindSignature(const vector<uint8_t>& x509Certificate);
 
+// Extracts notBefore and notAfter from the top-most certificate in |certificateChain
+// (which should be a concatenated chain of DER-encoded X.509 certificates).
+//
+// Returns notBefore and notAfter in that order.
+//
+optional<pair<time_t, time_t>> certificateGetValidity(const vector<uint8_t>& x509Certificate);
+
 // Generates a X.509 certificate for |publicKey| (which must be in the format
 // returned by ecKeyPairGetPublicKey()).
 //
@@ -351,6 +363,15 @@
 // Utility functions specific to IdentityCredential.
 // ---------------------------------------------------------------------------
 
+optional<vector<uint8_t>> calcMac(const vector<uint8_t>& sessionTranscriptEncoded,
+                                  const string& docType,
+                                  const vector<uint8_t>& deviceNameSpacesEncoded,
+                                  const vector<uint8_t>& eMacKey);
+
+optional<vector<uint8_t>> calcEMacKey(const vector<uint8_t>& privateKey,
+                                      const vector<uint8_t>& publicKey,
+                                      const vector<uint8_t>& sessionTranscriptBytes);
+
 // Returns the testing AES-128 key where all bits are set to 0.
 const vector<uint8_t>& getTestHardwareBoundKey();
 
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index 747f182..77b795b 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -44,6 +44,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <charconv>
 
 #include <cppbor.h>
 #include <cppbor_parse.h>
@@ -54,6 +55,7 @@
 #include <keymaster/contexts/soft_attestation_cert.h>
 #include <keymaster/keymaster_tags.h>
 #include <keymaster/km_openssl/attestation_utils.h>
+#include <keymaster/km_openssl/certificate_utils.h>
 
 namespace android {
 namespace hardware {
@@ -870,16 +872,109 @@
     return hmac;
 }
 
+int parseDigits(const char** s, int numDigits) {
+    int result;
+    auto [_, ec] = std::from_chars(*s, *s + numDigits, result);
+    if (ec != std::errc()) {
+        LOG(ERROR) << "Error parsing " << numDigits << " digits "
+                   << " from " << s;
+        return 0;
+    }
+    *s += numDigits;
+    return result;
+}
+
+bool parseAsn1Time(const ASN1_TIME* asn1Time, time_t* outTime) {
+    struct tm tm;
+
+    memset(&tm, '\0', sizeof(tm));
+    const char* timeStr = (const char*)asn1Time->data;
+    const char* s = timeStr;
+    if (asn1Time->type == V_ASN1_UTCTIME) {
+        tm.tm_year = parseDigits(&s, 2);
+        if (tm.tm_year < 70) {
+            tm.tm_year += 100;
+        }
+    } else if (asn1Time->type == V_ASN1_GENERALIZEDTIME) {
+        tm.tm_year = parseDigits(&s, 4) - 1900;
+        tm.tm_year -= 1900;
+    } else {
+        LOG(ERROR) << "Unsupported ASN1_TIME type " << asn1Time->type;
+        return false;
+    }
+    tm.tm_mon = parseDigits(&s, 2) - 1;
+    tm.tm_mday = parseDigits(&s, 2);
+    tm.tm_hour = parseDigits(&s, 2);
+    tm.tm_min = parseDigits(&s, 2);
+    tm.tm_sec = parseDigits(&s, 2);
+    // This may need to be updated if someone create certificates using +/- instead of Z.
+    //
+    if (*s != 'Z') {
+        LOG(ERROR) << "Expected Z in string '" << timeStr << "' at offset " << (s - timeStr);
+        return false;
+    }
+
+    time_t t = timegm(&tm);
+    if (t == -1) {
+        LOG(ERROR) << "Error converting broken-down time to time_t";
+        return false;
+    }
+    *outTime = t;
+    return true;
+}
+
 // Generates the attestation certificate with the parameters passed in.  Note
 // that the passed in |activeTimeMilliSeconds| |expireTimeMilliSeconds| are in
 // milli seconds since epoch.  We are setting them to milliseconds due to
 // requirement in AuthorizationSet KM_DATE fields.  The certificate created is
 // actually in seconds.
-optional<vector<vector<uint8_t>>> createAttestation(const EVP_PKEY* key,
-                                                    const vector<uint8_t>& applicationId,
-                                                    const vector<uint8_t>& challenge,
-                                                    uint64_t activeTimeMilliSeconds,
-                                                    uint64_t expireTimeMilliSeconds) {
+//
+// If 0 is passed for expiration time, the expiration time from batch
+// certificate will be used.
+//
+optional<vector<vector<uint8_t>>> createAttestation(
+        const EVP_PKEY* key, const vector<uint8_t>& applicationId, const vector<uint8_t>& challenge,
+        uint64_t activeTimeMilliSeconds, uint64_t expireTimeMilliSeconds, bool isTestCredential) {
+    const keymaster_cert_chain_t* attestation_chain =
+            ::keymaster::getAttestationChain(KM_ALGORITHM_EC, nullptr);
+    if (attestation_chain == nullptr) {
+        LOG(ERROR) << "Error getting attestation chain";
+        return {};
+    }
+    if (expireTimeMilliSeconds == 0) {
+        if (attestation_chain->entry_count < 1) {
+            LOG(ERROR) << "Expected at least one entry in attestation chain";
+            return {};
+        }
+        keymaster_blob_t* bcBlob = &(attestation_chain->entries[0]);
+        const uint8_t* bcData = bcBlob->data;
+        auto bc = X509_Ptr(d2i_X509(nullptr, &bcData, bcBlob->data_length));
+        time_t bcNotAfter;
+        if (!parseAsn1Time(X509_get0_notAfter(bc.get()), &bcNotAfter)) {
+            LOG(ERROR) << "Error getting notAfter from batch certificate";
+            return {};
+        }
+        expireTimeMilliSeconds = bcNotAfter * 1000;
+    }
+    const keymaster_key_blob_t* attestation_signing_key =
+            ::keymaster::getAttestationKey(KM_ALGORITHM_EC, nullptr);
+    if (attestation_signing_key == nullptr) {
+        LOG(ERROR) << "Error getting attestation key";
+        return {};
+    }
+
+    ::keymaster::X509_NAME_Ptr subjectName;
+    if (KM_ERROR_OK !=
+        ::keymaster::make_name_from_str("Android Identity Credential Key", &subjectName)) {
+        LOG(ERROR) << "Cannot create attestation subject";
+        return {};
+    }
+
+    vector<uint8_t> subject(i2d_X509_NAME(subjectName.get(), NULL));
+    unsigned char* subjectPtr = subject.data();
+
+    i2d_X509_NAME(subjectName.get(), &subjectPtr);
+
     ::keymaster::AuthorizationSet auth_set(
             ::keymaster::AuthorizationSetBuilder()
                     .Authorization(::keymaster::TAG_ATTESTATION_CHALLENGE, challenge.data(),
@@ -894,6 +989,8 @@
                     // includes app id.
                     .Authorization(::keymaster::TAG_ATTESTATION_APPLICATION_ID,
                                    applicationId.data(), applicationId.size())
+                    .Authorization(::keymaster::TAG_CERTIFICATE_SUBJECT, subject.data(),
+                                   subject.size())
                     .Authorization(::keymaster::TAG_USAGE_EXPIRE_DATETIME, expireTimeMilliSeconds));
 
     // Unique id and device id is not applicable for identity credential attestation,
@@ -901,7 +998,7 @@
     ::keymaster::AuthorizationSet swEnforced(::keymaster::AuthorizationSetBuilder().Authorization(
             ::keymaster::TAG_CREATION_DATETIME, activeTimeMilliSeconds));
 
-    ::keymaster::AuthorizationSet hwEnforced(
+    ::keymaster::AuthorizationSetBuilder hwEnforcedBuilder =
             ::keymaster::AuthorizationSetBuilder()
                     .Authorization(::keymaster::TAG_PURPOSE, KM_PURPOSE_SIGN)
                     .Authorization(::keymaster::TAG_KEY_SIZE, 256)
@@ -909,34 +1006,29 @@
                     .Authorization(::keymaster::TAG_NO_AUTH_REQUIRED)
                     .Authorization(::keymaster::TAG_DIGEST, KM_DIGEST_SHA_2_256)
                     .Authorization(::keymaster::TAG_EC_CURVE, KM_EC_CURVE_P_256)
-                    .Authorization(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY));
+                    .Authorization(::keymaster::TAG_OS_VERSION, 42)
+                    .Authorization(::keymaster::TAG_OS_PATCHLEVEL, 43);
 
-    const keymaster_cert_chain_t* attestation_chain =
-            ::keymaster::getAttestationChain(KM_ALGORITHM_EC, nullptr);
-
-    if (attestation_chain == nullptr) {
-        LOG(ERROR) << "Error getting attestation chain";
-        return {};
+    // Only include TAG_IDENTITY_CREDENTIAL_KEY if it's not a test credential
+    if (!isTestCredential) {
+        hwEnforcedBuilder.Authorization(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY);
     }
-
-    const keymaster_key_blob_t* attestation_signing_key =
-            ::keymaster::getAttestationKey(KM_ALGORITHM_EC, nullptr);
-    if (attestation_signing_key == nullptr) {
-        LOG(ERROR) << "Error getting attestation key";
-        return {};
-    }
+    ::keymaster::AuthorizationSet hwEnforced(hwEnforcedBuilder);
 
     keymaster_error_t error;
     ::keymaster::CertChainPtr cert_chain_out;
-    ::keymaster::PureSoftKeymasterContext context;
 
-    // set identity version to 10 per hal requirements specified in IWriteableCredential.hal
-    // For now, the identity version in the attestation is set in the keymaster
-    // version field in the portable keymaster lib, which is a bit misleading.
-    uint identity_version = 10;
+    // Pretend to be implemented in a trusted environment just so we can pass
+    // the VTS tests. Of course, this is a pretend-only game since hopefully no
+    // relying party is ever going to trust our batch key and those keys above
+    // it.
+    //
+    ::keymaster::PureSoftKeymasterContext context(::keymaster::KmVersion::KEYMASTER_4_1,
+                                                  KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT);
+
     error = generate_attestation_from_EVP(key, swEnforced, hwEnforced, auth_set, context,
-                                          identity_version, *attestation_chain,
-                                          *attestation_signing_key, &cert_chain_out);
+                                          *attestation_chain, *attestation_signing_key,
+                                          &cert_chain_out);
 
     if (KM_ERROR_OK != error || !cert_chain_out) {
         LOG(ERROR) << "Error generate attestation from EVP key" << error;
@@ -957,7 +1049,8 @@
 }
 
 optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
-        const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
+        const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
+        bool isTestCredential) {
     auto ec_key = ::keymaster::EC_KEY_Ptr(EC_KEY_new());
     auto pkey = ::keymaster::EVP_PKEY_Ptr(EVP_PKEY_new());
     auto group = ::keymaster::EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
@@ -978,12 +1071,11 @@
         return {};
     }
 
-    uint64_t now = time(nullptr);
-    uint64_t secondsInOneYear = 365 * 24 * 60 * 60;
-    uint64_t expireTimeMs = (now + secondsInOneYear) * 1000;
+    uint64_t nowMs = time(nullptr) * 1000;
+    uint64_t expireTimeMs = 0;  // Set to same as batch certificate
 
-    optional<vector<vector<uint8_t>>> attestationCert =
-            createAttestation(pkey.get(), applicationId, challenge, now * 1000, expireTimeMs);
+    optional<vector<vector<uint8_t>>> attestationCert = createAttestation(
+            pkey.get(), applicationId, challenge, nowMs, expireTimeMs, isTestCredential);
     if (!attestationCert) {
         LOG(ERROR) << "Error create attestation from key and challenge";
         return {};
@@ -1031,14 +1123,12 @@
         return {};
     }
 
-    uint64_t now = (std::chrono::duration_cast<std::chrono::nanoseconds>(
-                    std::chrono::system_clock::now().time_since_epoch()).
-                    count()/ 1000000000);
-    uint64_t secondsInOneYear = 365 * 24 * 60 * 60;
-    uint64_t expireTimeMs = (now + secondsInOneYear) * 1000;
+    uint64_t nowMs = time(nullptr) * 1000;
+    uint64_t expireTimeMs = 0;  // Set to same as batch certificate
 
     optional<vector<vector<uint8_t>>> attestationCert =
-            createAttestation(pkey.get(), applicationId, challenge, now * 1000, expireTimeMs);
+            createAttestation(pkey.get(), applicationId, challenge, nowMs, expireTimeMs,
+                              false /* isTestCredential */);
     if (!attestationCert) {
         LOG(ERROR) << "Error create attestation from key and challenge";
         return {};
@@ -1646,6 +1736,32 @@
     return std::make_pair(tbsCertificateOffset, tbsCertificateSize);
 }
 
+optional<pair<time_t, time_t>> certificateGetValidity(const vector<uint8_t>& x509Certificate) {
+    vector<X509_Ptr> certs;
+    if (!parseX509Certificates(x509Certificate, certs)) {
+        LOG(ERROR) << "Error parsing certificates";
+        return {};
+    }
+    if (certs.size() < 1) {
+        LOG(ERROR) << "No certificates in chain";
+        return {};
+    }
+
+    time_t notBefore;
+    time_t notAfter;
+    if (!parseAsn1Time(X509_get0_notBefore(certs[0].get()), &notBefore)) {
+        LOG(ERROR) << "Error parsing notBefore";
+        return {};
+    }
+
+    if (!parseAsn1Time(X509_get0_notAfter(certs[0].get()), &notAfter)) {
+        LOG(ERROR) << "Error parsing notAfter";
+        return {};
+    }
+
+    return std::make_pair(notBefore, notAfter);
+}
+
 optional<pair<size_t, size_t>> certificateFindSignature(const vector<uint8_t>& x509Certificate) {
     vector<X509_Ptr> certs;
     if (!parseX509Certificates(x509Certificate, certs)) {
@@ -2218,6 +2334,49 @@
 // Utility functions specific to IdentityCredential.
 // ---------------------------------------------------------------------------
 
+optional<vector<uint8_t>> calcEMacKey(const vector<uint8_t>& privateKey,
+                                      const vector<uint8_t>& publicKey,
+                                      const vector<uint8_t>& sessionTranscriptBytes) {
+    optional<vector<uint8_t>> sharedSecret = support::ecdh(publicKey, privateKey);
+    if (!sharedSecret) {
+        LOG(ERROR) << "Error performing ECDH";
+        return {};
+    }
+    vector<uint8_t> salt = support::sha256(sessionTranscriptBytes);
+    vector<uint8_t> info = {'E', 'M', 'a', 'c', 'K', 'e', 'y'};
+    optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
+    if (!derivedKey) {
+        LOG(ERROR) << "Error performing HKDF";
+        return {};
+    }
+    return derivedKey.value();
+}
+
+optional<vector<uint8_t>> calcMac(const vector<uint8_t>& sessionTranscriptEncoded,
+                                  const string& docType,
+                                  const vector<uint8_t>& deviceNameSpacesEncoded,
+                                  const vector<uint8_t>& eMacKey) {
+    auto [sessionTranscriptItem, _, errMsg] = cppbor::parse(sessionTranscriptEncoded);
+    if (sessionTranscriptItem == nullptr) {
+        LOG(ERROR) << "Error parsing sessionTranscriptEncoded: " << errMsg;
+        return {};
+    }
+    // The data that is MACed is ["DeviceAuthentication", sessionTranscript, docType,
+    // deviceNameSpacesBytes] so build up that structure
+    cppbor::Array deviceAuthentication =
+            cppbor::Array()
+                    .add("DeviceAuthentication")
+                    .add(std::move(sessionTranscriptItem))
+                    .add(docType)
+                    .add(cppbor::Semantic(kSemanticTagEncodedCbor, deviceNameSpacesEncoded));
+    vector<uint8_t> deviceAuthenticationBytes =
+            cppbor::Semantic(kSemanticTagEncodedCbor, deviceAuthentication.encode()).encode();
+    optional<vector<uint8_t>> calculatedMac =
+            support::coseMac0(eMacKey, {},                 // payload
+                              deviceAuthenticationBytes);  // detached content
+    return calculatedMac;
+}
+
 vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize) {
     vector<vector<uint8_t>> ret;
 
@@ -2244,7 +2403,6 @@
     return ret;
 }
 
-
 vector<uint8_t> testHardwareBoundKey = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
 const vector<uint8_t>& getTestHardwareBoundKey() {
diff --git a/identity/support/tests/IdentityCredentialSupportTest.cpp b/identity/support/tests/IdentityCredentialSupportTest.cpp
index c356549..266f263 100644
--- a/identity/support/tests/IdentityCredentialSupportTest.cpp
+++ b/identity/support/tests/IdentityCredentialSupportTest.cpp
@@ -436,6 +436,300 @@
             support::cborPrettyPrint(mac.value()));
 }
 
+// Generates a private key in DER format for a small value of 'd'.
+//
+// Used for test vectors.
+//
+vector<uint8_t> p256PrivateKeyFromD(uint8_t d) {
+    vector<uint8_t> privateUncompressed;
+    privateUncompressed.resize(32);
+    privateUncompressed[31] = d;
+    optional<vector<uint8_t>> privateKey = support::ecPrivateKeyToKeyPair(privateUncompressed);
+    return privateKey.value();
+}
+
+std::pair<vector<uint8_t>, vector<uint8_t>> p256PrivateKeyGetXandY(
+        const vector<uint8_t> privateKey) {
+    optional<vector<uint8_t>> publicUncompressed = support::ecKeyPairGetPublicKey(privateKey);
+    vector<uint8_t> x = vector<uint8_t>(publicUncompressed.value().begin() + 1,
+                                        publicUncompressed.value().begin() + 33);
+    vector<uint8_t> y = vector<uint8_t>(publicUncompressed.value().begin() + 33,
+                                        publicUncompressed.value().begin() + 65);
+    return std::make_pair(x, y);
+}
+
+const cppbor::Item* findValueForTstr(const cppbor::Map* map, const string& keyValue) {
+    // TODO: Need cast until libcppbor's Map::get() is marked as const
+    auto [item, found] = ((cppbor::Map*)map)->get(keyValue);
+    if (!found) {
+        return nullptr;
+    }
+    return item.get();
+}
+
+const cppbor::Array* findArrayValueForTstr(const cppbor::Map* map, const string& keyValue) {
+    const cppbor::Item* item = findValueForTstr(map, keyValue);
+    if (item == nullptr) {
+        return nullptr;
+    }
+    return item->asArray();
+}
+
+const cppbor::Map* findMapValueForTstr(const cppbor::Map* map, const string& keyValue) {
+    const cppbor::Item* item = findValueForTstr(map, keyValue);
+    if (item == nullptr) {
+        return nullptr;
+    }
+    return item->asMap();
+}
+
+const cppbor::Semantic* findSemanticValueForTstr(const cppbor::Map* map, const string& keyValue) {
+    const cppbor::Item* item = findValueForTstr(map, keyValue);
+    if (item == nullptr) {
+        return nullptr;
+    }
+    return item->asSemantic();
+}
+
+const std::string findStringValueForTstr(const cppbor::Map* map, const string& keyValue) {
+    const cppbor::Item* item = findValueForTstr(map, keyValue);
+    if (item == nullptr) {
+        return nullptr;
+    }
+    const cppbor::Tstr* tstr = item->asTstr();
+    if (tstr == nullptr) {
+        return "";
+    }
+    return tstr->value();
+}
+
+TEST(IdentityCredentialSupport, testVectors_18013_5) {
+    // This is a test against known vectors for ISO 18013-5.
+    //
+    // The objective of this test is to verify that support::calcEMacKey() and
+    // support::calcMac() agree with the given test vectors.
+    //
+
+    // We're given static device key:
+    //
+    //     x: 28412803729898893058558238221310261427084375743576167377786533380249859400145
+    //     y: 65403602826180996396520286939226973026599920614829401631985882360676038096704
+    //     d: 11
+    //
+    vector<uint8_t> deviceKey = p256PrivateKeyFromD(11);
+    auto [deviceKeyX, deviceKeyY] = p256PrivateKeyGetXandY(deviceKey);
+    EXPECT_EQ(support::encodeHex(deviceKeyX),
+              "3ed113b7883b4c590638379db0c21cda16742ed0255048bf433391d374bc21d1");
+    EXPECT_EQ(support::encodeHex(deviceKeyY),
+              "9099209accc4c8a224c843afa4f4c68a090d04da5e9889dae2f8eefce82a3740");
+
+    // We're given Ephemeral reader key:
+    //
+    //   x: 59535862115950685744176693329402396749019581632805653266809849538337418304154
+    //   y: 53776829996815113213100700404832701936765102413212294632483274374518863708344
+    //   d: 20
+    //
+    vector<uint8_t> ephemeralReaderKey = p256PrivateKeyFromD(20);
+    auto [ephemeralReaderKeyX, ephemeralReaderKeyY] = p256PrivateKeyGetXandY(ephemeralReaderKey);
+    EXPECT_EQ(support::encodeHex(ephemeralReaderKeyX),
+              "83a01a9378395bab9bcd6a0ad03cc56d56e6b19250465a94a234dc4c6b28da9a");
+    EXPECT_EQ(support::encodeHex(ephemeralReaderKeyY),
+              "76e49b6de2f73234ae6a5eb9d612b75c9f2202bb6923f54ff8240aaa86f640b8");
+    vector<uint8_t> ephemeralReaderKeyPublic =
+            support::ecKeyPairGetPublicKey(ephemeralReaderKey).value();
+
+    // We're given SessionEstablishment.
+    //
+    //   SessionEstablishment = {
+    //     "eReaderKey" : EReaderKeyBytes,
+    //     "data" : bstr ; Encrypted mdoc request
+    //   }
+    //
+    // Fish out EReaderKey from this.
+    //
+    // Note that the test vector below is incorrect insofar that it uses
+    // "eReaderKeyBytes" instead of just "eReaderKey". This will be corrected in
+    // the future.
+    //
+    optional<vector<uint8_t>> sessionEstablishmentEncoded = support::decodeHex(
+            "a26f655265616465724b65794279746573d818584ba40102200121582083a01a9378395bab9bcd6a0ad03c"
+            "c56d56e6b19250465a94a234dc4c6b28da9a22582076e49b6de2f73234ae6a5eb9d612b75c9f2202bb6923"
+            "f54ff8240aaa86f640b864646174615902d945b31040c57491acb6d46a71f6c1f67a0b837df1bda9089fd0"
+            "3d0b1fdac3eeb2874a4ef6f90c97d03397186ba00a91102faae7e992e15f761d5662c3c37e3c6c2cfd2ebc"
+            "0bf59dbb8795e377bd7dd353230a41ba2d82294b45871a39b42ca531f26b52f46e356fbaf5075c8fd5b8b0"
+            "8a0df4a1d2e1bdd2e5d69169c1efbb51e393e608d833d325bebfbccb2e15ec08f94b264582fa7b93f7cebc"
+            "aa69f4f0cac2744d4fe35b04df26b2ae69273eed33024949080c1c95a6ef046beede959e9494297dd770af"
+            "4ac6fdd56783aa012555c213dc05cf0f41d1c95119720fcfe1621027f80e2ddd56ea3c1fc596f7b2579333"
+            "5a887ec788092b4a69d23b6219e27d0249b50b3fdcb95b5227007689362e0416b3bae3dae7cb56b4394666"
+            "4e3a3f60dce8d0b678fcd754bebf87bd2b0278dd782d952488a46f2874e34c2dd97bb74084a62b850e9719"
+            "252cd1dca7dbf1858193f6cf093cb3735312bbe1138cf29d8f350e285923f8ef07065299926720b42264e8"
+            "fd5d4b133e72f47c4e999ea689c353f8b41e50a59838e1a0d09eca4a557f77a9c389a0591ad1639119ce86"
+            "edc3320130480ee5101effae6066e8c85aac9ead2ae83e49c1e508aab02f753decbb522ea2200d62fd5d26"
+            "094bd35100bffaa1cdc6af9f7e9cfe7b63da6b5671cd5ac2cf5da450c72addc64cde441f3b7f7fdaf930ad"
+            "1e13388e8a7308d8ca4607e59e082db431a232e7e12cb692baeb4b2127e110ff24cea322ffdbc2e4d9c4c6"
+            "bed27753137d07897c8613627a799a560cf1a2d1edb3de029442862940a5ed7785eea8b6ace93aa6af0792"
+            "fd82877f62d07b757d0179ecbb7347004ecc9c0690d41f75f188cb17ffd2cec2ad8c9675466bb33b737a2a"
+            "e7592b2dcb8132aced2e572266f3f5413a5f9d6d4339a1e4662622af2e7e157a4ea3bfd5c4247e2ec91d8c"
+            "5c3c17427d5edfae673d0e0f782a8d40fa805fd8bc82ae3cb21a65cdad863e02309f6b01d1753fa884b778"
+            "f6e019a2004d8964deeb11f1fd478fcb");
+    ASSERT_TRUE(sessionEstablishmentEncoded);
+    auto [sessionEstablishmentItem, _se, _se2] = cppbor::parse(sessionEstablishmentEncoded.value());
+    const cppbor::Map* sessionEstablishment = sessionEstablishmentItem->asMap();
+    ASSERT_NE(sessionEstablishment, nullptr);
+    const cppbor::Semantic* eReaderKeyBytes =
+            findSemanticValueForTstr(sessionEstablishment, "eReaderKeyBytes");
+    ASSERT_NE(eReaderKeyBytes, nullptr);
+    ASSERT_EQ(eReaderKeyBytes->value(), 24);
+    const cppbor::Bstr* eReaderKeyBstr = eReaderKeyBytes->child()->asBstr();
+    ASSERT_NE(eReaderKeyBstr, nullptr);
+    vector<uint8_t> eReaderKeyEncoded = eReaderKeyBstr->value();
+    // TODO: verify this agrees with ephemeralReaderKeyX and ephemeralReaderKeyY
+
+    // We're given DeviceEngagement.
+    //
+    vector<uint8_t> deviceEngagementEncoded =
+            support::decodeHex(
+                    "a20063312e30018201d818584ba401022001215820cef66d6b2a3a993e591214d1ea223fb545ca"
+                    "6c471c48306e4c36069404c5723f225820878662a229aaae906e123cdd9d3b4c10590ded29fe75"
+                    "1eeeca34bbaa44af0773")
+                    .value();
+
+    // Now calculate SessionTranscriptBytes. It is defined as
+    //
+    //   SessionTranscript = [
+    //      DeviceEngagementBytes,
+    //      EReaderKeyBytes,
+    //      Handover
+    //   ]
+    //
+    //   SessionTranscriptBytes = #6.24(bstr .cbor SessionTranscript)
+    //
+    cppbor::Array sessionTranscript;
+    sessionTranscript.add(cppbor::Semantic(24, deviceEngagementEncoded));
+    sessionTranscript.add(cppbor::Semantic(24, eReaderKeyEncoded));
+    sessionTranscript.add(cppbor::Null());
+    vector<uint8_t> sessionTranscriptEncoded = sessionTranscript.encode();
+    vector<uint8_t> sessionTranscriptBytes =
+            cppbor::Semantic(24, sessionTranscriptEncoded).encode();
+
+    // The expected EMacKey is 4c1ebb8aacc633465390fa44edfdb49cb57f2e079aaa771d812584699c0b97e2
+    //
+    // Verify that support::calcEMacKey() gets the same result.
+    //
+    optional<vector<uint8_t>> eMacKey =
+            support::calcEMacKey(support::ecKeyPairGetPrivateKey(deviceKey).value(),  // private key
+                                 ephemeralReaderKeyPublic,                            // public key
+                                 sessionTranscriptBytes);  // sessionTranscriptBytes
+    ASSERT_TRUE(eMacKey);
+    ASSERT_EQ(support::encodeHex(eMacKey.value()),
+              "4c1ebb8aacc633465390fa44edfdb49cb57f2e079aaa771d812584699c0b97e2");
+
+    // Also do it the other way around
+    //
+    optional<vector<uint8_t>> eMacKey2 = support::calcEMacKey(
+            support::ecKeyPairGetPrivateKey(ephemeralReaderKey).value(),  // private key
+            support::ecKeyPairGetPublicKey(deviceKey).value(),            // public key
+            sessionTranscriptBytes);                                      // sessionTranscriptBytes
+    ASSERT_TRUE(eMacKey2);
+    ASSERT_EQ(support::encodeHex(eMacKey2.value()),
+              "4c1ebb8aacc633465390fa44edfdb49cb57f2e079aaa771d812584699c0b97e2");
+
+    // We're given DeviceResponse
+    //
+    vector<uint8_t> deviceResponseEncoded =
+            support::decodeHex(
+                    "a36776657273696f6e63312e3069646f63756d656e747381a367646f6354797065756f72672e69"
+                    "736f2e31383031332e352e312e6d444c6c6973737565725369676e6564a26a6e616d6553706163"
+                    "6573a2716f72672e69736f2e31383031332e352e3181d8185863a4686469676573744944016672"
+                    "616e646f6d58208798645b20ea200e19ffabac92624bee6aec63aceedecfb1b80077d22bfc20e9"
+                    "71656c656d656e744964656e7469666965726b66616d696c795f6e616d656c656c656d656e7456"
+                    "616c756563446f656b636f6d2e6578616d706c6581d8185864a468646967657374494401667261"
+                    "6e646f6d5820218ecf13521b53f4b96abaebe56417afec0e4c91fc8fb26086cd1e5cdc1a94ff71"
+                    "656c656d656e744964656e7469666965726f616e6f746865725f656c656d656e746c656c656d65"
+                    "6e7456616c75650a6a697373756572417574688443a10126a118215901d2308201ce30820174a0"
+                    "0302010202141f7d44f4f107c5ee3f566049cf5d72de294b0d23300a06082a8648ce3d04030230"
+                    "233114301206035504030c0b75746f7069612069616361310b3009060355040613025553301e17"
+                    "0d3230313030313030303030305a170d3231313030313030303030305a30213112301006035504"
+                    "030c0975746f706961206473310b30090603550406130255533059301306072a8648ce3d020106"
+                    "082a8648ce3d03010703420004301d9e502dc7e05da85da026a7ae9aa0fac9db7d52a95b3e3e3f"
+                    "9aa0a1b45b8b6551b6f6b3061223e0d23c026b017d72298d9ae46887ca61d58db6aea17ee267a3"
+                    "8187308184301e0603551d120417301581136578616d706c65406578616d706c652e636f6d301c"
+                    "0603551d1f041530133011a00fa00d820b6578616d706c652e636f6d301d0603551d0e04160414"
+                    "7bef4db59a1ffb07592bfc57f4743b8a73aea792300e0603551d0f0101ff040403020780301506"
+                    "03551d250101ff040b3009060728818c5d050102300a06082a8648ce3d04030203480030450220"
+                    "21d52fb1fbda80e5bfda1e8dfb1bc7bf0acb7261d5c9ff54425af76eb21571c602210082bf301f"
+                    "89e0a2cb9ca9c9050352de80b47956764f7a3e07bf6a8cd87528a3b55901d2d8185901cda66776"
+                    "657273696f6e63312e306f646967657374416c676f726974686d675348412d3235366c76616c75"
+                    "6544696765737473a2716f72672e69736f2e31383031332e352e31a20058203b22af1126771f02"
+                    "f0ea0d546d4ee3c5b51637381154f5211b79daf5f9facaa8015820f2cba0ce3cde5df901a3da75"
+                    "13a4d7f7225fdfe5a306544529bf3dbcce655ca06b636f6d2e6578616d706c65a200582072636d"
+                    "ddc282424a63499f4b3927aaa3b74da7b9c0134178bf735e949e4a761e01582006322d3cbe6603"
+                    "876bdacc5b6679b51b0fc53d029c244fd5ea719d9028459c916d6465766963654b6579496e666f"
+                    "a1696465766963654b6579a4010220012158203ed113b7883b4c590638379db0c21cda16742ed0"
+                    "255048bf433391d374bc21d12258209099209accc4c8a224c843afa4f4c68a090d04da5e9889da"
+                    "e2f8eefce82a374067646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c"
+                    "76616c6964697479496e666fa3667369676e6564c074323032302d31302d30315431333a33303a"
+                    "30325a6976616c696446726f6dc074323032302d31302d30315431333a33303a30325a6a76616c"
+                    "6964556e74696cc074323032312d31302d30315431333a33303a30325a5840273ec1b59817d571"
+                    "b5a2c5c0ab0ea213d42acb18547fd7097afcc888a22ecbb863c6461ce0e240880895b4aaa84308"
+                    "784571c7be7aa3a2e7e3a2ea1a145ed1966c6465766963655369676e6564a26a6e616d65537061"
+                    "636573d81841a06a64657669636541757468a1696465766963654d61638443a10105a0f6582009"
+                    "da7c964ac004ec36ec64edd0c1abf50c03433c215c3ddb144768abcdf20a60667374617475730"
+                    "0")
+                    .value();
+    auto [deviceResponseItem, _, _2] = cppbor::parse(deviceResponseEncoded);
+    const cppbor::Map* deviceResponse = deviceResponseItem->asMap();
+    ASSERT_NE(deviceResponse, nullptr);
+    const cppbor::Array* documents = findArrayValueForTstr(deviceResponse, "documents");
+    ASSERT_NE(documents, nullptr);
+    ASSERT_EQ(documents->size(), 1);
+    const cppbor::Map* document = ((*documents)[0])->asMap();
+    ASSERT_NE(document, nullptr);
+
+    // Get docType
+    string docType = findStringValueForTstr(document, "docType");
+    ASSERT_EQ(docType, "org.iso.18013.5.1.mDL");
+
+    // Drill down...
+    const cppbor::Map* deviceSigned = findMapValueForTstr(document, "deviceSigned");
+    ASSERT_NE(deviceSigned, nullptr);
+
+    // Dig out the encoded form of DeviceNameSpaces
+    //
+    const cppbor::Semantic* deviceNameSpacesBytes =
+            findSemanticValueForTstr(deviceSigned, "nameSpaces");
+    ASSERT_NE(deviceNameSpacesBytes, nullptr);
+    ASSERT_EQ(deviceNameSpacesBytes->value(), 24);
+    const cppbor::Bstr* deviceNameSpacesBstr = deviceNameSpacesBytes->child()->asBstr();
+    ASSERT_NE(deviceNameSpacesBstr, nullptr);
+    vector<uint8_t> deviceNameSpacesEncoded = deviceNameSpacesBstr->value();
+
+    // (For this version of 18013-5, DeviceNameSpaces is always supposed to be empty, check that.)
+    EXPECT_EQ(deviceNameSpacesEncoded, cppbor::Map().encode());
+
+    const cppbor::Map* deviceAuth = findMapValueForTstr(deviceSigned, "deviceAuth");
+    ASSERT_NE(deviceAuth, nullptr);
+    // deviceMac is is the COSE_Mac0.. dig out the encoded form to check that
+    // support::calcMac() gives exactly the same bytes.
+    //
+    const cppbor::Array* deviceMac = findArrayValueForTstr(deviceAuth, "deviceMac");
+    ASSERT_NE(deviceMac, nullptr);
+    vector<uint8_t> deviceMacEncoded = deviceMac->encode();
+
+    // Now we calculate what it should be..
+    optional<vector<uint8_t>> calculatedMac =
+            support::calcMac(sessionTranscriptEncoded,  // SessionTranscript
+                             docType,                   // DocType
+                             deviceNameSpacesEncoded,   // DeviceNamespaces
+                             eMacKey.value());          // EMacKey
+    ASSERT_TRUE(calculatedMac);
+
+    // ... and hopefully it's the same!
+    ASSERT_EQ(calculatedMac.value().size(), deviceMacEncoded.size());
+    EXPECT_TRUE(memcmp(calculatedMac.value().data(), deviceMacEncoded.data(),
+                       deviceMacEncoded.size()) == 0);
+}
+
 }  // namespace identity
 }  // namespace hardware
 }  // namespace android
diff --git a/keymaster/3.0/IKeymasterDevice.hal b/keymaster/3.0/IKeymasterDevice.hal
index 2664765..9bd8602 100644
--- a/keymaster/3.0/IKeymasterDevice.hal
+++ b/keymaster/3.0/IKeymasterDevice.hal
@@ -20,6 +20,7 @@
  * Keymaster device definition.  For thorough documentation see the implementer's reference, at
  * https://source.android.com/security/keystore/implementer-ref.html
  */
+@SensitiveData
 interface IKeymasterDevice {
 
     /**
diff --git a/keymaster/3.0/default/KeymasterDevice.cpp b/keymaster/3.0/default/KeymasterDevice.cpp
index 7d3e6f2..8b416c3 100644
--- a/keymaster/3.0/default/KeymasterDevice.cpp
+++ b/keymaster/3.0/default/KeymasterDevice.cpp
@@ -22,7 +22,6 @@
 #include <log/log.h>
 
 #include <AndroidKeymaster3Device.h>
-#include <hardware/keymaster0.h>
 #include <hardware/keymaster1.h>
 #include <hardware/keymaster2.h>
 #include <hardware/keymaster_defs.h>
@@ -33,16 +32,6 @@
 namespace V3_0 {
 namespace implementation {
 
-static int get_keymaster0_dev(keymaster0_device_t** dev, const hw_module_t* mod) {
-    int rc = keymaster0_open(mod, dev);
-    if (rc) {
-        ALOGE("Error opening keystore keymaster0 device.");
-        *dev = nullptr;
-        return rc;
-    }
-    return 0;
-}
-
 static int get_keymaster1_dev(keymaster1_device_t** dev, const hw_module_t* mod) {
     int rc = keymaster1_open(mod, dev);
     if (rc) {
@@ -75,11 +64,7 @@
     }
 
     if (mod->module_api_version < KEYMASTER_MODULE_API_VERSION_1_0) {
-        keymaster0_device_t* dev = nullptr;
-        if (get_keymaster0_dev(&dev, mod)) {
-            return nullptr;
-        }
-        return ::keymaster::ng::CreateKeymasterDevice(dev);
+        return nullptr;
     } else if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) {
         keymaster1_device_t* dev = nullptr;
         if (get_keymaster1_dev(&dev, mod)) {
diff --git a/keymaster/4.0/IKeymasterDevice.hal b/keymaster/4.0/IKeymasterDevice.hal
index 3475f79..dfde060 100644
--- a/keymaster/4.0/IKeymasterDevice.hal
+++ b/keymaster/4.0/IKeymasterDevice.hal
@@ -195,7 +195,7 @@
  * Tag::VENDOR_PATCHLEVEL, and Tag::BOOT_PATCHLEVEL must be cryptographically bound to every
  * IKeymasterDevice key, as described in the Key Access Control section above.
  */
-
+@SensitiveData
 interface IKeymasterDevice {
 
     /**
diff --git a/keymaster/4.1/IKeymasterDevice.hal b/keymaster/4.1/IKeymasterDevice.hal
index bbeccaa..ccb9f2e 100644
--- a/keymaster/4.1/IKeymasterDevice.hal
+++ b/keymaster/4.1/IKeymasterDevice.hal
@@ -37,6 +37,7 @@
  * versions will be numbered as major_version * 10 + minor version.  The addition of new attestable
  * tags changes the attestation format again, slightly, so the attestationVersion must be 4.
  */
+@SensitiveData
 interface IKeymasterDevice extends @4.0::IKeymasterDevice {
     /**
      * Called by client to notify the IKeymasterDevice that the device is now locked, and keys with
diff --git a/keymaster/4.1/support/attestation_record.cpp b/keymaster/4.1/support/attestation_record.cpp
index 598b6b5..207a7e8 100644
--- a/keymaster/4.1/support/attestation_record.cpp
+++ b/keymaster/4.1/support/attestation_record.cpp
@@ -102,6 +102,7 @@
     ASN1_INTEGER* boot_patchlevel;
     ASN1_NULL* early_boot_only;
     ASN1_NULL* device_unique_attestation;
+    ASN1_NULL* identity_credential_key;
 } KM_AUTH_LIST;
 
 ASN1_SEQUENCE(KM_AUTH_LIST) = {
@@ -145,6 +146,8 @@
         ASN1_EXP_OPT(KM_AUTH_LIST, early_boot_only, ASN1_NULL, TAG_EARLY_BOOT_ONLY.maskedTag()),
         ASN1_EXP_OPT(KM_AUTH_LIST, device_unique_attestation, ASN1_NULL,
                      TAG_DEVICE_UNIQUE_ATTESTATION.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, identity_credential_key, ASN1_NULL,
+                     TAG_IDENTITY_CREDENTIAL_KEY.maskedTag()),
 } ASN1_SEQUENCE_END(KM_AUTH_LIST);
 IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST);
 
@@ -285,6 +288,7 @@
     copyAuthTag(record->unlocked_device_required, TAG_UNLOCKED_DEVICE_REQUIRED, auth_list);
     copyAuthTag(record->early_boot_only, TAG_EARLY_BOOT_ONLY, auth_list);
     copyAuthTag(record->device_unique_attestation, TAG_DEVICE_UNIQUE_ATTESTATION, auth_list);
+    copyAuthTag(record->identity_credential_key, TAG_IDENTITY_CREDENTIAL_KEY, auth_list);
 
     return ErrorCode::OK;
 }
@@ -327,7 +331,10 @@
 
     p = attest_rec->data;
     KM_KEY_DESCRIPTION_Ptr record(d2i_KM_KEY_DESCRIPTION(nullptr, &p, attest_rec->length));
-    if (!record.get()) return {ErrorCode::UNKNOWN_ERROR, {}};
+    if (!record.get()) {
+        LOG(ERROR) << "Unable to get key description";
+        return {ErrorCode::UNKNOWN_ERROR, {}};
+    }
 
     AttestationRecord result;
 
@@ -352,10 +359,12 @@
     if (error != ErrorCode::OK) return {error, {}};
 
     KM_ROOT_OF_TRUST* root_of_trust = nullptr;
+    SecurityLevel root_of_trust_security_level = SecurityLevel::TRUSTED_ENVIRONMENT;
     if (record->tee_enforced && record->tee_enforced->root_of_trust) {
         root_of_trust = record->tee_enforced->root_of_trust;
     } else if (record->software_enforced && record->software_enforced->root_of_trust) {
         root_of_trust = record->software_enforced->root_of_trust;
+        root_of_trust_security_level = SecurityLevel::SOFTWARE;
     } else {
         LOG(ERROR) << AT << " Failed root of trust parsing";
         return {ErrorCode::INVALID_ARGUMENT, {}};
@@ -373,6 +382,7 @@
     rot.verified_boot_state = static_cast<keymaster_verified_boot_t>(
             ASN1_ENUMERATED_get(root_of_trust->verified_boot_state));
     rot.device_locked = root_of_trust->device_locked;
+    rot.security_level = root_of_trust_security_level;
 
     auto& vb_hash = root_of_trust->verified_boot_hash;
     if (!vb_hash) {
diff --git a/keymaster/aidl/Android.bp b/keymaster/aidl/Android.bp
index 5206721..56a3ca9 100644
--- a/keymaster/aidl/Android.bp
+++ b/keymaster/aidl/Android.bp
@@ -20,12 +20,3 @@
         "2",
     ],
 }
-
-// This is a reminder that the next version of keymaster should be frozen at
-// version "5" to avoid confusion with other versions of this interface.
-cc_library {
-    name: "android.hardware.keymaster-V3-java",
-}
-cc_library {
-    name: "android.hardware.keymaster-V4-java",
-}
diff --git a/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl b/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl
index f129783..00578a4 100644
--- a/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
index 4b2f108..19ea944 100644
--- a/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
@@ -16,6 +16,14 @@
 
 package android.hardware.keymaster;
 
+
+/**
+ * Time in milliseconds since some arbitrary point in time.  Time must be monotonically increasing,
+ * and a secure environment's notion of "current time" must not repeat until the Android device
+ * reboots, or until at least 50 million years have elapsed (note that this requirement is satisfied
+ * by setting the clock to zero during each boot, and then counting time accurately).
+ */
+
 @VintfStability
 parcelable Timestamp {
     long milliSeconds;
diff --git a/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
index eff9ca6..f053254 100644
--- a/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@
 
 import android.hardware.keymaster.SecurityLevel;
 import android.hardware.keymaster.Timestamp;
-import android.hardware.keymaster.HardwareAuthenticatorType;
 
 /**
  * VerificationToken instances are used for secure environments to authenticate one another.
@@ -40,6 +39,7 @@
      */
     Timestamp timestamp;
 
+
     /**
      * SecurityLevel of the secure environment that generated the token.
      */
diff --git a/neuralnetworks/1.0/utils/Android.bp b/neuralnetworks/1.0/utils/Android.bp
index 57a052f..4d61fc0 100644
--- a/neuralnetworks/1.0/utils/Android.bp
+++ b/neuralnetworks/1.0/utils/Android.bp
@@ -20,6 +20,7 @@
     srcs: ["src/*"],
     local_include_dirs: ["include/nnapi/hal/1.0/"],
     export_include_dirs: ["include"],
+    cflags: ["-Wthread-safety"],
     static_libs: [
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
new file mode 100644
index 0000000..65b75e5
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_CALLBACKS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_CALLBACKS_H
+
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+class PreparedModelCallback final : public IPreparedModelCallback,
+                                    public hal::utils::IProtectedCallback {
+  public:
+    using Data = nn::GeneralResult<nn::SharedPreparedModel>;
+
+    Return<void> notify(ErrorStatus status, const sp<IPreparedModel>& preparedModel) override;
+
+    void notifyAsDeadObject() override;
+
+    Data get();
+
+  private:
+    void notifyInternal(Data result);
+
+    hal::utils::TransferValue<Data> mData;
+};
+
+class ExecutionCallback final : public IExecutionCallback, public hal::utils::IProtectedCallback {
+  public:
+    using Data = nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>;
+
+    Return<void> notify(ErrorStatus status) override;
+
+    void notifyAsDeadObject() override;
+
+    Data get();
+
+  private:
+    void notifyInternal(Data result);
+
+    hal::utils::TransferValue<Data> mData;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_CALLBACKS_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
index 8ad98cb..d3d933b 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
@@ -24,42 +24,59 @@
 
 namespace android::nn {
 
-Result<OperandType> convert(const hal::V1_0::OperandType& operandType);
-Result<OperationType> convert(const hal::V1_0::OperationType& operationType);
-Result<Operand::LifeTime> convert(const hal::V1_0::OperandLifeTime& lifetime);
-Result<DeviceStatus> convert(const hal::V1_0::DeviceStatus& deviceStatus);
-Result<Capabilities::PerformanceInfo> convert(const hal::V1_0::PerformanceInfo& performanceInfo);
-Result<Capabilities> convert(const hal::V1_0::Capabilities& capabilities);
-Result<DataLocation> convert(const hal::V1_0::DataLocation& location);
-Result<Operand> convert(const hal::V1_0::Operand& operand);
-Result<Operation> convert(const hal::V1_0::Operation& operation);
-Result<Model::OperandValues> convert(const hardware::hidl_vec<uint8_t>& operandValues);
-Result<Memory> convert(const hardware::hidl_memory& memory);
-Result<Model> convert(const hal::V1_0::Model& model);
-Result<Request::Argument> convert(const hal::V1_0::RequestArgument& requestArgument);
-Result<Request> convert(const hal::V1_0::Request& request);
-Result<ErrorStatus> convert(const hal::V1_0::ErrorStatus& status);
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_0::OperandType& operandType);
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_0::OperationType& operationType);
+GeneralResult<Operand::LifeTime> unvalidatedConvert(const hal::V1_0::OperandLifeTime& lifetime);
+GeneralResult<DeviceStatus> unvalidatedConvert(const hal::V1_0::DeviceStatus& deviceStatus);
+GeneralResult<Capabilities::PerformanceInfo> unvalidatedConvert(
+        const hal::V1_0::PerformanceInfo& performanceInfo);
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_0::Capabilities& capabilities);
+GeneralResult<DataLocation> unvalidatedConvert(const hal::V1_0::DataLocation& location);
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_0::Operand& operand);
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_0::Operation& operation);
+GeneralResult<Model::OperandValues> unvalidatedConvert(
+        const hardware::hidl_vec<uint8_t>& operandValues);
+GeneralResult<Memory> unvalidatedConvert(const hardware::hidl_memory& memory);
+GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model);
+GeneralResult<Request::Argument> unvalidatedConvert(
+        const hal::V1_0::RequestArgument& requestArgument);
+GeneralResult<Request> unvalidatedConvert(const hal::V1_0::Request& request);
+GeneralResult<ErrorStatus> unvalidatedConvert(const hal::V1_0::ErrorStatus& status);
+
+GeneralResult<DeviceStatus> convert(const hal::V1_0::DeviceStatus& deviceStatus);
+GeneralResult<Capabilities> convert(const hal::V1_0::Capabilities& capabilities);
+GeneralResult<Model> convert(const hal::V1_0::Model& model);
+GeneralResult<Request> convert(const hal::V1_0::Request& request);
+GeneralResult<ErrorStatus> convert(const hal::V1_0::ErrorStatus& status);
 
 }  // namespace android::nn
 
 namespace android::hardware::neuralnetworks::V1_0::utils {
 
-nn::Result<OperandType> convert(const nn::OperandType& operandType);
-nn::Result<OperationType> convert(const nn::OperationType& operationType);
-nn::Result<OperandLifeTime> convert(const nn::Operand::LifeTime& lifetime);
-nn::Result<DeviceStatus> convert(const nn::DeviceStatus& deviceStatus);
-nn::Result<PerformanceInfo> convert(const nn::Capabilities::PerformanceInfo& performanceInfo);
-nn::Result<Capabilities> convert(const nn::Capabilities& capabilities);
-nn::Result<DataLocation> convert(const nn::DataLocation& location);
-nn::Result<Operand> convert(const nn::Operand& operand);
-nn::Result<Operation> convert(const nn::Operation& operation);
-nn::Result<hidl_vec<uint8_t>> convert(const nn::Model::OperandValues& operandValues);
-nn::Result<hidl_memory> convert(const nn::Memory& memory);
-nn::Result<Model> convert(const nn::Model& model);
-nn::Result<RequestArgument> convert(const nn::Request::Argument& requestArgument);
-nn::Result<hidl_memory> convert(const nn::Request::MemoryPool& memoryPool);
-nn::Result<Request> convert(const nn::Request& request);
-nn::Result<ErrorStatus> convert(const nn::ErrorStatus& status);
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType);
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& lifetime);
+nn::GeneralResult<DeviceStatus> unvalidatedConvert(const nn::DeviceStatus& deviceStatus);
+nn::GeneralResult<PerformanceInfo> unvalidatedConvert(
+        const nn::Capabilities::PerformanceInfo& performanceInfo);
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities);
+nn::GeneralResult<DataLocation> unvalidatedConvert(const nn::DataLocation& location);
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand);
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<RequestArgument> unvalidatedConvert(const nn::Request::Argument& requestArgument);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Request::MemoryPool& memoryPool);
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request);
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& status);
+
+nn::GeneralResult<DeviceStatus> convert(const nn::DeviceStatus& deviceStatus);
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Model> convert(const nn::Model& model);
+nn::GeneralResult<Request> convert(const nn::Request& request);
+nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& status);
 
 }  // namespace android::hardware::neuralnetworks::V1_0::utils
 
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
new file mode 100644
index 0000000..ee103ba
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_DEVICE_H
+
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+class Device final : public nn::IDevice {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Device>> create(std::string name,
+                                                                   sp<V1_0::IDevice> device);
+
+    Device(PrivateConstructorTag tag, std::string name, nn::Capabilities capabilities,
+           sp<V1_0::IDevice> device, hal::utils::DeathHandler deathHandler);
+
+    const std::string& getName() const override;
+    const std::string& getVersionString() const override;
+    nn::Version getFeatureLevel() const override;
+    nn::DeviceType getType() const override;
+    const std::vector<nn::Extension>& getSupportedExtensions() const override;
+    const nn::Capabilities& getCapabilities() const override;
+    std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
+
+    nn::GeneralResult<void> wait() const override;
+
+    nn::GeneralResult<std::vector<bool>> getSupportedOperations(
+            const nn::Model& model) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModel(
+            const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedBuffer> allocate(
+            const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+            const std::vector<nn::BufferRole>& inputRoles,
+            const std::vector<nn::BufferRole>& outputRoles) const override;
+
+  private:
+    const std::string kName;
+    const std::string kVersionString = "UNKNOWN";
+    const std::vector<nn::Extension> kExtensions;
+    const nn::Capabilities kCapabilities;
+    const sp<V1_0::IDevice> kDevice;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_DEVICE_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
new file mode 100644
index 0000000..31f366d
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_PREPARED_MODEL_H
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+class PreparedModel final : public nn::IPreparedModel {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const PreparedModel>> create(
+            sp<V1_0::IPreparedModel> preparedModel);
+
+    PreparedModel(PrivateConstructorTag tag, sp<V1_0::IPreparedModel> preparedModel,
+                  hal::utils::DeathHandler deathHandler);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalTimeoutDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> executeFenced(
+            const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
+            nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalTimeoutDuration& loopTimeoutDuration,
+            const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const override;
+
+    std::any getUnderlyingResource() const override;
+
+  private:
+    const sp<V1_0::IPreparedModel> kPreparedModel;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Service.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Service.h
new file mode 100644
index 0000000..11fbb9e
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Service.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_SERVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_SERVICE_H
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name);
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_SERVICE_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
index ec8da06..4cec545 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
@@ -23,18 +23,14 @@
 #include <android/hardware/neuralnetworks/1.0/types.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
-#include <nnapi/Validation.h>
 
 namespace android::hardware::neuralnetworks::V1_0::utils {
 
-constexpr auto kVersion = nn::Version::ANDROID_OC_MR1;
-
 template <typename Type>
 nn::Result<void> validate(const Type& halObject) {
-    const auto canonical = NN_TRY(nn::convert(halObject));
-    const auto version = NN_TRY(nn::validate(canonical));
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "";
+    const auto maybeCanonical = nn::convert(halObject);
+    if (!maybeCanonical.has_value()) {
+        return nn::error() << maybeCanonical.error().message;
     }
     return {};
 }
@@ -48,16 +44,6 @@
     return result.has_value();
 }
 
-template <typename Type>
-decltype(nn::convert(std::declval<Type>())) validatedConvertToCanonical(const Type& halObject) {
-    auto canonical = NN_TRY(nn::convert(halObject));
-    const auto version = NN_TRY(nn::validate(canonical));
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "";
-    }
-    return canonical;
-}
-
 }  // namespace android::hardware::neuralnetworks::V1_0::utils
 
 #endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_H
diff --git a/neuralnetworks/1.0/utils/src/Callbacks.cpp b/neuralnetworks/1.0/utils/src/Callbacks.cpp
new file mode 100644
index 0000000..b1259c3
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/Callbacks.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Callbacks.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+#include <utility>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+namespace {
+
+nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
+        const sp<IPreparedModel>& preparedModel) {
+    return NN_TRY(utils::PreparedModel::create(preparedModel));
+}
+
+}  // namespace
+
+Return<void> PreparedModelCallback::notify(ErrorStatus status,
+                                           const sp<IPreparedModel>& preparedModel) {
+    if (status != ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
+    } else if (preparedModel == nullptr) {
+        notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                       << "Returned preparedModel is nullptr");
+    } else {
+        notifyInternal(convertPreparedModel(preparedModel));
+    }
+    return Void();
+}
+
+void PreparedModelCallback::notifyAsDeadObject() {
+    notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+PreparedModelCallback::Data PreparedModelCallback::get() {
+    return mData.take();
+}
+
+void PreparedModelCallback::notifyInternal(PreparedModelCallback::Data result) {
+    mData.put(std::move(result));
+}
+
+// ExecutionCallback methods begin here
+
+Return<void> ExecutionCallback::notify(ErrorStatus status) {
+    if (status != ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
+    } else {
+        notifyInternal({});
+    }
+    return Void();
+}
+
+void ExecutionCallback::notifyAsDeadObject() {
+    notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+ExecutionCallback::Data ExecutionCallback::get() {
+    return mData.take();
+}
+
+void ExecutionCallback::notifyInternal(ExecutionCallback::Data result) {
+    mData.put(std::move(result));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Conversions.cpp b/neuralnetworks/1.0/utils/src/Conversions.cpp
index 4a58f3b..fde7346 100644
--- a/neuralnetworks/1.0/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.0/utils/src/Conversions.cpp
@@ -22,7 +22,9 @@
 #include <nnapi/OperationTypes.h>
 #include <nnapi/Result.h>
 #include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
+#include <nnapi/Validation.h>
 #include <nnapi/hal/CommonUtils.h>
 
 #include <algorithm>
@@ -40,6 +42,8 @@
     return static_cast<std::underlying_type_t<Type>>(value);
 }
 
+constexpr auto kVersion = android::nn::Version::ANDROID_OC_MR1;
+
 }  // namespace
 
 namespace android::nn {
@@ -49,46 +53,64 @@
 using hardware::hidl_vec;
 
 template <typename Input>
-using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-Result<std::vector<ConvertOutput<Type>>> convert(const hidl_vec<Type>& arguments) {
-    std::vector<ConvertOutput<Type>> canonical;
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<unvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
-        canonical.push_back(NN_TRY(nn::convert(argument)));
+        canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
+    }
+    return canonical;
+}
+
+template <typename Type>
+decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+    auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
+    const auto maybeVersion = validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
     return canonical;
 }
 
 }  // anonymous namespace
 
-Result<OperandType> convert(const hal::V1_0::OperandType& operandType) {
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_0::OperandType& operandType) {
     return static_cast<OperandType>(operandType);
 }
 
-Result<OperationType> convert(const hal::V1_0::OperationType& operationType) {
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_0::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-Result<Operand::LifeTime> convert(const hal::V1_0::OperandLifeTime& lifetime) {
+GeneralResult<Operand::LifeTime> unvalidatedConvert(const hal::V1_0::OperandLifeTime& lifetime) {
     return static_cast<Operand::LifeTime>(lifetime);
 }
 
-Result<DeviceStatus> convert(const hal::V1_0::DeviceStatus& deviceStatus) {
+GeneralResult<DeviceStatus> unvalidatedConvert(const hal::V1_0::DeviceStatus& deviceStatus) {
     return static_cast<DeviceStatus>(deviceStatus);
 }
 
-Result<Capabilities::PerformanceInfo> convert(const hal::V1_0::PerformanceInfo& performanceInfo) {
+GeneralResult<Capabilities::PerformanceInfo> unvalidatedConvert(
+        const hal::V1_0::PerformanceInfo& performanceInfo) {
     return Capabilities::PerformanceInfo{
             .execTime = performanceInfo.execTime,
             .powerUsage = performanceInfo.powerUsage,
     };
 }
 
-Result<Capabilities> convert(const hal::V1_0::Capabilities& capabilities) {
-    const auto quantized8Performance = NN_TRY(convert(capabilities.quantized8Performance));
-    const auto float32Performance = NN_TRY(convert(capabilities.float32Performance));
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_0::Capabilities& capabilities) {
+    const auto quantized8Performance =
+            NN_TRY(unvalidatedConvert(capabilities.quantized8Performance));
+    const auto float32Performance = NN_TRY(unvalidatedConvert(capabilities.float32Performance));
 
     auto table = hal::utils::makeQuantized8PerformanceConsistentWithP(float32Performance,
                                                                       quantized8Performance);
@@ -100,7 +122,7 @@
     };
 }
 
-Result<DataLocation> convert(const hal::V1_0::DataLocation& location) {
+GeneralResult<DataLocation> unvalidatedConvert(const hal::V1_0::DataLocation& location) {
     return DataLocation{
             .poolIndex = location.poolIndex,
             .offset = location.offset,
@@ -108,35 +130,35 @@
     };
 }
 
-Result<Operand> convert(const hal::V1_0::Operand& operand) {
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_0::Operand& operand) {
     return Operand{
-            .type = NN_TRY(convert(operand.type)),
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
             .dimensions = operand.dimensions,
             .scale = operand.scale,
             .zeroPoint = operand.zeroPoint,
-            .lifetime = NN_TRY(convert(operand.lifetime)),
-            .location = NN_TRY(convert(operand.location)),
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
     };
 }
 
-Result<Operation> convert(const hal::V1_0::Operation& operation) {
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_0::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-Result<Model::OperandValues> convert(const hidl_vec<uint8_t>& operandValues) {
+GeneralResult<Model::OperandValues> unvalidatedConvert(const hidl_vec<uint8_t>& operandValues) {
     return Model::OperandValues(operandValues.data(), operandValues.size());
 }
 
-Result<Memory> convert(const hidl_memory& memory) {
+GeneralResult<Memory> unvalidatedConvert(const hidl_memory& memory) {
     return createSharedMemoryFromHidlMemory(memory);
 }
 
-Result<Model> convert(const hal::V1_0::Model& model) {
-    auto operations = NN_TRY(convert(model.operations));
+GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model) {
+    auto operations = NN_TRY(unvalidatedConvert(model.operations));
 
     // Verify number of consumers.
     const auto numberOfConsumers =
@@ -144,14 +166,14 @@
     CHECK(model.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < model.operands.size(); ++i) {
         if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
-            return NN_ERROR() << "Invalid numberOfConsumers for operand " << i << ", expected "
-                              << numberOfConsumers[i] << " but found "
-                              << model.operands[i].numberOfConsumers;
+            return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
+                   << "Invalid numberOfConsumers for operand " << i << ", expected "
+                   << numberOfConsumers[i] << " but found " << model.operands[i].numberOfConsumers;
         }
     }
 
     auto main = Model::Subgraph{
-            .operands = NN_TRY(convert(model.operands)),
+            .operands = NN_TRY(unvalidatedConvert(model.operands)),
             .operations = std::move(operations),
             .inputIndexes = model.inputIndexes,
             .outputIndexes = model.outputIndexes,
@@ -159,35 +181,35 @@
 
     return Model{
             .main = std::move(main),
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
     };
 }
 
-Result<Request::Argument> convert(const hal::V1_0::RequestArgument& argument) {
+GeneralResult<Request::Argument> unvalidatedConvert(const hal::V1_0::RequestArgument& argument) {
     const auto lifetime = argument.hasNoValue ? Request::Argument::LifeTime::NO_VALUE
                                               : Request::Argument::LifeTime::POOL;
     return Request::Argument{
             .lifetime = lifetime,
-            .location = NN_TRY(convert(argument.location)),
+            .location = NN_TRY(unvalidatedConvert(argument.location)),
             .dimensions = argument.dimensions,
     };
 }
 
-Result<Request> convert(const hal::V1_0::Request& request) {
-    auto memories = NN_TRY(convert(request.pools));
+GeneralResult<Request> unvalidatedConvert(const hal::V1_0::Request& request) {
+    auto memories = NN_TRY(unvalidatedConvert(request.pools));
     std::vector<Request::MemoryPool> pools;
     pools.reserve(memories.size());
     std::move(memories.begin(), memories.end(), std::back_inserter(pools));
 
     return Request{
-            .inputs = NN_TRY(convert(request.inputs)),
-            .outputs = NN_TRY(convert(request.outputs)),
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
             .pools = std::move(pools),
     };
 }
 
-Result<ErrorStatus> convert(const hal::V1_0::ErrorStatus& status) {
+GeneralResult<ErrorStatus> unvalidatedConvert(const hal::V1_0::ErrorStatus& status) {
     switch (status) {
         case hal::V1_0::ErrorStatus::NONE:
         case hal::V1_0::ErrorStatus::DEVICE_UNAVAILABLE:
@@ -196,7 +218,28 @@
         case hal::V1_0::ErrorStatus::INVALID_ARGUMENT:
             return static_cast<ErrorStatus>(status);
     }
-    return NN_ERROR() << "Invalid ErrorStatus " << underlyingType(status);
+    return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
+           << "Invalid ErrorStatus " << underlyingType(status);
+}
+
+GeneralResult<DeviceStatus> convert(const hal::V1_0::DeviceStatus& deviceStatus) {
+    return validatedConvert(deviceStatus);
+}
+
+GeneralResult<Capabilities> convert(const hal::V1_0::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+GeneralResult<Model> convert(const hal::V1_0::Model& model) {
+    return validatedConvert(model);
+}
+
+GeneralResult<Request> convert(const hal::V1_0::Request& request) {
+    return validatedConvert(request);
+}
+
+GeneralResult<ErrorStatus> convert(const hal::V1_0::ErrorStatus& status) {
+    return validatedConvert(status);
 }
 
 }  // namespace android::nn
@@ -205,55 +248,72 @@
 namespace {
 
 template <typename Input>
-using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-nn::Result<hidl_vec<ConvertOutput<Type>>> convert(const std::vector<Type>& arguments) {
-    hidl_vec<ConvertOutput<Type>> halObject(arguments.size());
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
     for (size_t i = 0; i < arguments.size(); ++i) {
-        halObject[i] = NN_TRY(utils::convert(arguments[i]));
+        halObject[i] = NN_TRY(utils::unvalidatedConvert(arguments[i]));
     }
     return halObject;
 }
 
+template <typename Type>
+decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
+    const auto maybeVersion = nn::validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return nn::error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return utils::unvalidatedConvert(canonical);
+}
+
 }  // anonymous namespace
 
-nn::Result<OperandType> convert(const nn::OperandType& operandType) {
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
     return static_cast<OperandType>(operandType);
 }
 
-nn::Result<OperationType> convert(const nn::OperationType& operationType) {
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-nn::Result<OperandLifeTime> convert(const nn::Operand::LifeTime& lifetime) {
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& lifetime) {
     if (lifetime == nn::Operand::LifeTime::POINTER) {
-        return NN_ERROR() << "Model cannot be converted because it contains pointer-based memory";
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Model cannot be unvalidatedConverted because it contains pointer-based memory";
     }
     return static_cast<OperandLifeTime>(lifetime);
 }
 
-nn::Result<DeviceStatus> convert(const nn::DeviceStatus& deviceStatus) {
+nn::GeneralResult<DeviceStatus> unvalidatedConvert(const nn::DeviceStatus& deviceStatus) {
     return static_cast<DeviceStatus>(deviceStatus);
 }
 
-nn::Result<PerformanceInfo> convert(const nn::Capabilities::PerformanceInfo& performanceInfo) {
+nn::GeneralResult<PerformanceInfo> unvalidatedConvert(
+        const nn::Capabilities::PerformanceInfo& performanceInfo) {
     return PerformanceInfo{
             .execTime = performanceInfo.execTime,
             .powerUsage = performanceInfo.powerUsage,
     };
 }
 
-nn::Result<Capabilities> convert(const nn::Capabilities& capabilities) {
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities) {
     return Capabilities{
-            .float32Performance = NN_TRY(convert(
+            .float32Performance = NN_TRY(unvalidatedConvert(
                     capabilities.operandPerformance.lookup(nn::OperandType::TENSOR_FLOAT32))),
-            .quantized8Performance = NN_TRY(convert(
+            .quantized8Performance = NN_TRY(unvalidatedConvert(
                     capabilities.operandPerformance.lookup(nn::OperandType::TENSOR_QUANT8_ASYMM))),
     };
 }
 
-nn::Result<DataLocation> convert(const nn::DataLocation& location) {
+nn::GeneralResult<DataLocation> unvalidatedConvert(const nn::DataLocation& location) {
     return DataLocation{
             .poolIndex = location.poolIndex,
             .offset = location.offset,
@@ -261,43 +321,43 @@
     };
 }
 
-nn::Result<Operand> convert(const nn::Operand& operand) {
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand) {
     return Operand{
-            .type = NN_TRY(convert(operand.type)),
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
             .dimensions = operand.dimensions,
             .numberOfConsumers = 0,
             .scale = operand.scale,
             .zeroPoint = operand.zeroPoint,
-            .lifetime = NN_TRY(convert(operand.lifetime)),
-            .location = NN_TRY(convert(operand.location)),
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
     };
 }
 
-nn::Result<Operation> convert(const nn::Operation& operation) {
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-nn::Result<hidl_vec<uint8_t>> convert(const nn::Model::OperandValues& operandValues) {
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues) {
     return hidl_vec<uint8_t>(operandValues.data(), operandValues.data() + operandValues.size());
 }
 
-nn::Result<hidl_memory> convert(const nn::Memory& memory) {
-    const auto hidlMemory = hidl_memory(memory.name, memory.handle->handle(), memory.size);
-    // Copy memory to force the native_handle_t to be copied.
-    auto copiedMemory = hidlMemory;
-    return copiedMemory;
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
+    return hidl_memory(memory.name, NN_TRY(hal::utils::hidlHandleFromSharedHandle(memory.handle)),
+                       memory.size);
 }
 
-nn::Result<Model> convert(const nn::Model& model) {
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
     if (!hal::utils::hasNoPointerData(model)) {
-        return NN_ERROR() << "Mdoel cannot be converted because it contains pointer-based memory";
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Mdoel cannot be unvalidatedConverted because it contains pointer-based memory";
     }
 
-    auto operands = NN_TRY(convert(model.main.operands));
+    auto operands = NN_TRY(unvalidatedConvert(model.main.operands));
 
     // Update number of consumers.
     const auto numberOfConsumers =
@@ -309,43 +369,46 @@
 
     return Model{
             .operands = std::move(operands),
-            .operations = NN_TRY(convert(model.main.operations)),
+            .operations = NN_TRY(unvalidatedConvert(model.main.operations)),
             .inputIndexes = model.main.inputIndexes,
             .outputIndexes = model.main.outputIndexes,
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
     };
 }
 
-nn::Result<RequestArgument> convert(const nn::Request::Argument& requestArgument) {
+nn::GeneralResult<RequestArgument> unvalidatedConvert(
+        const nn::Request::Argument& requestArgument) {
     if (requestArgument.lifetime == nn::Request::Argument::LifeTime::POINTER) {
-        return NN_ERROR() << "Request cannot be converted because it contains pointer-based memory";
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Request cannot be unvalidatedConverted because it contains pointer-based memory";
     }
     const bool hasNoValue = requestArgument.lifetime == nn::Request::Argument::LifeTime::NO_VALUE;
     return RequestArgument{
             .hasNoValue = hasNoValue,
-            .location = NN_TRY(convert(requestArgument.location)),
+            .location = NN_TRY(unvalidatedConvert(requestArgument.location)),
             .dimensions = requestArgument.dimensions,
     };
 }
 
-nn::Result<hidl_memory> convert(const nn::Request::MemoryPool& memoryPool) {
-    return convert(std::get<nn::Memory>(memoryPool));
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Request::MemoryPool& memoryPool) {
+    return unvalidatedConvert(std::get<nn::Memory>(memoryPool));
 }
 
-nn::Result<Request> convert(const nn::Request& request) {
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request) {
     if (!hal::utils::hasNoPointerData(request)) {
-        return NN_ERROR() << "Request cannot be converted because it contains pointer-based memory";
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Request cannot be unvalidatedConverted because it contains pointer-based memory";
     }
 
     return Request{
-            .inputs = NN_TRY(convert(request.inputs)),
-            .outputs = NN_TRY(convert(request.outputs)),
-            .pools = NN_TRY(convert(request.pools)),
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
+            .pools = NN_TRY(unvalidatedConvert(request.pools)),
     };
 }
 
-nn::Result<ErrorStatus> convert(const nn::ErrorStatus& status) {
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& status) {
     switch (status) {
         case nn::ErrorStatus::NONE:
         case nn::ErrorStatus::DEVICE_UNAVAILABLE:
@@ -358,4 +421,24 @@
     }
 }
 
+nn::GeneralResult<DeviceStatus> convert(const nn::DeviceStatus& deviceStatus) {
+    return validatedConvert(deviceStatus);
+}
+
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+    return validatedConvert(model);
+}
+
+nn::GeneralResult<Request> convert(const nn::Request& request) {
+    return validatedConvert(request);
+}
+
+nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& status) {
+    return validatedConvert(status);
+}
+
 }  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Device.cpp b/neuralnetworks/1.0/utils/src/Device.cpp
new file mode 100644
index 0000000..285c515
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/Device.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Device.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+namespace {
+
+nn::GeneralResult<nn::Capabilities> initCapabilities(V1_0::IDevice* device) {
+    CHECK(device != nullptr);
+
+    nn::GeneralResult<nn::Capabilities> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                                 << "uninitialized";
+    const auto cb = [&result](ErrorStatus status, const Capabilities& capabilities) {
+        if (status != ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical) << "getCapabilities failed with " << toString(status);
+        } else {
+            result = nn::convert(capabilities);
+        }
+    };
+
+    const auto ret = device->getCapabilities(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const Device>> Device::create(std::string name,
+                                                                sp<V1_0::IDevice> device) {
+    if (name.empty()) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_0::utils::Device::create must have non-empty name";
+    }
+    if (device == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_0::utils::Device::create must have non-null device";
+    }
+
+    auto capabilities = NN_TRY(initCapabilities(device.get()));
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
+    return std::make_shared<const Device>(PrivateConstructorTag{}, std::move(name),
+                                          std::move(capabilities), std::move(device),
+                                          std::move(deathHandler));
+}
+
+Device::Device(PrivateConstructorTag /*tag*/, std::string name, nn::Capabilities capabilities,
+               sp<V1_0::IDevice> device, hal::utils::DeathHandler deathHandler)
+    : kName(std::move(name)),
+      kCapabilities(std::move(capabilities)),
+      kDevice(std::move(device)),
+      kDeathHandler(std::move(deathHandler)) {}
+
+const std::string& Device::getName() const {
+    return kName;
+}
+
+const std::string& Device::getVersionString() const {
+    return kVersionString;
+}
+
+nn::Version Device::getFeatureLevel() const {
+    return nn::Version::ANDROID_OC_MR1;
+}
+
+nn::DeviceType Device::getType() const {
+    return nn::DeviceType::OTHER;
+}
+
+const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
+    return kExtensions;
+}
+
+const nn::Capabilities& Device::getCapabilities() const {
+    return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
+    return std::make_pair(/*numModelCache=*/0, /*numDataCache=*/0);
+}
+
+nn::GeneralResult<void> Device::wait() const {
+    const auto ret = kDevice->ping();
+    HANDLE_TRANSPORT_FAILURE(ret);
+    return {};
+}
+
+nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+
+    nn::GeneralResult<std::vector<bool>> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                                  << "uninitialized";
+    auto cb = [&result, &model](ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
+        if (status != ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical)
+                     << "getSupportedOperations failed with " << toString(status);
+        } else if (supportedOperations.size() != model.main.operations.size()) {
+            result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                     << "getSupportedOperations returned vector of size "
+                     << supportedOperations.size() << " but expected "
+                     << model.main.operations.size();
+        } else {
+            result = supportedOperations;
+        }
+    };
+
+    const auto ret = kDevice->getSupportedOperations(hidlModel, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
+        const nn::Model& model, nn::ExecutionPreference /*preference*/, nn::Priority /*priority*/,
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/,
+        const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+
+    const auto cb = sp<PreparedModelCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kDevice->prepareModel(hidlModel, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    if (status != ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        return NN_ERROR(canonical) << "prepareModel failed with " << toString(status);
+    }
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache(
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/,
+        const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IDevice::prepareModelFromCache not supported on 1.0 HAL service";
+}
+
+nn::GeneralResult<nn::SharedBuffer> Device::allocate(
+        const nn::BufferDesc& /*desc*/,
+        const std::vector<nn::SharedPreparedModel>& /*preparedModels*/,
+        const std::vector<nn::BufferRole>& /*inputRoles*/,
+        const std::vector<nn::BufferRole>& /*outputRoles*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IDevice::allocate not supported on 1.0 HAL service";
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/PreparedModel.cpp b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
new file mode 100644
index 0000000..46dd3f8
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PreparedModel.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
+        sp<V1_0::IPreparedModel> preparedModel) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_0::utils::PreparedModel::create must have non-null preparedModel";
+    }
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(preparedModel));
+    return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, std::move(preparedModel),
+                                                 std::move(deathHandler));
+}
+
+PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, sp<V1_0::IPreparedModel> preparedModel,
+                             hal::utils::DeathHandler deathHandler)
+    : kPreparedModel(std::move(preparedModel)), kDeathHandler(std::move(deathHandler)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute(
+        const nn::Request& request, nn::MeasureTiming /*measure*/,
+        const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalTimeoutDuration& /*loopTimeoutDuration*/) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
+            hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
+
+    const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
+
+    const auto cb = sp<ExecutionCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kPreparedModel->execute(hidlRequest, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    if (status != ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        return NN_ERROR(canonical) << "execute failed with " << toString(status);
+    }
+
+    auto result = NN_TRY(cb->get());
+    NN_TRY(hal::utils::makeExecutionFailure(
+            hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
+
+    return result;
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFenced(
+        const nn::Request& /*request*/, const std::vector<nn::SyncFence>& /*waitFor*/,
+        nn::MeasureTiming /*measure*/, const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalTimeoutDuration& /*loopTimeoutDuration*/,
+        const nn::OptionalTimeoutDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IPreparedModel::executeFenced is not supported on 1.0 HAL service";
+}
+
+std::any PreparedModel::getUnderlyingResource() const {
+    sp<V1_0::IPreparedModel> resource = kPreparedModel;
+    return resource;
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Service.cpp b/neuralnetworks/1.0/utils/src/Service.cpp
new file mode 100644
index 0000000..ec28b1d
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/Service.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Service.h"
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientDevice.h>
+#include <string>
+#include "Device.h"
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name) {
+    hal::utils::ResilientDevice::Factory makeDevice =
+            [name](bool blocking) -> nn::GeneralResult<nn::SharedDevice> {
+        auto service = blocking ? IDevice::getService(name) : IDevice::tryGetService(name);
+        if (service == nullptr) {
+            return NN_ERROR() << (blocking ? "getService" : "tryGetService") << " returned nullptr";
+        }
+        return Device::create(name, std::move(service));
+    };
+
+    return hal::utils::ResilientDevice::create(std::move(makeDevice));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.1/utils/Android.bp b/neuralnetworks/1.1/utils/Android.bp
index 85a32c5..909575b 100644
--- a/neuralnetworks/1.1/utils/Android.bp
+++ b/neuralnetworks/1.1/utils/Android.bp
@@ -20,6 +20,7 @@
     srcs: ["src/*"],
     local_include_dirs: ["include/nnapi/hal/1.1/"],
     export_include_dirs: ["include"],
+    cflags: ["-Wthread-safety"],
     static_libs: [
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h
index d0c5397..f646462 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h
@@ -24,21 +24,32 @@
 
 namespace android::nn {
 
-Result<OperationType> convert(const hal::V1_1::OperationType& operationType);
-Result<Capabilities> convert(const hal::V1_1::Capabilities& capabilities);
-Result<Operation> convert(const hal::V1_1::Operation& operation);
-Result<Model> convert(const hal::V1_1::Model& model);
-Result<ExecutionPreference> convert(const hal::V1_1::ExecutionPreference& executionPreference);
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_1::OperationType& operationType);
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_1::Capabilities& capabilities);
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_1::Operation& operation);
+GeneralResult<Model> unvalidatedConvert(const hal::V1_1::Model& model);
+GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const hal::V1_1::ExecutionPreference& executionPreference);
+
+GeneralResult<Capabilities> convert(const hal::V1_1::Capabilities& capabilities);
+GeneralResult<Model> convert(const hal::V1_1::Model& model);
+GeneralResult<ExecutionPreference> convert(
+        const hal::V1_1::ExecutionPreference& executionPreference);
 
 }  // namespace android::nn
 
 namespace android::hardware::neuralnetworks::V1_1::utils {
 
-nn::Result<OperationType> convert(const nn::OperationType& operationType);
-nn::Result<Capabilities> convert(const nn::Capabilities& capabilities);
-nn::Result<Operation> convert(const nn::Operation& operation);
-nn::Result<Model> convert(const nn::Model& model);
-nn::Result<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference);
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const nn::ExecutionPreference& executionPreference);
+
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Model> convert(const nn::Model& model);
+nn::GeneralResult<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference);
 
 }  // namespace android::hardware::neuralnetworks::V1_1::utils
 
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
new file mode 100644
index 0000000..c1e95fe1a
--- /dev/null
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_DEVICE_H
+
+#include <android/hardware/neuralnetworks/1.1/IDevice.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_1::utils {
+
+class Device final : public nn::IDevice {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Device>> create(std::string name,
+                                                                   sp<V1_1::IDevice> device);
+
+    Device(PrivateConstructorTag tag, std::string name, nn::Capabilities capabilities,
+           sp<V1_1::IDevice> device, hal::utils::DeathHandler deathHandler);
+
+    const std::string& getName() const override;
+    const std::string& getVersionString() const override;
+    nn::Version getFeatureLevel() const override;
+    nn::DeviceType getType() const override;
+    const std::vector<nn::Extension>& getSupportedExtensions() const override;
+    const nn::Capabilities& getCapabilities() const override;
+    std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
+
+    nn::GeneralResult<void> wait() const override;
+
+    nn::GeneralResult<std::vector<bool>> getSupportedOperations(
+            const nn::Model& model) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModel(
+            const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedBuffer> allocate(
+            const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+            const std::vector<nn::BufferRole>& inputRoles,
+            const std::vector<nn::BufferRole>& outputRoles) const override;
+
+  private:
+    const std::string kName;
+    const std::string kVersionString = "UNKNOWN";
+    const std::vector<nn::Extension> kExtensions;
+    const nn::Capabilities kCapabilities;
+    const sp<V1_1::IDevice> kDevice;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_1::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_DEVICE_H
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Service.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Service.h
new file mode 100644
index 0000000..a3ad3cf
--- /dev/null
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Service.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_SERVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_SERVICE_H
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_1::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name);
+
+}  // namespace android::hardware::neuralnetworks::V1_1::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_SERVICE_H
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
index 6f9aa60..052d88e 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
@@ -23,20 +23,17 @@
 #include <android/hardware/neuralnetworks/1.1/types.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
-#include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
 
 namespace android::hardware::neuralnetworks::V1_1::utils {
 
 constexpr auto kDefaultExecutionPreference = ExecutionPreference::FAST_SINGLE_ANSWER;
-constexpr auto kVersion = nn::Version::ANDROID_P;
 
 template <typename Type>
 nn::Result<void> validate(const Type& halObject) {
-    const auto canonical = NN_TRY(nn::convert(halObject));
-    const auto version = NN_TRY(nn::validate(canonical));
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "";
+    const auto maybeCanonical = nn::convert(halObject);
+    if (!maybeCanonical.has_value()) {
+        return nn::error() << maybeCanonical.error().message;
     }
     return {};
 }
@@ -50,16 +47,6 @@
     return result.has_value();
 }
 
-template <typename Type>
-decltype(nn::convert(std::declval<Type>())) validatedConvertToCanonical(const Type& halObject) {
-    auto canonical = NN_TRY(nn::convert(halObject));
-    const auto version = NN_TRY(nn::validate(canonical));
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "";
-    }
-    return canonical;
-}
-
 }  // namespace android::hardware::neuralnetworks::V1_1::utils
 
 #endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_H
diff --git a/neuralnetworks/1.1/utils/src/Conversions.cpp b/neuralnetworks/1.1/utils/src/Conversions.cpp
index 7fee16b..359f68a 100644
--- a/neuralnetworks/1.1/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.1/utils/src/Conversions.cpp
@@ -23,7 +23,9 @@
 #include <nnapi/OperationTypes.h>
 #include <nnapi/Result.h>
 #include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
+#include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
 #include <nnapi/hal/CommonUtils.h>
 
@@ -33,35 +35,58 @@
 #include <type_traits>
 #include <utility>
 
+namespace {
+
+constexpr auto kVersion = android::nn::Version::ANDROID_P;
+
+}  // namespace
+
 namespace android::nn {
 namespace {
 
 using hardware::hidl_vec;
 
 template <typename Input>
-using convertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-Result<std::vector<convertOutput<Type>>> convert(const hidl_vec<Type>& arguments) {
-    std::vector<convertOutput<Type>> canonical;
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<unvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
-        canonical.push_back(NN_TRY(nn::convert(argument)));
+        canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
+    }
+    return canonical;
+}
+
+template <typename Type>
+decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+    auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
+    const auto maybeVersion = validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
     }
     return canonical;
 }
 
 }  // anonymous namespace
 
-Result<OperationType> convert(const hal::V1_1::OperationType& operationType) {
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_1::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-Result<Capabilities> convert(const hal::V1_1::Capabilities& capabilities) {
-    const auto quantized8Performance = NN_TRY(convert(capabilities.quantized8Performance));
-    const auto float32Performance = NN_TRY(convert(capabilities.float32Performance));
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_1::Capabilities& capabilities) {
+    const auto quantized8Performance =
+            NN_TRY(unvalidatedConvert(capabilities.quantized8Performance));
+    const auto float32Performance = NN_TRY(unvalidatedConvert(capabilities.float32Performance));
     const auto relaxedFloat32toFloat16Performance =
-            NN_TRY(convert(capabilities.relaxedFloat32toFloat16Performance));
+            NN_TRY(unvalidatedConvert(capabilities.relaxedFloat32toFloat16Performance));
 
     auto table = hal::utils::makeQuantized8PerformanceConsistentWithP(float32Performance,
                                                                       quantized8Performance);
@@ -73,16 +98,16 @@
     };
 }
 
-Result<Operation> convert(const hal::V1_1::Operation& operation) {
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_1::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-Result<Model> convert(const hal::V1_1::Model& model) {
-    auto operations = NN_TRY(convert(model.operations));
+GeneralResult<Model> unvalidatedConvert(const hal::V1_1::Model& model) {
+    auto operations = NN_TRY(unvalidatedConvert(model.operations));
 
     // Verify number of consumers.
     const auto numberOfConsumers =
@@ -90,14 +115,14 @@
     CHECK(model.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < model.operands.size(); ++i) {
         if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
-            return NN_ERROR() << "Invalid numberOfConsumers for operand " << i << ", expected "
-                              << numberOfConsumers[i] << " but found "
-                              << model.operands[i].numberOfConsumers;
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                   << "Invalid numberOfConsumers for operand " << i << ", expected "
+                   << numberOfConsumers[i] << " but found " << model.operands[i].numberOfConsumers;
         }
     }
 
     auto main = Model::Subgraph{
-            .operands = NN_TRY(convert(model.operands)),
+            .operands = NN_TRY(unvalidatedConvert(model.operands)),
             .operations = std::move(operations),
             .inputIndexes = model.inputIndexes,
             .outputIndexes = model.outputIndexes,
@@ -105,83 +130,114 @@
 
     return Model{
             .main = std::move(main),
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
     };
 }
 
-Result<ExecutionPreference> convert(const hal::V1_1::ExecutionPreference& executionPreference) {
+GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const hal::V1_1::ExecutionPreference& executionPreference) {
     return static_cast<ExecutionPreference>(executionPreference);
 }
 
+GeneralResult<Capabilities> convert(const hal::V1_1::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+GeneralResult<Model> convert(const hal::V1_1::Model& model) {
+    return validatedConvert(model);
+}
+
+GeneralResult<ExecutionPreference> convert(
+        const hal::V1_1::ExecutionPreference& executionPreference) {
+    return validatedConvert(executionPreference);
+}
+
 }  // namespace android::nn
 
 namespace android::hardware::neuralnetworks::V1_1::utils {
 namespace {
 
-using utils::convert;
+using utils::unvalidatedConvert;
 
-nn::Result<V1_0::PerformanceInfo> convert(
+nn::GeneralResult<V1_0::PerformanceInfo> unvalidatedConvert(
         const nn::Capabilities::PerformanceInfo& performanceInfo) {
-    return V1_0::utils::convert(performanceInfo);
+    return V1_0::utils::unvalidatedConvert(performanceInfo);
 }
 
-nn::Result<V1_0::Operand> convert(const nn::Operand& operand) {
-    return V1_0::utils::convert(operand);
+nn::GeneralResult<V1_0::Operand> unvalidatedConvert(const nn::Operand& operand) {
+    return V1_0::utils::unvalidatedConvert(operand);
 }
 
-nn::Result<hidl_vec<uint8_t>> convert(const nn::Model::OperandValues& operandValues) {
-    return V1_0::utils::convert(operandValues);
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues) {
+    return V1_0::utils::unvalidatedConvert(operandValues);
 }
 
-nn::Result<hidl_memory> convert(const nn::Memory& memory) {
-    return V1_0::utils::convert(memory);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
+    return V1_0::utils::unvalidatedConvert(memory);
 }
 
 template <typename Input>
-using convertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-nn::Result<hidl_vec<convertOutput<Type>>> convert(const std::vector<Type>& arguments) {
-    hidl_vec<convertOutput<Type>> halObject(arguments.size());
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
     for (size_t i = 0; i < arguments.size(); ++i) {
-        halObject[i] = NN_TRY(convert(arguments[i]));
+        halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
     }
     return halObject;
 }
 
+template <typename Type>
+decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
+    const auto maybeVersion = nn::validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return nn::error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return utils::unvalidatedConvert(canonical);
+}
+
 }  // anonymous namespace
 
-nn::Result<OperationType> convert(const nn::OperationType& operationType) {
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-nn::Result<Capabilities> convert(const nn::Capabilities& capabilities) {
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities) {
     return Capabilities{
-            .float32Performance = NN_TRY(convert(
+            .float32Performance = NN_TRY(unvalidatedConvert(
                     capabilities.operandPerformance.lookup(nn::OperandType::TENSOR_FLOAT32))),
-            .quantized8Performance = NN_TRY(convert(
+            .quantized8Performance = NN_TRY(unvalidatedConvert(
                     capabilities.operandPerformance.lookup(nn::OperandType::TENSOR_QUANT8_ASYMM))),
-            .relaxedFloat32toFloat16Performance =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
+            .relaxedFloat32toFloat16Performance = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
     };
 }
 
-nn::Result<Operation> convert(const nn::Operation& operation) {
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-nn::Result<Model> convert(const nn::Model& model) {
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
     if (!hal::utils::hasNoPointerData(model)) {
-        return NN_ERROR() << "Mdoel cannot be converted because it contains pointer-based memory";
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Mdoel cannot be unvalidatedConverted because it contains pointer-based memory";
     }
 
-    auto operands = NN_TRY(convert(model.main.operands));
+    auto operands = NN_TRY(unvalidatedConvert(model.main.operands));
 
     // Update number of consumers.
     const auto numberOfConsumers =
@@ -193,17 +249,30 @@
 
     return Model{
             .operands = std::move(operands),
-            .operations = NN_TRY(convert(model.main.operations)),
+            .operations = NN_TRY(unvalidatedConvert(model.main.operations)),
             .inputIndexes = model.main.inputIndexes,
             .outputIndexes = model.main.outputIndexes,
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
     };
 }
 
-nn::Result<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference) {
+nn::GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const nn::ExecutionPreference& executionPreference) {
     return static_cast<ExecutionPreference>(executionPreference);
 }
 
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+    return validatedConvert(model);
+}
+
+nn::GeneralResult<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference) {
+    return validatedConvert(executionPreference);
+}
+
 }  // namespace android::hardware::neuralnetworks::V1_1::utils
diff --git a/neuralnetworks/1.1/utils/src/Device.cpp b/neuralnetworks/1.1/utils/src/Device.cpp
new file mode 100644
index 0000000..f73d3f8
--- /dev/null
+++ b/neuralnetworks/1.1/utils/src/Device.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Device.h"
+
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/IDevice.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_1::utils {
+namespace {
+
+nn::GeneralResult<nn::Capabilities> initCapabilities(V1_1::IDevice* device) {
+    CHECK(device != nullptr);
+
+    nn::GeneralResult<nn::Capabilities> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                                 << "uninitialized";
+    const auto cb = [&result](V1_0::ErrorStatus status, const Capabilities& capabilities) {
+        if (status != V1_0::ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical) << "getCapabilities_1_1 failed with " << toString(status);
+        } else {
+            result = nn::convert(capabilities);
+        }
+    };
+
+    const auto ret = device->getCapabilities_1_1(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const Device>> Device::create(std::string name,
+                                                                sp<V1_1::IDevice> device) {
+    if (name.empty()) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_1::utils::Device::create must have non-empty name";
+    }
+    if (device == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_1::utils::Device::create must have non-null device";
+    }
+
+    auto capabilities = NN_TRY(initCapabilities(device.get()));
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
+    return std::make_shared<const Device>(PrivateConstructorTag{}, std::move(name),
+                                          std::move(capabilities), std::move(device),
+                                          std::move(deathHandler));
+}
+
+Device::Device(PrivateConstructorTag /*tag*/, std::string name, nn::Capabilities capabilities,
+               sp<V1_1::IDevice> device, hal::utils::DeathHandler deathHandler)
+    : kName(std::move(name)),
+      kCapabilities(std::move(capabilities)),
+      kDevice(std::move(device)),
+      kDeathHandler(std::move(deathHandler)) {}
+
+const std::string& Device::getName() const {
+    return kName;
+}
+
+const std::string& Device::getVersionString() const {
+    return kVersionString;
+}
+
+nn::Version Device::getFeatureLevel() const {
+    return nn::Version::ANDROID_P;
+}
+
+nn::DeviceType Device::getType() const {
+    return nn::DeviceType::UNKNOWN;
+}
+
+const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
+    return kExtensions;
+}
+
+const nn::Capabilities& Device::getCapabilities() const {
+    return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
+    return std::make_pair(/*numModelCache=*/0, /*numDataCache=*/0);
+}
+
+nn::GeneralResult<void> Device::wait() const {
+    const auto ret = kDevice->ping();
+    HANDLE_TRANSPORT_FAILURE(ret);
+    return {};
+}
+
+nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+
+    nn::GeneralResult<std::vector<bool>> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                                  << "uninitialized";
+    auto cb = [&result, &model](V1_0::ErrorStatus status,
+                                const hidl_vec<bool>& supportedOperations) {
+        if (status != V1_0::ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical)
+                     << "getSupportedOperations_1_1 failed with " << toString(status);
+        } else if (supportedOperations.size() != model.main.operations.size()) {
+            result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                     << "getSupportedOperations_1_1 returned vector of size "
+                     << supportedOperations.size() << " but expected "
+                     << model.main.operations.size();
+        } else {
+            result = supportedOperations;
+        }
+    };
+
+    const auto ret = kDevice->getSupportedOperations_1_1(hidlModel, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
+        const nn::Model& model, nn::ExecutionPreference preference, nn::Priority /*priority*/,
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/,
+        const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+    const auto hidlPreference = NN_TRY(convert(preference));
+
+    const auto cb = sp<V1_0::utils::PreparedModelCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kDevice->prepareModel_1_1(hidlModel, hidlPreference, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    if (status != V1_0::ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        return NN_ERROR(canonical) << "prepareModel failed with " << toString(status);
+    }
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache(
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/,
+        const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IDevice::prepareModelFromCache not supported on 1.1 HAL service";
+}
+
+nn::GeneralResult<nn::SharedBuffer> Device::allocate(
+        const nn::BufferDesc& /*desc*/,
+        const std::vector<nn::SharedPreparedModel>& /*preparedModels*/,
+        const std::vector<nn::BufferRole>& /*inputRoles*/,
+        const std::vector<nn::BufferRole>& /*outputRoles*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IDevice::allocate not supported on 1.1 HAL service";
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_1::utils
diff --git a/neuralnetworks/1.1/utils/src/Service.cpp b/neuralnetworks/1.1/utils/src/Service.cpp
new file mode 100644
index 0000000..e2d3240
--- /dev/null
+++ b/neuralnetworks/1.1/utils/src/Service.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Service.h"
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientDevice.h>
+#include <string>
+#include "Device.h"
+
+namespace android::hardware::neuralnetworks::V1_1::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name) {
+    hal::utils::ResilientDevice::Factory makeDevice =
+            [name](bool blocking) -> nn::GeneralResult<nn::SharedDevice> {
+        auto service = blocking ? IDevice::getService(name) : IDevice::tryGetService(name);
+        if (service == nullptr) {
+            return NN_ERROR() << (blocking ? "getService" : "tryGetService") << " returned nullptr";
+        }
+        return Device::create(name, std::move(service));
+    };
+
+    return hal::utils::ResilientDevice::create(std::move(makeDevice));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_1::utils
diff --git a/neuralnetworks/1.2/utils/Android.bp b/neuralnetworks/1.2/utils/Android.bp
index a1dd3d0..22e8659 100644
--- a/neuralnetworks/1.2/utils/Android.bp
+++ b/neuralnetworks/1.2/utils/Android.bp
@@ -20,6 +20,7 @@
     srcs: ["src/*"],
     local_include_dirs: ["include/nnapi/hal/1.2/"],
     export_include_dirs: ["include"],
+    cflags: ["-Wthread-safety"],
     static_libs: [
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
new file mode 100644
index 0000000..bc7d92a
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_CALLBACKS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_CALLBACKS_H
+
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+class PreparedModelCallback final : public IPreparedModelCallback,
+                                    public hal::utils::IProtectedCallback {
+  public:
+    using Data = nn::GeneralResult<nn::SharedPreparedModel>;
+
+    Return<void> notify(V1_0::ErrorStatus status,
+                        const sp<V1_0::IPreparedModel>& preparedModel) override;
+    Return<void> notify_1_2(V1_0::ErrorStatus status,
+                            const sp<IPreparedModel>& preparedModel) override;
+
+    void notifyAsDeadObject() override;
+
+    Data get();
+
+  private:
+    void notifyInternal(Data result);
+
+    hal::utils::TransferValue<Data> mData;
+};
+
+class ExecutionCallback final : public IExecutionCallback, public hal::utils::IProtectedCallback {
+  public:
+    using Data = nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>;
+
+    Return<void> notify(V1_0::ErrorStatus status) override;
+    Return<void> notify_1_2(V1_0::ErrorStatus status, const hidl_vec<OutputShape>& outputShapes,
+                            const Timing& timing) override;
+
+    void notifyAsDeadObject() override;
+
+    Data get();
+
+  private:
+    void notifyInternal(Data result);
+
+    hal::utils::TransferValue<Data> mData;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_CALLBACKS_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
index 81bf792..5dcbc0b 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
@@ -24,62 +24,78 @@
 
 namespace android::nn {
 
-Result<OperandType> convert(const hal::V1_2::OperandType& operandType);
-Result<OperationType> convert(const hal::V1_2::OperationType& operationType);
-Result<DeviceType> convert(const hal::V1_2::DeviceType& deviceType);
-Result<Capabilities> convert(const hal::V1_2::Capabilities& capabilities);
-Result<Capabilities::OperandPerformance> convert(
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_2::OperandType& operandType);
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_2::OperationType& operationType);
+GeneralResult<DeviceType> unvalidatedConvert(const hal::V1_2::DeviceType& deviceType);
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_2::Capabilities& capabilities);
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
         const hal::V1_2::Capabilities::OperandPerformance& operandPerformance);
-Result<Operation> convert(const hal::V1_2::Operation& operation);
-Result<Operand::SymmPerChannelQuantParams> convert(
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_2::Operation& operation);
+GeneralResult<Operand::SymmPerChannelQuantParams> unvalidatedConvert(
         const hal::V1_2::SymmPerChannelQuantParams& symmPerChannelQuantParams);
-Result<Operand> convert(const hal::V1_2::Operand& operand);
-Result<Operand::ExtraParams> convert(const hal::V1_2::Operand::ExtraParams& extraParams);
-Result<Model> convert(const hal::V1_2::Model& model);
-Result<Model::ExtensionNameAndPrefix> convert(
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_2::Operand& operand);
+GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const hal::V1_2::Operand::ExtraParams& extraParams);
+GeneralResult<Model> unvalidatedConvert(const hal::V1_2::Model& model);
+GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
         const hal::V1_2::Model::ExtensionNameAndPrefix& extensionNameAndPrefix);
-Result<OutputShape> convert(const hal::V1_2::OutputShape& outputShape);
-Result<MeasureTiming> convert(const hal::V1_2::MeasureTiming& measureTiming);
-Result<Timing> convert(const hal::V1_2::Timing& timing);
-Result<Extension> convert(const hal::V1_2::Extension& extension);
-Result<Extension::OperandTypeInformation> convert(
+GeneralResult<OutputShape> unvalidatedConvert(const hal::V1_2::OutputShape& outputShape);
+GeneralResult<MeasureTiming> unvalidatedConvert(const hal::V1_2::MeasureTiming& measureTiming);
+GeneralResult<Timing> unvalidatedConvert(const hal::V1_2::Timing& timing);
+GeneralResult<Extension> unvalidatedConvert(const hal::V1_2::Extension& extension);
+GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
         const hal::V1_2::Extension::OperandTypeInformation& operandTypeInformation);
-Result<NativeHandle> convert(const hardware::hidl_handle& handle);
+GeneralResult<SharedHandle> unvalidatedConvert(const hardware::hidl_handle& handle);
 
-Result<std::vector<Extension>> convert(const hardware::hidl_vec<hal::V1_2::Extension>& extensions);
-Result<std::vector<NativeHandle>> convert(const hardware::hidl_vec<hardware::hidl_handle>& handles);
-Result<std::vector<OutputShape>> convert(
+GeneralResult<DeviceType> convert(const hal::V1_2::DeviceType& deviceType);
+GeneralResult<Capabilities> convert(const hal::V1_2::Capabilities& capabilities);
+GeneralResult<Model> convert(const hal::V1_2::Model& model);
+GeneralResult<MeasureTiming> convert(const hal::V1_2::MeasureTiming& measureTiming);
+GeneralResult<Timing> convert(const hal::V1_2::Timing& timing);
+
+GeneralResult<std::vector<Extension>> convert(
+        const hardware::hidl_vec<hal::V1_2::Extension>& extensions);
+GeneralResult<std::vector<SharedHandle>> convert(
+        const hardware::hidl_vec<hardware::hidl_handle>& handles);
+GeneralResult<std::vector<OutputShape>> convert(
         const hardware::hidl_vec<hal::V1_2::OutputShape>& outputShapes);
 
 }  // namespace android::nn
 
 namespace android::hardware::neuralnetworks::V1_2::utils {
 
-nn::Result<OperandType> convert(const nn::OperandType& operandType);
-nn::Result<OperationType> convert(const nn::OperationType& operationType);
-nn::Result<DeviceType> convert(const nn::DeviceType& deviceType);
-nn::Result<Capabilities> convert(const nn::Capabilities& capabilities);
-nn::Result<Capabilities::OperandPerformance> convert(
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType);
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<DeviceType> unvalidatedConvert(const nn::DeviceType& deviceType);
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
         const nn::Capabilities::OperandPerformance& operandPerformance);
-nn::Result<Operation> convert(const nn::Operation& operation);
-nn::Result<SymmPerChannelQuantParams> convert(
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<SymmPerChannelQuantParams> unvalidatedConvert(
         const nn::Operand::SymmPerChannelQuantParams& symmPerChannelQuantParams);
-nn::Result<Operand> convert(const nn::Operand& operand);
-nn::Result<Operand::ExtraParams> convert(const nn::Operand::ExtraParams& extraParams);
-nn::Result<Model> convert(const nn::Model& model);
-nn::Result<Model::ExtensionNameAndPrefix> convert(
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand);
+nn::GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const nn::Operand::ExtraParams& extraParams);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
         const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix);
-nn::Result<OutputShape> convert(const nn::OutputShape& outputShape);
-nn::Result<MeasureTiming> convert(const nn::MeasureTiming& measureTiming);
-nn::Result<Timing> convert(const nn::Timing& timing);
-nn::Result<Extension> convert(const nn::Extension& extension);
-nn::Result<Extension::OperandTypeInformation> convert(
+nn::GeneralResult<OutputShape> unvalidatedConvert(const nn::OutputShape& outputShape);
+nn::GeneralResult<MeasureTiming> unvalidatedConvert(const nn::MeasureTiming& measureTiming);
+nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing);
+nn::GeneralResult<Extension> unvalidatedConvert(const nn::Extension& extension);
+nn::GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
         const nn::Extension::OperandTypeInformation& operandTypeInformation);
-nn::Result<hidl_handle> convert(const nn::NativeHandle& handle);
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle);
 
-nn::Result<hidl_vec<Extension>> convert(const std::vector<nn::Extension>& extensions);
-nn::Result<hidl_vec<hidl_handle>> convert(const std::vector<nn::NativeHandle>& handles);
-nn::Result<hidl_vec<OutputShape>> convert(const std::vector<nn::OutputShape>& outputShapes);
+nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType);
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Model> convert(const nn::Model& model);
+nn::GeneralResult<MeasureTiming> convert(const nn::MeasureTiming& measureTiming);
+nn::GeneralResult<Timing> convert(const nn::Timing& timing);
+
+nn::GeneralResult<hidl_vec<Extension>> convert(const std::vector<nn::Extension>& extensions);
+nn::GeneralResult<hidl_vec<hidl_handle>> convert(const std::vector<nn::SharedHandle>& handles);
+nn::GeneralResult<hidl_vec<OutputShape>> convert(const std::vector<nn::OutputShape>& outputShapes);
 
 }  // namespace android::hardware::neuralnetworks::V1_2::utils
 
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
new file mode 100644
index 0000000..a68830d
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_DEVICE_H
+
+#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+nn::GeneralResult<std::string> initVersionString(V1_2::IDevice* device);
+nn::GeneralResult<nn::DeviceType> initDeviceType(V1_2::IDevice* device);
+nn::GeneralResult<std::vector<nn::Extension>> initExtensions(V1_2::IDevice* device);
+nn::GeneralResult<std::pair<uint32_t, uint32_t>> initNumberOfCacheFilesNeeded(
+        V1_2::IDevice* device);
+
+class Device final : public nn::IDevice {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Device>> create(std::string name,
+                                                                   sp<V1_2::IDevice> device);
+
+    Device(PrivateConstructorTag tag, std::string name, std::string versionString,
+           nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
+           nn::Capabilities capabilities, std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
+           sp<V1_2::IDevice> device, hal::utils::DeathHandler deathHandler);
+
+    const std::string& getName() const override;
+    const std::string& getVersionString() const override;
+    nn::Version getFeatureLevel() const override;
+    nn::DeviceType getType() const override;
+    const std::vector<nn::Extension>& getSupportedExtensions() const override;
+    const nn::Capabilities& getCapabilities() const override;
+    std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
+
+    nn::GeneralResult<void> wait() const override;
+
+    nn::GeneralResult<std::vector<bool>> getSupportedOperations(
+            const nn::Model& model) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModel(
+            const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedBuffer> allocate(
+            const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+            const std::vector<nn::BufferRole>& inputRoles,
+            const std::vector<nn::BufferRole>& outputRoles) const override;
+
+  private:
+    const std::string kName;
+    const std::string kVersionString;
+    const nn::DeviceType kDeviceType;
+    const std::vector<nn::Extension> kExtensions;
+    const nn::Capabilities kCapabilities;
+    const std::pair<uint32_t, uint32_t> kNumberOfCacheFilesNeeded;
+    const sp<V1_2::IDevice> kDevice;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_DEVICE_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
new file mode 100644
index 0000000..65e1e8a
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_PREPARED_MODEL_H
+
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+class PreparedModel final : public nn::IPreparedModel {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const PreparedModel>> create(
+            sp<V1_2::IPreparedModel> preparedModel);
+
+    PreparedModel(PrivateConstructorTag tag, sp<V1_2::IPreparedModel> preparedModel,
+                  hal::utils::DeathHandler deathHandler);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalTimeoutDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> executeFenced(
+            const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
+            nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalTimeoutDuration& loopTimeoutDuration,
+            const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const override;
+
+    std::any getUnderlyingResource() const override;
+
+  private:
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeSynchronously(
+            const V1_0::Request& request, MeasureTiming measure) const;
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeAsynchronously(
+            const V1_0::Request& request, MeasureTiming measure) const;
+
+    const sp<V1_2::IPreparedModel> kPreparedModel;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Service.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Service.h
new file mode 100644
index 0000000..44f004f
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Service.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_SERVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_SERVICE_H
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name);
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_SERVICE_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
index b1c2f1a..70149a2 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
@@ -23,7 +23,6 @@
 #include <android/hardware/neuralnetworks/1.2/types.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
-#include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
 #include <nnapi/hal/1.1/Conversions.h>
 
@@ -34,14 +33,12 @@
 constexpr auto kDefaultMesaureTiming = MeasureTiming::NO;
 constexpr auto kNoTiming = Timing{.timeOnDevice = std::numeric_limits<uint64_t>::max(),
                                   .timeInDriver = std::numeric_limits<uint64_t>::max()};
-constexpr auto kVersion = nn::Version::ANDROID_Q;
 
 template <typename Type>
 nn::Result<void> validate(const Type& halObject) {
-    const auto canonical = NN_TRY(nn::convert(halObject));
-    const auto version = NN_TRY(nn::validate(canonical));
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "";
+    const auto maybeCanonical = nn::convert(halObject);
+    if (!maybeCanonical.has_value()) {
+        return nn::error() << maybeCanonical.error().message;
     }
     return {};
 }
@@ -55,16 +52,6 @@
     return result.has_value();
 }
 
-template <typename Type>
-decltype(nn::convert(std::declval<Type>())) validatedConvertToCanonical(const Type& halObject) {
-    auto canonical = NN_TRY(nn::convert(halObject));
-    const auto version = NN_TRY(nn::validate(canonical));
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "";
-    }
-    return canonical;
-}
-
 }  // namespace android::hardware::neuralnetworks::V1_2::utils
 
 #endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_H
diff --git a/neuralnetworks/1.2/utils/src/Callbacks.cpp b/neuralnetworks/1.2/utils/src/Callbacks.cpp
new file mode 100644
index 0000000..39f88c2
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Callbacks.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Callbacks.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/PreparedModel.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+#include <utility>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
+        const sp<V1_0::IPreparedModel>& preparedModel) {
+    return NN_TRY(V1_0::utils::PreparedModel::create(preparedModel));
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
+        const sp<IPreparedModel>& preparedModel) {
+    return NN_TRY(utils::PreparedModel::create(preparedModel));
+}
+
+nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+convertExecutionGeneralResultsHelper(const hidl_vec<OutputShape>& outputShapes,
+                                     const Timing& timing) {
+    return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+convertExecutionGeneralResults(const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
+    return hal::utils::makeExecutionFailure(
+            convertExecutionGeneralResultsHelper(outputShapes, timing));
+}
+
+}  // namespace
+
+Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus status,
+                                           const sp<V1_0::IPreparedModel>& preparedModel) {
+    if (status != V1_0::ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
+    } else if (preparedModel == nullptr) {
+        notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                       << "Returned preparedModel is nullptr");
+    } else {
+        notifyInternal(convertPreparedModel(preparedModel));
+    }
+    return Void();
+}
+
+Return<void> PreparedModelCallback::notify_1_2(V1_0::ErrorStatus status,
+                                               const sp<IPreparedModel>& preparedModel) {
+    if (status != V1_0::ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
+    } else if (preparedModel == nullptr) {
+        notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                       << "Returned preparedModel is nullptr");
+    } else {
+        notifyInternal(convertPreparedModel(preparedModel));
+    }
+    return Void();
+}
+
+void PreparedModelCallback::notifyAsDeadObject() {
+    notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+PreparedModelCallback::Data PreparedModelCallback::get() {
+    return mData.take();
+}
+
+void PreparedModelCallback::notifyInternal(PreparedModelCallback::Data result) {
+    mData.put(std::move(result));
+}
+
+// ExecutionCallback methods begin here
+
+Return<void> ExecutionCallback::notify(V1_0::ErrorStatus status) {
+    if (status != V1_0::ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
+    } else {
+        notifyInternal({});
+    }
+    return Void();
+}
+
+Return<void> ExecutionCallback::notify_1_2(V1_0::ErrorStatus status,
+                                           const hidl_vec<OutputShape>& outputShapes,
+                                           const Timing& timing) {
+    if (status != V1_0::ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
+    } else {
+        notifyInternal(convertExecutionGeneralResults(outputShapes, timing));
+    }
+    return Void();
+}
+
+void ExecutionCallback::notifyAsDeadObject() {
+    notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+ExecutionCallback::Data ExecutionCallback::get() {
+    return mData.take();
+}
+
+void ExecutionCallback::notifyInternal(ExecutionCallback::Data result) {
+    mData.put(std::move(result));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp
index fed314b..f11474f 100644
--- a/neuralnetworks/1.2/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.2/utils/src/Conversions.cpp
@@ -24,8 +24,10 @@
 #include <nnapi/SharedMemory.h>
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
+#include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
 #include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
 
 #include <algorithm>
 #include <functional>
@@ -41,6 +43,8 @@
     return static_cast<std::underlying_type_t<Type>>(value);
 }
 
+constexpr auto kVersion = android::nn::Version::ANDROID_Q;
+
 }  // namespace
 
 namespace android::nn {
@@ -75,57 +79,86 @@
 using hardware::hidl_vec;
 
 template <typename Input>
-using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-Result<std::vector<ConvertOutput<Type>>> convertVec(const hidl_vec<Type>& arguments) {
-    std::vector<ConvertOutput<Type>> canonical;
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+        const hidl_vec<Type>& arguments) {
+    std::vector<unvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
-        canonical.push_back(NN_TRY(nn::convert(argument)));
+        canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
     }
     return canonical;
 }
 
 template <typename Type>
-Result<std::vector<ConvertOutput<Type>>> convert(const hidl_vec<Type>& arguments) {
-    return convertVec(arguments);
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const hidl_vec<Type>& arguments) {
+    return unvalidatedConvertVec(arguments);
+}
+
+template <typename Type>
+decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+    auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
+    const auto maybeVersion = validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return canonical;
+}
+
+template <typename Type>
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> validatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<unvalidatedConvertOutput<Type>> canonical;
+    canonical.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        canonical.push_back(NN_TRY(validatedConvert(argument)));
+    }
+    return canonical;
 }
 
 }  // anonymous namespace
 
-Result<OperandType> convert(const hal::V1_2::OperandType& operandType) {
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_2::OperandType& operandType) {
     return static_cast<OperandType>(operandType);
 }
 
-Result<OperationType> convert(const hal::V1_2::OperationType& operationType) {
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_2::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-Result<DeviceType> convert(const hal::V1_2::DeviceType& deviceType) {
+GeneralResult<DeviceType> unvalidatedConvert(const hal::V1_2::DeviceType& deviceType) {
     return static_cast<DeviceType>(deviceType);
 }
 
-Result<Capabilities> convert(const hal::V1_2::Capabilities& capabilities) {
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_2::Capabilities& capabilities) {
     const bool validOperandTypes = std::all_of(
             capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
             [](const hal::V1_2::Capabilities::OperandPerformance& operandPerformance) {
-                const auto maybeType = convert(operandPerformance.type);
+                const auto maybeType = unvalidatedConvert(operandPerformance.type);
                 return !maybeType.has_value() ? false : validOperandType(maybeType.value());
             });
     if (!validOperandTypes) {
-        return NN_ERROR()
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
                << "Invalid OperandType when converting OperandPerformance in Capabilities";
     }
 
     const auto relaxedFloat32toFloat16PerformanceScalar =
-            NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceScalar));
+            NN_TRY(unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar));
     const auto relaxedFloat32toFloat16PerformanceTensor =
-            NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceTensor));
-    auto operandPerformance = NN_TRY(convert(capabilities.operandPerformance));
+            NN_TRY(unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor));
+    auto operandPerformance = NN_TRY(unvalidatedConvert(capabilities.operandPerformance));
 
-    auto table =
-            NN_TRY(Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)));
+    auto table = NN_TRY(hal::utils::makeGeneralFailure(
+            Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)),
+            nn::ErrorStatus::GENERAL_FAILURE));
 
     return Capabilities{
             .relaxedFloat32toFloat16PerformanceScalar = relaxedFloat32toFloat16PerformanceScalar,
@@ -134,23 +167,23 @@
     };
 }
 
-Result<Capabilities::OperandPerformance> convert(
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
         const hal::V1_2::Capabilities::OperandPerformance& operandPerformance) {
     return Capabilities::OperandPerformance{
-            .type = NN_TRY(convert(operandPerformance.type)),
-            .info = NN_TRY(convert(operandPerformance.info)),
+            .type = NN_TRY(unvalidatedConvert(operandPerformance.type)),
+            .info = NN_TRY(unvalidatedConvert(operandPerformance.info)),
     };
 }
 
-Result<Operation> convert(const hal::V1_2::Operation& operation) {
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_2::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-Result<Operand::SymmPerChannelQuantParams> convert(
+GeneralResult<Operand::SymmPerChannelQuantParams> unvalidatedConvert(
         const hal::V1_2::SymmPerChannelQuantParams& symmPerChannelQuantParams) {
     return Operand::SymmPerChannelQuantParams{
             .scales = symmPerChannelQuantParams.scales,
@@ -158,34 +191,36 @@
     };
 }
 
-Result<Operand> convert(const hal::V1_2::Operand& operand) {
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_2::Operand& operand) {
     return Operand{
-            .type = NN_TRY(convert(operand.type)),
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
             .dimensions = operand.dimensions,
             .scale = operand.scale,
             .zeroPoint = operand.zeroPoint,
-            .lifetime = NN_TRY(convert(operand.lifetime)),
-            .location = NN_TRY(convert(operand.location)),
-            .extraParams = NN_TRY(convert(operand.extraParams)),
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
     };
 }
 
-Result<Operand::ExtraParams> convert(const hal::V1_2::Operand::ExtraParams& extraParams) {
+GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const hal::V1_2::Operand::ExtraParams& extraParams) {
     using Discriminator = hal::V1_2::Operand::ExtraParams::hidl_discriminator;
     switch (extraParams.getDiscriminator()) {
         case Discriminator::none:
             return Operand::NoParams{};
         case Discriminator::channelQuant:
-            return convert(extraParams.channelQuant());
+            return unvalidatedConvert(extraParams.channelQuant());
         case Discriminator::extension:
             return extraParams.extension();
     }
-    return NN_ERROR() << "Unrecognized Operand::ExtraParams discriminator: "
-                      << underlyingType(extraParams.getDiscriminator());
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "Unrecognized Operand::ExtraParams discriminator: "
+           << underlyingType(extraParams.getDiscriminator());
 }
 
-Result<Model> convert(const hal::V1_2::Model& model) {
-    auto operations = NN_TRY(convert(model.operations));
+GeneralResult<Model> unvalidatedConvert(const hal::V1_2::Model& model) {
+    auto operations = NN_TRY(unvalidatedConvert(model.operations));
 
     // Verify number of consumers.
     const auto numberOfConsumers =
@@ -193,14 +228,14 @@
     CHECK(model.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < model.operands.size(); ++i) {
         if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
-            return NN_ERROR() << "Invalid numberOfConsumers for operand " << i << ", expected "
-                              << numberOfConsumers[i] << " but found "
-                              << model.operands[i].numberOfConsumers;
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                   << "Invalid numberOfConsumers for operand " << i << ", expected "
+                   << numberOfConsumers[i] << " but found " << model.operands[i].numberOfConsumers;
         }
     }
 
     auto main = Model::Subgraph{
-            .operands = NN_TRY(convert(model.operands)),
+            .operands = NN_TRY(unvalidatedConvert(model.operands)),
             .operations = std::move(operations),
             .inputIndexes = model.inputIndexes,
             .outputIndexes = model.outputIndexes,
@@ -208,14 +243,14 @@
 
     return Model{
             .main = std::move(main),
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
-            .extensionNameToPrefix = NN_TRY(convert(model.extensionNameToPrefix)),
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
     };
 }
 
-Result<Model::ExtensionNameAndPrefix> convert(
+GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
         const hal::V1_2::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) {
     return Model::ExtensionNameAndPrefix{
             .name = extensionNameAndPrefix.name,
@@ -223,29 +258,29 @@
     };
 }
 
-Result<OutputShape> convert(const hal::V1_2::OutputShape& outputShape) {
+GeneralResult<OutputShape> unvalidatedConvert(const hal::V1_2::OutputShape& outputShape) {
     return OutputShape{
             .dimensions = outputShape.dimensions,
             .isSufficient = outputShape.isSufficient,
     };
 }
 
-Result<MeasureTiming> convert(const hal::V1_2::MeasureTiming& measureTiming) {
+GeneralResult<MeasureTiming> unvalidatedConvert(const hal::V1_2::MeasureTiming& measureTiming) {
     return static_cast<MeasureTiming>(measureTiming);
 }
 
-Result<Timing> convert(const hal::V1_2::Timing& timing) {
+GeneralResult<Timing> unvalidatedConvert(const hal::V1_2::Timing& timing) {
     return Timing{.timeOnDevice = timing.timeOnDevice, .timeInDriver = timing.timeInDriver};
 }
 
-Result<Extension> convert(const hal::V1_2::Extension& extension) {
+GeneralResult<Extension> unvalidatedConvert(const hal::V1_2::Extension& extension) {
     return Extension{
             .name = extension.name,
-            .operandTypes = NN_TRY(convert(extension.operandTypes)),
+            .operandTypes = NN_TRY(unvalidatedConvert(extension.operandTypes)),
     };
 }
 
-Result<Extension::OperandTypeInformation> convert(
+GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
         const hal::V1_2::Extension::OperandTypeInformation& operandTypeInformation) {
     return Extension::OperandTypeInformation{
             .type = operandTypeInformation.type,
@@ -254,21 +289,41 @@
     };
 }
 
-Result<NativeHandle> convert(const hidl_handle& handle) {
-    auto* cloned = native_handle_clone(handle.getNativeHandle());
-    return ::android::NativeHandle::create(cloned, /*ownsHandle=*/true);
+GeneralResult<SharedHandle> unvalidatedConvert(const hidl_handle& hidlHandle) {
+    return hal::utils::sharedHandleFromNativeHandle(hidlHandle.getNativeHandle());
 }
 
-Result<std::vector<Extension>> convert(const hidl_vec<hal::V1_2::Extension>& extensions) {
-    return convertVec(extensions);
+GeneralResult<DeviceType> convert(const hal::V1_2::DeviceType& deviceType) {
+    return validatedConvert(deviceType);
 }
 
-Result<std::vector<NativeHandle>> convert(const hidl_vec<hidl_handle>& handles) {
-    return convertVec(handles);
+GeneralResult<Capabilities> convert(const hal::V1_2::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
 }
 
-Result<std::vector<OutputShape>> convert(const hidl_vec<hal::V1_2::OutputShape>& outputShapes) {
-    return convertVec(outputShapes);
+GeneralResult<Model> convert(const hal::V1_2::Model& model) {
+    return validatedConvert(model);
+}
+
+GeneralResult<MeasureTiming> convert(const hal::V1_2::MeasureTiming& measureTiming) {
+    return validatedConvert(measureTiming);
+}
+
+GeneralResult<Timing> convert(const hal::V1_2::Timing& timing) {
+    return validatedConvert(timing);
+}
+
+GeneralResult<std::vector<Extension>> convert(const hidl_vec<hal::V1_2::Extension>& extensions) {
+    return validatedConvert(extensions);
+}
+
+GeneralResult<std::vector<SharedHandle>> convert(const hidl_vec<hidl_handle>& handles) {
+    return validatedConvert(handles);
+}
+
+GeneralResult<std::vector<OutputShape>> convert(
+        const hidl_vec<hal::V1_2::OutputShape>& outputShapes) {
+    return validatedConvert(outputShapes);
 }
 
 }  // namespace android::nn
@@ -276,87 +331,116 @@
 namespace android::hardware::neuralnetworks::V1_2::utils {
 namespace {
 
-using utils::convert;
+using utils::unvalidatedConvert;
 
-nn::Result<V1_0::OperandLifeTime> convert(const nn::Operand::LifeTime& lifetime) {
-    return V1_0::utils::convert(lifetime);
+nn::GeneralResult<V1_0::OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& lifetime) {
+    return V1_0::utils::unvalidatedConvert(lifetime);
 }
 
-nn::Result<V1_0::PerformanceInfo> convert(
+nn::GeneralResult<V1_0::PerformanceInfo> unvalidatedConvert(
         const nn::Capabilities::PerformanceInfo& performanceInfo) {
-    return V1_0::utils::convert(performanceInfo);
+    return V1_0::utils::unvalidatedConvert(performanceInfo);
 }
 
-nn::Result<V1_0::DataLocation> convert(const nn::DataLocation& location) {
-    return V1_0::utils::convert(location);
+nn::GeneralResult<V1_0::DataLocation> unvalidatedConvert(const nn::DataLocation& location) {
+    return V1_0::utils::unvalidatedConvert(location);
 }
 
-nn::Result<hidl_vec<uint8_t>> convert(const nn::Model::OperandValues& operandValues) {
-    return V1_0::utils::convert(operandValues);
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues) {
+    return V1_0::utils::unvalidatedConvert(operandValues);
 }
 
-nn::Result<hidl_memory> convert(const nn::Memory& memory) {
-    return V1_0::utils::convert(memory);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
+    return V1_0::utils::unvalidatedConvert(memory);
 }
 
 template <typename Input>
-using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-nn::Result<hidl_vec<ConvertOutput<Type>>> convertVec(const std::vector<Type>& arguments) {
-    hidl_vec<ConvertOutput<Type>> halObject(arguments.size());
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+        const std::vector<Type>& arguments) {
+    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
     for (size_t i = 0; i < arguments.size(); ++i) {
-        halObject[i] = NN_TRY(convert(arguments[i]));
+        halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
     }
     return halObject;
 }
 
 template <typename Type>
-nn::Result<hidl_vec<ConvertOutput<Type>>> convert(const std::vector<Type>& arguments) {
-    return convertVec(arguments);
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    return unvalidatedConvertVec(arguments);
 }
 
-nn::Result<Operand::ExtraParams> makeExtraParams(nn::Operand::NoParams /*noParams*/) {
+nn::GeneralResult<Operand::ExtraParams> makeExtraParams(nn::Operand::NoParams /*noParams*/) {
     return Operand::ExtraParams{};
 }
 
-nn::Result<Operand::ExtraParams> makeExtraParams(
+nn::GeneralResult<Operand::ExtraParams> makeExtraParams(
         const nn::Operand::SymmPerChannelQuantParams& channelQuant) {
     Operand::ExtraParams ret;
-    ret.channelQuant(NN_TRY(convert(channelQuant)));
+    ret.channelQuant(NN_TRY(unvalidatedConvert(channelQuant)));
     return ret;
 }
 
-nn::Result<Operand::ExtraParams> makeExtraParams(const nn::Operand::ExtensionParams& extension) {
+nn::GeneralResult<Operand::ExtraParams> makeExtraParams(
+        const nn::Operand::ExtensionParams& extension) {
     Operand::ExtraParams ret;
     ret.extension(extension);
     return ret;
 }
 
+template <typename Type>
+decltype(utils::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
+    const auto maybeVersion = nn::validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return nn::error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return utils::unvalidatedConvert(canonical);
+}
+
+template <typename Type>
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> validatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+    for (size_t i = 0; i < arguments.size(); ++i) {
+        halObject[i] = NN_TRY(validatedConvert(arguments[i]));
+    }
+    return halObject;
+}
+
 }  // anonymous namespace
 
-nn::Result<OperandType> convert(const nn::OperandType& operandType) {
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
     return static_cast<OperandType>(operandType);
 }
 
-nn::Result<OperationType> convert(const nn::OperationType& operationType) {
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-nn::Result<DeviceType> convert(const nn::DeviceType& deviceType) {
+nn::GeneralResult<DeviceType> unvalidatedConvert(const nn::DeviceType& deviceType) {
     switch (deviceType) {
         case nn::DeviceType::UNKNOWN:
-            return NN_ERROR() << "Invalid DeviceType UNKNOWN";
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Invalid DeviceType UNKNOWN";
         case nn::DeviceType::OTHER:
         case nn::DeviceType::CPU:
         case nn::DeviceType::GPU:
         case nn::DeviceType::ACCELERATOR:
             return static_cast<DeviceType>(deviceType);
     }
-    return NN_ERROR() << "Invalid DeviceType " << underlyingType(deviceType);
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "Invalid DeviceType " << underlyingType(deviceType);
 }
 
-nn::Result<Capabilities> convert(const nn::Capabilities& capabilities) {
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities) {
     std::vector<nn::Capabilities::OperandPerformance> operandPerformance;
     operandPerformance.reserve(capabilities.operandPerformance.asVector().size());
     std::copy_if(capabilities.operandPerformance.asVector().begin(),
@@ -367,31 +451,31 @@
                  });
 
     return Capabilities{
-            .relaxedFloat32toFloat16PerformanceScalar =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
-            .relaxedFloat32toFloat16PerformanceTensor =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
-            .operandPerformance = NN_TRY(convert(operandPerformance)),
+            .relaxedFloat32toFloat16PerformanceScalar = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
+            .relaxedFloat32toFloat16PerformanceTensor = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
+            .operandPerformance = NN_TRY(unvalidatedConvert(operandPerformance)),
     };
 }
 
-nn::Result<Capabilities::OperandPerformance> convert(
+nn::GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
         const nn::Capabilities::OperandPerformance& operandPerformance) {
     return Capabilities::OperandPerformance{
-            .type = NN_TRY(convert(operandPerformance.type)),
-            .info = NN_TRY(convert(operandPerformance.info)),
+            .type = NN_TRY(unvalidatedConvert(operandPerformance.type)),
+            .info = NN_TRY(unvalidatedConvert(operandPerformance.info)),
     };
 }
 
-nn::Result<Operation> convert(const nn::Operation& operation) {
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-nn::Result<SymmPerChannelQuantParams> convert(
+nn::GeneralResult<SymmPerChannelQuantParams> unvalidatedConvert(
         const nn::Operand::SymmPerChannelQuantParams& symmPerChannelQuantParams) {
     return SymmPerChannelQuantParams{
             .scales = symmPerChannelQuantParams.scales,
@@ -399,29 +483,31 @@
     };
 }
 
-nn::Result<Operand> convert(const nn::Operand& operand) {
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand) {
     return Operand{
-            .type = NN_TRY(convert(operand.type)),
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
             .dimensions = operand.dimensions,
             .numberOfConsumers = 0,
             .scale = operand.scale,
             .zeroPoint = operand.zeroPoint,
-            .lifetime = NN_TRY(convert(operand.lifetime)),
-            .location = NN_TRY(convert(operand.location)),
-            .extraParams = NN_TRY(convert(operand.extraParams)),
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
     };
 }
 
-nn::Result<Operand::ExtraParams> convert(const nn::Operand::ExtraParams& extraParams) {
+nn::GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const nn::Operand::ExtraParams& extraParams) {
     return std::visit([](const auto& x) { return makeExtraParams(x); }, extraParams);
 }
 
-nn::Result<Model> convert(const nn::Model& model) {
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
     if (!hal::utils::hasNoPointerData(model)) {
-        return NN_ERROR() << "Model cannot be converted because it contains pointer-based memory";
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Model cannot be unvalidatedConverted because it contains pointer-based memory";
     }
 
-    auto operands = NN_TRY(convert(model.main.operands));
+    auto operands = NN_TRY(unvalidatedConvert(model.main.operands));
 
     // Update number of consumers.
     const auto numberOfConsumers =
@@ -433,17 +519,17 @@
 
     return Model{
             .operands = std::move(operands),
-            .operations = NN_TRY(convert(model.main.operations)),
+            .operations = NN_TRY(unvalidatedConvert(model.main.operations)),
             .inputIndexes = model.main.inputIndexes,
             .outputIndexes = model.main.outputIndexes,
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
-            .extensionNameToPrefix = NN_TRY(convert(model.extensionNameToPrefix)),
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
     };
 }
 
-nn::Result<Model::ExtensionNameAndPrefix> convert(
+nn::GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
         const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) {
     return Model::ExtensionNameAndPrefix{
             .name = extensionNameAndPrefix.name,
@@ -451,27 +537,27 @@
     };
 }
 
-nn::Result<OutputShape> convert(const nn::OutputShape& outputShape) {
+nn::GeneralResult<OutputShape> unvalidatedConvert(const nn::OutputShape& outputShape) {
     return OutputShape{.dimensions = outputShape.dimensions,
                        .isSufficient = outputShape.isSufficient};
 }
 
-nn::Result<MeasureTiming> convert(const nn::MeasureTiming& measureTiming) {
+nn::GeneralResult<MeasureTiming> unvalidatedConvert(const nn::MeasureTiming& measureTiming) {
     return static_cast<MeasureTiming>(measureTiming);
 }
 
-nn::Result<Timing> convert(const nn::Timing& timing) {
+nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing) {
     return Timing{.timeOnDevice = timing.timeOnDevice, .timeInDriver = timing.timeInDriver};
 }
 
-nn::Result<Extension> convert(const nn::Extension& extension) {
+nn::GeneralResult<Extension> unvalidatedConvert(const nn::Extension& extension) {
     return Extension{
             .name = extension.name,
-            .operandTypes = NN_TRY(convert(extension.operandTypes)),
+            .operandTypes = NN_TRY(unvalidatedConvert(extension.operandTypes)),
     };
 }
 
-nn::Result<Extension::OperandTypeInformation> convert(
+nn::GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
         const nn::Extension::OperandTypeInformation& operandTypeInformation) {
     return Extension::OperandTypeInformation{
             .type = operandTypeInformation.type,
@@ -480,23 +566,40 @@
     };
 }
 
-nn::Result<hidl_handle> convert(const nn::NativeHandle& handle) {
-    const auto hidlHandle = hidl_handle(handle->handle());
-    // Copy memory to force the native_handle_t to be copied.
-    auto copiedHandle = hidlHandle;
-    return copiedHandle;
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
+    return hal::utils::hidlHandleFromSharedHandle(handle);
 }
 
-nn::Result<hidl_vec<Extension>> convert(const std::vector<nn::Extension>& extensions) {
-    return convertVec(extensions);
+nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType) {
+    return validatedConvert(deviceType);
 }
 
-nn::Result<hidl_vec<hidl_handle>> convert(const std::vector<nn::NativeHandle>& handles) {
-    return convertVec(handles);
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
 }
 
-nn::Result<hidl_vec<OutputShape>> convert(const std::vector<nn::OutputShape>& outputShapes) {
-    return convertVec(outputShapes);
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+    return validatedConvert(model);
+}
+
+nn::GeneralResult<MeasureTiming> convert(const nn::MeasureTiming& measureTiming) {
+    return validatedConvert(measureTiming);
+}
+
+nn::GeneralResult<Timing> convert(const nn::Timing& timing) {
+    return validatedConvert(timing);
+}
+
+nn::GeneralResult<hidl_vec<Extension>> convert(const std::vector<nn::Extension>& extensions) {
+    return validatedConvert(extensions);
+}
+
+nn::GeneralResult<hidl_vec<hidl_handle>> convert(const std::vector<nn::SharedHandle>& handles) {
+    return validatedConvert(handles);
+}
+
+nn::GeneralResult<hidl_vec<OutputShape>> convert(const std::vector<nn::OutputShape>& outputShapes) {
+    return validatedConvert(outputShapes);
 }
 
 }  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/Device.cpp b/neuralnetworks/1.2/utils/src/Device.cpp
new file mode 100644
index 0000000..0061065
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Device.cpp
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Device.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.1/Conversions.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+nn::GeneralResult<nn::Capabilities> initCapabilities(V1_2::IDevice* device) {
+    CHECK(device != nullptr);
+
+    nn::GeneralResult<nn::Capabilities> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                                 << "uninitialized";
+    const auto cb = [&result](V1_0::ErrorStatus status, const Capabilities& capabilities) {
+        if (status != V1_0::ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical) << "getCapabilities_1_2 failed with " << toString(status);
+        } else {
+            result = nn::convert(capabilities);
+        }
+    };
+
+    const auto ret = device->getCapabilities_1_2(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+}  // namespace
+
+nn::GeneralResult<std::string> initVersionString(V1_2::IDevice* device) {
+    CHECK(device != nullptr);
+
+    nn::GeneralResult<std::string> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                            << "uninitialized";
+    const auto cb = [&result](V1_0::ErrorStatus status, const hidl_string& versionString) {
+        if (status != V1_0::ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical) << "getVersionString failed with " << toString(status);
+        } else {
+            result = versionString;
+        }
+    };
+
+    const auto ret = device->getVersionString(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+nn::GeneralResult<nn::DeviceType> initDeviceType(V1_2::IDevice* device) {
+    CHECK(device != nullptr);
+
+    nn::GeneralResult<nn::DeviceType> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                               << "uninitialized";
+    const auto cb = [&result](V1_0::ErrorStatus status, DeviceType deviceType) {
+        if (status != V1_0::ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical) << "getDeviceType failed with " << toString(status);
+        } else {
+            result = nn::convert(deviceType);
+        }
+    };
+
+    const auto ret = device->getType(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+nn::GeneralResult<std::vector<nn::Extension>> initExtensions(V1_2::IDevice* device) {
+    CHECK(device != nullptr);
+
+    nn::GeneralResult<std::vector<nn::Extension>> result =
+            NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
+    const auto cb = [&result](V1_0::ErrorStatus status, const hidl_vec<Extension>& extensions) {
+        if (status != V1_0::ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical) << "getExtensions failed with " << toString(status);
+        } else {
+            result = nn::convert(extensions);
+        }
+    };
+
+    const auto ret = device->getSupportedExtensions(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+nn::GeneralResult<std::pair<uint32_t, uint32_t>> initNumberOfCacheFilesNeeded(
+        V1_2::IDevice* device) {
+    CHECK(device != nullptr);
+
+    nn::GeneralResult<std::pair<uint32_t, uint32_t>> result =
+            NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
+    const auto cb = [&result](V1_0::ErrorStatus status, uint32_t numModelCache,
+                              uint32_t numDataCache) {
+        if (status != V1_0::ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical)
+                     << "getNumberOfCacheFilesNeeded failed with " << toString(status);
+        } else {
+            result = std::make_pair(numModelCache, numDataCache);
+        }
+    };
+
+    const auto ret = device->getNumberOfCacheFilesNeeded(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+nn::GeneralResult<std::shared_ptr<const Device>> Device::create(std::string name,
+                                                                sp<V1_2::IDevice> device) {
+    if (name.empty()) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_2::utils::Device::create must have non-empty name";
+    }
+    if (device == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_2::utils::Device::create must have non-null device";
+    }
+
+    auto versionString = NN_TRY(initVersionString(device.get()));
+    const auto deviceType = NN_TRY(initDeviceType(device.get()));
+    auto extensions = NN_TRY(initExtensions(device.get()));
+    auto capabilities = NN_TRY(initCapabilities(device.get()));
+    const auto numberOfCacheFilesNeeded = NN_TRY(initNumberOfCacheFilesNeeded(device.get()));
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
+    return std::make_shared<const Device>(
+            PrivateConstructorTag{}, std::move(name), std::move(versionString), deviceType,
+            std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded,
+            std::move(device), std::move(deathHandler));
+}
+
+Device::Device(PrivateConstructorTag /*tag*/, std::string name, std::string versionString,
+               nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
+               nn::Capabilities capabilities,
+               std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded, sp<V1_2::IDevice> device,
+               hal::utils::DeathHandler deathHandler)
+    : kName(std::move(name)),
+      kVersionString(std::move(versionString)),
+      kDeviceType(deviceType),
+      kExtensions(std::move(extensions)),
+      kCapabilities(std::move(capabilities)),
+      kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded),
+      kDevice(std::move(device)),
+      kDeathHandler(std::move(deathHandler)) {}
+
+const std::string& Device::getName() const {
+    return kName;
+}
+
+const std::string& Device::getVersionString() const {
+    return kVersionString;
+}
+
+nn::Version Device::getFeatureLevel() const {
+    return nn::Version::ANDROID_Q;
+}
+
+nn::DeviceType Device::getType() const {
+    return kDeviceType;
+}
+
+const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
+    return kExtensions;
+}
+
+const nn::Capabilities& Device::getCapabilities() const {
+    return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
+    return kNumberOfCacheFilesNeeded;
+}
+
+nn::GeneralResult<void> Device::wait() const {
+    const auto ret = kDevice->ping();
+    HANDLE_TRANSPORT_FAILURE(ret);
+    return {};
+}
+
+nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+
+    nn::GeneralResult<std::vector<bool>> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                                  << "uninitialized";
+    auto cb = [&result, &model](V1_0::ErrorStatus status,
+                                const hidl_vec<bool>& supportedOperations) {
+        if (status != V1_0::ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical)
+                     << "getSupportedOperations_1_2 failed with " << toString(status);
+        } else if (supportedOperations.size() != model.main.operations.size()) {
+            result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                     << "getSupportedOperations_1_2 returned vector of size "
+                     << supportedOperations.size() << " but expected "
+                     << model.main.operations.size();
+        } else {
+            result = supportedOperations;
+        }
+    };
+
+    const auto ret = kDevice->getSupportedOperations_1_2(hidlModel, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
+        const nn::Model& model, nn::ExecutionPreference preference, nn::Priority /*priority*/,
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+    const auto hidlPreference = NN_TRY(V1_1::utils::convert(preference));
+    const auto hidlModelCache = NN_TRY(convert(modelCache));
+    const auto hidlDataCache = NN_TRY(convert(dataCache));
+    const auto hidlToken = token;
+
+    const auto cb = sp<PreparedModelCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kDevice->prepareModel_1_2(hidlModel, hidlPreference, hidlModelCache,
+                                               hidlDataCache, hidlToken, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    if (status != V1_0::ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        return NN_ERROR(canonical) << "prepareModel_1_2 failed with " << toString(status);
+    }
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache(
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    const auto hidlModelCache = NN_TRY(convert(modelCache));
+    const auto hidlDataCache = NN_TRY(convert(dataCache));
+    const auto hidlToken = token;
+
+    const auto cb = sp<PreparedModelCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kDevice->prepareModelFromCache(hidlModelCache, hidlDataCache, hidlToken, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    if (status != V1_0::ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        return NN_ERROR(canonical) << "prepareModelFromCache failed with " << toString(status);
+    }
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedBuffer> Device::allocate(
+        const nn::BufferDesc& /*desc*/,
+        const std::vector<nn::SharedPreparedModel>& /*preparedModels*/,
+        const std::vector<nn::BufferRole>& /*inputRoles*/,
+        const std::vector<nn::BufferRole>& /*outputRoles*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IDevice::allocate not supported on 1.2 HAL service";
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
new file mode 100644
index 0000000..dad9a7e
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PreparedModel.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+convertExecutionResultsHelper(const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
+    return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertExecutionResults(
+        const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
+    return hal::utils::makeExecutionFailure(convertExecutionResultsHelper(outputShapes, timing));
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
+        sp<V1_2::IPreparedModel> preparedModel) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_2::utils::PreparedModel::create must have non-null preparedModel";
+    }
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(preparedModel));
+    return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, std::move(preparedModel),
+                                                 std::move(deathHandler));
+}
+
+PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, sp<V1_2::IPreparedModel> preparedModel,
+                             hal::utils::DeathHandler deathHandler)
+    : kPreparedModel(std::move(preparedModel)), kDeathHandler(std::move(deathHandler)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeSynchronously(const V1_0::Request& request, MeasureTiming measure) const {
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
+            NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
+    const auto cb = [&result](V1_0::ErrorStatus status, const hidl_vec<OutputShape>& outputShapes,
+                              const Timing& timing) {
+        if (status != V1_0::ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical) << "executeSynchronously failed with " << toString(status);
+        } else {
+            result = convertExecutionResults(outputShapes, timing);
+        }
+    };
+
+    const auto ret = kPreparedModel->executeSynchronously(request, measure, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeAsynchronously(const V1_0::Request& request, MeasureTiming measure) const {
+    const auto cb = sp<ExecutionCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kPreparedModel->execute_1_2(request, measure, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    if (status != V1_0::ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        return NN_ERROR(canonical) << "execute failed with " << toString(status);
+    }
+
+    return cb->get();
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalTimeoutDuration& /*loopTimeoutDuration*/) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
+            hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
+
+    const auto hidlRequest =
+            NN_TRY(hal::utils::makeExecutionFailure(V1_0::utils::convert(requestInShared)));
+    const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
+            NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
+    const bool preferSynchronous = true;
+
+    // Execute synchronously if allowed.
+    if (preferSynchronous) {
+        result = executeSynchronously(hidlRequest, hidlMeasure);
+    }
+
+    // Run asymchronous execution if execution has not already completed.
+    if (!result.has_value()) {
+        result = executeAsynchronously(hidlRequest, hidlMeasure);
+    }
+
+    // Flush output buffers if suxcessful execution.
+    if (result.has_value()) {
+        NN_TRY(hal::utils::makeExecutionFailure(
+                hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
+    }
+
+    return result;
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFenced(
+        const nn::Request& /*request*/, const std::vector<nn::SyncFence>& /*waitFor*/,
+        nn::MeasureTiming /*measure*/, const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalTimeoutDuration& /*loopTimeoutDuration*/,
+        const nn::OptionalTimeoutDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IPreparedModel::executeFenced is not supported on 1.2 HAL service";
+}
+
+std::any PreparedModel::getUnderlyingResource() const {
+    sp<V1_0::IPreparedModel> resource = kPreparedModel;
+    return resource;
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/Service.cpp b/neuralnetworks/1.2/utils/src/Service.cpp
new file mode 100644
index 0000000..110188f
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Service.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Service.h"
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientDevice.h>
+#include <string>
+#include "Device.h"
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name) {
+    hal::utils::ResilientDevice::Factory makeDevice =
+            [name](bool blocking) -> nn::GeneralResult<nn::SharedDevice> {
+        auto service = blocking ? IDevice::getService(name) : IDevice::tryGetService(name);
+        if (service == nullptr) {
+            return NN_ERROR() << (blocking ? "getService" : "tryGetService") << " returned nullptr";
+        }
+        return Device::create(name, std::move(service));
+    };
+
+    return hal::utils::ResilientDevice::create(std::move(makeDevice));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.3/utils/Android.bp b/neuralnetworks/1.3/utils/Android.bp
index 279b250..d5d897d 100644
--- a/neuralnetworks/1.3/utils/Android.bp
+++ b/neuralnetworks/1.3/utils/Android.bp
@@ -20,6 +20,7 @@
     srcs: ["src/*"],
     local_include_dirs: ["include/nnapi/hal/1.3/"],
     export_include_dirs: ["include"],
+    cflags: ["-Wthread-safety"],
     static_libs: [
         "neuralnetworks_types",
         "neuralnetworks_utils_hal_common",
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h
new file mode 100644
index 0000000..637179d
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_BUFFER_H
+
+#include <android/hardware/neuralnetworks/1.3/IBuffer.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <memory>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class Buffer final : public nn::IBuffer {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Buffer>> create(
+            sp<V1_3::IBuffer> buffer, nn::Request::MemoryDomainToken token);
+
+    Buffer(PrivateConstructorTag tag, sp<V1_3::IBuffer> buffer,
+           nn::Request::MemoryDomainToken token);
+
+    nn::Request::MemoryDomainToken getToken() const override;
+
+    nn::GeneralResult<void> copyTo(const nn::Memory& dst) const override;
+    nn::GeneralResult<void> copyFrom(const nn::Memory& src,
+                                     const nn::Dimensions& dimensions) const override;
+
+  private:
+    const sp<V1_3::IBuffer> kBuffer;
+    const nn::Request::MemoryDomainToken kToken;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_BUFFER_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
new file mode 100644
index 0000000..d46b111
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_CALLBACKS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_CALLBACKS_H
+
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class PreparedModelCallback final : public IPreparedModelCallback,
+                                    public hal::utils::IProtectedCallback {
+  public:
+    using Data = nn::GeneralResult<nn::SharedPreparedModel>;
+
+    Return<void> notify(V1_0::ErrorStatus status,
+                        const sp<V1_0::IPreparedModel>& preparedModel) override;
+    Return<void> notify_1_2(V1_0::ErrorStatus status,
+                            const sp<V1_2::IPreparedModel>& preparedModel) override;
+    Return<void> notify_1_3(ErrorStatus status, const sp<IPreparedModel>& preparedModel) override;
+
+    void notifyAsDeadObject() override;
+
+    Data get();
+
+  private:
+    void notifyInternal(Data result);
+
+    hal::utils::TransferValue<Data> mData;
+};
+
+class ExecutionCallback final : public IExecutionCallback, public hal::utils::IProtectedCallback {
+  public:
+    using Data = nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>;
+
+    Return<void> notify(V1_0::ErrorStatus status) override;
+    Return<void> notify_1_2(V1_0::ErrorStatus status,
+                            const hidl_vec<V1_2::OutputShape>& outputShapes,
+                            const V1_2::Timing& timing) override;
+    Return<void> notify_1_3(ErrorStatus status, const hidl_vec<V1_2::OutputShape>& outputShapes,
+                            const V1_2::Timing& timing) override;
+
+    void notifyAsDeadObject() override;
+
+    Data get();
+
+  private:
+    void notifyInternal(Data result);
+
+    hal::utils::TransferValue<Data> mData;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_CALLBACKS_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
index 43987a9..9653a05 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
@@ -25,54 +25,83 @@
 
 namespace android::nn {
 
-Result<OperandType> convert(const hal::V1_3::OperandType& operandType);
-Result<OperationType> convert(const hal::V1_3::OperationType& operationType);
-Result<Priority> convert(const hal::V1_3::Priority& priority);
-Result<Capabilities> convert(const hal::V1_3::Capabilities& capabilities);
-Result<Capabilities::OperandPerformance> convert(
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_3::OperandType& operandType);
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_3::OperationType& operationType);
+GeneralResult<Priority> unvalidatedConvert(const hal::V1_3::Priority& priority);
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_3::Capabilities& capabilities);
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
         const hal::V1_3::Capabilities::OperandPerformance& operandPerformance);
-Result<Operation> convert(const hal::V1_3::Operation& operation);
-Result<Operand::LifeTime> convert(const hal::V1_3::OperandLifeTime& operandLifeTime);
-Result<Operand> convert(const hal::V1_3::Operand& operand);
-Result<Model> convert(const hal::V1_3::Model& model);
-Result<Model::Subgraph> convert(const hal::V1_3::Subgraph& subgraph);
-Result<BufferDesc> convert(const hal::V1_3::BufferDesc& bufferDesc);
-Result<BufferRole> convert(const hal::V1_3::BufferRole& bufferRole);
-Result<Request> convert(const hal::V1_3::Request& request);
-Result<Request::MemoryPool> convert(const hal::V1_3::Request::MemoryPool& memoryPool);
-Result<OptionalTimePoint> convert(const hal::V1_3::OptionalTimePoint& optionalTimePoint);
-Result<OptionalTimeoutDuration> convert(
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_3::Operation& operation);
+GeneralResult<Operand::LifeTime> unvalidatedConvert(
+        const hal::V1_3::OperandLifeTime& operandLifeTime);
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_3::Operand& operand);
+GeneralResult<Model> unvalidatedConvert(const hal::V1_3::Model& model);
+GeneralResult<Model::Subgraph> unvalidatedConvert(const hal::V1_3::Subgraph& subgraph);
+GeneralResult<BufferDesc> unvalidatedConvert(const hal::V1_3::BufferDesc& bufferDesc);
+GeneralResult<BufferRole> unvalidatedConvert(const hal::V1_3::BufferRole& bufferRole);
+GeneralResult<Request> unvalidatedConvert(const hal::V1_3::Request& request);
+GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const hal::V1_3::Request::MemoryPool& memoryPool);
+GeneralResult<OptionalTimePoint> unvalidatedConvert(
+        const hal::V1_3::OptionalTimePoint& optionalTimePoint);
+GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
         const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration);
-Result<ErrorStatus> convert(const hal::V1_3::ErrorStatus& errorStatus);
+GeneralResult<ErrorStatus> unvalidatedConvert(const hal::V1_3::ErrorStatus& errorStatus);
 
-Result<std::vector<BufferRole>> convert(
+GeneralResult<Priority> convert(const hal::V1_3::Priority& priority);
+GeneralResult<Capabilities> convert(const hal::V1_3::Capabilities& capabilities);
+GeneralResult<Model> convert(const hal::V1_3::Model& model);
+GeneralResult<BufferDesc> convert(const hal::V1_3::BufferDesc& bufferDesc);
+GeneralResult<Request> convert(const hal::V1_3::Request& request);
+GeneralResult<OptionalTimePoint> convert(const hal::V1_3::OptionalTimePoint& optionalTimePoint);
+GeneralResult<OptionalTimeoutDuration> convert(
+        const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration);
+GeneralResult<ErrorStatus> convert(const hal::V1_3::ErrorStatus& errorStatus);
+
+GeneralResult<SharedHandle> convert(const hardware::hidl_handle& handle);
+GeneralResult<Memory> convert(const hardware::hidl_memory& memory);
+GeneralResult<std::vector<BufferRole>> convert(
         const hardware::hidl_vec<hal::V1_3::BufferRole>& bufferRoles);
 
 }  // namespace android::nn
 
 namespace android::hardware::neuralnetworks::V1_3::utils {
 
-nn::Result<OperandType> convert(const nn::OperandType& operandType);
-nn::Result<OperationType> convert(const nn::OperationType& operationType);
-nn::Result<Priority> convert(const nn::Priority& priority);
-nn::Result<Capabilities> convert(const nn::Capabilities& capabilities);
-nn::Result<Capabilities::OperandPerformance> convert(
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType);
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<Priority> unvalidatedConvert(const nn::Priority& priority);
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
         const nn::Capabilities::OperandPerformance& operandPerformance);
-nn::Result<Operation> convert(const nn::Operation& operation);
-nn::Result<OperandLifeTime> convert(const nn::Operand::LifeTime& operandLifeTime);
-nn::Result<Operand> convert(const nn::Operand& operand);
-nn::Result<Model> convert(const nn::Model& model);
-nn::Result<Subgraph> convert(const nn::Model::Subgraph& subgraph);
-nn::Result<BufferDesc> convert(const nn::BufferDesc& bufferDesc);
-nn::Result<BufferRole> convert(const nn::BufferRole& bufferRole);
-nn::Result<Request> convert(const nn::Request& request);
-nn::Result<Request::MemoryPool> convert(const nn::Request::MemoryPool& memoryPool);
-nn::Result<OptionalTimePoint> convert(const nn::OptionalTimePoint& optionalTimePoint);
-nn::Result<OptionalTimeoutDuration> convert(
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& operandLifeTime);
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<Subgraph> unvalidatedConvert(const nn::Model::Subgraph& subgraph);
+nn::GeneralResult<BufferDesc> unvalidatedConvert(const nn::BufferDesc& bufferDesc);
+nn::GeneralResult<BufferRole> unvalidatedConvert(const nn::BufferRole& bufferRole);
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request);
+nn::GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const nn::Request::MemoryPool& memoryPool);
+nn::GeneralResult<OptionalTimePoint> unvalidatedConvert(
+        const nn::OptionalTimePoint& optionalTimePoint);
+nn::GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
         const nn::OptionalTimeoutDuration& optionalTimeoutDuration);
-nn::Result<ErrorStatus> convert(const nn::ErrorStatus& errorStatus);
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& errorStatus);
 
-nn::Result<hidl_vec<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles);
+nn::GeneralResult<Priority> convert(const nn::Priority& priority);
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Model> convert(const nn::Model& model);
+nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc);
+nn::GeneralResult<Request> convert(const nn::Request& request);
+nn::GeneralResult<OptionalTimePoint> convert(const nn::OptionalTimePoint& optionalTimePoint);
+nn::GeneralResult<OptionalTimeoutDuration> convert(
+        const nn::OptionalTimeoutDuration& optionalTimeoutDuration);
+nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus);
+
+nn::GeneralResult<hidl_handle> convert(const nn::SharedHandle& handle);
+nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory);
+nn::GeneralResult<hidl_vec<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles);
 
 }  // namespace android::hardware::neuralnetworks::V1_3::utils
 
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
new file mode 100644
index 0000000..0f5234b
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_DEVICE_H
+
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class Device final : public nn::IDevice {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Device>> create(std::string name,
+                                                                   sp<V1_3::IDevice> device);
+
+    Device(PrivateConstructorTag tag, std::string name, std::string versionString,
+           nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
+           nn::Capabilities capabilities, std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
+           sp<V1_3::IDevice> device, hal::utils::DeathHandler deathHandler);
+
+    const std::string& getName() const override;
+    const std::string& getVersionString() const override;
+    nn::Version getFeatureLevel() const override;
+    nn::DeviceType getType() const override;
+    const std::vector<nn::Extension>& getSupportedExtensions() const override;
+    const nn::Capabilities& getCapabilities() const override;
+    std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
+
+    nn::GeneralResult<void> wait() const override;
+
+    nn::GeneralResult<std::vector<bool>> getSupportedOperations(
+            const nn::Model& model) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModel(
+            const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedBuffer> allocate(
+            const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+            const std::vector<nn::BufferRole>& inputRoles,
+            const std::vector<nn::BufferRole>& outputRoles) const override;
+
+  private:
+    const std::string kName;
+    const std::string kVersionString;
+    const nn::DeviceType kDeviceType;
+    const std::vector<nn::Extension> kExtensions;
+    const nn::Capabilities kCapabilities;
+    const std::pair<uint32_t, uint32_t> kNumberOfCacheFilesNeeded;
+    const sp<V1_3::IDevice> kDevice;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_DEVICE_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
new file mode 100644
index 0000000..e0d69dd
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_PREPARED_MODEL_H
+
+#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class PreparedModel final : public nn::IPreparedModel {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const PreparedModel>> create(
+            sp<V1_3::IPreparedModel> preparedModel);
+
+    PreparedModel(PrivateConstructorTag tag, sp<V1_3::IPreparedModel> preparedModel,
+                  hal::utils::DeathHandler deathHandler);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalTimeoutDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> executeFenced(
+            const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
+            nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalTimeoutDuration& loopTimeoutDuration,
+            const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const override;
+
+    std::any getUnderlyingResource() const override;
+
+  private:
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeSynchronously(
+            const Request& request, V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
+            const OptionalTimeoutDuration& loopTimeoutDuration) const;
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeAsynchronously(
+            const Request& request, V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
+            const OptionalTimeoutDuration& loopTimeoutDuration) const;
+
+    const sp<V1_3::IPreparedModel> kPreparedModel;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Service.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Service.h
new file mode 100644
index 0000000..2bc3257
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Service.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_SERVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_SERVICE_H
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name);
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_SERVICE_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
index f8c975d..29b0c80 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
@@ -23,7 +23,6 @@
 #include <android/hardware/neuralnetworks/1.3/types.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
-#include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
 #include <nnapi/hal/1.1/Conversions.h>
 #include <nnapi/hal/1.2/Conversions.h>
@@ -31,14 +30,12 @@
 namespace android::hardware::neuralnetworks::V1_3::utils {
 
 constexpr auto kDefaultPriority = Priority::MEDIUM;
-constexpr auto kVersion = nn::Version::ANDROID_R;
 
 template <typename Type>
 nn::Result<void> validate(const Type& halObject) {
-    const auto canonical = NN_TRY(nn::convert(halObject));
-    const auto version = NN_TRY(nn::validate(canonical));
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "";
+    const auto maybeCanonical = nn::convert(halObject);
+    if (!maybeCanonical.has_value()) {
+        return nn::error() << maybeCanonical.error().message;
     }
     return {};
 }
@@ -52,16 +49,6 @@
     return result.has_value();
 }
 
-template <typename Type>
-decltype(nn::convert(std::declval<Type>())) validatedConvertToCanonical(const Type& halObject) {
-    auto canonical = NN_TRY(nn::convert(halObject));
-    const auto version = NN_TRY(nn::validate(canonical));
-    if (version > utils::kVersion) {
-        return NN_ERROR() << "";
-    }
-    return canonical;
-}
-
 }  // namespace android::hardware::neuralnetworks::V1_3::utils
 
 #endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_H
diff --git a/neuralnetworks/1.3/utils/src/Buffer.cpp b/neuralnetworks/1.3/utils/src/Buffer.cpp
new file mode 100644
index 0000000..ffdeccd
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/Buffer.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Buffer.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IBuffer.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/HandleError.h>
+
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <memory>
+#include <utility>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+nn::GeneralResult<std::shared_ptr<const Buffer>> Buffer::create(
+        sp<V1_3::IBuffer> buffer, nn::Request::MemoryDomainToken token) {
+    if (buffer == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_3::utils::Buffer::create must have non-null buffer";
+    }
+    if (token == static_cast<nn::Request::MemoryDomainToken>(0)) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_3::utils::Buffer::create must have non-zero token";
+    }
+
+    return std::make_shared<const Buffer>(PrivateConstructorTag{}, std::move(buffer), token);
+}
+
+Buffer::Buffer(PrivateConstructorTag /*tag*/, sp<V1_3::IBuffer> buffer,
+               nn::Request::MemoryDomainToken token)
+    : kBuffer(std::move(buffer)), kToken(token) {
+    CHECK(kBuffer != nullptr);
+    CHECK(kToken != static_cast<nn::Request::MemoryDomainToken>(0));
+}
+
+nn::Request::MemoryDomainToken Buffer::getToken() const {
+    return kToken;
+}
+
+nn::GeneralResult<void> Buffer::copyTo(const nn::Memory& dst) const {
+    const auto hidlDst = NN_TRY(convert(dst));
+
+    const auto ret = kBuffer->copyTo(hidlDst);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    if (status != ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        return NN_ERROR(canonical) << "IBuffer::copyTo failed with " << toString(status);
+    }
+
+    return {};
+}
+
+nn::GeneralResult<void> Buffer::copyFrom(const nn::Memory& src,
+                                         const nn::Dimensions& dimensions) const {
+    const auto hidlSrc = NN_TRY(convert(src));
+    const auto hidlDimensions = hidl_vec<uint32_t>(dimensions);
+
+    const auto ret = kBuffer->copyFrom(hidlSrc, hidlDimensions);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    if (status != ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        return NN_ERROR(canonical) << "IBuffer::copyFrom failed with " << toString(status);
+    }
+
+    return {};
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/Callbacks.cpp b/neuralnetworks/1.3/utils/src/Callbacks.cpp
new file mode 100644
index 0000000..e3c6074
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/Callbacks.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Callbacks.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/PreparedModel.h>
+#include <nnapi/hal/1.2/Conversions.h>
+#include <nnapi/hal/1.2/PreparedModel.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+#include <utility>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+namespace {
+
+nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
+        const sp<V1_0::IPreparedModel>& preparedModel) {
+    return NN_TRY(V1_0::utils::PreparedModel::create(preparedModel));
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
+        const sp<V1_2::IPreparedModel>& preparedModel) {
+    return NN_TRY(V1_2::utils::PreparedModel::create(preparedModel));
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> convertPreparedModel(
+        const sp<IPreparedModel>& preparedModel) {
+    return NN_TRY(utils::PreparedModel::create(preparedModel));
+}
+
+nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+convertExecutionGeneralResultsHelper(const hidl_vec<V1_2::OutputShape>& outputShapes,
+                                     const V1_2::Timing& timing) {
+    return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+convertExecutionGeneralResults(const hidl_vec<V1_2::OutputShape>& outputShapes,
+                               const V1_2::Timing& timing) {
+    return hal::utils::makeExecutionFailure(
+            convertExecutionGeneralResultsHelper(outputShapes, timing));
+}
+
+}  // namespace
+
+Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus status,
+                                           const sp<V1_0::IPreparedModel>& preparedModel) {
+    if (status != V1_0::ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
+    } else if (preparedModel == nullptr) {
+        notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                       << "Returned preparedModel is nullptr");
+    } else {
+        notifyInternal(convertPreparedModel(preparedModel));
+    }
+    return Void();
+}
+
+Return<void> PreparedModelCallback::notify_1_2(V1_0::ErrorStatus status,
+                                               const sp<V1_2::IPreparedModel>& preparedModel) {
+    if (status != V1_0::ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
+    } else if (preparedModel == nullptr) {
+        notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                       << "Returned preparedModel is nullptr");
+    } else {
+        notifyInternal(convertPreparedModel(preparedModel));
+    }
+    return Void();
+}
+
+Return<void> PreparedModelCallback::notify_1_3(ErrorStatus status,
+                                               const sp<IPreparedModel>& preparedModel) {
+    if (status != ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        notifyInternal(NN_ERROR(canonical) << "preparedModel failed with " << toString(status));
+    } else if (preparedModel == nullptr) {
+        notifyInternal(NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                       << "Returned preparedModel is nullptr");
+    } else {
+        notifyInternal(convertPreparedModel(preparedModel));
+    }
+    return Void();
+}
+
+void PreparedModelCallback::notifyAsDeadObject() {
+    notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+PreparedModelCallback::Data PreparedModelCallback::get() {
+    return mData.take();
+}
+
+void PreparedModelCallback::notifyInternal(PreparedModelCallback::Data result) {
+    mData.put(std::move(result));
+}
+
+// ExecutionCallback methods begin here
+
+Return<void> ExecutionCallback::notify(V1_0::ErrorStatus status) {
+    if (status != V1_0::ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
+    } else {
+        notifyInternal({});
+    }
+    return Void();
+}
+
+Return<void> ExecutionCallback::notify_1_2(V1_0::ErrorStatus status,
+                                           const hidl_vec<V1_2::OutputShape>& outputShapes,
+                                           const V1_2::Timing& timing) {
+    if (status != V1_0::ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
+    } else {
+        notifyInternal(convertExecutionGeneralResults(outputShapes, timing));
+    }
+    return Void();
+}
+
+Return<void> ExecutionCallback::notify_1_3(ErrorStatus status,
+                                           const hidl_vec<V1_2::OutputShape>& outputShapes,
+                                           const V1_2::Timing& timing) {
+    if (status != ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        notifyInternal(NN_ERROR(canonical) << "execute failed with " << toString(status));
+    } else {
+        notifyInternal(convertExecutionGeneralResults(outputShapes, timing));
+    }
+    return Void();
+}
+
+void ExecutionCallback::notifyAsDeadObject() {
+    notifyInternal(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+ExecutionCallback::Data ExecutionCallback::get() {
+    return mData.take();
+}
+
+void ExecutionCallback::notifyInternal(ExecutionCallback::Data result) {
+    mData.put(std::move(result));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp
index 4c54e3b..949dd0d 100644
--- a/neuralnetworks/1.3/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.3/utils/src/Conversions.cpp
@@ -24,9 +24,11 @@
 #include <nnapi/SharedMemory.h>
 #include <nnapi/TypeUtils.h>
 #include <nnapi/Types.h>
+#include <nnapi/Validation.h>
 #include <nnapi/hal/1.0/Conversions.h>
 #include <nnapi/hal/1.2/Conversions.h>
 #include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
 
 #include <algorithm>
 #include <chrono>
@@ -43,6 +45,8 @@
     return static_cast<std::underlying_type_t<Type>>(value);
 }
 
+constexpr auto kVersion = android::nn::Version::ANDROID_R;
+
 }  // namespace
 
 namespace android::nn {
@@ -76,109 +80,140 @@
 using hardware::hidl_vec;
 
 template <typename Input>
-using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-Result<std::vector<ConvertOutput<Type>>> convertVec(const hidl_vec<Type>& arguments) {
-    std::vector<ConvertOutput<Type>> canonical;
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+        const hidl_vec<Type>& arguments) {
+    std::vector<unvalidatedConvertOutput<Type>> canonical;
     canonical.reserve(arguments.size());
     for (const auto& argument : arguments) {
-        canonical.push_back(NN_TRY(nn::convert(argument)));
+        canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
     }
     return canonical;
 }
 
 template <typename Type>
-Result<std::vector<ConvertOutput<Type>>> convert(const hidl_vec<Type>& arguments) {
-    return convertVec(arguments);
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const hidl_vec<Type>& arguments) {
+    return unvalidatedConvertVec(arguments);
+}
+
+template <typename Type>
+decltype(nn::unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& halObject) {
+    auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
+    const auto maybeVersion = validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return canonical;
+}
+
+template <typename Type>
+GeneralResult<std::vector<unvalidatedConvertOutput<Type>>> validatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<unvalidatedConvertOutput<Type>> canonical;
+    canonical.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        canonical.push_back(NN_TRY(validatedConvert(argument)));
+    }
+    return canonical;
 }
 
 }  // anonymous namespace
 
-Result<OperandType> convert(const hal::V1_3::OperandType& operandType) {
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_3::OperandType& operandType) {
     return static_cast<OperandType>(operandType);
 }
 
-Result<OperationType> convert(const hal::V1_3::OperationType& operationType) {
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_3::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-Result<Priority> convert(const hal::V1_3::Priority& priority) {
+GeneralResult<Priority> unvalidatedConvert(const hal::V1_3::Priority& priority) {
     return static_cast<Priority>(priority);
 }
 
-Result<Capabilities> convert(const hal::V1_3::Capabilities& capabilities) {
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_3::Capabilities& capabilities) {
     const bool validOperandTypes = std::all_of(
             capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
             [](const hal::V1_3::Capabilities::OperandPerformance& operandPerformance) {
-                const auto maybeType = convert(operandPerformance.type);
+                const auto maybeType = unvalidatedConvert(operandPerformance.type);
                 return !maybeType.has_value() ? false : validOperandType(maybeType.value());
             });
     if (!validOperandTypes) {
-        return NN_ERROR()
-               << "Invalid OperandType when converting OperandPerformance in Capabilities";
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+               << "Invalid OperandType when unvalidatedConverting OperandPerformance in "
+                  "Capabilities";
     }
 
-    auto operandPerformance = NN_TRY(convert(capabilities.operandPerformance));
-    auto table =
-            NN_TRY(Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)));
+    auto operandPerformance = NN_TRY(unvalidatedConvert(capabilities.operandPerformance));
+    auto table = NN_TRY(hal::utils::makeGeneralFailure(
+            Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)),
+            nn::ErrorStatus::GENERAL_FAILURE));
 
     return Capabilities{
-            .relaxedFloat32toFloat16PerformanceScalar =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
-            .relaxedFloat32toFloat16PerformanceTensor =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
+            .relaxedFloat32toFloat16PerformanceScalar = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
+            .relaxedFloat32toFloat16PerformanceTensor = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
             .operandPerformance = std::move(table),
-            .ifPerformance = NN_TRY(convert(capabilities.ifPerformance)),
-            .whilePerformance = NN_TRY(convert(capabilities.whilePerformance)),
+            .ifPerformance = NN_TRY(unvalidatedConvert(capabilities.ifPerformance)),
+            .whilePerformance = NN_TRY(unvalidatedConvert(capabilities.whilePerformance)),
     };
 }
 
-Result<Capabilities::OperandPerformance> convert(
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
         const hal::V1_3::Capabilities::OperandPerformance& operandPerformance) {
     return Capabilities::OperandPerformance{
-            .type = NN_TRY(convert(operandPerformance.type)),
-            .info = NN_TRY(convert(operandPerformance.info)),
+            .type = NN_TRY(unvalidatedConvert(operandPerformance.type)),
+            .info = NN_TRY(unvalidatedConvert(operandPerformance.info)),
     };
 }
 
-Result<Operation> convert(const hal::V1_3::Operation& operation) {
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_3::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-Result<Operand::LifeTime> convert(const hal::V1_3::OperandLifeTime& operandLifeTime) {
+GeneralResult<Operand::LifeTime> unvalidatedConvert(
+        const hal::V1_3::OperandLifeTime& operandLifeTime) {
     return static_cast<Operand::LifeTime>(operandLifeTime);
 }
 
-Result<Operand> convert(const hal::V1_3::Operand& operand) {
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_3::Operand& operand) {
     return Operand{
-            .type = NN_TRY(convert(operand.type)),
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
             .dimensions = operand.dimensions,
             .scale = operand.scale,
             .zeroPoint = operand.zeroPoint,
-            .lifetime = NN_TRY(convert(operand.lifetime)),
-            .location = NN_TRY(convert(operand.location)),
-            .extraParams = NN_TRY(convert(operand.extraParams)),
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
     };
 }
 
-Result<Model> convert(const hal::V1_3::Model& model) {
+GeneralResult<Model> unvalidatedConvert(const hal::V1_3::Model& model) {
     return Model{
-            .main = NN_TRY(convert(model.main)),
-            .referenced = NN_TRY(convert(model.referenced)),
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .main = NN_TRY(unvalidatedConvert(model.main)),
+            .referenced = NN_TRY(unvalidatedConvert(model.referenced)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
-            .extensionNameToPrefix = NN_TRY(convert(model.extensionNameToPrefix)),
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
     };
 }
 
-Result<Model::Subgraph> convert(const hal::V1_3::Subgraph& subgraph) {
-    auto operations = NN_TRY(convert(subgraph.operations));
+GeneralResult<Model::Subgraph> unvalidatedConvert(const hal::V1_3::Subgraph& subgraph) {
+    auto operations = NN_TRY(unvalidatedConvert(subgraph.operations));
 
     // Verify number of consumers.
     const auto numberOfConsumers =
@@ -186,25 +221,26 @@
     CHECK(subgraph.operands.size() == numberOfConsumers.size());
     for (size_t i = 0; i < subgraph.operands.size(); ++i) {
         if (subgraph.operands[i].numberOfConsumers != numberOfConsumers[i]) {
-            return NN_ERROR() << "Invalid numberOfConsumers for operand " << i << ", expected "
-                              << numberOfConsumers[i] << " but found "
-                              << subgraph.operands[i].numberOfConsumers;
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                   << "Invalid numberOfConsumers for operand " << i << ", expected "
+                   << numberOfConsumers[i] << " but found "
+                   << subgraph.operands[i].numberOfConsumers;
         }
     }
 
     return Model::Subgraph{
-            .operands = NN_TRY(convert(subgraph.operands)),
+            .operands = NN_TRY(unvalidatedConvert(subgraph.operands)),
             .operations = std::move(operations),
             .inputIndexes = subgraph.inputIndexes,
             .outputIndexes = subgraph.outputIndexes,
     };
 }
 
-Result<BufferDesc> convert(const hal::V1_3::BufferDesc& bufferDesc) {
+GeneralResult<BufferDesc> unvalidatedConvert(const hal::V1_3::BufferDesc& bufferDesc) {
     return BufferDesc{.dimensions = bufferDesc.dimensions};
 }
 
-Result<BufferRole> convert(const hal::V1_3::BufferRole& bufferRole) {
+GeneralResult<BufferRole> unvalidatedConvert(const hal::V1_3::BufferRole& bufferRole) {
     return BufferRole{
             .modelIndex = bufferRole.modelIndex,
             .ioIndex = bufferRole.ioIndex,
@@ -212,15 +248,16 @@
     };
 }
 
-Result<Request> convert(const hal::V1_3::Request& request) {
+GeneralResult<Request> unvalidatedConvert(const hal::V1_3::Request& request) {
     return Request{
-            .inputs = NN_TRY(convert(request.inputs)),
-            .outputs = NN_TRY(convert(request.outputs)),
-            .pools = NN_TRY(convert(request.pools)),
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
+            .pools = NN_TRY(unvalidatedConvert(request.pools)),
     };
 }
 
-Result<Request::MemoryPool> convert(const hal::V1_3::Request::MemoryPool& memoryPool) {
+GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const hal::V1_3::Request::MemoryPool& memoryPool) {
     using Discriminator = hal::V1_3::Request::MemoryPool::hidl_discriminator;
     switch (memoryPool.getDiscriminator()) {
         case Discriminator::hidlMemory:
@@ -228,16 +265,19 @@
         case Discriminator::token:
             return static_cast<Request::MemoryDomainToken>(memoryPool.token());
     }
-    return NN_ERROR() << "Invalid Request::MemoryPool discriminator "
-                      << underlyingType(memoryPool.getDiscriminator());
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "Invalid Request::MemoryPool discriminator "
+           << underlyingType(memoryPool.getDiscriminator());
 }
 
-Result<OptionalTimePoint> convert(const hal::V1_3::OptionalTimePoint& optionalTimePoint) {
+GeneralResult<OptionalTimePoint> unvalidatedConvert(
+        const hal::V1_3::OptionalTimePoint& optionalTimePoint) {
     constexpr auto kTimePointMaxCount = TimePoint::max().time_since_epoch().count();
-    const auto makeTimePoint = [](uint64_t count) -> Result<OptionalTimePoint> {
+    const auto makeTimePoint = [](uint64_t count) -> GeneralResult<OptionalTimePoint> {
         if (count > kTimePointMaxCount) {
-            return NN_ERROR()
-                   << "Unable to convert OptionalTimePoint because the count exceeds the max";
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                   << "Unable to unvalidatedConvert OptionalTimePoint because the count exceeds "
+                      "the max";
         }
         const auto nanoseconds = std::chrono::nanoseconds{count};
         return TimePoint{nanoseconds};
@@ -250,17 +290,19 @@
         case Discriminator::nanosecondsSinceEpoch:
             return makeTimePoint(optionalTimePoint.nanosecondsSinceEpoch());
     }
-    return NN_ERROR() << "Invalid OptionalTimePoint discriminator "
-                      << underlyingType(optionalTimePoint.getDiscriminator());
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "Invalid OptionalTimePoint discriminator "
+           << underlyingType(optionalTimePoint.getDiscriminator());
 }
 
-Result<OptionalTimeoutDuration> convert(
+GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
         const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration) {
     constexpr auto kTimeoutDurationMaxCount = TimeoutDuration::max().count();
-    const auto makeTimeoutDuration = [](uint64_t count) -> Result<OptionalTimeoutDuration> {
+    const auto makeTimeoutDuration = [](uint64_t count) -> GeneralResult<OptionalTimeoutDuration> {
         if (count > kTimeoutDurationMaxCount) {
-            return NN_ERROR()
-                   << "Unable to convert OptionalTimeoutDuration because the count exceeds the max";
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                   << "Unable to unvalidatedConvert OptionalTimeoutDuration because the count "
+                      "exceeds the max";
         }
         return TimeoutDuration{count};
     };
@@ -272,11 +314,12 @@
         case Discriminator::nanoseconds:
             return makeTimeoutDuration(optionalTimeoutDuration.nanoseconds());
     }
-    return NN_ERROR() << "Invalid OptionalTimeoutDuration discriminator "
-                      << underlyingType(optionalTimeoutDuration.getDiscriminator());
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "Invalid OptionalTimeoutDuration discriminator "
+           << underlyingType(optionalTimeoutDuration.getDiscriminator());
 }
 
-Result<ErrorStatus> convert(const hal::V1_3::ErrorStatus& status) {
+GeneralResult<ErrorStatus> unvalidatedConvert(const hal::V1_3::ErrorStatus& status) {
     switch (status) {
         case hal::V1_3::ErrorStatus::NONE:
         case hal::V1_3::ErrorStatus::DEVICE_UNAVAILABLE:
@@ -289,12 +332,54 @@
         case hal::V1_3::ErrorStatus::RESOURCE_EXHAUSTED_PERSISTENT:
             return static_cast<ErrorStatus>(status);
     }
-    return NN_ERROR() << "Invalid ErrorStatus " << underlyingType(status);
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "Invalid ErrorStatus " << underlyingType(status);
 }
 
-Result<std::vector<BufferRole>> convert(
+GeneralResult<Priority> convert(const hal::V1_3::Priority& priority) {
+    return validatedConvert(priority);
+}
+
+GeneralResult<Capabilities> convert(const hal::V1_3::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+GeneralResult<Model> convert(const hal::V1_3::Model& model) {
+    return validatedConvert(model);
+}
+
+GeneralResult<BufferDesc> convert(const hal::V1_3::BufferDesc& bufferDesc) {
+    return validatedConvert(bufferDesc);
+}
+
+GeneralResult<Request> convert(const hal::V1_3::Request& request) {
+    return validatedConvert(request);
+}
+
+GeneralResult<OptionalTimePoint> convert(const hal::V1_3::OptionalTimePoint& optionalTimePoint) {
+    return validatedConvert(optionalTimePoint);
+}
+
+GeneralResult<OptionalTimeoutDuration> convert(
+        const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration) {
+    return validatedConvert(optionalTimeoutDuration);
+}
+
+GeneralResult<ErrorStatus> convert(const hal::V1_3::ErrorStatus& errorStatus) {
+    return validatedConvert(errorStatus);
+}
+
+GeneralResult<SharedHandle> convert(const hardware::hidl_handle& handle) {
+    return validatedConvert(handle);
+}
+
+GeneralResult<Memory> convert(const hardware::hidl_memory& memory) {
+    return validatedConvert(memory);
+}
+
+GeneralResult<std::vector<BufferRole>> convert(
         const hardware::hidl_vec<hal::V1_3::BufferRole>& bufferRoles) {
-    return convertVec(bufferRoles);
+    return validatedConvert(bufferRoles);
 }
 
 }  // namespace android::nn
@@ -302,87 +387,120 @@
 namespace android::hardware::neuralnetworks::V1_3::utils {
 namespace {
 
-using utils::convert;
+using utils::unvalidatedConvert;
 
-nn::Result<V1_0::PerformanceInfo> convert(
+nn::GeneralResult<V1_0::PerformanceInfo> unvalidatedConvert(
         const nn::Capabilities::PerformanceInfo& performanceInfo) {
-    return V1_0::utils::convert(performanceInfo);
+    return V1_0::utils::unvalidatedConvert(performanceInfo);
 }
 
-nn::Result<V1_0::DataLocation> convert(const nn::DataLocation& dataLocation) {
-    return V1_0::utils::convert(dataLocation);
+nn::GeneralResult<V1_0::DataLocation> unvalidatedConvert(const nn::DataLocation& dataLocation) {
+    return V1_0::utils::unvalidatedConvert(dataLocation);
 }
 
-nn::Result<hidl_vec<uint8_t>> convert(const nn::Model::OperandValues& operandValues) {
-    return V1_0::utils::convert(operandValues);
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues) {
+    return V1_0::utils::unvalidatedConvert(operandValues);
 }
 
-nn::Result<hidl_memory> convert(const nn::Memory& memory) {
-    return V1_0::utils::convert(memory);
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
+    return V1_2::utils::unvalidatedConvert(handle);
 }
 
-nn::Result<V1_0::RequestArgument> convert(const nn::Request::Argument& argument) {
-    return V1_0::utils::convert(argument);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Memory& memory) {
+    return V1_0::utils::unvalidatedConvert(memory);
 }
 
-nn::Result<V1_2::Operand::ExtraParams> convert(const nn::Operand::ExtraParams& extraParams) {
-    return V1_2::utils::convert(extraParams);
+nn::GeneralResult<V1_0::RequestArgument> unvalidatedConvert(const nn::Request::Argument& argument) {
+    return V1_0::utils::unvalidatedConvert(argument);
 }
 
-nn::Result<V1_2::Model::ExtensionNameAndPrefix> convert(
+nn::GeneralResult<V1_2::Operand::ExtraParams> unvalidatedConvert(
+        const nn::Operand::ExtraParams& extraParams) {
+    return V1_2::utils::unvalidatedConvert(extraParams);
+}
+
+nn::GeneralResult<V1_2::Model::ExtensionNameAndPrefix> unvalidatedConvert(
         const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) {
-    return V1_2::utils::convert(extensionNameAndPrefix);
+    return V1_2::utils::unvalidatedConvert(extensionNameAndPrefix);
 }
 
 template <typename Input>
-using ConvertOutput = std::decay_t<decltype(convert(std::declval<Input>()).value())>;
+using unvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
 
 template <typename Type>
-nn::Result<hidl_vec<ConvertOutput<Type>>> convertVec(const std::vector<Type>& arguments) {
-    hidl_vec<ConvertOutput<Type>> halObject(arguments.size());
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+        const std::vector<Type>& arguments) {
+    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
     for (size_t i = 0; i < arguments.size(); ++i) {
-        halObject[i] = NN_TRY(convert(arguments[i]));
+        halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
     }
     return halObject;
 }
 
 template <typename Type>
-nn::Result<hidl_vec<ConvertOutput<Type>>> convert(const std::vector<Type>& arguments) {
-    return convertVec(arguments);
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    return unvalidatedConvertVec(arguments);
 }
 
-nn::Result<Request::MemoryPool> makeMemoryPool(const nn::Memory& memory) {
+nn::GeneralResult<Request::MemoryPool> makeMemoryPool(const nn::Memory& memory) {
     Request::MemoryPool ret;
-    ret.hidlMemory(NN_TRY(convert(memory)));
+    ret.hidlMemory(NN_TRY(unvalidatedConvert(memory)));
     return ret;
 }
 
-nn::Result<Request::MemoryPool> makeMemoryPool(const nn::Request::MemoryDomainToken& token) {
+nn::GeneralResult<Request::MemoryPool> makeMemoryPool(const nn::Request::MemoryDomainToken& token) {
     Request::MemoryPool ret;
     ret.token(underlyingType(token));
     return ret;
 }
 
-nn::Result<Request::MemoryPool> makeMemoryPool(
-        const std::shared_ptr<const nn::IBuffer>& /*buffer*/) {
-    return NN_ERROR() << "Unable to make memory pool from IBuffer";
+nn::GeneralResult<Request::MemoryPool> makeMemoryPool(const nn::SharedBuffer& /*buffer*/) {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Unable to make memory pool from IBuffer";
+}
+
+using utils::unvalidatedConvert;
+
+template <typename Type>
+decltype(unvalidatedConvert(std::declval<Type>())) validatedConvert(const Type& canonical) {
+    const auto maybeVersion = nn::validate(canonical);
+    if (!maybeVersion.has_value()) {
+        return nn::error() << maybeVersion.error();
+    }
+    const auto version = maybeVersion.value();
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return unvalidatedConvert(canonical);
+}
+
+template <typename Type>
+nn::GeneralResult<hidl_vec<unvalidatedConvertOutput<Type>>> validatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<unvalidatedConvertOutput<Type>> halObject(arguments.size());
+    for (size_t i = 0; i < arguments.size(); ++i) {
+        halObject[i] = NN_TRY(validatedConvert(arguments[i]));
+    }
+    return halObject;
 }
 
 }  // anonymous namespace
 
-nn::Result<OperandType> convert(const nn::OperandType& operandType) {
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
     return static_cast<OperandType>(operandType);
 }
 
-nn::Result<OperationType> convert(const nn::OperationType& operationType) {
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
     return static_cast<OperationType>(operationType);
 }
 
-nn::Result<Priority> convert(const nn::Priority& priority) {
+nn::GeneralResult<Priority> unvalidatedConvert(const nn::Priority& priority) {
     return static_cast<Priority>(priority);
 }
 
-nn::Result<Capabilities> convert(const nn::Capabilities& capabilities) {
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities) {
     std::vector<nn::Capabilities::OperandPerformance> operandPerformance;
     operandPerformance.reserve(capabilities.operandPerformance.asVector().size());
     std::copy_if(capabilities.operandPerformance.asVector().begin(),
@@ -393,69 +511,72 @@
                  });
 
     return Capabilities{
-            .relaxedFloat32toFloat16PerformanceScalar =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
-            .relaxedFloat32toFloat16PerformanceTensor =
-                    NN_TRY(convert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
-            .operandPerformance = NN_TRY(convert(operandPerformance)),
-            .ifPerformance = NN_TRY(convert(capabilities.ifPerformance)),
-            .whilePerformance = NN_TRY(convert(capabilities.whilePerformance)),
+            .relaxedFloat32toFloat16PerformanceScalar = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
+            .relaxedFloat32toFloat16PerformanceTensor = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
+            .operandPerformance = NN_TRY(unvalidatedConvert(operandPerformance)),
+            .ifPerformance = NN_TRY(unvalidatedConvert(capabilities.ifPerformance)),
+            .whilePerformance = NN_TRY(unvalidatedConvert(capabilities.whilePerformance)),
     };
 }
 
-nn::Result<Capabilities::OperandPerformance> convert(
+nn::GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
         const nn::Capabilities::OperandPerformance& operandPerformance) {
     return Capabilities::OperandPerformance{
-            .type = NN_TRY(convert(operandPerformance.type)),
-            .info = NN_TRY(convert(operandPerformance.info)),
+            .type = NN_TRY(unvalidatedConvert(operandPerformance.type)),
+            .info = NN_TRY(unvalidatedConvert(operandPerformance.info)),
     };
 }
 
-nn::Result<Operation> convert(const nn::Operation& operation) {
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
     return Operation{
-            .type = NN_TRY(convert(operation.type)),
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
             .inputs = operation.inputs,
             .outputs = operation.outputs,
     };
 }
 
-nn::Result<OperandLifeTime> convert(const nn::Operand::LifeTime& operandLifeTime) {
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(
+        const nn::Operand::LifeTime& operandLifeTime) {
     if (operandLifeTime == nn::Operand::LifeTime::POINTER) {
-        return NN_ERROR() << "Model cannot be converted because it contains pointer-based memory";
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Model cannot be unvalidatedConverted because it contains pointer-based memory";
     }
     return static_cast<OperandLifeTime>(operandLifeTime);
 }
 
-nn::Result<Operand> convert(const nn::Operand& operand) {
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand) {
     return Operand{
-            .type = NN_TRY(convert(operand.type)),
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
             .dimensions = operand.dimensions,
             .numberOfConsumers = 0,
             .scale = operand.scale,
             .zeroPoint = operand.zeroPoint,
-            .lifetime = NN_TRY(convert(operand.lifetime)),
-            .location = NN_TRY(convert(operand.location)),
-            .extraParams = NN_TRY(convert(operand.extraParams)),
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
     };
 }
 
-nn::Result<Model> convert(const nn::Model& model) {
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
     if (!hal::utils::hasNoPointerData(model)) {
-        return NN_ERROR() << "Model cannot be converted because it contains pointer-based memory";
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Model cannot be unvalidatedConverted because it contains pointer-based memory";
     }
 
     return Model{
-            .main = NN_TRY(convert(model.main)),
-            .referenced = NN_TRY(convert(model.referenced)),
-            .operandValues = NN_TRY(convert(model.operandValues)),
-            .pools = NN_TRY(convert(model.pools)),
+            .main = NN_TRY(unvalidatedConvert(model.main)),
+            .referenced = NN_TRY(unvalidatedConvert(model.referenced)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
             .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
-            .extensionNameToPrefix = NN_TRY(convert(model.extensionNameToPrefix)),
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
     };
 }
 
-nn::Result<Subgraph> convert(const nn::Model::Subgraph& subgraph) {
-    auto operands = NN_TRY(convert(subgraph.operands));
+nn::GeneralResult<Subgraph> unvalidatedConvert(const nn::Model::Subgraph& subgraph) {
+    auto operands = NN_TRY(unvalidatedConvert(subgraph.operands));
 
     // Update number of consumers.
     const auto numberOfConsumers =
@@ -467,17 +588,17 @@
 
     return Subgraph{
             .operands = std::move(operands),
-            .operations = NN_TRY(convert(subgraph.operations)),
+            .operations = NN_TRY(unvalidatedConvert(subgraph.operations)),
             .inputIndexes = subgraph.inputIndexes,
             .outputIndexes = subgraph.outputIndexes,
     };
 }
 
-nn::Result<BufferDesc> convert(const nn::BufferDesc& bufferDesc) {
+nn::GeneralResult<BufferDesc> unvalidatedConvert(const nn::BufferDesc& bufferDesc) {
     return BufferDesc{.dimensions = bufferDesc.dimensions};
 }
 
-nn::Result<BufferRole> convert(const nn::BufferRole& bufferRole) {
+nn::GeneralResult<BufferRole> unvalidatedConvert(const nn::BufferRole& bufferRole) {
     return BufferRole{
             .modelIndex = bufferRole.modelIndex,
             .ioIndex = bufferRole.ioIndex,
@@ -485,50 +606,56 @@
     };
 }
 
-nn::Result<Request> convert(const nn::Request& request) {
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request) {
     if (!hal::utils::hasNoPointerData(request)) {
-        return NN_ERROR() << "Request cannot be converted because it contains pointer-based memory";
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Request cannot be unvalidatedConverted because it contains pointer-based memory";
     }
 
     return Request{
-            .inputs = NN_TRY(convert(request.inputs)),
-            .outputs = NN_TRY(convert(request.outputs)),
-            .pools = NN_TRY(convert(request.pools)),
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
+            .pools = NN_TRY(unvalidatedConvert(request.pools)),
     };
 }
 
-nn::Result<Request::MemoryPool> convert(const nn::Request::MemoryPool& memoryPool) {
+nn::GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const nn::Request::MemoryPool& memoryPool) {
     return std::visit([](const auto& o) { return makeMemoryPool(o); }, memoryPool);
 }
 
-nn::Result<OptionalTimePoint> convert(const nn::OptionalTimePoint& optionalTimePoint) {
+nn::GeneralResult<OptionalTimePoint> unvalidatedConvert(
+        const nn::OptionalTimePoint& optionalTimePoint) {
     OptionalTimePoint ret;
     if (optionalTimePoint.has_value()) {
         const auto count = optionalTimePoint.value().time_since_epoch().count();
         if (count < 0) {
-            return NN_ERROR() << "Unable to convert OptionalTimePoint because time since epoch "
-                                 "count is negative";
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                   << "Unable to unvalidatedConvert OptionalTimePoint because time since epoch "
+                      "count is "
+                      "negative";
         }
         ret.nanosecondsSinceEpoch(count);
     }
     return ret;
 }
 
-nn::Result<OptionalTimeoutDuration> convert(
+nn::GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
         const nn::OptionalTimeoutDuration& optionalTimeoutDuration) {
     OptionalTimeoutDuration ret;
     if (optionalTimeoutDuration.has_value()) {
         const auto count = optionalTimeoutDuration.value().count();
         if (count < 0) {
-            return NN_ERROR()
-                   << "Unable to convert OptionalTimeoutDuration because count is negative";
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                   << "Unable to unvalidatedConvert OptionalTimeoutDuration because count is "
+                      "negative";
         }
         ret.nanoseconds(count);
     }
     return ret;
 }
 
-nn::Result<ErrorStatus> convert(const nn::ErrorStatus& errorStatus) {
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& errorStatus) {
     switch (errorStatus) {
         case nn::ErrorStatus::NONE:
         case nn::ErrorStatus::DEVICE_UNAVAILABLE:
@@ -545,8 +672,49 @@
     }
 }
 
-nn::Result<hidl_vec<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles) {
-    return convertVec(bufferRoles);
+nn::GeneralResult<Priority> convert(const nn::Priority& priority) {
+    return validatedConvert(priority);
+}
+
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+    return validatedConvert(model);
+}
+
+nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc) {
+    return validatedConvert(bufferDesc);
+}
+
+nn::GeneralResult<Request> convert(const nn::Request& request) {
+    return validatedConvert(request);
+}
+
+nn::GeneralResult<OptionalTimePoint> convert(const nn::OptionalTimePoint& optionalTimePoint) {
+    return validatedConvert(optionalTimePoint);
+}
+
+nn::GeneralResult<OptionalTimeoutDuration> convert(
+        const nn::OptionalTimeoutDuration& optionalTimeoutDuration) {
+    return validatedConvert(optionalTimeoutDuration);
+}
+
+nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus) {
+    return validatedConvert(errorStatus);
+}
+
+nn::GeneralResult<hidl_handle> convert(const nn::SharedHandle& handle) {
+    return validatedConvert(handle);
+}
+
+nn::GeneralResult<hidl_memory> convert(const nn::Memory& memory) {
+    return validatedConvert(memory);
+}
+
+nn::GeneralResult<hidl_vec<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles) {
+    return validatedConvert(bufferRoles);
 }
 
 }  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/Device.cpp b/neuralnetworks/1.3/utils/src/Device.cpp
new file mode 100644
index 0000000..82837ba
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/Device.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Device.h"
+
+#include "Buffer.h"
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.1/Conversions.h>
+#include <nnapi/hal/1.2/Conversions.h>
+#include <nnapi/hal/1.2/Device.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <any>
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+namespace {
+
+nn::GeneralResult<hidl_vec<sp<IPreparedModel>>> convert(
+        const std::vector<nn::SharedPreparedModel>& preparedModels) {
+    hidl_vec<sp<IPreparedModel>> hidlPreparedModels(preparedModels.size());
+    for (size_t i = 0; i < preparedModels.size(); ++i) {
+        std::any underlyingResource = preparedModels[i]->getUnderlyingResource();
+        if (const auto* hidlPreparedModel =
+                    std::any_cast<sp<IPreparedModel>>(&underlyingResource)) {
+            hidlPreparedModels[i] = *hidlPreparedModel;
+        } else {
+            return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+                   << "Unable to convert from nn::IPreparedModel to V1_3::IPreparedModel";
+        }
+    }
+    return hidlPreparedModels;
+}
+
+nn::GeneralResult<nn::SharedBuffer> convert(
+        nn::GeneralResult<std::shared_ptr<const Buffer>> result) {
+    return NN_TRY(std::move(result));
+}
+
+nn::GeneralResult<nn::Capabilities> initCapabilities(V1_3::IDevice* device) {
+    CHECK(device != nullptr);
+
+    nn::GeneralResult<nn::Capabilities> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                                 << "uninitialized";
+    const auto cb = [&result](ErrorStatus status, const Capabilities& capabilities) {
+        if (status != ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical) << "getCapabilities_1_3 failed with " << toString(status);
+        } else {
+            result = nn::convert(capabilities);
+        }
+    };
+
+    const auto ret = device->getCapabilities_1_3(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const Device>> Device::create(std::string name,
+                                                                sp<V1_3::IDevice> device) {
+    if (name.empty()) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_3::utils::Device::create must have non-empty name";
+    }
+    if (device == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_3::utils::Device::create must have non-null device";
+    }
+
+    auto versionString = NN_TRY(V1_2::utils::initVersionString(device.get()));
+    const auto deviceType = NN_TRY(V1_2::utils::initDeviceType(device.get()));
+    auto extensions = NN_TRY(V1_2::utils::initExtensions(device.get()));
+    auto capabilities = NN_TRY(initCapabilities(device.get()));
+    const auto numberOfCacheFilesNeeded =
+            NN_TRY(V1_2::utils::initNumberOfCacheFilesNeeded(device.get()));
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
+    return std::make_shared<const Device>(
+            PrivateConstructorTag{}, std::move(name), std::move(versionString), deviceType,
+            std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded,
+            std::move(device), std::move(deathHandler));
+}
+
+Device::Device(PrivateConstructorTag /*tag*/, std::string name, std::string versionString,
+               nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
+               nn::Capabilities capabilities,
+               std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded, sp<V1_3::IDevice> device,
+               hal::utils::DeathHandler deathHandler)
+    : kName(std::move(name)),
+      kVersionString(std::move(versionString)),
+      kDeviceType(deviceType),
+      kExtensions(std::move(extensions)),
+      kCapabilities(std::move(capabilities)),
+      kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded),
+      kDevice(std::move(device)),
+      kDeathHandler(std::move(deathHandler)) {}
+
+const std::string& Device::getName() const {
+    return kName;
+}
+
+const std::string& Device::getVersionString() const {
+    return kVersionString;
+}
+
+nn::Version Device::getFeatureLevel() const {
+    return nn::Version::ANDROID_R;
+}
+
+nn::DeviceType Device::getType() const {
+    return kDeviceType;
+}
+
+const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
+    return kExtensions;
+}
+
+const nn::Capabilities& Device::getCapabilities() const {
+    return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
+    return kNumberOfCacheFilesNeeded;
+}
+
+nn::GeneralResult<void> Device::wait() const {
+    const auto ret = kDevice->ping();
+    HANDLE_TRANSPORT_FAILURE(ret);
+    return {};
+}
+
+nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+
+    nn::GeneralResult<std::vector<bool>> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                                  << "uninitialized";
+    auto cb = [&result, &model](ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
+        if (status != ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical)
+                     << "IDevice::getSupportedOperations_1_3 failed with " << toString(status);
+        } else if (supportedOperations.size() != model.main.operations.size()) {
+            result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                     << "IDevice::getSupportedOperations_1_3 returned vector of size "
+                     << supportedOperations.size() << " but expected "
+                     << model.main.operations.size();
+        } else {
+            result = supportedOperations;
+        }
+    };
+
+    const auto ret = kDevice->getSupportedOperations_1_3(hidlModel, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
+        const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+        nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+    const auto hidlPreference = NN_TRY(V1_1::utils::convert(preference));
+    const auto hidlPriority = NN_TRY(convert(priority));
+    const auto hidlDeadline = NN_TRY(convert(deadline));
+    const auto hidlModelCache = NN_TRY(V1_2::utils::convert(modelCache));
+    const auto hidlDataCache = NN_TRY(V1_2::utils::convert(dataCache));
+    const auto hidlToken = token;
+
+    const auto cb = sp<PreparedModelCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret =
+            kDevice->prepareModel_1_3(hidlModel, hidlPreference, hidlPriority, hidlDeadline,
+                                      hidlModelCache, hidlDataCache, hidlToken, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    if (status != ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        return NN_ERROR(canonical) << "prepareModel_1_3 failed with " << toString(status);
+    }
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache(
+        nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    const auto hidlDeadline = NN_TRY(convert(deadline));
+    const auto hidlModelCache = NN_TRY(V1_2::utils::convert(modelCache));
+    const auto hidlDataCache = NN_TRY(V1_2::utils::convert(dataCache));
+    const auto hidlToken = token;
+
+    const auto cb = sp<PreparedModelCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kDevice->prepareModelFromCache_1_3(hidlDeadline, hidlModelCache, hidlDataCache,
+                                                        hidlToken, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    if (status != ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        return NN_ERROR(canonical) << "prepareModelFromCache_1_3 failed with " << toString(status);
+    }
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedBuffer> Device::allocate(
+        const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+        const std::vector<nn::BufferRole>& inputRoles,
+        const std::vector<nn::BufferRole>& outputRoles) const {
+    const auto hidlDesc = NN_TRY(convert(desc));
+    const auto hidlPreparedModels = NN_TRY(convert(preparedModels));
+    const auto hidlInputRoles = NN_TRY(convert(inputRoles));
+    const auto hidlOutputRoles = NN_TRY(convert(outputRoles));
+
+    nn::GeneralResult<nn::SharedBuffer> result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                                 << "uninitialized";
+    auto cb = [&result](ErrorStatus status, const sp<IBuffer>& buffer, uint32_t token) {
+        if (status != ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical) << "IDevice::allocate failed with " << toString(status);
+        } else if (buffer == nullptr) {
+            result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Returned buffer is nullptr";
+        } else if (token == 0) {
+            result = NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Returned token is invalid (0)";
+        } else {
+            result = convert(
+                    Buffer::create(buffer, static_cast<nn::Request::MemoryDomainToken>(token)));
+        }
+    };
+
+    const auto ret =
+            kDevice->allocate(hidlDesc, hidlPreparedModels, hidlInputRoles, hidlOutputRoles, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/PreparedModel.cpp b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
new file mode 100644
index 0000000..49b9b0b
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PreparedModel.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.2/Conversions.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+namespace {
+
+nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+convertExecutionResultsHelper(const hidl_vec<V1_2::OutputShape>& outputShapes,
+                              const V1_2::Timing& timing) {
+    return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertExecutionResults(
+        const hidl_vec<V1_2::OutputShape>& outputShapes, const V1_2::Timing& timing) {
+    return hal::utils::makeExecutionFailure(convertExecutionResultsHelper(outputShapes, timing));
+}
+
+nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> convertFencedExecutionCallbackResults(
+        const V1_2::Timing& timingLaunched, const V1_2::Timing& timingFenced) {
+    return std::make_pair(NN_TRY(nn::convert(timingLaunched)), NN_TRY(nn::convert(timingFenced)));
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+convertExecuteFencedResults(const hidl_handle& syncFence,
+                            const sp<IFencedExecutionCallback>& callback) {
+    auto resultSyncFence = nn::SyncFence::createAsSignaled();
+    if (syncFence.getNativeHandle() != nullptr) {
+        auto sharedHandle = NN_TRY(nn::convert(syncFence));
+        resultSyncFence = NN_TRY(hal::utils::makeGeneralFailure(
+                nn::SyncFence::create(std::move(sharedHandle)), nn::ErrorStatus::GENERAL_FAILURE));
+    }
+
+    if (callback == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "callback is null";
+    }
+
+    // Create callback which can be used to retrieve the execution error status and timings.
+    nn::ExecuteFencedInfoCallback resultCallback =
+            [callback]() -> nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> {
+        nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> result =
+                NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
+        auto cb = [&result](ErrorStatus status, const V1_2::Timing& timingLaunched,
+                            const V1_2::Timing& timingFenced) {
+            if (status != ErrorStatus::NONE) {
+                const auto canonical =
+                        nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+                result = NN_ERROR(canonical) << "getExecutionInfo failed with " << toString(status);
+            } else {
+                result = convertFencedExecutionCallbackResults(timingLaunched, timingFenced);
+            }
+        };
+
+        const auto ret = callback->getExecutionInfo(cb);
+        HANDLE_TRANSPORT_FAILURE(ret);
+
+        return result;
+    };
+
+    return std::make_pair(std::move(resultSyncFence), std::move(resultCallback));
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
+        sp<V1_3::IPreparedModel> preparedModel) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_3::utils::PreparedModel::create must have non-null preparedModel";
+    }
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(preparedModel));
+    return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, std::move(preparedModel),
+                                                 std::move(deathHandler));
+}
+
+PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, sp<V1_3::IPreparedModel> preparedModel,
+                             hal::utils::DeathHandler deathHandler)
+    : kPreparedModel(std::move(preparedModel)), kDeathHandler(std::move(deathHandler)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeSynchronously(const Request& request, V1_2::MeasureTiming measure,
+                                    const OptionalTimePoint& deadline,
+                                    const OptionalTimeoutDuration& loopTimeoutDuration) const {
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
+            NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
+    const auto cb = [&result](ErrorStatus status, const hidl_vec<V1_2::OutputShape>& outputShapes,
+                              const V1_2::Timing& timing) {
+        if (status != ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical) << "executeSynchronously failed with " << toString(status);
+        } else {
+            result = convertExecutionResults(outputShapes, timing);
+        }
+    };
+
+    const auto ret = kPreparedModel->executeSynchronously_1_3(request, measure, deadline,
+                                                              loopTimeoutDuration, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return result;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeAsynchronously(const Request& request, V1_2::MeasureTiming measure,
+                                     const OptionalTimePoint& deadline,
+                                     const OptionalTimeoutDuration& loopTimeoutDuration) const {
+    const auto cb = sp<ExecutionCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret =
+            kPreparedModel->execute_1_3(request, measure, deadline, loopTimeoutDuration, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    if (status != ErrorStatus::NONE) {
+        const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+        return NN_ERROR(canonical) << "executeAsynchronously failed with " << toString(status);
+    }
+
+    return cb->get();
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalTimePoint& deadline,
+        const nn::OptionalTimeoutDuration& loopTimeoutDuration) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::makeExecutionFailure(
+            hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared)));
+
+    const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
+    const auto hidlMeasure =
+            NN_TRY(hal::utils::makeExecutionFailure(V1_2::utils::convert(measure)));
+    const auto hidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+    const auto hidlLoopTimeoutDuration =
+            NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> result =
+            NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
+    const bool preferSynchronous = true;
+
+    // Execute synchronously if allowed.
+    if (preferSynchronous) {
+        result = executeSynchronously(hidlRequest, hidlMeasure, hidlDeadline,
+                                      hidlLoopTimeoutDuration);
+    }
+
+    // Run asymchronous execution if execution has not already completed.
+    if (!result.has_value()) {
+        result = executeAsynchronously(hidlRequest, hidlMeasure, hidlDeadline,
+                                       hidlLoopTimeoutDuration);
+    }
+
+    // Flush output buffers if suxcessful execution.
+    if (result.has_value()) {
+        NN_TRY(hal::utils::makeExecutionFailure(
+                hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared)));
+    }
+
+    return result;
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFenced(const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
+                             nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
+                             const nn::OptionalTimeoutDuration& loopTimeoutDuration,
+                             const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&request, &maybeRequestInShared));
+
+    const auto hidlRequest = NN_TRY(convert(requestInShared));
+    const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
+    const auto hidlMeasure = NN_TRY(V1_2::utils::convert(measure));
+    const auto hidlDeadline = NN_TRY(convert(deadline));
+    const auto hidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
+    const auto hidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> result =
+            NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "uninitialized";
+    auto cb = [&result](ErrorStatus status, const hidl_handle& syncFence,
+                        const sp<IFencedExecutionCallback>& callback) {
+        if (status != ErrorStatus::NONE) {
+            const auto canonical = nn::convert(status).value_or(nn::ErrorStatus::GENERAL_FAILURE);
+            result = NN_ERROR(canonical) << "executeFenced failed with " << toString(status);
+        } else {
+            result = convertExecuteFencedResults(syncFence, callback);
+        }
+    };
+
+    const auto ret = kPreparedModel->executeFenced(hidlRequest, hidlWaitFor, hidlMeasure,
+                                                   hidlDeadline, hidlLoopTimeoutDuration,
+                                                   hidlTimeoutDurationAfterFence, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+    auto [syncFence, callback] = NN_TRY(std::move(result));
+
+    // If executeFenced required the request memory to be moved into shared memory, block here until
+    // the fenced execution has completed and flush the memory back.
+    if (maybeRequestInShared.has_value()) {
+        const auto state = syncFence.syncWait({});
+        if (state != nn::SyncFence::FenceState::SIGNALED) {
+            return NN_ERROR() << "syncWait failed with " << state;
+        }
+        NN_TRY(hal::utils::unflushDataFromSharedToPointer(request, maybeRequestInShared));
+    }
+
+    return std::make_pair(std::move(syncFence), std::move(callback));
+}
+
+std::any PreparedModel::getUnderlyingResource() const {
+    sp<V1_3::IPreparedModel> resource = kPreparedModel;
+    return resource;
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/Service.cpp b/neuralnetworks/1.3/utils/src/Service.cpp
new file mode 100644
index 0000000..62887fb
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/Service.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Service.h"
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientDevice.h>
+#include <string>
+#include "Device.h"
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name) {
+    hal::utils::ResilientDevice::Factory makeDevice =
+            [name](bool blocking) -> nn::GeneralResult<nn::SharedDevice> {
+        auto service = blocking ? IDevice::getService(name) : IDevice::tryGetService(name);
+        if (service == nullptr) {
+            return NN_ERROR() << (blocking ? "getService" : "tryGetService") << " returned nullptr";
+        }
+        return Device::create(name, std::move(service));
+    };
+
+    return hal::utils::ResilientDevice::create(std::move(makeDevice));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/TEST_MAPPING b/neuralnetworks/TEST_MAPPING
index 0cefffa..ca5041d 100644
--- a/neuralnetworks/TEST_MAPPING
+++ b/neuralnetworks/TEST_MAPPING
@@ -21,7 +21,9 @@
           "include-filter": "-*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*"
         }
       ]
-    },
+    }
+  ],
+  "presubmit-large": [
     {
       "name": "VtsHalNeuralnetworksV1_2TargetTest",
       "options": [
diff --git a/neuralnetworks/utils/README.md b/neuralnetworks/utils/README.md
new file mode 100644
index 0000000..0dee103
--- /dev/null
+++ b/neuralnetworks/utils/README.md
@@ -0,0 +1,50 @@
+# NNAPI Conversions
+
+`convert` fails if either the source type or the destination type is invalid, and it yields a valid
+object if the conversion succeeds. For example, let's say that an enumeration in the current
+version has fewer possible values than the "same" canonical enumeration, such as `OperationType`.
+The new value of `HARD_SWISH` (introduced in Android R / NN HAL 1.3) does not map to any valid
+existing value in `OperationType`, but an older value of `ADD` (introduced in Android OC-MR1 / NN
+HAL 1.0) is valid. This can be seen in the following model conversions:
+
+```cpp
+// Unsuccessful conversion
+const nn::Model canonicalModel = createModelWhichHasV1_3Operations();
+const nn::Result<V1_0::Model> maybeVersionedModel = V1_0::utils::convert(canonicalModel);
+EXPECT_FALSE(maybeVersionedModel.has_value());
+```
+```cpp
+// Successful conversion
+const nn::Model canonicalModel = createModelWhichHasOnlyV1_0Operations();
+const nn::Result<V1_0::Model> maybeVersionedModel = V1_0::utils::convert(canonicalModel);
+ASSERT_TRUE(maybeVersionedModel.has_value());
+const V1_0::Model& versionedModel = maybeVersionedModel.value();
+EXPECT_TRUE(V1_0::utils::valid(versionedModel));
+```
+
+`V1_X::utils::convert` does not guarantee that all information is preserved. For example, In the
+case of `nn::ErrorStatus`, the new value of `MISSED_DEADLINE_TRANSIENT` can be represented by the
+existing value of `V1_0::GENERAL_FAILURE`:
+
+```cpp
+// Lossy Canonical -> HAL -> Canonical conversion
+const nn::ErrorStatus canonicalBefore = nn::ErrorStatus::MISSED_DEADLINE_TRANSIENT;
+const V1_0::ErrorStatus versioned = V1_0::utils::convert(canonicalBefore).value();
+const nn::ErrorStatus canonicalAfter = nn::convert(versioned).value();
+EXPECT_NE(canonicalBefore, canonicalAfter);
+```
+
+However, `nn::convert` is guaranteed to preserve all information:
+
+```cpp
+// Lossless HAL -> Canonical -> HAL conversion
+const V1_0::ErrorStatus versionedBefore = V1_0::ErrorStatus::GENERAL_FAILURE;
+const nn::ErrorStatus canonical = nn::convert(versionedBefore).value();
+const V1_0::ErrorStatus versionedAfter = V1_0::utils::convert(canonical).value();
+EXPECT_EQ(versionedBefore, versionedAfter);
+```
+
+The `convert` functions operate only on types that used in a HIDL method call directly. The
+`unvalidatedConvert` functions operate on types that are either used in a HIDL method call directly
+(i.e., not as a nested class) or used in a subsequent version of the NN HAL. Prefer using `convert`
+over `unvalidatedConvert`.
diff --git a/neuralnetworks/utils/common/Android.bp b/neuralnetworks/utils/common/Android.bp
index b61dc97..21562cf 100644
--- a/neuralnetworks/utils/common/Android.bp
+++ b/neuralnetworks/utils/common/Android.bp
@@ -20,6 +20,7 @@
     srcs: ["src/*"],
     local_include_dirs: ["include/nnapi/hal"],
     export_include_dirs: ["include"],
+    cflags: ["-Wthread-safety"],
     static_libs: [
         "neuralnetworks_types",
     ],
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
index 8c01368..43bb0c6 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
@@ -17,8 +17,11 @@
 #ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_COMMON_UTILS_H
 #define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_COMMON_UTILS_H
 
+#include <cutils/native_handle.h>
+#include <hidl/HidlSupport.h>
 #include <nnapi/Result.h>
 #include <nnapi/Types.h>
+#include <functional>
 #include <vector>
 
 // Shorthand
@@ -42,18 +45,27 @@
 bool hasNoPointerData(const nn::Request& request);
 
 // Relocate pointer-based data to shared memory.
-nn::Result<nn::Model> flushDataFromPointerToShared(const nn::Model& model);
-nn::Result<nn::Request> flushDataFromPointerToShared(const nn::Request& request);
+nn::GeneralResult<std::reference_wrapper<const nn::Model>> flushDataFromPointerToShared(
+        const nn::Model* model, std::optional<nn::Model>* maybeModelInSharedOut);
+nn::GeneralResult<std::reference_wrapper<const nn::Request>> flushDataFromPointerToShared(
+        const nn::Request* request, std::optional<nn::Request>* maybeRequestInSharedOut);
 
 // Undoes `flushDataFromPointerToShared` on a Request object. More specifically,
 // `unflushDataFromSharedToPointer` copies the output shared memory data from the transformed
 // Request object back to the output pointer-based memory in the original Request object.
-nn::Result<void> unflushDataFromSharedToPointer(const nn::Request& request,
-                                                const nn::Request& requestInShared);
+nn::GeneralResult<void> unflushDataFromSharedToPointer(
+        const nn::Request& request, const std::optional<nn::Request>& maybeRequestInShared);
 
 std::vector<uint32_t> countNumberOfConsumers(size_t numberOfOperands,
                                              const std::vector<nn::Operation>& operations);
 
+nn::GeneralResult<nn::Memory> createSharedMemoryFromHidlMemory(const hidl_memory& memory);
+
+nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::SharedHandle& handle);
+nn::GeneralResult<nn::SharedHandle> sharedHandleFromNativeHandle(const native_handle_t* handle);
+nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
+        const std::vector<nn::SyncFence>& fences);
+
 }  // namespace android::hardware::neuralnetworks::utils
 
 #endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_COMMON_UTILS_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h b/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
new file mode 100644
index 0000000..78b2a12
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/hidl/base/1.0/IBase.h>
+#include <hidl/HidlSupport.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <type_traits>
+
+namespace android::hardware::neuralnetworks::utils {
+
+template <typename Type>
+nn::GeneralResult<Type> handleTransportError(const hardware::Return<Type>& ret) {
+    if (ret.isDeadObject()) {
+        return nn::error(nn::ErrorStatus::DEAD_OBJECT)
+               << "Return<>::isDeadObject returned true: " << ret.description();
+    }
+    if (!ret.isOk()) {
+        return nn::error(nn::ErrorStatus::GENERAL_FAILURE)
+               << "Return<>::isOk returned false: " << ret.description();
+    }
+    if constexpr (!std::is_same_v<Type, void>) {
+        return static_cast<Type>(ret);
+    } else {
+        return {};
+    }
+}
+
+#define HANDLE_TRANSPORT_FAILURE(ret)                                                        \
+    ({                                                                                       \
+        auto result = ::android::hardware::neuralnetworks::utils::handleTransportError(ret); \
+        if (!result.has_value()) {                                                           \
+            return NN_ERROR(result.error().code) << result.error().message;                  \
+        }                                                                                    \
+        std::move(result).value();                                                           \
+    })
+
+template <typename Type>
+nn::GeneralResult<Type> makeGeneralFailure(nn::Result<Type> result, nn::ErrorStatus status) {
+    if (!result.has_value()) {
+        return nn::error(status) << std::move(result).error();
+    }
+    if constexpr (!std::is_same_v<Type, void>) {
+        return std::move(result).value();
+    } else {
+        return {};
+    }
+}
+
+template <typename Type>
+nn::ExecutionResult<Type> makeExecutionFailure(nn::GeneralResult<Type> result) {
+    if (!result.has_value()) {
+        const auto [message, status] = std::move(result).error();
+        return nn::error(status) << message;
+    }
+    if constexpr (!std::is_same_v<Type, void>) {
+        return std::move(result).value();
+    } else {
+        return {};
+    }
+}
+
+template <typename Type>
+nn::ExecutionResult<Type> makeExecutionFailure(nn::Result<Type> result, nn::ErrorStatus status) {
+    return makeExecutionFailure(makeGeneralFailure(result, status));
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
\ No newline at end of file
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h
new file mode 100644
index 0000000..8c04b88
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_BUFFER_H
+
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidBuffer final : public nn::IBuffer {
+  public:
+    nn::Request::MemoryDomainToken getToken() const override;
+
+    nn::GeneralResult<void> copyTo(const nn::Memory& dst) const override;
+
+    nn::GeneralResult<void> copyFrom(const nn::Memory& src,
+                                     const nn::Dimensions& dimensions) const override;
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_BUFFER_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
new file mode 100644
index 0000000..5e62b9a
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_DEVICE_H
+
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidDevice final : public nn::IDevice {
+  public:
+    InvalidDevice(std::string name, std::string versionString, nn::Version featureLevel,
+                  nn::DeviceType type, std::vector<nn::Extension> extensions,
+                  nn::Capabilities capabilities,
+                  std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded);
+
+    const std::string& getName() const override;
+    const std::string& getVersionString() const override;
+    nn::Version getFeatureLevel() const override;
+    nn::DeviceType getType() const override;
+    const std::vector<nn::Extension>& getSupportedExtensions() const override;
+    const nn::Capabilities& getCapabilities() const override;
+    std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
+
+    nn::GeneralResult<void> wait() const override;
+
+    nn::GeneralResult<std::vector<bool>> getSupportedOperations(
+            const nn::Model& model) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModel(
+            const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedBuffer> allocate(
+            const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+            const std::vector<nn::BufferRole>& inputRoles,
+            const std::vector<nn::BufferRole>& outputRoles) const override;
+
+  private:
+    const std::string kName;
+    const std::string kVersionString;
+    const nn::Version kFeatureLevel;
+    const nn::DeviceType kType;
+    const std::vector<nn::Extension> kExtensions;
+    const nn::Capabilities kCapabilities;
+    const std::pair<uint32_t, uint32_t> kNumberOfCacheFilesNeeded;
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_DEVICE_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
new file mode 100644
index 0000000..4b32b4e
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_PREPARED_MODEL_H
+
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidPreparedModel final : public nn::IPreparedModel {
+  public:
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalTimeoutDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> executeFenced(
+            const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
+            nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalTimeoutDuration& loopTimeoutDuration,
+            const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const override;
+
+    std::any getUnderlyingResource() const override;
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_PREPARED_MODEL_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h b/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
new file mode 100644
index 0000000..85bd613
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_PROTECT_CALLBACK_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_PROTECT_CALLBACK_H
+
+#include <android-base/scopeguard.h>
+#include <android-base/thread_annotations.h>
+#include <android/hidl/base/1.0/IBase.h>
+#include <hidl/HidlSupport.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <mutex>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class IProtectedCallback {
+  public:
+    /**
+     * Marks this object as a dead object.
+     */
+    virtual void notifyAsDeadObject() = 0;
+
+    // Public virtual destructor to allow objects to be stored (and destroyed) as smart pointers.
+    // E.g., std::unique_ptr<IProtectedCallback>.
+    virtual ~IProtectedCallback() = default;
+
+  protected:
+    // Protect the non-destructor special member functions to prevent object slicing.
+    IProtectedCallback() = default;
+    IProtectedCallback(const IProtectedCallback&) = default;
+    IProtectedCallback(IProtectedCallback&&) noexcept = default;
+    IProtectedCallback& operator=(const IProtectedCallback&) = default;
+    IProtectedCallback& operator=(IProtectedCallback&&) noexcept = default;
+};
+
+// Thread safe class
+class DeathRecipient final : public hidl_death_recipient {
+  public:
+    void serviceDied(uint64_t /*cookie*/, const wp<hidl::base::V1_0::IBase>& /*who*/) override;
+    // Precondition: `killable` must be non-null.
+    void add(IProtectedCallback* killable) const;
+    // Precondition: `killable` must be non-null.
+    void remove(IProtectedCallback* killable) const;
+
+  private:
+    mutable std::mutex mMutex;
+    mutable std::vector<IProtectedCallback*> mObjects GUARDED_BY(mMutex);
+};
+
+class DeathHandler final {
+  public:
+    static nn::GeneralResult<DeathHandler> create(sp<hidl::base::V1_0::IBase> object);
+
+    DeathHandler(const DeathHandler&) = delete;
+    DeathHandler(DeathHandler&&) noexcept = default;
+    DeathHandler& operator=(const DeathHandler&) = delete;
+    DeathHandler& operator=(DeathHandler&&) noexcept = delete;
+    ~DeathHandler();
+
+    using Cleanup = std::function<void()>;
+    // Precondition: `killable` must be non-null.
+    [[nodiscard]] base::ScopeGuard<Cleanup> protectCallback(IProtectedCallback* killable) const;
+
+  private:
+    DeathHandler(sp<hidl::base::V1_0::IBase> object, sp<DeathRecipient> deathRecipient);
+
+    sp<hidl::base::V1_0::IBase> kObject;
+    sp<DeathRecipient> kDeathRecipient;
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_PROTECT_CALLBACK_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h
new file mode 100644
index 0000000..996ec1e
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBuffer.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_BUFFER_H
+
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class ResilientBuffer final : public nn::IBuffer {
+    struct PrivateConstructorTag {};
+
+  public:
+    using Factory = std::function<nn::GeneralResult<nn::SharedBuffer>(bool blocking)>;
+
+    static nn::GeneralResult<std::shared_ptr<const ResilientBuffer>> create(Factory makeBuffer);
+
+    explicit ResilientBuffer(PrivateConstructorTag tag, Factory makeBuffer,
+                             nn::SharedBuffer buffer);
+
+    nn::SharedBuffer getBuffer() const;
+    nn::SharedBuffer recover(const nn::IBuffer* failingBuffer, bool blocking) const;
+
+    nn::Request::MemoryDomainToken getToken() const override;
+
+    nn::GeneralResult<void> copyTo(const nn::Memory& dst) const override;
+
+    nn::GeneralResult<void> copyFrom(const nn::Memory& src,
+                                     const nn::Dimensions& dimensions) const override;
+
+  private:
+    const Factory kMakeBuffer;
+    mutable std::mutex mMutex;
+    mutable nn::SharedBuffer mBuffer GUARDED_BY(mMutex);
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_BUFFER_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
new file mode 100644
index 0000000..4bfed6c
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_DEVICE_H
+
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class ResilientDevice final : public nn::IDevice,
+                              public std::enable_shared_from_this<ResilientDevice> {
+    struct PrivateConstructorTag {};
+
+  public:
+    using Factory = std::function<nn::GeneralResult<nn::SharedDevice>(bool blocking)>;
+
+    static nn::GeneralResult<std::shared_ptr<const ResilientDevice>> create(Factory makeDevice);
+
+    explicit ResilientDevice(PrivateConstructorTag tag, Factory makeDevice, std::string name,
+                             std::string versionString, std::vector<nn::Extension> extensions,
+                             nn::Capabilities capabilities, nn::SharedDevice device);
+
+    nn::SharedDevice getDevice() const EXCLUDES(mMutex);
+    nn::SharedDevice recover(const nn::IDevice* failingDevice, bool blocking) const
+            EXCLUDES(mMutex);
+
+    const std::string& getName() const override;
+    const std::string& getVersionString() const override;
+    nn::Version getFeatureLevel() const override;
+    nn::DeviceType getType() const override;
+    const std::vector<nn::Extension>& getSupportedExtensions() const override;
+    const nn::Capabilities& getCapabilities() const override;
+    std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
+
+    nn::GeneralResult<void> wait() const override;
+
+    nn::GeneralResult<std::vector<bool>> getSupportedOperations(
+            const nn::Model& model) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModel(
+            const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedBuffer> allocate(
+            const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+            const std::vector<nn::BufferRole>& inputRoles,
+            const std::vector<nn::BufferRole>& outputRoles) const override;
+
+  private:
+    bool isValidInternal() const EXCLUDES(mMutex);
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelInternal(
+            bool blocking, const nn::Model& model, nn::ExecutionPreference preference,
+            nn::Priority priority, nn::OptionalTimePoint deadline,
+            const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const;
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCacheInternal(
+            bool blocking, nn::OptionalTimePoint deadline,
+            const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const;
+    nn::GeneralResult<nn::SharedBuffer> allocateInternal(
+            bool blocking, const nn::BufferDesc& desc,
+            const std::vector<nn::SharedPreparedModel>& preparedModels,
+            const std::vector<nn::BufferRole>& inputRoles,
+            const std::vector<nn::BufferRole>& outputRoles) const;
+
+    const Factory kMakeDevice;
+    const std::string kName;
+    const std::string kVersionString;
+    const std::vector<nn::Extension> kExtensions;
+    const nn::Capabilities kCapabilities;
+    mutable std::mutex mMutex;
+    mutable nn::SharedDevice mDevice GUARDED_BY(mMutex);
+    mutable bool mIsValid GUARDED_BY(mMutex) = true;
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_DEVICE_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
new file mode 100644
index 0000000..c2940d1
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_PREPARED_MODEL_H
+
+#include <android-base/thread_annotations.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class ResilientPreparedModel final : public nn::IPreparedModel {
+    struct PrivateConstructorTag {};
+
+  public:
+    using Factory = std::function<nn::GeneralResult<nn::SharedPreparedModel>(bool blocking)>;
+
+    static nn::GeneralResult<std::shared_ptr<const ResilientPreparedModel>> create(
+            Factory makePreparedModel);
+
+    explicit ResilientPreparedModel(PrivateConstructorTag tag, Factory makePreparedModel,
+                                    nn::SharedPreparedModel preparedModel);
+
+    nn::SharedPreparedModel getPreparedModel() const;
+    nn::SharedPreparedModel recover(const nn::IPreparedModel* failingPreparedModel,
+                                    bool blocking) const;
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalTimeoutDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> executeFenced(
+            const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
+            nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalTimeoutDuration& loopTimeoutDuration,
+            const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const override;
+
+    std::any getUnderlyingResource() const override;
+
+  private:
+    const Factory kMakePreparedModel;
+    mutable std::mutex mMutex;
+    mutable nn::SharedPreparedModel mPreparedModel GUARDED_BY(mMutex);
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_PREPARED_MODEL_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/TransferValue.h b/neuralnetworks/utils/common/include/nnapi/hal/TransferValue.h
new file mode 100644
index 0000000..7103c6b
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/TransferValue.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_TRANSFER_VALUE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_TRANSFER_VALUE_H
+
+#include <android-base/thread_annotations.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <optional>
+
+namespace android::hardware::neuralnetworks::utils {
+
+// This class is thread safe.
+template <typename Type>
+class TransferValue final {
+  public:
+    void put(Type object) const;
+    [[nodiscard]] Type take() const;
+
+  private:
+    mutable std::mutex mMutex;
+    mutable std::condition_variable mCondition;
+    mutable std::optional<Type> mObject GUARDED_BY(mMutex);
+};
+
+// template implementation
+
+template <typename Type>
+void TransferValue<Type>::put(Type object) const {
+    {
+        std::lock_guard guard(mMutex);
+        // Immediately return if value already exists.
+        if (mObject.has_value()) return;
+        mObject.emplace(std::move(object));
+    }
+    mCondition.notify_all();
+}
+
+template <typename Type>
+Type TransferValue<Type>::take() const {
+    std::unique_lock lock(mMutex);
+    base::ScopedLockAssertion lockAssertion(mMutex);
+    mCondition.wait(lock, [this]() REQUIRES(mMutex) { return mObject.has_value(); });
+    std::optional<Type> object;
+    std::swap(object, mObject);
+    return std::move(object).value();
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_TRANSFER_VALUE_H
diff --git a/neuralnetworks/utils/common/src/CommonUtils.cpp b/neuralnetworks/utils/common/src/CommonUtils.cpp
index 667189b..c04c8df 100644
--- a/neuralnetworks/utils/common/src/CommonUtils.cpp
+++ b/neuralnetworks/utils/common/src/CommonUtils.cpp
@@ -16,7 +16,10 @@
 
 #include "CommonUtils.h"
 
+#include "HandleError.h"
+
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <nnapi/Result.h>
 #include <nnapi/SharedMemory.h>
 #include <nnapi/TypeUtils.h>
@@ -25,6 +28,7 @@
 
 #include <algorithm>
 #include <any>
+#include <functional>
 #include <optional>
 #include <variant>
 #include <vector>
@@ -111,8 +115,18 @@
     return hasNoPointerData(request.inputs) && hasNoPointerData(request.outputs);
 }
 
-nn::Result<nn::Model> flushDataFromPointerToShared(const nn::Model& model) {
-    auto modelInShared = model;
+nn::GeneralResult<std::reference_wrapper<const nn::Model>> flushDataFromPointerToShared(
+        const nn::Model* model, std::optional<nn::Model>* maybeModelInSharedOut) {
+    CHECK(model != nullptr);
+    CHECK(maybeModelInSharedOut != nullptr);
+
+    if (hasNoPointerData(*model)) {
+        return *model;
+    }
+
+    // Make a copy of the model in order to make modifications. The modified model is returned to
+    // the caller through `maybeModelInSharedOut` if the function succeeds.
+    nn::Model modelInShared = *model;
 
     nn::ConstantMemoryBuilder memoryBuilder(modelInShared.pools.size());
     copyPointersToSharedMemory(&modelInShared.main, &memoryBuilder);
@@ -126,11 +140,22 @@
         modelInShared.pools.push_back(std::move(memory));
     }
 
-    return modelInShared;
+    *maybeModelInSharedOut = modelInShared;
+    return **maybeModelInSharedOut;
 }
 
-nn::Result<nn::Request> flushDataFromPointerToShared(const nn::Request& request) {
-    auto requestInShared = request;
+nn::GeneralResult<std::reference_wrapper<const nn::Request>> flushDataFromPointerToShared(
+        const nn::Request* request, std::optional<nn::Request>* maybeRequestInSharedOut) {
+    CHECK(request != nullptr);
+    CHECK(maybeRequestInSharedOut != nullptr);
+
+    if (hasNoPointerData(*request)) {
+        return *request;
+    }
+
+    // Make a copy of the request in order to make modifications. The modified request is returned
+    // to the caller through `maybeRequestInSharedOut` if the function succeeds.
+    nn::Request requestInShared = *request;
 
     // Change input pointers to shared memory.
     nn::ConstantMemoryBuilder inputBuilder(requestInShared.pools.size());
@@ -171,15 +196,17 @@
         requestInShared.pools.push_back(std::move(memory));
     }
 
-    return requestInShared;
+    *maybeRequestInSharedOut = requestInShared;
+    return **maybeRequestInSharedOut;
 }
 
-nn::Result<void> unflushDataFromSharedToPointer(const nn::Request& request,
-                                                const nn::Request& requestInShared) {
-    if (requestInShared.pools.empty() ||
-        !std::holds_alternative<nn::Memory>(requestInShared.pools.back())) {
+nn::GeneralResult<void> unflushDataFromSharedToPointer(
+        const nn::Request& request, const std::optional<nn::Request>& maybeRequestInShared) {
+    if (!maybeRequestInShared.has_value() || maybeRequestInShared->pools.empty() ||
+        !std::holds_alternative<nn::Memory>(maybeRequestInShared->pools.back())) {
         return {};
     }
+    const auto& requestInShared = *maybeRequestInShared;
 
     // Map the memory.
     const auto& outputMemory = std::get<nn::Memory>(requestInShared.pools.back());
@@ -221,4 +248,67 @@
     return nn::countNumberOfConsumers(numberOfOperands, operations);
 }
 
+nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::SharedHandle& handle) {
+    if (handle == nullptr) {
+        return {};
+    }
+
+    std::vector<base::unique_fd> fds;
+    fds.reserve(handle->fds.size());
+    for (const auto& fd : handle->fds) {
+        int dupFd = dup(fd);
+        if (dupFd == -1) {
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Failed to dup the fd";
+        }
+        fds.emplace_back(dupFd);
+    }
+
+    native_handle_t* nativeHandle = native_handle_create(handle->fds.size(), handle->ints.size());
+    if (nativeHandle == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Failed to create native_handle";
+    }
+    for (size_t i = 0; i < fds.size(); ++i) {
+        nativeHandle->data[i] = fds[i].release();
+    }
+    std::copy(handle->ints.begin(), handle->ints.end(), &nativeHandle->data[nativeHandle->numFds]);
+
+    hidl_handle hidlHandle;
+    hidlHandle.setTo(nativeHandle, /*shouldOwn=*/true);
+    return hidlHandle;
+}
+
+nn::GeneralResult<nn::SharedHandle> sharedHandleFromNativeHandle(const native_handle_t* handle) {
+    if (handle == nullptr) {
+        return nullptr;
+    }
+
+    std::vector<base::unique_fd> fds;
+    fds.reserve(handle->numFds);
+    for (int i = 0; i < handle->numFds; ++i) {
+        int dupFd = dup(handle->data[i]);
+        if (dupFd == -1) {
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Failed to dup the fd";
+        }
+        fds.emplace_back(dupFd);
+    }
+
+    std::vector<int> ints(&handle->data[handle->numFds],
+                          &handle->data[handle->numFds + handle->numInts]);
+
+    return std::make_shared<const nn::Handle>(nn::Handle{
+            .fds = std::move(fds),
+            .ints = std::move(ints),
+    });
+}
+
+nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
+        const std::vector<nn::SyncFence>& syncFences) {
+    hidl_vec<hidl_handle> handles(syncFences.size());
+    for (size_t i = 0; i < syncFences.size(); ++i) {
+        handles[i] =
+                NN_TRY(hal::utils::hidlHandleFromSharedHandle(syncFences[i].getSharedHandle()));
+    }
+    return handles;
+}
+
 }  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidBuffer.cpp b/neuralnetworks/utils/common/src/InvalidBuffer.cpp
new file mode 100644
index 0000000..c6f75d7
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidBuffer.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InvalidBuffer.h"
+
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+nn::Request::MemoryDomainToken InvalidBuffer::getToken() const {
+    return nn::Request::MemoryDomainToken{};
+}
+
+nn::GeneralResult<void> InvalidBuffer::copyTo(const nn::Memory& /*dst*/) const {
+    return NN_ERROR() << "InvalidBuffer";
+}
+
+nn::GeneralResult<void> InvalidBuffer::copyFrom(const nn::Memory& /*src*/,
+                                                const nn::Dimensions& /*dimensions*/) const {
+    return NN_ERROR() << "InvalidBuffer";
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidDevice.cpp b/neuralnetworks/utils/common/src/InvalidDevice.cpp
new file mode 100644
index 0000000..535ccb4
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidDevice.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InvalidDevice.h"
+
+#include "InvalidBuffer.h"
+#include "InvalidPreparedModel.h"
+
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+InvalidDevice::InvalidDevice(std::string name, std::string versionString, nn::Version featureLevel,
+                             nn::DeviceType type, std::vector<nn::Extension> extensions,
+                             nn::Capabilities capabilities,
+                             std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded)
+    : kName(std::move(name)),
+      kVersionString(std::move(versionString)),
+      kFeatureLevel(featureLevel),
+      kType(type),
+      kExtensions(std::move(extensions)),
+      kCapabilities(std::move(capabilities)),
+      kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded) {}
+
+const std::string& InvalidDevice::getName() const {
+    return kName;
+}
+
+const std::string& InvalidDevice::getVersionString() const {
+    return kVersionString;
+}
+
+nn::Version InvalidDevice::getFeatureLevel() const {
+    return kFeatureLevel;
+}
+
+nn::DeviceType InvalidDevice::getType() const {
+    return kType;
+}
+
+const std::vector<nn::Extension>& InvalidDevice::getSupportedExtensions() const {
+    return kExtensions;
+}
+
+const nn::Capabilities& InvalidDevice::getCapabilities() const {
+    return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> InvalidDevice::getNumberOfCacheFilesNeeded() const {
+    return kNumberOfCacheFilesNeeded;
+}
+
+nn::GeneralResult<void> InvalidDevice::wait() const {
+    return NN_ERROR() << "InvalidDevice";
+}
+
+nn::GeneralResult<std::vector<bool>> InvalidDevice::getSupportedOperations(
+        const nn::Model& /*model*/) const {
+    return NN_ERROR() << "InvalidDevice";
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> InvalidDevice::prepareModel(
+        const nn::Model& /*model*/, nn::ExecutionPreference /*preference*/,
+        nn::Priority /*priority*/, nn::OptionalTimePoint /*deadline*/,
+        const std::vector<nn::SharedHandle>& /*modelCache*/,
+        const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+    return NN_ERROR() << "InvalidDevice";
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> InvalidDevice::prepareModelFromCache(
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/,
+        const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+    return NN_ERROR() << "InvalidDevice";
+}
+
+nn::GeneralResult<nn::SharedBuffer> InvalidDevice::allocate(
+        const nn::BufferDesc& /*desc*/,
+        const std::vector<nn::SharedPreparedModel>& /*preparedModels*/,
+        const std::vector<nn::BufferRole>& /*inputRoles*/,
+        const std::vector<nn::BufferRole>& /*outputRoles*/) const {
+    return NN_ERROR() << "InvalidDevice";
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
new file mode 100644
index 0000000..9ae7a63
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InvalidPreparedModel.h"
+
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+InvalidPreparedModel::execute(const nn::Request& /*request*/, nn::MeasureTiming /*measure*/,
+                              const nn::OptionalTimePoint& /*deadline*/,
+                              const nn::OptionalTimeoutDuration& /*loopTimeoutDuration*/) const {
+    return NN_ERROR() << "InvalidPreparedModel";
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+InvalidPreparedModel::executeFenced(
+        const nn::Request& /*request*/, const std::vector<nn::SyncFence>& /*waitFor*/,
+        nn::MeasureTiming /*measure*/, const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalTimeoutDuration& /*loopTimeoutDuration*/,
+        const nn::OptionalTimeoutDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR() << "InvalidPreparedModel";
+}
+
+std::any InvalidPreparedModel::getUnderlyingResource() const {
+    return {};
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ProtectCallback.cpp b/neuralnetworks/utils/common/src/ProtectCallback.cpp
new file mode 100644
index 0000000..abe4cb6
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ProtectCallback.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ProtectCallback.h"
+
+#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+#include <android-base/thread_annotations.h>
+#include <android/hidl/base/1.0/IBase.h>
+#include <hidl/HidlSupport.h>
+#include <nnapi/Result.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <algorithm>
+#include <functional>
+#include <mutex>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+void DeathRecipient::serviceDied(uint64_t /*cookie*/, const wp<hidl::base::V1_0::IBase>& /*who*/) {
+    std::lock_guard guard(mMutex);
+    std::for_each(mObjects.begin(), mObjects.end(),
+                  [](IProtectedCallback* killable) { killable->notifyAsDeadObject(); });
+}
+
+void DeathRecipient::add(IProtectedCallback* killable) const {
+    CHECK(killable != nullptr);
+    std::lock_guard guard(mMutex);
+    mObjects.push_back(killable);
+}
+
+void DeathRecipient::remove(IProtectedCallback* killable) const {
+    CHECK(killable != nullptr);
+    std::lock_guard guard(mMutex);
+    const auto removedIter = std::remove(mObjects.begin(), mObjects.end(), killable);
+    mObjects.erase(removedIter);
+}
+
+nn::GeneralResult<DeathHandler> DeathHandler::create(sp<hidl::base::V1_0::IBase> object) {
+    if (object == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "utils::DeathHandler::create must have non-null object";
+    }
+    auto deathRecipient = sp<DeathRecipient>::make();
+
+    const auto ret = object->linkToDeath(deathRecipient, /*cookie=*/0);
+    const bool success = HANDLE_TRANSPORT_FAILURE(ret);
+    if (!success) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "IBase::linkToDeath returned false";
+    }
+
+    return DeathHandler(std::move(object), std::move(deathRecipient));
+}
+
+DeathHandler::DeathHandler(sp<hidl::base::V1_0::IBase> object, sp<DeathRecipient> deathRecipient)
+    : kObject(std::move(object)), kDeathRecipient(std::move(deathRecipient)) {
+    CHECK(kObject != nullptr);
+    CHECK(kDeathRecipient != nullptr);
+}
+
+DeathHandler::~DeathHandler() {
+    if (kObject != nullptr && kDeathRecipient != nullptr) {
+        const auto ret = kObject->unlinkToDeath(kDeathRecipient);
+        const auto maybeSuccess = handleTransportError(ret);
+        if (!maybeSuccess.has_value()) {
+            LOG(ERROR) << maybeSuccess.error().message;
+        } else if (!maybeSuccess.value()) {
+            LOG(ERROR) << "IBase::linkToDeath returned false";
+        }
+    }
+}
+
+[[nodiscard]] base::ScopeGuard<DeathHandler::Cleanup> DeathHandler::protectCallback(
+        IProtectedCallback* killable) const {
+    CHECK(killable != nullptr);
+    kDeathRecipient->add(killable);
+    return base::make_scope_guard(
+            [deathRecipient = kDeathRecipient, killable] { deathRecipient->remove(killable); });
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientBuffer.cpp b/neuralnetworks/utils/common/src/ResilientBuffer.cpp
new file mode 100644
index 0000000..984295b
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientBuffer.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ResilientBuffer.h"
+
+#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+nn::GeneralResult<std::shared_ptr<const ResilientBuffer>> ResilientBuffer::create(
+        Factory makeBuffer) {
+    if (makeBuffer == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "utils::ResilientBuffer::create must have non-empty makeBuffer";
+    }
+    auto buffer = NN_TRY(makeBuffer(/*blocking=*/true));
+    CHECK(buffer != nullptr);
+    return std::make_shared<const ResilientBuffer>(PrivateConstructorTag{}, std::move(makeBuffer),
+                                                   std::move(buffer));
+}
+
+ResilientBuffer::ResilientBuffer(PrivateConstructorTag /*tag*/, Factory makeBuffer,
+                                 nn::SharedBuffer buffer)
+    : kMakeBuffer(std::move(makeBuffer)), mBuffer(std::move(buffer)) {
+    CHECK(kMakeBuffer != nullptr);
+    CHECK(mBuffer != nullptr);
+}
+
+nn::SharedBuffer ResilientBuffer::getBuffer() const {
+    std::lock_guard guard(mMutex);
+    return mBuffer;
+}
+nn::SharedBuffer ResilientBuffer::recover(const nn::IBuffer* /*failingBuffer*/,
+                                          bool /*blocking*/) const {
+    std::lock_guard guard(mMutex);
+    return mBuffer;
+}
+
+nn::Request::MemoryDomainToken ResilientBuffer::getToken() const {
+    return getBuffer()->getToken();
+}
+
+nn::GeneralResult<void> ResilientBuffer::copyTo(const nn::Memory& dst) const {
+    return getBuffer()->copyTo(dst);
+}
+
+nn::GeneralResult<void> ResilientBuffer::copyFrom(const nn::Memory& src,
+                                                  const nn::Dimensions& dimensions) const {
+    return getBuffer()->copyFrom(src, dimensions);
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientDevice.cpp b/neuralnetworks/utils/common/src/ResilientDevice.cpp
new file mode 100644
index 0000000..2f83c5c
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientDevice.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ResilientDevice.h"
+
+#include "InvalidBuffer.h"
+#include "InvalidDevice.h"
+#include "InvalidPreparedModel.h"
+#include "ResilientBuffer.h"
+#include "ResilientPreparedModel.h"
+
+#include <android-base/logging.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+template <typename FnType>
+auto protect(const ResilientDevice& resilientDevice, const FnType& fn, bool blocking)
+        -> decltype(fn(*resilientDevice.getDevice())) {
+    auto device = resilientDevice.getDevice();
+    auto result = fn(*device);
+
+    // Immediately return if device is not dead.
+    if (result.has_value() || result.error().code != nn::ErrorStatus::DEAD_OBJECT) {
+        return result;
+    }
+
+    device = resilientDevice.recover(device.get(), blocking);
+    return fn(*device);
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const ResilientDevice>> ResilientDevice::create(
+        Factory makeDevice) {
+    if (makeDevice == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "utils::ResilientDevice::create must have non-empty makeDevice";
+    }
+    auto device = NN_TRY(makeDevice(/*blocking=*/true));
+    CHECK(device != nullptr);
+
+    auto name = device->getName();
+    auto versionString = device->getVersionString();
+    auto extensions = device->getSupportedExtensions();
+    auto capabilities = device->getCapabilities();
+
+    return std::make_shared<ResilientDevice>(PrivateConstructorTag{}, std::move(makeDevice),
+                                             std::move(name), std::move(versionString),
+                                             std::move(extensions), std::move(capabilities),
+                                             std::move(device));
+}
+
+ResilientDevice::ResilientDevice(PrivateConstructorTag /*tag*/, Factory makeDevice,
+                                 std::string name, std::string versionString,
+                                 std::vector<nn::Extension> extensions,
+                                 nn::Capabilities capabilities, nn::SharedDevice device)
+    : kMakeDevice(std::move(makeDevice)),
+      kName(std::move(name)),
+      kVersionString(std::move(versionString)),
+      kExtensions(std::move(extensions)),
+      kCapabilities(std::move(capabilities)),
+      mDevice(std::move(device)) {
+    CHECK(kMakeDevice != nullptr);
+    CHECK(mDevice != nullptr);
+}
+
+nn::SharedDevice ResilientDevice::getDevice() const {
+    std::lock_guard guard(mMutex);
+    return mDevice;
+}
+
+nn::SharedDevice ResilientDevice::recover(const nn::IDevice* failingDevice, bool blocking) const {
+    std::lock_guard guard(mMutex);
+
+    // Another caller updated the failing device.
+    if (mDevice.get() != failingDevice) {
+        return mDevice;
+    }
+
+    auto maybeDevice = kMakeDevice(blocking);
+    if (!maybeDevice.has_value()) {
+        const auto& [message, code] = maybeDevice.error();
+        LOG(ERROR) << "Failed to recover dead device with error " << code << ": " << message;
+        return mDevice;
+    }
+    auto device = std::move(maybeDevice).value();
+
+    // If recovered device has different metadata than what is cached (i.e., because it was
+    // updated), mark the device as invalid and preserve the cached data.
+    auto compare = [this, &device](auto fn) REQUIRES(mMutex) {
+        return std::invoke(fn, mDevice) != std::invoke(fn, device);
+    };
+    if (compare(&IDevice::getName) || compare(&IDevice::getVersionString) ||
+        compare(&IDevice::getFeatureLevel) || compare(&IDevice::getType) ||
+        compare(&IDevice::getSupportedExtensions) || compare(&IDevice::getCapabilities)) {
+        LOG(ERROR) << "Recovered device has different metadata than what is cached. Marking "
+                      "IDevice object as invalid.";
+        device = std::make_shared<const InvalidDevice>(
+                kName, kVersionString, mDevice->getFeatureLevel(), mDevice->getType(), kExtensions,
+                kCapabilities, mDevice->getNumberOfCacheFilesNeeded());
+        mIsValid = false;
+    }
+
+    mDevice = std::move(device);
+    return mDevice;
+}
+
+const std::string& ResilientDevice::getName() const {
+    return kName;
+}
+
+const std::string& ResilientDevice::getVersionString() const {
+    return kVersionString;
+}
+
+nn::Version ResilientDevice::getFeatureLevel() const {
+    return getDevice()->getFeatureLevel();
+}
+
+nn::DeviceType ResilientDevice::getType() const {
+    return getDevice()->getType();
+}
+
+const std::vector<nn::Extension>& ResilientDevice::getSupportedExtensions() const {
+    return kExtensions;
+}
+
+const nn::Capabilities& ResilientDevice::getCapabilities() const {
+    return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> ResilientDevice::getNumberOfCacheFilesNeeded() const {
+    return getDevice()->getNumberOfCacheFilesNeeded();
+}
+
+nn::GeneralResult<void> ResilientDevice::wait() const {
+    const auto fn = [](const nn::IDevice& device) { return device.wait(); };
+    return protect(*this, fn, /*blocking=*/true);
+}
+
+nn::GeneralResult<std::vector<bool>> ResilientDevice::getSupportedOperations(
+        const nn::Model& model) const {
+    const auto fn = [&model](const nn::IDevice& device) {
+        return device.getSupportedOperations(model);
+    };
+    return protect(*this, fn, /*blocking=*/false);
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModel(
+        const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+        nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    auto self = shared_from_this();
+    ResilientPreparedModel::Factory makePreparedModel =
+            [device = std::move(self), model, preference, priority, deadline, modelCache, dataCache,
+             token](bool blocking) -> nn::GeneralResult<nn::SharedPreparedModel> {
+        return device->prepareModelInternal(blocking, model, preference, priority, deadline,
+                                            modelCache, dataCache, token);
+    };
+    return ResilientPreparedModel::create(std::move(makePreparedModel));
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModelFromCache(
+        nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    auto self = shared_from_this();
+    ResilientPreparedModel::Factory makePreparedModel =
+            [device = std::move(self), deadline, modelCache, dataCache,
+             token](bool blocking) -> nn::GeneralResult<nn::SharedPreparedModel> {
+        return device->prepareModelFromCacheInternal(blocking, deadline, modelCache, dataCache,
+                                                     token);
+    };
+    return ResilientPreparedModel::create(std::move(makePreparedModel));
+}
+
+nn::GeneralResult<nn::SharedBuffer> ResilientDevice::allocate(
+        const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+        const std::vector<nn::BufferRole>& inputRoles,
+        const std::vector<nn::BufferRole>& outputRoles) const {
+    auto self = shared_from_this();
+    ResilientBuffer::Factory makeBuffer =
+            [device = std::move(self), desc, preparedModels, inputRoles,
+             outputRoles](bool blocking) -> nn::GeneralResult<nn::SharedBuffer> {
+        return device->allocateInternal(blocking, desc, preparedModels, inputRoles, outputRoles);
+    };
+    return ResilientBuffer::create(std::move(makeBuffer));
+}
+
+bool ResilientDevice::isValidInternal() const {
+    std::lock_guard hold(mMutex);
+    return mIsValid;
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModelInternal(
+        bool blocking, const nn::Model& model, nn::ExecutionPreference preference,
+        nn::Priority priority, nn::OptionalTimePoint deadline,
+        const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    if (!isValidInternal()) {
+        return std::make_shared<const InvalidPreparedModel>();
+    }
+    const auto fn = [&model, preference, priority, deadline, &modelCache, &dataCache,
+                     token](const nn::IDevice& device) {
+        return device.prepareModel(model, preference, priority, deadline, modelCache, dataCache,
+                                   token);
+    };
+    return protect(*this, fn, blocking);
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModelFromCacheInternal(
+        bool blocking, nn::OptionalTimePoint deadline,
+        const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    if (!isValidInternal()) {
+        return std::make_shared<const InvalidPreparedModel>();
+    }
+    const auto fn = [deadline, &modelCache, &dataCache, token](const nn::IDevice& device) {
+        return device.prepareModelFromCache(deadline, modelCache, dataCache, token);
+    };
+    return protect(*this, fn, blocking);
+}
+
+nn::GeneralResult<nn::SharedBuffer> ResilientDevice::allocateInternal(
+        bool blocking, const nn::BufferDesc& desc,
+        const std::vector<nn::SharedPreparedModel>& preparedModels,
+        const std::vector<nn::BufferRole>& inputRoles,
+        const std::vector<nn::BufferRole>& outputRoles) const {
+    if (!isValidInternal()) {
+        return std::make_shared<const InvalidBuffer>();
+    }
+    const auto fn = [&desc, &preparedModels, &inputRoles, &outputRoles](const nn::IDevice& device) {
+        return device.allocate(desc, preparedModels, inputRoles, outputRoles);
+    };
+    return protect(*this, fn, blocking);
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
new file mode 100644
index 0000000..1c9ecba
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ResilientPreparedModel.h"
+
+#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+nn::GeneralResult<std::shared_ptr<const ResilientPreparedModel>> ResilientPreparedModel::create(
+        Factory makePreparedModel) {
+    if (makePreparedModel == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "utils::ResilientPreparedModel::create must have non-empty makePreparedModel";
+    }
+    auto preparedModel = NN_TRY(makePreparedModel(/*blocking=*/true));
+    CHECK(preparedModel != nullptr);
+    return std::make_shared<ResilientPreparedModel>(
+            PrivateConstructorTag{}, std::move(makePreparedModel), std::move(preparedModel));
+}
+
+ResilientPreparedModel::ResilientPreparedModel(PrivateConstructorTag /*tag*/,
+                                               Factory makePreparedModel,
+                                               nn::SharedPreparedModel preparedModel)
+    : kMakePreparedModel(std::move(makePreparedModel)), mPreparedModel(std::move(preparedModel)) {
+    CHECK(kMakePreparedModel != nullptr);
+    CHECK(mPreparedModel != nullptr);
+}
+
+nn::SharedPreparedModel ResilientPreparedModel::getPreparedModel() const {
+    std::lock_guard guard(mMutex);
+    return mPreparedModel;
+}
+
+nn::SharedPreparedModel ResilientPreparedModel::recover(
+        const nn::IPreparedModel* /*failingPreparedModel*/, bool /*blocking*/) const {
+    std::lock_guard guard(mMutex);
+    return mPreparedModel;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+ResilientPreparedModel::execute(const nn::Request& request, nn::MeasureTiming measure,
+                                const nn::OptionalTimePoint& deadline,
+                                const nn::OptionalTimeoutDuration& loopTimeoutDuration) const {
+    return getPreparedModel()->execute(request, measure, deadline, loopTimeoutDuration);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+ResilientPreparedModel::executeFenced(
+        const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
+        nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
+        const nn::OptionalTimeoutDuration& loopTimeoutDuration,
+        const nn::OptionalTimeoutDuration& timeoutDurationAfterFence) const {
+    return getPreparedModel()->executeFenced(request, waitFor, measure, deadline,
+                                             loopTimeoutDuration, timeoutDurationAfterFence);
+}
+
+std::any ResilientPreparedModel::getUnderlyingResource() const {
+    return getPreparedModel()->getUnderlyingResource();
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/service/Android.bp b/neuralnetworks/utils/service/Android.bp
new file mode 100644
index 0000000..402598c
--- /dev/null
+++ b/neuralnetworks/utils/service/Android.bp
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+    name: "neuralnetworks_utils_hal_service",
+    defaults: ["neuralnetworks_utils_defaults"],
+    srcs: ["src/*"],
+    local_include_dirs: ["include/nnapi/hal"],
+    export_include_dirs: ["include"],
+    static_libs: [
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_1_0",
+        "neuralnetworks_utils_hal_1_1",
+        "neuralnetworks_utils_hal_1_2",
+        "neuralnetworks_utils_hal_1_3",
+        "neuralnetworks_utils_hal_common",
+    ],
+    shared_libs: [
+        "android.hardware.neuralnetworks@1.0",
+        "android.hardware.neuralnetworks@1.1",
+        "android.hardware.neuralnetworks@1.2",
+        "android.hardware.neuralnetworks@1.3",
+    ],
+}
diff --git a/neuralnetworks/utils/service/include/nnapi/hal/Service.h b/neuralnetworks/utils/service/include/nnapi/hal/Service.h
new file mode 100644
index 0000000..e339627
--- /dev/null
+++ b/neuralnetworks/utils/service/include/nnapi/hal/Service.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_SERVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_SERVICE_H
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Types.h>
+#include <memory>
+#include <vector>
+
+namespace android::nn::hal {
+
+std::vector<nn::SharedDevice> getDevices();
+
+}  // namespace android::nn::hal
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_SERVICE_H
diff --git a/neuralnetworks/utils/service/src/Service.cpp b/neuralnetworks/utils/service/src/Service.cpp
new file mode 100644
index 0000000..a59549d
--- /dev/null
+++ b/neuralnetworks/utils/service/src/Service.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Service.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <android/hardware/neuralnetworks/1.1/IDevice.h>
+#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+#include <hidl/ServiceManagement.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Service.h>
+#include <nnapi/hal/1.1/Service.h>
+#include <nnapi/hal/1.2/Service.h>
+#include <nnapi/hal/1.3/Service.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <unordered_set>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::service {
+namespace {
+
+using getDeviceFn = std::add_pointer_t<nn::GeneralResult<nn::SharedDevice>(const std::string&)>;
+
+void getDevicesForVersion(const std::string& descriptor, getDeviceFn getDevice,
+                          std::vector<nn::SharedDevice>* devices,
+                          std::unordered_set<std::string>* registeredDevices) {
+    CHECK(devices != nullptr);
+    CHECK(registeredDevices != nullptr);
+
+    const auto names = getAllHalInstanceNames(descriptor);
+    for (const auto& name : names) {
+        if (const auto [it, unregistered] = registeredDevices->insert(name); unregistered) {
+            auto maybeDevice = getDevice(name);
+            if (maybeDevice.has_value()) {
+                auto device = std::move(maybeDevice).value();
+                CHECK(device != nullptr);
+                devices->push_back(std::move(device));
+            } else {
+                LOG(ERROR) << "getDevice(" << name << ") failed with " << maybeDevice.error().code
+                           << ": " << maybeDevice.error().message;
+            }
+        }
+    }
+}
+
+std::vector<nn::SharedDevice> getDevices() {
+    std::vector<nn::SharedDevice> devices;
+    std::unordered_set<std::string> registeredDevices;
+
+    getDevicesForVersion(V1_3::IDevice::descriptor, &V1_3::utils::getDevice, &devices,
+                         &registeredDevices);
+    getDevicesForVersion(V1_2::IDevice::descriptor, &V1_2::utils::getDevice, &devices,
+                         &registeredDevices);
+    getDevicesForVersion(V1_1::IDevice::descriptor, &V1_1::utils::getDevice, &devices,
+                         &registeredDevices);
+    getDevicesForVersion(V1_0::IDevice::descriptor, &V1_0::utils::getDevice, &devices,
+                         &registeredDevices);
+
+    return devices;
+}
+
+}  // namespace
+}  // namespace android::hardware::neuralnetworks::service
+
+namespace android::nn::hal {
+
+std::vector<nn::SharedDevice> getDevices() {
+    return hardware::neuralnetworks::service::getDevices();
+}
+
+}  // namespace android::nn::hal
diff --git a/radio/1.6/IRadio.hal b/radio/1.6/IRadio.hal
index 91709fa..4566558 100644
--- a/radio/1.6/IRadio.hal
+++ b/radio/1.6/IRadio.hal
@@ -114,6 +114,9 @@
      *     addresses of the existing data connection. The format is defined in RFC-4291 section 2.2.
      *     For example, "192.0.1.3" or "2001:db8::1". This parameter must be ignored unless reason
      *     is DataRequestReason:HANDOVER.
+     * @param pduSessionId The pdu session id to be used for this data call.  A value of 0 means
+     *     no pdu session id was attached to this call.
+     *     Reference: 3GPP TS 24.007 section 11.2.3.1b
      *
      * Response function is IRadioResponse.setupDataCallResponse_1_6()
      *
@@ -121,7 +124,8 @@
      */
     oneway setupDataCall_1_6(int32_t serial, AccessNetwork accessNetwork,
             DataProfileInfo dataProfileInfo, bool roamingAllowed,
-            DataRequestReason reason, vec<LinkAddress> addresses, vec<string> dnses);
+            DataRequestReason reason, vec<LinkAddress> addresses, vec<string> dnses,
+            int32_t pduSessionId);
 
     /**
      * Send an SMS message
@@ -213,6 +217,13 @@
      * Each subsequent request to this method is processed only after the
      * completion of the previous one.
      *
+     * When the SIM is in POWER_DOWN, the modem should send an empty vector of
+     * AppStatus in CardStatus.applications. If a SIM in the POWER_DOWN state
+     * is removed and a new SIM is inserted, the new SIM should be in POWER_UP
+     * mode by default. If the device is turned off or restarted while the SIM
+     * is in POWER_DOWN, then the SIM should turn on normally in POWER_UP mode
+     * when the device turns back on.
+     *
      * Response callback is IRadioResponse.setSimCardPowerResponse_1_6().
      * Note that this differs from setSimCardPower_1_1 in that the response
      * callback should only be sent once the device has finished executing
@@ -290,12 +301,12 @@
      * @param serial Serial number of request.
      * @param id callId The identifier of the data call which is provided in SetupDataCallResult
      *
-     * Response function is IRadioResponse.beginHandoverResponse()
+     * Response function is IRadioResponse.startHandoverResponse()
      */
-    oneway beginHandover(int32_t serial, int32_t callId);
+    oneway startHandover(int32_t serial, int32_t callId);
 
     /**
-     * Indicates that a handover was cancelled after a call to IRadio::beginHandover.
+     * Indicates that a handover was cancelled after a call to IRadio::startHandover.
      *
      * Since the handover was unsuccessful, the modem retains ownership over any of the resources
      * being transferred and is still responsible for releasing them.
@@ -316,12 +327,26 @@
      * @param serial Serial number of request.
      * @param networkTypeBitmap a 32-bit bearer bitmap of RadioAccessFamily
      *
-     * Response callbask is IRadioResponse.setNetworkTypeBitmapResponse()
+     * Response callback is IRadioResponse.setNetworkTypeBitmapResponse()
      */
     oneway setAllowedNetworkTypeBitmap(
             uint32_t serial, bitfield<RadioAccessFamily> networkTypeBitmap);
 
     /**
+     * Requests bitmap representing the currently allowed network types.
+     *
+     * Requests the bitmap set by the corresponding method
+     * setAllowedNetworkTypeBitmap, which sets a strict set of RATs for the
+     * radio to use. Differs from getPreferredNetworkType and getPreferredNetworkTypeBitmap
+     * in that those request *preferences*.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioResponse.getNetworkTypeBitmapResponse()
+     */
+    oneway getAllowedNetworkTypeBitmap(uint32_t serial);
+
+    /**
      * Control data throttling at modem.
      *   - DataThrottlingAction:NO_DATA_THROTTLING should clear any existing
      *     data throttling within the requested completion window.
@@ -336,13 +361,63 @@
      *
      * @param serial Serial number of request.
      * @param dataThrottlingAction DataThrottlingAction as defined in types.hal
-     * @param completionWindowSecs window, in seconds, in which the requested
-     *     throttling action has to be achieved. This must be 0 when
+     * @param completionDurationMillis window, in milliseconds, in which the
+     *     requested throttling action has to be achieved. This must be 0 when
      *     dataThrottlingAction is DataThrottlingAction:HOLD.
      *
      * Response function is IRadioResponse.setDataThrottlingResponse()
      */
     oneway setDataThrottling(int32_t serial,
             DataThrottlingAction dataThrottlingAction,
-            int32_t completionWindowSecs);
+            int64_t completionDurationMillis);
+
+    /**
+     * Get which bands the modem's background scan is acting on.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioResponse.getSystemSelectionChannelsResponse()
+     */
+    oneway getSystemSelectionChannels(int32_t serial);
+
+   /**
+     * Request all of the current cell information known to the radio. The radio
+     * must return list of all current cells, including the neighboring cells. If for a particular
+     * cell information isn't known then the appropriate unknown value will be returned.
+     * This does not cause or change the rate of unsolicited cellInfoList().
+     *
+     * This is identitcal to getCellInfoList in V1.0, but it requests updated version of CellInfo.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioResponse.getCellInfoListResponse()
+     */
+    oneway getCellInfoList_1_6(int32_t serial);
+
+    /**
+     * Request current voice registration state.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getVoiceRegistrationStateResponse_1_6()
+     */
+    oneway getVoiceRegistrationState_1_6(int32_t serial);
+
+    /**
+     * Requests current signal strength and associated information.  Must succeed if radio is on.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getSignalStrengthResponse_1_6()
+     */
+    oneway getSignalStrength_1_6(int32_t serial);
+
+    /**
+     * Request current data registration state.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getDataRegistrationStateResponse_1_6()
+     */
+    oneway getDataRegistrationState_1_6(int32_t serial);
 };
diff --git a/radio/1.6/IRadioIndication.hal b/radio/1.6/IRadioIndication.hal
index f195c0e..1b56d40 100644
--- a/radio/1.6/IRadioIndication.hal
+++ b/radio/1.6/IRadioIndication.hal
@@ -18,8 +18,11 @@
 
 import @1.0::RadioIndicationType;
 import @1.5::IRadioIndication;
-import @1.6::SetupDataCallResult;
+import @1.6::CellInfo;
 import @1.6::LinkCapacityEstimate;
+import @1.6::NetworkScanResult;
+import @1.6::SignalStrength;
+import @1.6::SetupDataCallResult;
 
 /**
  * Interface declaring unsolicited radio indications.
@@ -67,4 +70,35 @@
      * @param lce LinkCapacityEstimate
      */
     oneway currentLinkCapacityEstimate_1_6(RadioIndicationType type, LinkCapacityEstimate lce);
+
+
+    /**
+     * Indicates current signal strength of the radio.
+     *
+     * This is identical to currentSignalStrength_1_4 but uses an updated version of
+     * SignalStrength.
+     *
+     * @param type Type of radio indication
+     * @param signalStrength SignalStrength information
+     */
+    oneway currentSignalStrength_1_6(RadioIndicationType type, SignalStrength signalStrength);
+
+    /**
+     * Report all of the current cell information known to the radio.
+     *
+     * This indication is updated from IRadioIndication@1.5 to report the @1.6 version of
+     * CellInfo.
+     *
+     * @param type Type of radio indication
+     * @param records Current cell information
+     */
+    oneway cellInfoList_1_6(RadioIndicationType type, vec<CellInfo> records);
+
+    /**
+     * Incremental network scan results.
+     *
+     * This indication is updated from IRadioIndication@1.5 to report the @1.6 version of
+     * CellInfo.
+     */
+    oneway networkScanResult_1_6(RadioIndicationType type, NetworkScanResult result);
 };
diff --git a/radio/1.6/IRadioResponse.hal b/radio/1.6/IRadioResponse.hal
index acae5c7..f13ee2b 100644
--- a/radio/1.6/IRadioResponse.hal
+++ b/radio/1.6/IRadioResponse.hal
@@ -17,9 +17,13 @@
 package android.hardware.radio@1.6;
 
 import @1.0::SendSmsResult;
-import @1.6::RadioResponseInfo;
+import @1.4::RadioAccessFamily;
 import @1.5::IRadioResponse;
+import @1.6::CellInfo;
+import @1.6::RegStateResult;
+import @1.6::RadioResponseInfo;
 import @1.6::SetupDataCallResult;
+import @1.6::SignalStrength;
 
 /**
  * Interface declaring response functions to solicited radio requests.
@@ -207,7 +211,6 @@
      * Valid errors returned:
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:INVALID_ARGUMENTS
      *   RadioError:SIM_ERR (indicates a timeout or other issue making the SIM unresponsive)
      *
@@ -241,7 +244,7 @@
 
     /**
      * @param info Response info struct containing response type, serial no. and error
-     * @param id The allocated id. On an error, this is set to -1
+     * @param id The allocated id. On an error, this is set to 0.
      *
      * Valid errors returned:
      *   RadioError:NONE
@@ -275,7 +278,7 @@
      *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:INVALID_CALL_ID
      */
-    oneway beginHandoverResponse(RadioResponseInfo info);
+    oneway startHandoverResponse(RadioResponseInfo info);
 
     /**
      * @param info Response info struct containing response type, serial no. and error
@@ -308,6 +311,23 @@
     oneway setAllowedNetworkTypeBitmapResponse(RadioResponseInfo info);
 
     /**
+     * Callback of IRadio.getAllowedNetworkTypeBitmap(int, bitfield<RadioAccessFamily>)
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:OPERATION_NOT_ALLOWED
+     *   RadioError:MODE_NOT_SUPPORTED
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:MODEM_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:NO_RESOURCES
+     */
+    oneway getAllowedNetworkTypeBitmapResponse(
+            RadioResponseInfo info, bitfield<RadioAccessFamily> networkTypeBitmap);
+
+    /**
      * @param info Response info struct containing response type, serial no. and error
      *
      *  Valid errors returned:
@@ -317,4 +337,68 @@
      *  RadioError:INVALID_ARGUMENTS
      */
     oneway setDataThrottlingResponse(RadioResponseInfo info);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:INVALID_ARGUMENTS
+     */
+    oneway getSystemSelectionChannelsResponse(RadioResponseInfo info);
+
+    /**
+     * This is identical to getCellInfoListResponse_1_5 but uses an updated version of CellInfo.
+     *
+     * @param info Response info struct containing response type, serial no. and error
+     * @param cellInfo List of current cell information known to radio
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getCellInfoListResponse_1_6(RadioResponseInfo info, vec<CellInfo> cellInfo);
+
+    /**
+     * This is identical to getSignalStrengthResponse_1_4 but uses an updated version of
+     * SignalStrength.
+     *
+     * @param signalStrength Current signal strength
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getSignalStrengthResponse_1_6(RadioResponseInfo info, SignalStrength signalStrength);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param voiceRegResponse Current Voice registration response as defined by RegStateResult
+     *        in types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getVoiceRegistrationStateResponse_1_6(RadioResponseInfo info,
+            RegStateResult voiceRegResponse);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param dataRegResponse Current Data registration response as defined by RegStateResult in
+     *        types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:NOT_PROVISIONED
+     */
+    oneway getDataRegistrationStateResponse_1_6(RadioResponseInfo info,
+            RegStateResult dataRegResponse);
 };
diff --git a/radio/1.6/types.hal b/radio/1.6/types.hal
index 2cba915..20dc612 100644
--- a/radio/1.6/types.hal
+++ b/radio/1.6/types.hal
@@ -16,12 +16,35 @@
 
 package android.hardware.radio@1.6;
 
+import @1.0::CdmaSignalStrength;
+import @1.0::EvdoSignalStrength;
+import @1.0::GsmSignalStrength;
+import @1.0::LteSignalStrength;
 import @1.0::RadioError;
 import @1.0::RadioResponseType;
+import @1.0::RegState;
+import @1.1::ScanStatus;
+import @1.2::CellInfoCdma;
+import @1.2::CellConnectionStatus;
+import @1.2::TdscdmaSignalStrength;
+import @1.2::WcdmaSignalStrength;
 import @1.4::DataCallFailCause;
 import @1.4::DataConnActiveStatus;
+import @1.4::NrSignalStrength;
 import @1.4::PdpProtocolType;
+import @1.4::RadioTechnology;
+import @1.5::CellIdentity;
+import @1.5::CellIdentityLte;
+import @1.5::CellIdentityNr;
+import @1.5::CellInfoGsm;
+import @1.5::CellInfoWcdma;
+import @1.5::CellInfoTdscdma;
 import @1.5::LinkAddress;
+import @1.5::RegStateResult.AccessTechnologySpecificInfo.Cdma2000RegistrationInfo;
+import @1.5::RegStateResult.AccessTechnologySpecificInfo.EutranRegistrationInfo;
+import @1.5::RegistrationFailCause;
+import @1.5::SetupDataCallResult;
+
 import android.hidl.safe_union@1.0::Monostate;
 
 struct QosBandwidth {
@@ -416,3 +439,287 @@
      */
     HOLD = 3
 };
+
+/**
+ * Defines the values for VoPS indicator of NR as per 3gpp spec 24.501 sec 9.10.3.5
+ */
+enum VopsIndicator : uint8_t {
+    /** IMS voice over PS session not supported */
+    VOPS_NOT_SUPPORTED = 0,
+    /** IMS voice over PS session supported over 3GPP access */
+    VOPS_OVER_3GPP = 1,
+    /** IMS voice over PS session supported over non-3GPP access */
+    VOPS_OVER_NON_3GPP = 2,
+};
+
+/**
+ * Defines the values for emergency service indicator of NR
+ * as per 3gpp spec 24.501 sec 9.10.3.5
+ */
+enum EmcIndicator : uint8_t {
+    /** Emergency services not supported */
+    EMC_NOT_SUPPORTED = 0,
+    /** Emergency services supported in NR connected to 5GCN only */
+    EMC_NR_CONNECTED_TO_5GCN = 1,
+    /** Emergency services supported in E-UTRA connected to 5GCN only */
+    EMC_EUTRA_CONNECTED_TO_5GCN = 2,
+    /** Emergency services supported in NR connected to 5GCN and E-UTRA connected to 5GCN */
+    EMC_BOTH_NR_EUTRA_CONNECTED_TO_5GCN = 3
+};
+
+/**
+ * Defines the values for emergency service fallback indicator of NR
+ * as per 3gpp spec 24.501 sec 9.10.3.5
+ */
+enum EmfIndicator : uint8_t {
+    /** Emergency services fallback not supported */
+    EMF_NOT_SUPPORTED = 0,
+    /** Emergency services fallback supported in NR connected to 5GCN only */
+    EMF_NR_CONNECTED_TO_5GCN = 1,
+    /** Emergency services fallback supported in E-UTRA connected to 5GCN only */
+    EMF_EUTRA_CONNECTED_TO_5GCN = 2,
+    /**
+     * Emergency services fallback supported in NR connected to 5GCN and E-UTRA
+     * connected to 5GCN.
+     */
+    EMF_BOTH_NR_EUTRA_CONNECTED_TO_5GCN = 3
+};
+
+/**
+ * Type to define the NR specific network capabilities for voice over PS including
+ * emergency and normal voice calls.
+ */
+struct NrVopsInfo {
+    /**
+     * This indicates if the camped network supports VoNR services, and what kind of services
+     * it supports. This information is received from NR network during NR NAS registration
+     * procedure through NR REGISTRATION ACCEPT.
+     * Refer 3GPP 24.501 EPS 5GS network feature support -> IMS VoPS
+     */
+    VopsIndicator vopsSupported;
+
+    /**
+     * This indicates if the camped network supports VoNR emergency service. This information
+     * is received from NR network through two sources:
+     * a. During NR NAS registration procedure through NR REGISTRATION ACCEPT.
+     *    Refer 3GPP 24.501 EPS 5GS network feature support -> EMC
+     * b. In case the device is not registered on the network.
+     *    Refer 3GPP 38.331 SIB1 : ims-EmergencySupport
+     *    If device is registered on NR, then this field indicates whether the cell
+     *    supports IMS emergency bearer services for UEs in limited service mode.
+     */
+    EmcIndicator emcSupported;
+
+    /**
+     * This indicates if the camped network supports VoNR emergency service fallback. This
+     * information is received from NR network during NR NAS registration procedure through
+     * NR REGISTRATION ACCEPT.
+     * Refer 3GPP 24.501 EPS 5GS network feature support -> EMF
+     */
+    EmfIndicator emfSupported;
+};
+
+struct LteSignalStrength {
+    @1.0::LteSignalStrength base;
+
+    /**
+     * CSI channel quality indicator (CQI) table index. There are multiple CQI tables.
+     * The definition of CQI in each table is different.
+     *
+     * Reference: 3GPP TS 136.213 section 7.2.3.
+     *
+     * Range [1, 6], INT_MAX means invalid/unreported.
+     */
+    uint32_t cqiTableIndex;
+};
+
+struct NrSignalStrength {
+    @1.4::NrSignalStrength base;
+
+    /**
+     * CSI channel quality indicator (CQI) table index. There are multiple CQI tables.
+     * The definition of CQI in each table is different.
+     *
+     * Reference: 3GPP TS 138.214 section 5.2.2.1.
+     *
+     * Range [1, 3], INT_MAX means invalid/unreported.
+     */
+    uint32_t csiCqiTableIndex;
+
+    /**
+     * CSI channel quality indicator (CQI) for all subbands.
+     *
+     * If the CQI report is for the entire wideband, a single CQI index is provided.
+     * If the CQI report is for all subbands, one CQI index is provided for each subband,
+     * in ascending order of subband index.
+     * If CQI is not available, the CQI report is empty.
+     *
+     * Reference: 3GPP TS 138.214 section 5.2.2.1.
+     *
+     * Range [0, 15], INT_MAX means invalid/unreported.
+     */
+    vec<uint32_t> csiCqiReport;
+};
+
+/**
+ * Overwritten from @1.4::SignalStrength in order to update LteSignalStrength and NrSignalStrength.
+ */
+struct SignalStrength {
+    /**
+     * If GSM measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    GsmSignalStrength gsm;
+
+    /**
+     * If CDMA measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    CdmaSignalStrength cdma;
+
+    /**
+     * If EvDO measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    EvdoSignalStrength evdo;
+
+    /**
+     * If LTE measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    LteSignalStrength lte;
+
+    /**
+     * If TD-SCDMA measurements are provided, this structure must contain valid measurements;
+     * otherwise all fields should be set to INT_MAX to mark them as invalid.
+     */
+    TdscdmaSignalStrength tdscdma;
+
+    /**
+     * If WCDMA measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    WcdmaSignalStrength wcdma;
+
+    /**
+     * If NR 5G measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    NrSignalStrength nr;
+};
+
+/** Overwritten from @1.5::CellInfoLte in order to update LteSignalStrength. */
+struct CellInfoLte {
+    CellIdentityLte cellIdentityLte;
+    LteSignalStrength signalStrengthLte;
+};
+
+/** Overwritten from @1.5::CellInfoNr in order to update NrSignalStrength. */
+struct CellInfoNr {
+    CellIdentityNr cellIdentityNr;
+    NrSignalStrength signalStrengthNr;
+};
+
+/** Overwritten from @1.5::CellInfo in order to update LteSignalStrength and NrSignalStrength. */
+struct CellInfo {
+    /**
+     * True if this cell is registered false if not registered.
+     */
+    bool registered;
+    /**
+     * Connection status for the cell.
+     */
+    CellConnectionStatus connectionStatus;
+
+    safe_union CellInfoRatSpecificInfo {
+        /**
+         * 3gpp CellInfo types.
+         */
+        CellInfoGsm gsm;
+        CellInfoWcdma wcdma;
+        CellInfoTdscdma tdscdma;
+        CellInfoLte lte;
+        CellInfoNr nr;
+
+        /**
+         * 3gpp2 CellInfo types;
+         */
+        CellInfoCdma cdma;
+    } ratSpecificInfo;
+};
+
+/** Overwritten from @1.5::NetworkScanResult in order to update the CellInfo to 1.6 version. */
+struct NetworkScanResult {
+    /**
+     * The status of the scan.
+     */
+    ScanStatus status;
+
+    /**
+     * The error code of the incremental result.
+     */
+    RadioError error;
+
+    /**
+     * List of network information as CellInfo.
+     */
+    vec<CellInfo> networkInfos;
+};
+
+/**
+ * Overwritten from @1.5::RegStateResult to 1.6 to support NrRegistrationInfo
+ * version.
+ */
+struct RegStateResult {
+    /**
+     * Registration state
+     *
+     * If the RAT is indicated as a GERAN, UTRAN, or CDMA2000 technology, this value reports
+     * registration in the Circuit-switched domain.
+     * If the RAT is indicated as an EUTRAN, NGRAN, or another technology that does not support
+     * circuit-switched services, this value reports registration in the Packet-switched domain.
+     */
+    RegState regState;
+
+    /**
+     * Indicates the available voice radio technology, valid values as
+     * defined by RadioTechnology.
+     */
+    RadioTechnology rat;
+
+    /**
+     * Cause code reported by the network in case registration fails. This will be a mobility
+     * management cause code defined for MM, GMM, MME or equivalent as appropriate for the RAT.
+     */
+    RegistrationFailCause reasonForDenial;
+
+    /** CellIdentity */
+    CellIdentity cellIdentity;
+
+    /**
+     * The most-recent PLMN-ID upon which the UE registered (or attempted to register if a failure
+     * is reported in the reasonForDenial field). This PLMN shall be in standard format consisting
+     * of a 3 digit MCC concatenated with a 2 or 3 digit MNC.
+     */
+    string registeredPlmn;
+
+    /**
+     * Access-technology-specific registration information, such as for CDMA2000.
+     */
+    safe_union AccessTechnologySpecificInfo {
+        Monostate noinit;
+
+        Cdma2000RegistrationInfo cdmaInfo;
+
+        EutranRegistrationInfo eutranInfo;
+
+        struct NgranRegistrationInfo {
+            /**
+             * Network capabilities for voice over PS services. This info is valid only on NR
+             * network and must be present when the device is camped on NR. VopsInfo must be
+             * empty when the device is not camped on NR.
+             */
+            NrVopsInfo nrVopsInfo;
+        } ngranInfo;
+    } accessTechnologySpecificInfo;
+};
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
index ac84417..75772cd 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -57,7 +57,7 @@
             ::android::hardware::radio::V1_2::DataRequestReason::NORMAL;
 
     Return<void> res = radio_v1_6->setupDataCall_1_6(serial, accessNetwork, dataProfileInfo,
-                                                     roamingAllowed, reason, addresses, dnses);
+                                                     roamingAllowed, reason, addresses, dnses, -1);
     ASSERT_OK(res);
 
     EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -303,7 +303,7 @@
     serial = GetRandomSerialNumber();
 
     Return<void> res = radio_v1_6->setDataThrottling(
-            serial, DataThrottlingAction::THROTTLE_SECONDARY_CARRIER, 60);
+            serial, DataThrottlingAction::THROTTLE_SECONDARY_CARRIER, 60000);
     ASSERT_OK(res);
 
     EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -318,7 +318,22 @@
 
     serial = GetRandomSerialNumber();
 
-    res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::THROTTLE_ANCHOR_CARRIER, 60);
+    res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::THROTTLE_ANCHOR_CARRIER,
+                                        60000);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    ASSERT_TRUE(
+            CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
+                             {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                              ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+                              ::android::hardware::radio::V1_6::RadioError::NONE,
+                              ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
+
+    serial = GetRandomSerialNumber();
+
+    res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::HOLD, 60000);
     ASSERT_OK(res);
 
     EXPECT_EQ(std::cv_status::no_timeout, wait());
@@ -333,9 +348,8 @@
 
     serial = GetRandomSerialNumber();
 
-    res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::HOLD, 60);
+    res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::NO_DATA_THROTTLING, 60000);
     ASSERT_OK(res);
-
     EXPECT_EQ(std::cv_status::no_timeout, wait());
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
@@ -345,19 +359,50 @@
                               ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
                               ::android::hardware::radio::V1_6::RadioError::NONE,
                               ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
+}
 
+/*
+ * Test IRadio.setSimCardPower_1_6() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, setSimCardPower_1_6) {
+    /* Test setSimCardPower power down */
     serial = GetRandomSerialNumber();
-
-    res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::NO_DATA_THROTTLING, 60);
-    ASSERT_OK(res);
-
+    radio_v1_6->setSimCardPower_1_6(serial, CardPowerState::POWER_DOWN);
     EXPECT_EQ(std::cv_status::no_timeout, wait());
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
     ASSERT_TRUE(
             CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
-                             {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
-                              ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
-                              ::android::hardware::radio::V1_6::RadioError::NONE,
-                              ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
-}
\ No newline at end of file
+                             {::android::hardware::radio::V1_6::RadioError::NONE,
+                              ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+                              ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE}));
+
+    // setSimCardPower_1_6 does not return  until the request is handled, and should not trigger
+    // CardState::ABSENT when turning off power
+    if (radioRsp_v1_6->rspInfo.error == ::android::hardware::radio::V1_6::RadioError::NONE) {
+        /* Wait some time for setting sim power down and then verify it */
+        updateSimCardStatus();
+        EXPECT_EQ(CardState::PRESENT, cardStatus.base.base.base.cardState);
+        // applications should be an empty vector of AppStatus
+        EXPECT_EQ(0, cardStatus.applications.size());
+    }
+
+    /* Test setSimCardPower power up */
+    serial = GetRandomSerialNumber();
+    radio_v1_6->setSimCardPower_1_6(serial, CardPowerState::POWER_UP);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    ASSERT_TRUE(
+            CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
+                             {::android::hardware::radio::V1_6::RadioError::NONE,
+                              ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+                              ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE}));
+
+    // setSimCardPower_1_6 does not return  until the request is handled. Just verify that we still
+    // have CardState::PRESENT after turning the power back on
+    if (radioRsp_v1_6->rspInfo.error == ::android::hardware::radio::V1_6::RadioError::NONE) {
+        updateSimCardStatus();
+        EXPECT_EQ(CardState::PRESENT, cardStatus.base.base.base.cardState);
+    }
+}
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
index 31bb1d7..ab56540 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
+++ b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
@@ -784,7 +784,7 @@
     Return<void> releasePduSessionIdResponse(
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
 
-    Return<void> beginHandoverResponse(
+    Return<void> startHandoverResponse(
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
 
     Return<void> cancelHandoverResponse(
@@ -793,8 +793,34 @@
     Return<void> setAllowedNetworkTypeBitmapResponse(
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
 
+    Return<void> getAllowedNetworkTypeBitmapResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::hidl_bitfield<
+                    ::android::hardware::radio::V1_4::RadioAccessFamily>
+                    networkTypeBitmap);
+
     Return<void> setDataThrottlingResponse(
             const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+
+    Return<void> getSystemSelectionChannelsResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+
+    Return<void> getSignalStrengthResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_6::SignalStrength& sig_strength);
+
+    Return<void> getCellInfoListResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::CellInfo>&
+                    cellInfo);
+
+    Return<void> getVoiceRegistrationStateResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_6::RegStateResult& regResponse);
+
+    Return<void> getDataRegistrationStateResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_6::RegStateResult& regResponse);
 };
 
 /* Callback class for radio indication */
@@ -814,6 +840,19 @@
     Return<void> unthrottleApn(RadioIndicationType type,
                                const ::android::hardware::hidl_string& apn);
 
+    Return<void> currentSignalStrength_1_6(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_6::SignalStrength& signalStrength);
+
+    Return<void> networkScanResult_1_6(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_6::NetworkScanResult& result);
+
+    Return<void> cellInfoList_1_6(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::CellInfo>&
+                    records);
+
     /* 1.5 Api */
     Return<void> uiccApplicationsEnablementChanged(RadioIndicationType type, bool enabled);
 
diff --git a/radio/1.6/vts/functional/radio_indication.cpp b/radio/1.6/vts/functional/radio_indication.cpp
index afde291..bfc54c0 100644
--- a/radio/1.6/vts/functional/radio_indication.cpp
+++ b/radio/1.6/vts/functional/radio_indication.cpp
@@ -386,3 +386,22 @@
                                               const ::android::hardware::hidl_string& /*reason*/) {
     return Void();
 }
+
+Return<void> RadioIndication_v1_6::currentSignalStrength_1_6(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_6::SignalStrength& /*signalStrength*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::networkScanResult_1_6(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_6::NetworkScanResult& /*result*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cellInfoList_1_6(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_6::CellInfo>& /*records*/) {
+    return Void();
+}
diff --git a/radio/1.6/vts/functional/radio_response.cpp b/radio/1.6/vts/functional/radio_response.cpp
index 09a2bac..e37efdd 100644
--- a/radio/1.6/vts/functional/radio_response.cpp
+++ b/radio/1.6/vts/functional/radio_response.cpp
@@ -808,12 +808,6 @@
     return Void();
 }
 
-Return<void> RadioResponse_v1_6::getSignalStrengthResponse_1_4(
-        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
-        const ::android::hardware::radio::V1_4::SignalStrength& /*sig_strength*/) {
-    return Void();
-}
-
 Return<void> RadioResponse_v1_6::getCellInfoListResponse_1_2(
         const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
         const ::android::hardware::hidl_vec<
@@ -867,6 +861,12 @@
     return Void();
 }
 
+Return<void> RadioResponse_v1_6::getSignalStrengthResponse_1_4(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_4::SignalStrength& /*sig_strength*/) {
+    return Void();
+}
+
 Return<void> RadioResponse_v1_6::getCellInfoListResponse_1_4(
         const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
         const ::android::hardware::hidl_vec<
@@ -1136,7 +1136,7 @@
     return Void();
 }
 
-Return<void> RadioResponse_v1_6::beginHandoverResponse(
+Return<void> RadioResponse_v1_6::startHandoverResponse(
         const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
     rspInfo = info;
     parent_v1_6.notify(info.serial);
@@ -1157,9 +1157,53 @@
     return Void();
 }
 
+Return<void> RadioResponse_v1_6::getAllowedNetworkTypeBitmapResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_bitfield<
+                ::android::hardware::radio::V1_4::RadioAccessFamily>
+        /*networkTypeBitmap*/) {
+    return Void();
+}
+
 Return<void> RadioResponse_v1_6::setDataThrottlingResponse(
         const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
     rspInfo = info;
     parent_v1_6.notify(info.serial);
     return Void();
 }
+
+Return<void> RadioResponse_v1_6::getSignalStrengthResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_6::SignalStrength& /*sig_strength*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCellInfoListResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_6::CellInfo>& /*cellInfo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getSystemSelectionChannelsResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getVoiceRegistrationStateResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const ::android::hardware::radio::V1_6::RegStateResult& /*regResponse*/) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getDataRegistrationStateResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const ::android::hardware::radio::V1_6::RegStateResult& /*regResponse*/) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
diff --git a/radio/config/1.3/Android.bp b/radio/config/1.3/Android.bp
new file mode 100644
index 0000000..ace0de9
--- /dev/null
+++ b/radio/config/1.3/Android.bp
@@ -0,0 +1,21 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.radio.config@1.3",
+    root: "android.hardware",
+    srcs: [
+        "types.hal",
+        "IRadioConfig.hal",
+        "IRadioConfigResponse.hal",
+    ],
+    interfaces: [
+        "android.hardware.radio.config@1.0",
+        "android.hardware.radio.config@1.1",
+        "android.hardware.radio.config@1.2",
+        "android.hardware.radio@1.0",
+        "android.hardware.radio@1.6",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+    system_ext_specific: true,
+}
diff --git a/radio/config/1.3/IRadioConfig.hal b/radio/config/1.3/IRadioConfig.hal
new file mode 100644
index 0000000..83bcf92
--- /dev/null
+++ b/radio/config/1.3/IRadioConfig.hal
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ * This interface is used by telephony and telecom to talk to cellular radio for the purpose of
+ * radio configuration, and it is not associated with any specific modem or slot.
+ * All the functions have minimum one parameter:
+ * serial: which corresponds to serial no. of request. Serial numbers must only be memorized for the
+ * duration of a method call. If clients provide colliding serials (including passing the same
+ * serial to different methods), multiple responses (one for each method call) must still be served.
+ */
+
+package android.hardware.radio.config@1.3;
+
+import @1.1::IRadioConfig;
+import IRadioConfigResponse;
+
+interface IRadioConfig extends @1.1::IRadioConfig {
+    /**
+     * Gets the available Radio Hal capabilities on the current device.
+     *
+     * This is called once per device boot up.
+     *
+     * @param serial Serial number of request
+     *
+     * Response callback is
+     * IRadioConfigResponse.getHalDeviceCapabilitiesResponse()
+     */
+    oneway getHalDeviceCapabilities(int32_t serial);
+};
diff --git a/radio/config/1.3/IRadioConfigResponse.hal b/radio/config/1.3/IRadioConfigResponse.hal
new file mode 100644
index 0000000..863754f
--- /dev/null
+++ b/radio/config/1.3/IRadioConfigResponse.hal
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.config@1.3;
+
+import android.hardware.radio@1.6::RadioResponseInfo;
+import @1.2::IRadioConfigResponse;
+import HalDeviceCapabilities;
+
+/**
+ * Interface declaring response functions to solicited radio config requests.
+ */
+interface IRadioConfigResponse extends @1.2::IRadioConfigResponse {
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param capabilities Capabilities struct containing the capabilities of the
+     * device related to the Radio HAL
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getHalDeviceCapabilitiesResponse(RadioResponseInfo info,
+        HalDeviceCapabilities capabilities);
+};
diff --git a/radio/config/1.3/types.hal b/radio/config/1.3/types.hal
new file mode 100644
index 0000000..bedb709
--- /dev/null
+++ b/radio/config/1.3/types.hal
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.config@1.3;
+
+/**
+ * Contains the device capabilities with respect to the Radio HAL.
+ */
+struct HalDeviceCapabilities {};
diff --git a/radio/config/1.3/vts/functional/Android.bp b/radio/config/1.3/vts/functional/Android.bp
new file mode 100644
index 0000000..abd081f
--- /dev/null
+++ b/radio/config/1.3/vts/functional/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2019 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.
+//
+
+cc_test {
+    name: "VtsHalRadioConfigV1_3TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "radio_config_hidl_hal_api.cpp",
+        "radio_config_hidl_hal_test.cpp",
+        "radio_config_response.cpp",
+        "radio_config_indication.cpp",
+        "VtsHalRadioConfigV1_3TargetTest.cpp",
+    ],
+    static_libs: [
+        "RadioVtsTestUtilBase",
+        "android.hardware.radio.config@1.0",
+        "android.hardware.radio.config@1.1",
+        "android.hardware.radio.config@1.2",
+        "android.hardware.radio.config@1.3",
+    ],
+    header_libs: ["radio.util.header@1.0"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp b/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
new file mode 100644
index 0000000..5772d08
--- /dev/null
+++ b/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <radio_config_hidl_hal_utils.h>
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioConfigHidlTest);
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, RadioConfigHidlTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IRadioConfig::descriptor)),
+        android::hardware::PrintInstanceNameToString);
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
new file mode 100644
index 0000000..8df02dd
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <radio_config_hidl_hal_utils.h>
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+
+/*
+ * Test IRadioConfig.getHalDeviceCapabilities()
+ */
+TEST_P(RadioConfigHidlTest, getHalDeviceCapabilities) {
+    const int serial = GetRandomSerialNumber();
+    Return<void> res = radioConfig->getHalDeviceCapabilities(serial);
+    ASSERT_OK(res);
+    ALOGI("getHalDeviceCapabilities, rspInfo.error = %s\n",
+          toString(radioConfigRsp->rspInfo.error).c_str());
+}
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
new file mode 100644
index 0000000..de8365a
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <radio_config_hidl_hal_utils.h>
+
+void RadioConfigHidlTest::SetUp() {
+    radioConfig = IRadioConfig::getService(GetParam());
+    if (radioConfig == NULL) {
+        sleep(60);
+        radioConfig = IRadioConfig::getService(GetParam());
+    }
+    ASSERT_NE(nullptr, radioConfig.get());
+
+    radioConfigRsp = new (std::nothrow) RadioConfigResponse(*this);
+    ASSERT_NE(nullptr, radioConfigRsp.get());
+
+    count_ = 0;
+
+    radioConfig->setResponseFunctions(radioConfigRsp, nullptr);
+}
+
+/*
+ * Notify that the response message is received.
+ */
+void RadioConfigHidlTest::notify(int receivedSerial) {
+    std::unique_lock<std::mutex> lock(mtx_);
+    if (serial == receivedSerial) {
+        count_++;
+        cv_.notify_one();
+    }
+}
+
+/*
+ * Wait till the response message is notified or till TIMEOUT_PERIOD.
+ */
+std::cv_status RadioConfigHidlTest::wait() {
+    std::unique_lock<std::mutex> lock(mtx_);
+
+    std::cv_status status = std::cv_status::no_timeout;
+    auto now = std::chrono::system_clock::now();
+    while (count_ == 0) {
+        status = cv_.wait_until(lock, now + std::chrono::seconds(TIMEOUT_PERIOD));
+        if (status == std::cv_status::timeout) {
+            return status;
+        }
+    }
+    count_--;
+    return status;
+}
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
new file mode 100644
index 0000000..439eb70
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+#include <android/hardware/radio/config/1.1/IRadioConfig.h>
+#include <android/hardware/radio/config/1.1/types.h>
+#include <android/hardware/radio/config/1.2/IRadioConfigIndication.h>
+#include <android/hardware/radio/config/1.2/IRadioConfigResponse.h>
+#include <android/hardware/radio/config/1.2/types.h>
+#include <android/hardware/radio/config/1.3/IRadioConfig.h>
+#include <android/hardware/radio/config/1.3/IRadioConfigResponse.h>
+#include <android/hardware/radio/config/1.3/types.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include "vts_test_util.h"
+
+using namespace ::android::hardware::radio::config::V1_2;
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::radio::config::V1_1::ModemsConfig;
+using ::android::hardware::radio::config::V1_1::PhoneCapability;
+using ::android::hardware::radio::config::V1_2::SimSlotStatus;
+using ::android::hardware::radio::config::V1_3::HalDeviceCapabilities;
+using ::android::hardware::radio::config::V1_3::IRadioConfig;
+using ::android::hardware::radio::V1_0::RadioResponseInfo;
+
+#define TIMEOUT_PERIOD 75
+#define RADIO_SERVICE_NAME "slot1"
+
+class RadioConfigHidlTest;
+
+/* Callback class for radio config response */
+class RadioConfigResponse : public IRadioConfigResponse {
+  protected:
+    RadioConfigHidlTest& parent;
+
+  public:
+    RadioResponseInfo rspInfo;
+    PhoneCapability phoneCap;
+
+    RadioConfigResponse(RadioConfigHidlTest& parent);
+    virtual ~RadioConfigResponse() = default;
+
+    Return<void> getSimSlotsStatusResponse(
+            const RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<
+                    ::android::hardware::radio::config::V1_0::SimSlotStatus>& slotStatus);
+
+    Return<void> getSimSlotsStatusResponse_1_2(
+            const RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<SimSlotStatus>& slotStatus);
+
+    Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info);
+
+    Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
+                                            const PhoneCapability& phoneCapability);
+
+    Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info);
+
+    Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
+                                         const ModemsConfig& mConfig);
+
+    Return<void> setModemsConfigResponse(const RadioResponseInfo& info);
+
+    Return<void> getHalDeviceCapabilitiesResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const HalDeviceCapabilities& halDeviceCapabilities);
+};
+
+/* Callback class for radio config indication */
+class RadioConfigIndication : public IRadioConfigIndication {
+  protected:
+    RadioConfigHidlTest& parent;
+
+  public:
+    RadioConfigIndication(RadioConfigHidlTest& parent);
+    virtual ~RadioConfigIndication() = default;
+
+    Return<void> simSlotsStatusChanged_1_2(
+            ::android::hardware::radio::V1_0::RadioIndicationType type,
+            const ::android::hardware::hidl_vec<SimSlotStatus>& slotStatus);
+};
+
+// The main test class for Radio config HIDL.
+class RadioConfigHidlTest : public ::testing::TestWithParam<std::string> {
+  protected:
+    std::mutex mtx_;
+    std::condition_variable cv_;
+    int count_;
+
+  public:
+    virtual void SetUp() override;
+
+    /* Used as a mechanism to inform the test about data/event callback */
+    void notify(int receivedSerial);
+
+    /* Test code calls this function to wait for response */
+    std::cv_status wait();
+
+    void updateSimCardStatus();
+
+    /* Serial number for radio request */
+    int serial;
+
+    /* radio config service handle */
+    sp<IRadioConfig> radioConfig;
+
+    /* radio config response handle */
+    sp<RadioConfigResponse> radioConfigRsp;
+};
diff --git a/radio/config/1.3/vts/functional/radio_config_indication.cpp b/radio/config/1.3/vts/functional/radio_config_indication.cpp
new file mode 100644
index 0000000..6fa443c
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_indication.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <radio_config_hidl_hal_utils.h>
+
+RadioConfigIndication::RadioConfigIndication(RadioConfigHidlTest& parent) : parent(parent) {}
+
+Return<void> RadioConfigIndication::simSlotsStatusChanged_1_2(
+        ::android::hardware::radio::V1_0::RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<SimSlotStatus>& /*slotStatus*/) {
+    return Void();
+}
diff --git a/radio/config/1.3/vts/functional/radio_config_response.cpp b/radio/config/1.3/vts/functional/radio_config_response.cpp
new file mode 100644
index 0000000..2a8b28b
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_response.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <radio_config_hidl_hal_utils.h>
+
+// SimSlotStatus slotStatus;
+
+RadioConfigResponse::RadioConfigResponse(RadioConfigHidlTest& parent) : parent(parent) {}
+
+Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::config::V1_0::SimSlotStatus>& /* slotStatus */) {
+    return Void();
+}
+
+Return<void> RadioConfigResponse::getSimSlotsStatusResponse_1_2(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */,
+        const ::android::hardware::hidl_vec<SimSlotStatus>& /* slotStatus */) {
+    return Void();
+}
+
+Return<void> RadioConfigResponse::setSimSlotsMappingResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */) {
+    return Void();
+}
+
+Return<void> RadioConfigResponse::getPhoneCapabilityResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+        const PhoneCapability& phoneCapability) {
+    rspInfo = info;
+    phoneCap = phoneCapability;
+    parent.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioConfigResponse::setPreferredDataModemResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */) {
+    return Void();
+}
+
+Return<void> RadioConfigResponse::getModemsConfigResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */,
+        const ModemsConfig& /* mConfig */) {
+    return Void();
+}
+
+Return<void> RadioConfigResponse::setModemsConfigResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */) {
+    return Void();
+}
+
+Return<void> RadioConfigResponse::getHalDeviceCapabilitiesResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& /* info */,
+        const ::android::hardware::radio::config::V1_3::HalDeviceCapabilities& /* capabilities */) {
+    return Void();
+}
\ No newline at end of file
diff --git a/renderscript/1.0/types.hal b/renderscript/1.0/types.hal
index 7c32188..bb39baa 100644
--- a/renderscript/1.0/types.hal
+++ b/renderscript/1.0/types.hal
@@ -170,7 +170,6 @@
 };
 
 // Script to Script
-@export(name="RsScriptCall")
 struct ScriptCall {
     ForEachStrategy strategy;
     uint32_t        xStart;
diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
new file mode 100644
index 0000000..b5adac9
--- /dev/null
+++ b/security/keymint/aidl/Android.bp
@@ -0,0 +1,21 @@
+aidl_interface {
+    name: "android.hardware.security.keymint",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/security/keymint/*.aidl",
+    ],
+    stability: "vintf",
+    backend: {
+        java: {
+            sdk_version: "module_current",
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+        rust: {
+            enabled: true,
+        },
+    },
+}
diff --git a/security/keymint/aidl/OWNERS b/security/keymint/aidl/OWNERS
new file mode 100644
index 0000000..5c79db8
--- /dev/null
+++ b/security/keymint/aidl/OWNERS
@@ -0,0 +1,3 @@
+jdanis@google.com
+seleneh@google.com
+swillden@google.com
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Algorithm.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Algorithm.aidl
new file mode 100644
index 0000000..46e0ae0
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Algorithm.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum Algorithm {
+  RSA = 1,
+  EC = 3,
+  AES = 32,
+  TRIPLE_DES = 33,
+  HMAC = 128,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BeginResult.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BeginResult.aidl
new file mode 100644
index 0000000..ed96485
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BeginResult.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+parcelable BeginResult {
+  long challenge;
+  android.hardware.security.keymint.KeyParameter[] params;
+  android.hardware.security.keymint.IKeyMintOperation operation;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BlockMode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BlockMode.aidl
new file mode 100644
index 0000000..dddc9d8
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BlockMode.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum BlockMode {
+  ECB = 1,
+  CBC = 2,
+  CTR = 3,
+  GCM = 32,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
new file mode 100644
index 0000000..3d18a26
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ByteArray.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+parcelable ByteArray {
+  byte[] data;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Certificate.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Certificate.aidl
new file mode 100644
index 0000000..9e0f8dc
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Certificate.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+parcelable Certificate {
+  byte[] encodedCertificate;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Digest.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Digest.aidl
new file mode 100644
index 0000000..8fc4d42
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Digest.aidl
@@ -0,0 +1,28 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum Digest {
+  NONE = 0,
+  MD5 = 1,
+  SHA1 = 2,
+  SHA_2_224 = 3,
+  SHA_2_256 = 4,
+  SHA_2_384 = 5,
+  SHA_2_512 = 6,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/EcCurve.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/EcCurve.aidl
new file mode 100644
index 0000000..7c3f2f3
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/EcCurve.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum EcCurve {
+  P_224 = 0,
+  P_256 = 1,
+  P_384 = 2,
+  P_521 = 3,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
new file mode 100644
index 0000000..cdcb08d
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
@@ -0,0 +1,100 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum ErrorCode {
+  OK = 0,
+  ROOT_OF_TRUST_ALREADY_SET = -1,
+  UNSUPPORTED_PURPOSE = -2,
+  INCOMPATIBLE_PURPOSE = -3,
+  UNSUPPORTED_ALGORITHM = -4,
+  INCOMPATIBLE_ALGORITHM = -5,
+  UNSUPPORTED_KEY_SIZE = -6,
+  UNSUPPORTED_BLOCK_MODE = -7,
+  INCOMPATIBLE_BLOCK_MODE = -8,
+  UNSUPPORTED_MAC_LENGTH = -9,
+  UNSUPPORTED_PADDING_MODE = -10,
+  INCOMPATIBLE_PADDING_MODE = -11,
+  UNSUPPORTED_DIGEST = -12,
+  INCOMPATIBLE_DIGEST = -13,
+  INVALID_EXPIRATION_TIME = -14,
+  INVALID_USER_ID = -15,
+  INVALID_AUTHORIZATION_TIMEOUT = -16,
+  UNSUPPORTED_KEY_FORMAT = -17,
+  INCOMPATIBLE_KEY_FORMAT = -18,
+  UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = -19,
+  UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = -20,
+  INVALID_INPUT_LENGTH = -21,
+  KEY_EXPORT_OPTIONS_INVALID = -22,
+  DELEGATION_NOT_ALLOWED = -23,
+  KEY_NOT_YET_VALID = -24,
+  KEY_EXPIRED = -25,
+  KEY_USER_NOT_AUTHENTICATED = -26,
+  OUTPUT_PARAMETER_NULL = -27,
+  INVALID_OPERATION_HANDLE = -28,
+  INSUFFICIENT_BUFFER_SPACE = -29,
+  VERIFICATION_FAILED = -30,
+  TOO_MANY_OPERATIONS = -31,
+  UNEXPECTED_NULL_POINTER = -32,
+  INVALID_KEY_BLOB = -33,
+  IMPORTED_KEY_NOT_ENCRYPTED = -34,
+  IMPORTED_KEY_DECRYPTION_FAILED = -35,
+  IMPORTED_KEY_NOT_SIGNED = -36,
+  IMPORTED_KEY_VERIFICATION_FAILED = -37,
+  INVALID_ARGUMENT = -38,
+  UNSUPPORTED_TAG = -39,
+  INVALID_TAG = -40,
+  MEMORY_ALLOCATION_FAILED = -41,
+  IMPORT_PARAMETER_MISMATCH = -44,
+  SECURE_HW_ACCESS_DENIED = -45,
+  OPERATION_CANCELLED = -46,
+  CONCURRENT_ACCESS_CONFLICT = -47,
+  SECURE_HW_BUSY = -48,
+  SECURE_HW_COMMUNICATION_FAILED = -49,
+  UNSUPPORTED_EC_FIELD = -50,
+  MISSING_NONCE = -51,
+  INVALID_NONCE = -52,
+  MISSING_MAC_LENGTH = -53,
+  KEY_RATE_LIMIT_EXCEEDED = -54,
+  CALLER_NONCE_PROHIBITED = -55,
+  KEY_MAX_OPS_EXCEEDED = -56,
+  INVALID_MAC_LENGTH = -57,
+  MISSING_MIN_MAC_LENGTH = -58,
+  UNSUPPORTED_MIN_MAC_LENGTH = -59,
+  UNSUPPORTED_KDF = -60,
+  UNSUPPORTED_EC_CURVE = -61,
+  KEY_REQUIRES_UPGRADE = -62,
+  ATTESTATION_CHALLENGE_MISSING = -63,
+  KEYMINT_NOT_CONFIGURED = -64,
+  ATTESTATION_APPLICATION_ID_MISSING = -65,
+  CANNOT_ATTEST_IDS = -66,
+  ROLLBACK_RESISTANCE_UNAVAILABLE = -67,
+  HARDWARE_TYPE_UNAVAILABLE = -68,
+  PROOF_OF_PRESENCE_REQUIRED = -69,
+  CONCURRENT_PROOF_OF_PRESENCE_REQUESTED = -70,
+  NO_USER_CONFIRMATION = -71,
+  DEVICE_LOCKED = -72,
+  EARLY_BOOT_ENDED = -73,
+  ATTESTATION_KEYS_NOT_PROVISIONED = -74,
+  ATTESTATION_IDS_NOT_PROVISIONED = -75,
+  INVALID_OPERATION = -76,
+  STORAGE_KEY_UNSUPPORTED = -77,
+  UNIMPLEMENTED = -100,
+  VERSION_MISMATCH = -101,
+  UNKNOWN_ERROR = -1000,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl
new file mode 100644
index 0000000..9ea24f5
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+parcelable HardwareAuthToken {
+  long challenge;
+  long userId;
+  long authenticatorId;
+  android.hardware.security.keymint.HardwareAuthenticatorType authenticatorType;
+  android.hardware.security.keymint.Timestamp timestamp;
+  byte[] mac;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthenticatorType.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
new file mode 100644
index 0000000..aef5ee0
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum HardwareAuthenticatorType {
+  NONE = 0,
+  PASSWORD = 1,
+  FINGERPRINT = 2,
+  ANY = -1,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
new file mode 100644
index 0000000..3d08cfe
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -0,0 +1,33 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+interface IKeyMintDevice {
+  android.hardware.security.keymint.KeyMintHardwareInfo getHardwareInfo();
+  android.hardware.security.keymint.VerificationToken verifyAuthorization(in long challenge, in android.hardware.security.keymint.HardwareAuthToken token);
+  void addRngEntropy(in byte[] data);
+  void generateKey(in android.hardware.security.keymint.KeyParameter[] keyParams, out android.hardware.security.keymint.ByteArray generatedKeyBlob, out android.hardware.security.keymint.KeyCharacteristics generatedKeyCharacteristics, out android.hardware.security.keymint.Certificate[] outCertChain);
+  void importKey(in android.hardware.security.keymint.KeyParameter[] inKeyParams, in android.hardware.security.keymint.KeyFormat inKeyFormat, in byte[] inKeyData, out android.hardware.security.keymint.ByteArray outImportedKeyBlob, out android.hardware.security.keymint.KeyCharacteristics outImportedKeyCharacteristics, out android.hardware.security.keymint.Certificate[] outCertChain);
+  void importWrappedKey(in byte[] inWrappedKeyData, in byte[] inWrappingKeyBlob, in byte[] inMaskingKey, in android.hardware.security.keymint.KeyParameter[] inUnwrappingParams, in long inPasswordSid, in long inBiometricSid, out android.hardware.security.keymint.ByteArray outImportedKeyBlob, out android.hardware.security.keymint.KeyCharacteristics outImportedKeyCharacteristics);
+  byte[] upgradeKey(in byte[] inKeyBlobToUpgrade, in android.hardware.security.keymint.KeyParameter[] inUpgradeParams);
+  void deleteKey(in byte[] inKeyBlob);
+  void deleteAllKeys();
+  void destroyAttestationIds();
+  android.hardware.security.keymint.BeginResult begin(in android.hardware.security.keymint.KeyPurpose inPurpose, in byte[] inKeyBlob, in android.hardware.security.keymint.KeyParameter[] inParams, in android.hardware.security.keymint.HardwareAuthToken inAuthToken);
+  const int AUTH_TOKEN_MAC_LENGTH = 32;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
new file mode 100644
index 0000000..8e3b0fc
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+interface IKeyMintOperation {
+  int update(in @nullable android.hardware.security.keymint.KeyParameterArray inParams, in @nullable byte[] input, in @nullable android.hardware.security.keymint.HardwareAuthToken inAuthToken, in @nullable android.hardware.security.keymint.VerificationToken inVerificationToken, out @nullable android.hardware.security.keymint.KeyParameterArray outParams, out @nullable android.hardware.security.keymint.ByteArray output);
+  byte[] finish(in @nullable android.hardware.security.keymint.KeyParameterArray inParams, in @nullable byte[] input, in @nullable byte[] inSignature, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken, in @nullable android.hardware.security.keymint.VerificationToken inVerificationToken, out @nullable android.hardware.security.keymint.KeyParameterArray outParams);
+  void abort();
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCharacteristics.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCharacteristics.aidl
new file mode 100644
index 0000000..fb4214c
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCharacteristics.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+parcelable KeyCharacteristics {
+  android.hardware.security.keymint.KeyParameter[] softwareEnforced;
+  android.hardware.security.keymint.KeyParameter[] hardwareEnforced;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyDerivationFunction.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyDerivationFunction.aidl
new file mode 100644
index 0000000..83b7e6e
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyDerivationFunction.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum KeyDerivationFunction {
+  NONE = 0,
+  RFC5869_SHA256 = 1,
+  ISO18033_2_KDF1_SHA1 = 2,
+  ISO18033_2_KDF1_SHA256 = 3,
+  ISO18033_2_KDF2_SHA1 = 4,
+  ISO18033_2_KDF2_SHA256 = 5,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyFormat.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyFormat.aidl
new file mode 100644
index 0000000..f701c80
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyFormat.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum KeyFormat {
+  X509 = 0,
+  PKCS8 = 1,
+  RAW = 3,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
new file mode 100644
index 0000000..5e9f7ae
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+parcelable KeyMintHardwareInfo {
+  int versionNumber;
+  android.hardware.security.keymint.SecurityLevel securityLevel;
+  @utf8InCpp String keyMintName;
+  @utf8InCpp String keyMintAuthorName;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyOrigin.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyOrigin.aidl
new file mode 100644
index 0000000..9728bf9
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyOrigin.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum KeyOrigin {
+  GENERATED = 0,
+  DERIVED = 1,
+  IMPORTED = 2,
+  RESERVED = 3,
+  SECURELY_IMPORTED = 4,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameter.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameter.aidl
new file mode 100644
index 0000000..91f83e4
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameter.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+parcelable KeyParameter {
+  android.hardware.security.keymint.Tag tag;
+  boolean boolValue;
+  int integer;
+  long longInteger;
+  byte[] blob;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterArray.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterArray.aidl
new file mode 100644
index 0000000..2c3b768
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterArray.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+parcelable KeyParameterArray {
+  android.hardware.security.keymint.KeyParameter[] params;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
new file mode 100644
index 0000000..a6fd8c3
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum KeyPurpose {
+  ENCRYPT = 0,
+  DECRYPT = 1,
+  SIGN = 2,
+  VERIFY = 3,
+  WRAP_KEY = 5,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/PaddingMode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/PaddingMode.aidl
new file mode 100644
index 0000000..2ecfa1e
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/PaddingMode.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum PaddingMode {
+  NONE = 1,
+  RSA_OAEP = 2,
+  RSA_PSS = 3,
+  RSA_PKCS1_1_5_ENCRYPT = 4,
+  RSA_PKCS1_1_5_SIGN = 5,
+  PKCS7 = 64,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/SecurityLevel.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/SecurityLevel.aidl
new file mode 100644
index 0000000..601693f
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/SecurityLevel.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum SecurityLevel {
+  SOFTWARE = 0,
+  TRUSTED_ENVIRONMENT = 1,
+  STRONGBOX = 2,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
new file mode 100644
index 0000000..38eb6e6
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
@@ -0,0 +1,80 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum Tag {
+  INVALID = 0,
+  PURPOSE = 536870913,
+  ALGORITHM = 268435458,
+  KEY_SIZE = 805306371,
+  BLOCK_MODE = 536870916,
+  DIGEST = 536870917,
+  PADDING = 536870918,
+  CALLER_NONCE = 1879048199,
+  MIN_MAC_LENGTH = 805306376,
+  EC_CURVE = 268435466,
+  RSA_PUBLIC_EXPONENT = 1342177480,
+  INCLUDE_UNIQUE_ID = 1879048394,
+  BLOB_USAGE_REQUIREMENTS = 268435757,
+  BOOTLOADER_ONLY = 1879048494,
+  ROLLBACK_RESISTANCE = 1879048495,
+  HARDWARE_TYPE = 268435760,
+  EARLY_BOOT_ONLY = 1879048497,
+  ACTIVE_DATETIME = 1610613136,
+  ORIGINATION_EXPIRE_DATETIME = 1610613137,
+  USAGE_EXPIRE_DATETIME = 1610613138,
+  MIN_SECONDS_BETWEEN_OPS = 805306771,
+  MAX_USES_PER_BOOT = 805306772,
+  USER_ID = 805306869,
+  USER_SECURE_ID = -1610612234,
+  NO_AUTH_REQUIRED = 1879048695,
+  USER_AUTH_TYPE = 268435960,
+  AUTH_TIMEOUT = 805306873,
+  ALLOW_WHILE_ON_BODY = 1879048698,
+  TRUSTED_USER_PRESENCE_REQUIRED = 1879048699,
+  TRUSTED_CONFIRMATION_REQUIRED = 1879048700,
+  UNLOCKED_DEVICE_REQUIRED = 1879048701,
+  APPLICATION_ID = -1879047591,
+  APPLICATION_DATA = -1879047492,
+  CREATION_DATETIME = 1610613437,
+  ORIGIN = 268436158,
+  ROOT_OF_TRUST = -1879047488,
+  OS_VERSION = 805307073,
+  OS_PATCHLEVEL = 805307074,
+  UNIQUE_ID = -1879047485,
+  ATTESTATION_CHALLENGE = -1879047484,
+  ATTESTATION_APPLICATION_ID = -1879047483,
+  ATTESTATION_ID_BRAND = -1879047482,
+  ATTESTATION_ID_DEVICE = -1879047481,
+  ATTESTATION_ID_PRODUCT = -1879047480,
+  ATTESTATION_ID_SERIAL = -1879047479,
+  ATTESTATION_ID_IMEI = -1879047478,
+  ATTESTATION_ID_MEID = -1879047477,
+  ATTESTATION_ID_MANUFACTURER = -1879047476,
+  ATTESTATION_ID_MODEL = -1879047475,
+  VENDOR_PATCHLEVEL = 805307086,
+  BOOT_PATCHLEVEL = 805307087,
+  DEVICE_UNIQUE_ATTESTATION = 1879048912,
+  IDENTITY_CREDENTIAL_KEY = 1879048913,
+  STORAGE_KEY = 1879048914,
+  ASSOCIATED_DATA = -1879047192,
+  NONCE = -1879047191,
+  MAC_LENGTH = 805307371,
+  RESET_SINCE_ID_ROTATION = 1879049196,
+  CONFIRMATION_TOKEN = -1879047187,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/TagType.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/TagType.aidl
new file mode 100644
index 0000000..bb2766c
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/TagType.aidl
@@ -0,0 +1,32 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@Backing(type="int") @VintfStability
+enum TagType {
+  INVALID = 0,
+  ENUM = 268435456,
+  ENUM_REP = 536870912,
+  UINT = 805306368,
+  UINT_REP = 1073741824,
+  ULONG = 1342177280,
+  DATE = 1610612736,
+  BOOL = 1879048192,
+  BIGNUM = -2147483648,
+  BYTES = -1879048192,
+  ULONG_REP = -1610612736,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl
new file mode 100644
index 0000000..4d5b659
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+parcelable Timestamp {
+  long milliSeconds;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/VerificationToken.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/VerificationToken.aidl
new file mode 100644
index 0000000..5c76816
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/VerificationToken.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+@VintfStability
+parcelable VerificationToken {
+  long challenge;
+  android.hardware.security.keymint.Timestamp timestamp;
+  android.hardware.security.keymint.SecurityLevel securityLevel;
+  byte[] mac;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl b/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl
new file mode 100644
index 0000000..8300b0d
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Algorithms provided by IKeyMintDevice implementations.
+ */
+@VintfStability
+@Backing(type="int")
+enum Algorithm {
+    /** Asymmetric algorithms. */
+    RSA = 1,
+    /** 2 removed, do not reuse. */
+    EC = 3,
+
+    /** Block cipher algorithms */
+    AES = 32,
+    TRIPLE_DES = 33,
+
+    /** MAC algorithms */
+    HMAC = 128,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
new file mode 100644
index 0000000..aaf9f3c
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.IKeyMintOperation;
+import android.hardware.security.keymint.KeyParameter;
+
+/**
+ * This is all the results returned by the IKeyMintDevice begin() function.
+ */
+@VintfStability
+parcelable BeginResult {
+    /* This is the challenge used in verifyAuthorization.  It must be a nonce. */
+    long challenge;
+
+    /**
+     * begin() uses this field to return additional data from the operation
+     * initialization, notably to return the IV or nonce from operations
+     * that generate an IV or nonce.
+     */
+    KeyParameter[] params;
+    IKeyMintOperation operation;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl b/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl
new file mode 100644
index 0000000..629c89f
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Symmetric block cipher modes provided by IKeyMintDevice implementations.
+ */
+@VintfStability
+@Backing(type="int")
+enum BlockMode {
+    /*
+     * Unauthenticated modes, usable only for encryption/decryption and not generally recommended
+     * except for compatibility with existing other protocols.
+     */
+    ECB = 1,
+    CBC = 2,
+    CTR = 3,
+
+    /*
+     * Authenticated modes, usable for encryption/decryption and signing/verification.  Recommended
+     * over unauthenticated modes for all purposes.
+     */
+    GCM = 32,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl b/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
new file mode 100644
index 0000000..c3b402e
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/ByteArray.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * This is used to contain a byte[], to make out parameters of byte arrays
+ * more convenient for callers.
+ */
+@VintfStability
+parcelable ByteArray {
+    byte[] data;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl b/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl
new file mode 100644
index 0000000..a953859
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * This encodes the IKeyMintDevice attestation generated certificate.
+ */
+
+@VintfStability
+parcelable Certificate {
+    /**
+     * EncodedCertificate contains the bytes of a DER-encoded X.509 certificate.
+     */
+    byte[] encodedCertificate;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl b/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl
new file mode 100644
index 0000000..b44da5a
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Digests provided by keyMint implementations.
+ */
+@VintfStability
+@Backing(type="int")
+enum Digest {
+    NONE = 0,
+    MD5 = 1,
+    SHA1 = 2,
+    SHA_2_224 = 3,
+    SHA_2_256 = 4,
+    SHA_2_384 = 5,
+    SHA_2_512 = 6,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl b/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl
new file mode 100644
index 0000000..b9d1646
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Supported EC curves, used in ECDSA
+ */
+@VintfStability
+@Backing(type="int")
+enum EcCurve {
+    P_224 = 0,
+    P_256 = 1,
+    P_384 = 2,
+    P_521 = 3,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
new file mode 100644
index 0000000..fb24ad1
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * KeyMint error codes.  Aidl will return these error codes as service specific
+ * errors in EX_SERVICE_SPECIFIC.
+ */
+@VintfStability
+@Backing(type="int")
+enum ErrorCode {
+    OK = 0,
+    ROOT_OF_TRUST_ALREADY_SET = -1,
+    UNSUPPORTED_PURPOSE = -2,
+    INCOMPATIBLE_PURPOSE = -3,
+    UNSUPPORTED_ALGORITHM = -4,
+    INCOMPATIBLE_ALGORITHM = -5,
+    UNSUPPORTED_KEY_SIZE = -6,
+    UNSUPPORTED_BLOCK_MODE = -7,
+    INCOMPATIBLE_BLOCK_MODE = -8,
+    UNSUPPORTED_MAC_LENGTH = -9,
+    UNSUPPORTED_PADDING_MODE = -10,
+    INCOMPATIBLE_PADDING_MODE = -11,
+    UNSUPPORTED_DIGEST = -12,
+    INCOMPATIBLE_DIGEST = -13,
+    INVALID_EXPIRATION_TIME = -14,
+    INVALID_USER_ID = -15,
+    INVALID_AUTHORIZATION_TIMEOUT = -16,
+    UNSUPPORTED_KEY_FORMAT = -17,
+    INCOMPATIBLE_KEY_FORMAT = -18,
+    UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = -19,   /** For PKCS8 & PKCS12 */
+    UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = -20, /** For PKCS8 & PKCS12 */
+    INVALID_INPUT_LENGTH = -21,
+    KEY_EXPORT_OPTIONS_INVALID = -22,
+    DELEGATION_NOT_ALLOWED = -23,
+    KEY_NOT_YET_VALID = -24,
+    KEY_EXPIRED = -25,
+    KEY_USER_NOT_AUTHENTICATED = -26,
+    OUTPUT_PARAMETER_NULL = -27,
+    INVALID_OPERATION_HANDLE = -28,
+    INSUFFICIENT_BUFFER_SPACE = -29,
+    VERIFICATION_FAILED = -30,
+    TOO_MANY_OPERATIONS = -31,
+    UNEXPECTED_NULL_POINTER = -32,
+    INVALID_KEY_BLOB = -33,
+    IMPORTED_KEY_NOT_ENCRYPTED = -34,
+    IMPORTED_KEY_DECRYPTION_FAILED = -35,
+    IMPORTED_KEY_NOT_SIGNED = -36,
+    IMPORTED_KEY_VERIFICATION_FAILED = -37,
+    INVALID_ARGUMENT = -38,
+    UNSUPPORTED_TAG = -39,
+    INVALID_TAG = -40,
+    MEMORY_ALLOCATION_FAILED = -41,
+    IMPORT_PARAMETER_MISMATCH = -44,
+    SECURE_HW_ACCESS_DENIED = -45,
+    OPERATION_CANCELLED = -46,
+    CONCURRENT_ACCESS_CONFLICT = -47,
+    SECURE_HW_BUSY = -48,
+    SECURE_HW_COMMUNICATION_FAILED = -49,
+    UNSUPPORTED_EC_FIELD = -50,
+    MISSING_NONCE = -51,
+    INVALID_NONCE = -52,
+    MISSING_MAC_LENGTH = -53,
+    KEY_RATE_LIMIT_EXCEEDED = -54,
+    CALLER_NONCE_PROHIBITED = -55,
+    KEY_MAX_OPS_EXCEEDED = -56,
+    INVALID_MAC_LENGTH = -57,
+    MISSING_MIN_MAC_LENGTH = -58,
+    UNSUPPORTED_MIN_MAC_LENGTH = -59,
+    UNSUPPORTED_KDF = -60,
+    UNSUPPORTED_EC_CURVE = -61,
+    KEY_REQUIRES_UPGRADE = -62,
+    ATTESTATION_CHALLENGE_MISSING = -63,
+    KEYMINT_NOT_CONFIGURED = -64,
+    ATTESTATION_APPLICATION_ID_MISSING = -65,
+    CANNOT_ATTEST_IDS = -66,
+    ROLLBACK_RESISTANCE_UNAVAILABLE = -67,
+    HARDWARE_TYPE_UNAVAILABLE = -68,
+    PROOF_OF_PRESENCE_REQUIRED = -69,
+    CONCURRENT_PROOF_OF_PRESENCE_REQUESTED = -70,
+    NO_USER_CONFIRMATION = -71,
+    DEVICE_LOCKED = -72,
+    EARLY_BOOT_ENDED = -73,
+    ATTESTATION_KEYS_NOT_PROVISIONED = -74,
+    ATTESTATION_IDS_NOT_PROVISIONED = -75,
+    INVALID_OPERATION = -76,
+    STORAGE_KEY_UNSUPPORTED = -77,
+
+    UNIMPLEMENTED = -100,
+    VERSION_MISMATCH = -101,
+
+    UNKNOWN_ERROR = -1000,
+
+    // Implementer's namespace for error codes starts at -10000.
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
new file mode 100644
index 0000000..12d615f
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.Timestamp;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
+
+/**
+ * HardwareAuthToken is used to prove successful user authentication, to unlock the use of a key.
+ *
+ * HardwareAuthTokens are produced by other secure environment applications, notably GateKeeper and
+ * biometric authenticators, in response to successful user authentication events.  These tokens are passed to
+ * begin(), update(), and finish() to prove that authentication occurred.  See those methods for
+ * more details.  It is up to the caller to determine which of the generated auth tokens is
+ * appropriate for a given key operation.
+ */
+@VintfStability
+parcelable HardwareAuthToken {
+    /**
+     * challenge is a value that's used to enable authentication tokens to authorize specific
+     * events.  The primary use case for challenge is to authorize an IKeyMintDevice cryptographic
+     * operation, for keys that require authentication per operation. See begin() for details.
+     */
+    long challenge;
+
+    /**
+     *  userId is the a "secure" user ID.  It is not related to any Android user ID or UID, but is
+     *  created in the Gatekeeper application in the secure environment.
+     */
+    long userId;
+
+    /**
+     *  authenticatorId is the a "secure" user ID.  It is not related to any Android user ID or UID,
+     *  but is created in an authentication application in the secure environment, such as the
+     *  Fingerprint application.
+     */
+    long authenticatorId;
+
+    /**
+     * authenticatorType describes the type of authentication that took place, e.g. password or
+     * fingerprint.
+     */
+    HardwareAuthenticatorType authenticatorType;
+
+    /**
+     * timestamp indicates when the user authentication took place, in milliseconds since some
+     * starting point (generally the most recent device boot) which all of the applications within
+     * one secure environment must agree upon.  This timestamp is used to determine whether or not
+     * the authentication occurred recently enough to unlock a key (see Tag::AUTH_TIMEOUT).
+     */
+    Timestamp timestamp;
+
+    /**
+     * MACs are computed with a backward-compatible method, used by Keymaster 3.0, Gatekeeper 1.0
+     * and Fingerprint 1.0, as well as pre-treble HALs.
+     *
+     * The MAC is Constants::AUTH_TOKEN_MAC_LENGTH bytes in length and is computed as follows:
+     *
+     *     HMAC_SHA256(
+     *         H, 0 || challenge || user_id || authenticator_id || authenticator_type || timestamp)
+     *
+     * where ``||'' represents concatenation, the leading zero is a single byte, and all integers
+     * are represented as unsigned values, the full width of the type.  The challenge, userId and
+     * authenticatorId values are in machine order, but authenticatorType and timestamp are in
+     * network order (big-endian).  This odd construction is compatible with the hw_auth_token_t
+     * structure.
+     */
+    byte[] mac;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
new file mode 100644
index 0000000..33f71b8
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Hardware authentication type, used by HardwareAuthTokens to specify the mechanism used to
+ * authentiate the user, and in KeyCharacteristics to specify the allowable mechanisms for
+ * authenticating to activate a key.
+ */
+@VintfStability
+@Backing(type="int")
+enum HardwareAuthenticatorType {
+    NONE = 0,
+    PASSWORD = 1 << 0,
+    FINGERPRINT = 1 << 1,
+    // Additional entries must be powers of 2.
+    ANY = 0xFFFFFFFF,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
new file mode 100644
index 0000000..4944acb
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -0,0 +1,790 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.BeginResult;
+import android.hardware.security.keymint.ByteArray;
+import android.hardware.security.keymint.Certificate;
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.keymint.IKeyMintOperation;
+import android.hardware.security.keymint.KeyCharacteristics;
+import android.hardware.security.keymint.KeyFormat;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyMintHardwareInfo;
+import android.hardware.security.keymint.KeyPurpose;
+import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.keymint.VerificationToken;
+
+/**
+ * KeyMint device definition.
+ *
+ * == Features ==
+ *
+ * An IKeyMintDevice provides cryptographic services, including the following categories of
+ * operations:
+ *
+ * o   Key generation
+ * o   Import of asymmetric keys
+ * o   Import of raw symmetric keys
+ * o   Asymmetric decryption with appropriate padding modes
+ * o   Asymmetric signing with digesting and appropriate padding modes
+ * o   Symmetric encryption and decryption in appropriate modes, including an AEAD mode
+ * o   Generation and verification of symmetric message authentication codes
+ * o   Attestation to the presence and configuration of asymmetric keys.
+ *
+ * Protocol elements, such as purpose, mode and padding, as well as access control constraints, must
+ * be specified by the caller when keys are generated or imported and must be permanently bound to
+ * the key, ensuring that the key cannot be used in any other way.
+ *
+ * In addition to the list above, IKeyMintDevice implementations must provide one more service
+ * which is not exposed as an API but used internally: Random number generation.  The random number
+ * generator must be high-quality and must be used for generation of keys, initialization vectors,
+ * random padding and other elements of secure protocols that require randomness.
+ *
+ * == Types of IKeyMintDevices ==
+ *
+ * All of the operations and storage of key material must occur in a secure environment.  Secure
+ * environments may be either:
+ *
+ * 1.  Isolated execution environments, such as a separate virtual machine, hypervisor or
+ *      purpose-built trusted execution environment like ARM TrustZone.  The isolated environment
+ *      must provide complete separation from the Android kernel and user space (collectively called
+ *      the "non-secure world", or NSW) so that nothing running in the NSW can observe or manipulate
+ *      the results of any computation in the isolated environment.  Isolated execution environments
+ *      are identified by the SecurityLevel TRUSTED_ENVIRONMENT.
+ *
+ * 2.  Completely separate, purpose-built and certified secure CPUs, called "StrongBox" devices.
+ *      Examples of StrongBox devices are embedded Secure Elements (eSE) or on-SoC secure processing
+ *      units (iSE).  StrongBox environments are identified by the SecurityLevel STRONGBOX.  To
+ *      qualify as a StrongBox, a device must meet the requirements specified in CDD 9.11.2.
+ *
+ * == Necessary Primitives ==
+ *
+ * All IKeyMintDevice implementations must provide support for the following:
+ *
+ * o   RSA
+ *
+ *      - TRUSTED_ENVIRONMENT IKeyMintDevices must support 2048, 3072 and 4096-bit keys.
+ *        STRONGBOX IKeyMintDevices must support 2048-bit keys.
+ *      - Public exponent F4 (2^16+1)
+ *      - Unpadded, RSASSA-PSS and RSASSA-PKCS1-v1_5 padding modes for RSA signing
+ *      - TRUSTED_ENVIRONMENT IKeyMintDevices must support MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2
+ *        384 and SHA-2 512 digest modes for RSA signing.  STRONGBOX IKeyMintDevices must support
+ *        SHA-2 256.
+ *      - Unpadded, RSAES-OAEP and RSAES-PKCS1-v1_5 padding modes for RSA encryption.
+ *
+ * o   ECDSA
+ *
+ *      - TRUSTED_ENVIRONMENT IKeyMintDevices must support NIST curves P-224, P-256, P-384 and
+ *        P-521.  STRONGBOX IKeyMintDevices must support NIST curve P-256.
+ *      - TRUSTED_ENVIRONMENT IKeyMintDevices must support SHA1, SHA-2 224, SHA-2 256, SHA-2
+ *        384 and SHA-2 512 digest modes.  STRONGBOX IKeyMintDevices must support SHA-2 256.
+ *
+ * o   AES
+ *
+ *      - 128 and 256-bit keys
+ *      - CBC, CTR, ECB and GCM modes.  The GCM mode must not allow the use of tags smaller than 96
+ *        bits or nonce lengths other than 96 bits.
+ *      - CBC and ECB modes must support unpadded and PKCS7 padding modes.  With no padding CBC and
+ *        ECB-mode operations must fail with ErrorCode::INVALID_INPUT_LENGTH if the input isn't a
+ *        multiple of the AES block size.  With PKCS7 padding, GCM and CTR operations must fail with
+ *        ErrorCode::INCOMPATIBLE_PADDING_MODE.
+ *
+ * o   3DES
+ *
+ *      - 168-bit keys.
+ *      - CBC and ECB mode.
+
+ *      - CBC and ECB modes must support unpadded and PKCS7 padding modes.  With no padding CBC and
+ *        ECB-mode operations must fail with ErrorCode::INVALID_INPUT_LENGTH if the input isn't a
+ *        multiple of the DES block size.
+ *
+ * o   HMAC
+ *
+ *      - Any key size that is between 64 and 512 bits (inclusive) and a multiple of 8 must be
+ *        supported.  STRONGBOX IKeyMintDevices must not support keys larger than 512 bits.
+ *      - TRUSTED_ENVIRONMENT IKeyMintDevices must support MD-5, SHA1, SHA-2-224, SHA-2-256,
+ *        SHA-2-384 and SHA-2-512.  STRONGBOX IKeyMintDevices must support SHA-2-256.
+ *
+ * == Key Access Control ==
+ *
+ * Hardware-based keys that can never be extracted from the device don't provide much security if an
+ * attacker can use them at will (though they're more secure than keys which can be
+ * exfiltrated).  Therefore, IKeyMintDevice must enforce access controls.
+ *
+ * Access controls are defined as an "authorization list" of tag/value pairs.  Authorization tags
+ * are 32-bit integers from the Tag enum, and the values are a variety of types, defined in the
+ * TagType enum.  Some tags may be repeated to specify multiple values.  Whether a tag may be
+ * repeated is specified in the documentation for the tag and in the TagType.  When a key is
+ * created or imported, the caller specifies an authorization list.  The IKeyMintDevice must divide
+ * the caller-provided authorizations into two lists, those it enforces in tee secure zone and
+ * those enforced in the strongBox hardware.  These two lists are returned as the "teeEnforced"
+ * and "strongboxEnforced" elements of the KeyCharacteristics struct. Note that software enforced
+ * authorization list entries are not returned because they are not enforced by keymint.  The
+ * IKeyMintDevice must also add the following authorizations to the appropriate list:
+ *
+ * o    Tag::OS_VERSION
+ * o    Tag::OS_PATCHLEVEL
+ * o    Tag::VENDOR_PATCHLEVEL
+ * o    Tag::BOOT_PATCHLEVEL
+ * o    Tag::ORIGIN
+ *
+ * The IKeyMintDevice must ignore unknown tags.
+ *
+ * The caller must always provide the current date time in the keyParameter CREATION_DATETIME
+ * tags.
+ *
+ * All authorization tags and their values, both teeEnforced and strongboxEnforced, including
+ * unknown tags, must be cryptographically bound to the private/secret key material such that any
+ * modification of the portion of the key blob that contains the authorization list makes it
+ * impossible for the secure environment to obtain the private/secret key material.  The
+ * recommended approach to meet this requirement is to use the full set of authorization tags
+ * associated with a key as input to a secure key derivation function used to derive a key that
+ * is used to encrypt the private/secret key material.
+ *
+ * IKeyMintDevice implementations ignore any tags they cannot enforce and do not return them
+ * in KeyCharacteristics.  For example, Tag::ORIGINATION_EXPIRE_DATETIME provides the date and
+ * time after which a key may not be used to encrypt or sign new messages.  Unless the
+ * IKeyMintDevice has access to a secure source of current date/time information, it is not
+ * possible for the IKeyMintDevice to enforce this tag.  An IKeyMintDevice implementation will
+ * not rely on the non-secure world's notion of time, because it could be controlled by an
+ * attacker. Similarly, it cannot rely on GPSr time, even if it has exclusive control of the
+ * GPSr, because that might be spoofed by attacker RF signals.
+ *
+ * IKeyMintDevices do not use or enforce any tags they place in the softwareEnforced
+ * list.  The IKeyMintDevice caller must enforce them, and it is unnecessary to enforce them
+ * twice.
+ *
+ * Some tags must be enforced by the IKeyMintDevice.  See the detailed documentation on each Tag
+ * in Tag.aidl.
+ *
+ * == Root of Trust Binding ==
+ *
+ * IKeyMintDevice keys must be bound to a root of trust, which is a bitstring that must be
+ * provided to the secure environment (by an unspecified, implementation-defined mechanism) during
+ * startup, preferably by the bootloader.  This bitstring must be cryptographically bound to every
+ * key managed by the IKeyMintDevice.  As above, the recommended mechanism for this cryptographic
+ * binding is to include the Root of Trust data in the input to the key derivation function used to
+ * derive a key that is used to encryp the private/secret key material.
+ *
+ * The root of trust consists of a bitstring that must be derived from the public key used by
+ * Verified Boot to verify the signature on the boot image and from the lock state of the
+ * device.  If the public key is changed to allow a different system image to be used or if the
+ * lock state is changed, then all of the IKeyMintDevice-protected keys created by the previous
+ * system state must be unusable, unless the previous state is restored.  The goal is to increase
+ * the value of the software-enforced key access controls by making it impossible for an attacker-
+ * installed operating system to use IKeyMintDevice keys.
+ *
+ * == Version Binding ==
+ *
+ * All keys must also be bound to the operating system and patch level of the system image and the
+ * patch levels of the vendor image and boot image.  This ensures that an attacker who discovers a
+ * weakness in an old version of the software cannot roll a device back to the vulnerable version
+ * and use keys created with the newer version.  In addition, when a key with a given version and
+ * patch level is used on a device that has been upgraded to a newer version or patch level, the
+ * key must be upgraded (See IKeyMintDevice::upgradeKey()) before it can be used, and the previous
+ * version of the key must be invalidated.  In this way, as the device is upgraded, the keys will
+ * "ratchet" forward along with the device, but any reversion of the device to a previous release
+ * will cause the keys to be unusable.
+ *
+ * This version information must be associated with every key as a set of tag/value pairs in the
+ * hardwareEnforced authorization list.  Tag::OS_VERSION, Tag::OS_PATCHLEVEL,
+ * Tag::VENDOR_PATCHLEVEL, and Tag::BOOT_PATCHLEVEL must be cryptographically bound to every
+ * IKeyMintDevice key, as described in the Key Access Control section above.
+ */
+@VintfStability
+interface IKeyMintDevice {
+    const int AUTH_TOKEN_MAC_LENGTH = 32;
+
+    /**
+     * @return info which contains information about the underlying IKeyMintDevice hardware, such
+     *         as version number, security level, keyMint name and author name.
+     */
+    KeyMintHardwareInfo getHardwareInfo();
+
+    /**
+     * Verify authorizations for another IKeyMintDevice instance.
+     *
+     * On systems with both a StrongBox and a TEE IKeyMintDevice instance it is sometimes useful
+     * to ask the TEE KeyMintDevice to verify authorizations for a key hosted in StrongBox.
+     *
+     * For every StrongBox operation, Keystore is required to call this method on the TEE KeyMint,
+     * passing in the StrongBox key's hardwareEnforced authorization list and the challenge
+     * returned by StrongBox begin().  Keystore must then pass the VerificationToken to the
+     * subsequent invocations of StrongBox update() and finish().
+     *
+     * StrongBox implementations must return ErrorCode::UNIMPLEMENTED.
+     *
+     * @param the challenge returned by StrongBox's keyMint's begin().
+     *
+     * @param authToken A HardwareAuthToken if needed to authorize key usage.
+     *
+     * @return error ErrorCode::OK on success or ErrorCode::UNIMPLEMENTED if the KeyMintDevice is
+     *         a StrongBox.  If the IKeyMintDevice cannot verify one or more elements of
+     *         parametersToVerify it must not return an error code, but just omit the unverified
+     *         parameter from the VerificationToken.
+     *
+     * @return token the verification token.  See VerificationToken in VerificationToken.aidl for
+     *         details.
+     */
+    VerificationToken verifyAuthorization(in long challenge,
+                                          in HardwareAuthToken token);
+
+    /**
+     * Adds entropy to the RNG used by KeyMint.  Entropy added through this method must not be the
+     * only source of entropy used, and a secure mixing function must be used to mix the entropy
+     * provided by this method with internally-generated entropy.  The mixing function must be
+     * secure in the sense that if any one of the mixing function inputs is provided with any data
+     * the attacker cannot predict (or control), then the output of the seeded CRNG is
+     * indistinguishable from random.  Thus, if the entropy from any source is good, the output
+     * must be good.
+     *
+     * TODO(seleneh) specify what mixing functions and cprng we allow.
+     *
+     * @param data Bytes to be mixed into the CRNG seed.  The caller must not provide more than 2
+     *        KiB of data per invocation.
+     *
+     * @return error ErrorCode::OK on success; ErrorCode::INVALID_INPUT_LENGTH if the caller
+     *         provides more than 2 KiB of data.
+     */
+    void addRngEntropy(in byte[] data);
+
+    /**
+     * Generates a new cryptographic key, specifying associated parameters, which must be
+     * cryptographically bound to the key.  IKeyMintDevice implementations must disallow any use
+     * of a key in any way inconsistent with the authorizations specified at generation time.  With
+     * respect to parameters that the secure environment cannot enforce, the secure envionment's
+     * obligation is limited to ensuring that the unenforceable parameters associated with the key
+     * cannot be modified.  In addition, the characteristics returned by generateKey places
+     * parameters correctly in the tee-enforced and strongbox-enforced lists.
+     *
+     * In addition to the parameters provided, generateKey must add the following to the returned
+     * characteristics.
+     *
+     * o Tag::ORIGIN with the value KeyOrigin::GENERATED.
+     *
+     * o Tag::BLOB_USAGE_REQUIREMENTS with the appropriate value (see KeyBlobUsageRequirements in
+     *   Tag.aidl).
+     *
+     * o Tag::OS_VERSION, Tag::OS_PATCHLEVEL, Tag::VENDOR_PATCHLEVEL and Tag::BOOT_PATCHLEVEL with
+     *   appropriate values.
+     *
+     * The parameters provided to generateKey depend on the type of key being generated.  This
+     * section summarizes the necessary and optional tags for each type of key.  Tag::ALGORITHM is
+     * always necessary, to specify the type.
+     *
+     * == RSA Keys ==
+     *
+     * The following parameters are required to generate an RSA key:
+     *
+     * o Tag::Key_SIZE specifies the size of the public modulus, in bits.  If omitted, generateKey
+     *   must return ErrorCode::UNSUPPORTED_KEY_SIZE.  Required values for TEE IKeyMintDevice
+     *   implementations are 1024, 2048, 3072 and 4096.  StrongBox IKeyMintDevice implementations
+     *   must support 2048.
+     *
+     * o Tag::RSA_PUBLIC_EXPONENT specifies the RSA public exponent value.  If omitted, generateKey
+     *   must return ErrorCode::INVALID_ARGUMENT.  The values 3 and 65537 must be supported.  It is
+     *   recommended to support all prime values up to 2^64.  If provided with a non-prime value,
+     *   generateKey must return ErrorCode::INVALID_ARGUMENT.
+     *
+     * The following parameters are not necessary to generate a usable RSA key, but generateKey must
+     * not return an error if they are omitted:
+     *
+     * o Tag::PURPOSE specifies allowed purposes.  All KeyPurpose values (see KeyPurpose.aidl)
+     *   except AGREE_KEY must be supported for RSA keys.
+     *
+     * o Tag::DIGEST specifies digest algorithms that may be used with the new key.  TEE
+     *   IKeyMintDevice implementatiosn must support all Digest values (see digest.aidl) for RSA
+     *   keys.  StrongBox IKeyMintDevice implementations must support SHA_2_256.
+     *
+     * o Tag::PADDING specifies the padding modes that may be used with the new
+     *   key.  IKeyMintDevice implementations must support PaddingMode::NONE,
+     *   PaddingMode::RSA_OAEP, PaddingMode::RSA_PSS, PaddingMode::RSA_PKCS1_1_5_ENCRYPT and
+     *   PaddingMode::RSA_PKCS1_1_5_SIGN for RSA keys.
+     *
+     * == ECDSA Keys ==
+     *
+     * Either Tag::KEY_SIZE or Tag::EC_CURVE must be provided to generate an ECDSA key.  If neither
+     * is provided, generateKey must return ErrorCode::UNSUPPORTED_KEY_SIZE.  If Tag::KEY_SIZE is
+     * provided, the possible values are 224, 256, 384 and 521, and must be mapped to Tag::EC_CURVE
+     * values P_224, P_256, P_384 and P_521, respectively.  TEE IKeyMintDevice implementations
+     * must support all curves.  StrongBox implementations must support P_256.
+     *
+     * == AES Keys ==
+     *
+     * Only Tag::KEY_SIZE is required to generate an AES key.  If omitted, generateKey must return
+     * ErrorCode::UNSUPPORTED_KEY_SIZE.  128 and 256-bit key sizes must be supported.
+     *
+     * If Tag::BLOCK_MODE is specified with value BlockMode::GCM, then the caller must also provide
+     * Tag::MIN_MAC_LENGTH.  If omitted, generateKey must return ErrorCode::MISSING_MIN_MAC_LENGTH.
+     *
+     *
+     * @param keyParams Key generation parameters are defined as KeyMintDevice tag/value pairs,
+     *        provided in params.  See above for detailed specifications of which tags are required
+     *        for which types of keys.
+     *
+     * @return generatedKeyBlob Opaque descriptor of the generated key.  The recommended
+     *         implementation strategy is to include an encrypted copy of the key material, wrapped
+     *         in a key unavailable outside secure hardware.
+     *
+     * @return generatedKeyCharacteristics Description of the generated key, divided into two sets:
+     *         hardware-enforced and software-enforced.  The description here applies equally
+     *         to the key characteristics lists returned by generateKey, importKey and
+     *         importWrappedKey.  The characteristics returned by this parameter completely
+     *         describe the type and usage of the specified key.
+     *
+     *         The rule that IKeyMintDevice implementations must use for deciding whether a
+     *         given tag belongs in the hardware-enforced or software-enforced list is that if
+     *         the meaning of the tag is fully assured by secure hardware, it is hardware
+     *         enforced.  Otherwise, it's software enforced.
+     *
+     * @return outCertChain If the key is an asymmetric key, and proper keyparameters for
+     *         attestation (such as challenge) is provided, then this parameter will return the
+     *         attestation certificate.  If the signing of the attestation certificate is from a
+     *         factory key, additional certificates back to the root attestation certificate will
+     *         also be provided. Clients will need to check root certificate against a known-good
+     *         value. The certificates must be DER-encoded.  Caller needs to provide
+     *         CREATION_DATETIME as one of the attestation parameters, otherwise the attestation
+     *         certificate will not contain the creation datetime.  The first certificate in the
+     *         vector is the attestation for the generated key itself, the next certificate is
+     *         the key that signs the first certificate, and so forth.  The last certificate in
+     *         the chain is the root certificate.  If the key is a symmetric key, then no
+     *         certificate will be returned and this variable will return empty. TODO: change
+     *         certificate return to a single certificate and make it nullable b/163604282.
+     */
+    void generateKey(in KeyParameter[] keyParams, out ByteArray generatedKeyBlob,
+                     out KeyCharacteristics generatedKeyCharacteristics,
+                     out Certificate[] outCertChain);
+
+    /**
+     * Imports key material into an IKeyMintDevice.  Key definition parameters and return values
+     * are the same as for generateKey, with the following exceptions:
+     *
+     * o Tag::KEY_SIZE is not necessary in the input parameters.  If not provided, the
+     *   IKeyMintDevice must deduce the value from the provided key material and add the tag and
+     *   value to the key characteristics.  If Tag::KEY_SIZE is provided, the IKeyMintDevice must
+     *   validate it against the key material.  In the event of a mismatch, importKey must return
+     *   ErrorCode::IMPORT_PARAMETER_MISMATCH.
+     *
+     * o Tag::RSA_PUBLIC_EXPONENT (for RSA keys only) is not necessary in the input parameters.  If
+     *   not provided, the IKeyMintDevice must deduce the value from the provided key material and
+     *   add the tag and value to the key characteristics.  If Tag::RSA_PUBLIC_EXPONENT is provided,
+     *   the IKeyMintDevice must validate it against the key material.  In the event of a
+     *   mismatch, importKey must return ErrorCode::IMPORT_PARAMETER_MISMATCH.
+     *
+     * o Tag::ORIGIN (returned in keyCharacteristics) must have the value KeyOrigin::IMPORTED.
+     *
+     * @param inKeyParams Key generation parameters are defined as KeyMintDevice tag/value pairs,
+     *        provided in params.
+     *
+     * @param inKeyFormat The format of the key material to import.  See KeyFormat in
+     *        keyformat.aidl.
+     *
+     * @param inKeyData The key material to import, in the format specified in keyFormat.
+     *
+     * @return outImportedKeyBlob descriptor of the imported key.  The format of the keyblob will
+     *         be the google specified keyblob format.
+     *
+     * @return outImportedKeyCharacteristics Description of the generated key.  See the
+     *         keyCharacteristics description in generateKey.
+     *
+     * @return outCertChain If the key is an asymmetric key, and proper keyparameters for
+     *         attestation (such as challenge) is provided, then this parameter will return the
+     *         attestation certificate.  If the signing of the attestation certificate is from a
+     *         factory key, additional certificates back to the root attestation certificate will
+     *         also be provided. Clients will need to check root certificate against a known-good
+     *         value. The certificates must be DER-encoded.  Caller needs to provide
+     *         CREATION_DATETIME as one of the attestation parameters, otherwise the attestation
+     *         certificate will not contain the creation datetime.  The first certificate in the
+     *         vector is the attestation for the generated key itself, the next certificate is
+     *         the key that signs the first certificate, and so forth.  The last certificate in
+     *         the chain is the root certificate.  If the key is a symmetric key, then no
+     *         certificate will be returned and this variable will return empty.
+     */
+    void importKey(in KeyParameter[] inKeyParams, in KeyFormat inKeyFormat,
+                   in byte[] inKeyData, out ByteArray outImportedKeyBlob,
+                   out KeyCharacteristics outImportedKeyCharacteristics,
+                   out Certificate[] outCertChain);
+
+    /**
+     * Securely imports a key, or key pair, returning a key blob and a description of the imported
+     * key.
+     *
+     * @param inWrappedKeyData The wrapped key material to import.
+     *     TODO(seleneh) Decide if we want the wrapped key in DER-encoded ASN.1 format or CBOR
+     *     format or both.  And specify the standarized format.
+     *
+     *     KeyDescription ::= SEQUENCE(
+     *         keyFormat INTEGER,                   # Values from KeyFormat enum.
+     *         keyParams AuthorizationList,
+     *     )
+     *
+     *     SecureKeyWrapper ::= SEQUENCE(
+     *         version INTEGER,                     # Contains value 0
+     *         encryptedTransportKey OCTET_STRING,
+     *         initializationVector OCTET_STRING,
+     *         keyDescription KeyDescription,
+     *         encryptedKey OCTET_STRING,
+     *         tag OCTET_STRING
+     *     )
+     *
+     *     Where:
+     *
+     *     o keyFormat is an integer from the KeyFormat enum, defining the format of the plaintext
+     *       key material.
+     *     o keyParams is the characteristics of the key to be imported (as with generateKey or
+     *       importKey).  If the secure import is successful, these characteristics must be
+     *       associated with the key exactly as if the key material had been insecurely imported
+     *       with the IKeyMintDevice::importKey.  See attestKey() for documentation of the
+     *       AuthorizationList schema.
+     *     o encryptedTransportKey is a 256-bit AES key, XORed with a masking key and then encrypted
+     *       with the wrapping key specified by wrappingKeyBlob.
+     *     o keyDescription is a KeyDescription, above.
+     *     o encryptedKey is the key material of the key to be imported, in format keyFormat, and
+     *       encrypted with encryptedEphemeralKey in AES-GCM mode, with the DER-encoded
+     *       representation of keyDescription provided as additional authenticated data.
+     *     o tag is the tag produced by the AES-GCM encryption of encryptedKey.
+     *
+     * So, importWrappedKey does the following:
+     *
+     *     1. Get the private key material for wrappingKeyBlob, verifying that the wrapping key has
+     *        purpose KEY_WRAP, padding mode RSA_OAEP, and digest SHA_2_256, returning the
+     *        error INCOMPATIBLE_PURPOSE, INCOMPATIBLE_PADDING_MODE, or INCOMPATIBLE_DIGEST if any
+     *        of those requirements fail.
+     *     2. Extract the encryptedTransportKey field from the SecureKeyWrapper, and decrypt
+     *        it with the wrapping key.
+     *     3. XOR the result of step 2 with maskingKey.
+     *     4. Use the result of step 3 as an AES-GCM key to decrypt encryptedKey, using the encoded
+     *        value of keyDescription as the additional authenticated data.  Call the result
+     *        "keyData" for the next step.
+     *     5. Perform the equivalent of calling importKey(keyParams, keyFormat, keyData), except
+     *        that the origin tag should be set to SECURELY_IMPORTED.
+     *
+     * @param inWrappingKeyBlob The opaque key descriptor returned by generateKey() or importKey().
+     *        This key must have been created with Purpose::WRAP_KEY.
+     *
+     * @param inMaskingKey The 32-byte value XOR'd with the transport key in the SecureWrappedKey
+     *        structure.
+     *
+     * @param inUnwrappingParams must contain any parameters needed to perform the unwrapping
+     *        operation.  For example, if the wrapping key is an AES key the block and padding
+     *        modes must be specified in this argument.
+     *
+     * @param inPasswordSid specifies the password secure ID (SID) of the user that owns the key
+     *        being installed.  If the authorization list in wrappedKeyData contains a
+     *        Tag::USER_SECURE_IDwith a value that has the HardwareAuthenticatorType::PASSWORD
+     *        bit set, the constructed key must be bound to the SID value provided by this
+     *        argument.  If the wrappedKeyData does not contain such a tag and value, this argument
+     *        must be ignored.
+     *
+     * @param inBiometricSid specifies the biometric secure ID (SID) of the user that owns the key
+     *        being installed.  If the authorization list in wrappedKeyData contains a
+     *        Tag::USER_SECURE_ID with a value that has the HardwareAuthenticatorType::FINGERPRINT
+     *        bit set, the constructed key must be bound to the SID value provided by this argument.
+     *        If the wrappedKeyData does not contain such a tag and value, this argument must be
+     *        ignored.
+     *
+     * @return outImportedKeyBlob Opaque descriptor of the imported key.  It is recommended that
+     *         the keyBlob contain a copy of the key material, wrapped in a key unavailable outside
+     *         secure hardware.
+     *
+     * @return outImportedKeyCharacteristics Description of the generated key.  See the description
+     *         of keyCharacteristics parameter in generateKey.
+     */
+    void importWrappedKey(in byte[] inWrappedKeyData,
+                          in byte[] inWrappingKeyBlob,
+                          in byte[] inMaskingKey,
+                          in KeyParameter[] inUnwrappingParams,
+                          in long inPasswordSid,
+                          in long inBiometricSid,
+                          out ByteArray outImportedKeyBlob,
+                          out KeyCharacteristics outImportedKeyCharacteristics);
+
+    /**
+     * Upgrades an old key blob.  Keys can become "old" in two ways: IKeyMintDevice can be
+     * upgraded to a new version with an incompatible key blob format, or the system can be updated
+     * to invalidate the OS version (OS_VERSION tag), system patch level (OS_PATCHLEVEL tag),
+     * vendor patch level (VENDOR_PATCH_LEVEL tag), boot patch level (BOOT_PATCH_LEVEL tag) or
+     * other, implementation-defined patch level (keyMint implementers are encouraged to extend
+     * this HAL with a minor version extension to define validatable patch levels for other
+     * images; tags must be defined in the implementer's namespace, starting at 10000).  In either
+     * case, attempts to use an old key blob with begin() must result in IKeyMintDevice returning
+     * ErrorCode::KEY_REQUIRES_UPGRADE.  The caller must use this method to upgrade the key blob.
+     *
+     * The upgradeKey method must examine each version or patch level associated with the key.  If
+     * any one of them is higher than the corresponding current device value upgradeKey() must
+     * return ErrorCode::INVALID_ARGUMENT.  There is one exception: it is always permissible to
+     * "downgrade" from any OS_VERSION number to OS_VERSION 0.  For example, if the key has
+     * OS_VERSION 080001, it is permisible to upgrade the key if the current system version is
+     * 080100, because the new version is larger, or if the current system version is 0, because
+     * upgrades to 0 are always allowed.  If the system version were 080000, however, keyMint must
+     * return ErrorCode::INVALID_ARGUMENT because that value is smaller than 080001.  Values other
+     * than OS_VERSION must never be downgraded.
+     *
+     * Note that Keymaster versions 2 and 3 required that the system and boot images have the same
+     * patch level and OS version.  This requirement is relaxed for 4.0::IKeymasterDevice and
+     * IKeyMintDevice, and the OS version in the boot image footer is no longer used.
+     *
+     * @param inKeyBlobToUpgrade The opaque descriptor returned by generateKey() or importKey();
+     *
+     * @param inUpgradeParams A parameter list containing any parameters needed to complete the
+     *        upgrade, including Tag::APPLICATION_ID and Tag::APPLICATION_DATA.
+     *
+     * @return A new key blob that references the same key as keyBlobToUpgrade, but is in the new
+     *         format, or has the new version data.
+     */
+    byte[] upgradeKey(in byte[] inKeyBlobToUpgrade, in KeyParameter[] inUpgradeParams);
+
+    /**
+     * Deletes the key, or key pair, associated with the key blob.  Calling this function on
+     * a key with Tag::ROLLBACK_RESISTANCE in its hardware-enforced authorization list must
+     * render the key permanently unusable.  Keys without Tag::ROLLBACK_RESISTANCE may or
+     * may not be rendered unusable.
+     *
+     * @param inKeyBlob The opaque descriptor returned by generateKey() or importKey();
+     */
+    void deleteKey(in byte[] inKeyBlob);
+
+    /**
+     * Deletes all keys in the hardware keystore.  Used when keystore is reset completely.  After
+     * this function is called all keys with Tag::ROLLBACK_RESISTANCE in their hardware-enforced
+     * authorization lists must be rendered permanently unusable.  Keys without
+     * Tag::ROLLBACK_RESISTANCE may or may not be rendered unusable.
+     *
+     * @return error See the ErrorCode enum.
+     */
+    void deleteAllKeys();
+
+    /**
+     * Destroys knowledge of the device's ids.  This prevents all device id attestation in the
+     * future.  The destruction must be permanent so that not even a factory reset will restore the
+     * device ids.
+     *
+     * Device id attestation may be provided only if this method is fully implemented, allowing the
+     * user to permanently disable device id attestation.  If this cannot be guaranteed, the device
+     * must never attest any device ids.
+     *
+     * This is a NOP if device id attestation is not supported.
+     */
+    void destroyAttestationIds();
+
+    /**
+     * Begins a cryptographic operation using the specified key.  If all is well, begin() must
+     * return ErrorCode::OK and create an operation handle which must be passed to subsequent calls
+     * to update(), finish() or abort().
+     *
+     * It is critical that each call to begin() be paired with a subsequent call to finish() or
+     * abort(), to allow the IKeyMintDevice implementation to clean up any internal operation
+     * state.  The caller's failure to do this may leak internal state space or other internal
+     * resources and may eventually cause begin() to return ErrorCode::TOO_MANY_OPERATIONS when it
+     * runs out of space for operations.  Any result other than ErrorCode::OK from begin(), update()
+     * or finish() implicitly aborts the operation, in which case abort() need not be called (and
+     * must return ErrorCode::INVALID_OPERATION_HANDLE if called).  IKeyMintDevice implementations
+     * must support 32 concurrent operations.
+     *
+     * If Tag::APPLICATION_ID or Tag::APPLICATION_DATA were specified during key generation or
+     * import, calls to begin must include those tags with the originally-specified values in the
+     * inParams argument to this method.  If not, begin() must return ErrorCode::INVALID_KEY_BLOB.
+     *
+     * == Authorization Enforcement ==
+     *
+     * The following key authorization parameters must be enforced by the IKeyMintDevice secure
+     * environment if the tags were returned in the "hardwareEnforced" list in the
+     * KeyCharacteristics.  Public key operations, meaning KeyPurpose::ENCRYPT and
+     * KeyPurpose::VERIFY must be allowed to succeed even if authorization requirements are not met.
+     *
+     * -- All Key Types --
+     *
+     * The tags in this section apply to all key types.  See below for additional key type-specific
+     * tags.
+     *
+     * o Tag::PURPOSE: The purpose specified in the begin() call must match one of the purposes in
+     *   the key authorizations.  If the specified purpose does not match, begin() must return
+     *   ErrorCode::UNSUPPORTED_PURPOSE.
+     *
+     * o Tag::ACTIVE_DATETIME can only be enforced if a trusted UTC time source is available.  If
+     *   the current date and time is prior to the tag value, begin() must return
+     *   ErrorCode::KEY_NOT_YET_VALID.
+     *
+     * o Tag::ORIGINATION_EXPIRE_DATETIME can only be enforced if a trusted UTC time source is
+     *   available.  If the current date and time is later than the tag value and the purpose is
+     *   KeyPurpose::ENCRYPT or KeyPurpose::SIGN, begin() must return ErrorCode::KEY_EXPIRED.
+     *
+     * o Tag::USAGE_EXPIRE_DATETIME can only be enforced if a trusted UTC time source is
+     *   available.  If the current date and time is later than the tag value and the purpose is
+     *   KeyPurpose::DECRYPT or KeyPurpose::VERIFY, begin() must return ErrorCode::KEY_EXPIRED.
+     *
+     * o Tag::MAX_USES_PER_BOOT must be compared against a secure counter that tracks the uses of
+     *   the key since boot time.  If the count of previous uses exceeds the tag value, begin() must
+     *   return ErrorCode::KEY_MAX_OPS_EXCEEDED.
+     *
+     * o Tag::USER_SECURE_ID must be enforced by this method if and only if the key also has
+     *   Tag::AUTH_TIMEOUT (if it does not have Tag::AUTH_TIMEOUT, the Tag::USER_SECURE_ID
+     *   requirement must be enforced by update() and finish()).  If the key has both, then this
+     *   method must receive a non-empty HardwareAuthToken in the authToken argument.  For the auth
+     *   token to be valid, all of the following have to be true:
+     *
+     *   o The HMAC field must validate correctly.
+     *
+     *   o At least one of the Tag::USER_SECURE_ID values from the key must match at least one of
+     *     the secure ID values in the token.
+     *
+     *   o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
+     *
+     *   o The timestamp in the auth token plus the value of the Tag::AUTH_TIMEOUT must be less than
+     *     the current secure timestamp (which is a monotonic timer counting milliseconds since
+     *     boot.)
+     *
+     *   If any of these conditions are not met, begin() must return
+     *   ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * o Tag::CALLER_NONCE allows the caller to specify a nonce or initialization vector (IV).  If
+     *   the key doesn't have this tag, but the caller provided Tag::NONCE to this method,
+     *   ErrorCode::CALLER_NONCE_PROHIBITED must be returned.
+     *
+     * o Tag::BOOTLOADER_ONLY specifies that only the bootloader may use the key.  If this method is
+     *   called with a bootloader-only key after the bootloader has finished executing, it must
+     *   return ErrorCode::INVALID_KEY_BLOB.  The mechanism for notifying the IKeyMintDevice that
+     *   the bootloader has finished executing is implementation-defined.
+     *
+     * -- RSA Keys --
+     *
+     * All RSA key operations must specify exactly one padding mode in inParams.  If unspecified or
+     * specified more than once, the begin() must return ErrorCode::UNSUPPORTED_PADDING_MODE.
+     *
+     * RSA signing and verification operations need a digest, as do RSA encryption and decryption
+     * operations with OAEP padding mode.  For those cases, the caller must specify exactly one
+     * digest in inParams.  If unspecified or specified more than once, begin() must return
+     * ErrorCode::UNSUPPORTED_DIGEST.
+     *
+     * Private key operations (KeyPurpose::DECRYPT and KeyPurpose::SIGN) need authorization of
+     * digest and padding, which means that the key authorizations need to contain the specified
+     * values.  If not, begin() must return ErrorCode::INCOMPATIBLE_DIGEST or
+     * ErrorCode::INCOMPATIBLE_PADDING, as appropriate.  Public key operations (KeyPurpose::ENCRYPT
+     * and KeyPurpose::VERIFY) are permitted with unauthorized digest or padding modes.
+     *
+     * With the exception of PaddingMode::NONE, all RSA padding modes are applicable only to certain
+     * purposes.  Specifically, PaddingMode::RSA_PKCS1_1_5_SIGN and PaddingMode::RSA_PSS only
+     * support signing and verification, while PaddingMode::RSA_PKCS1_1_5_ENCRYPT and
+     * PaddingMode::RSA_OAEP only support encryption and decryption.  begin() must return
+     * ErrorCode::UNSUPPORTED_PADDING_MODE if the specified mode does not support the specified
+     * purpose.
+     *
+     * There are some important interactions between padding modes and digests:
+     *
+     * o PaddingMode::NONE indicates that a "raw" RSA operation is performed.  If signing or
+     *   verifying, Digest::NONE is specified for the digest.  No digest is necessary for unpadded
+     *   encryption or decryption.
+     *
+     * o PaddingMode::RSA_PKCS1_1_5_SIGN padding requires a digest.  The digest may be Digest::NONE,
+     *   in which case the KeyMint implementation cannot build a proper PKCS#1 v1.5 signature
+     *   structure, because it cannot add the DigestInfo structure.  Instead, the IKeyMintDevice
+     *   must construct 0x00 || 0x01 || PS || 0x00 || M, where M is the provided message and PS is a
+     *   random padding string at least eight bytes in length.  The size of the RSA key has to be at
+     *   least 11 bytes larger than the message, otherwise begin() must return
+     *   ErrorCode::INVALID_INPUT_LENGTH.
+     *
+     * o PaddingMode::RSA_PKCS1_1_1_5_ENCRYPT padding does not require a digest.
+     *
+     * o PaddingMode::RSA_PSS padding requires a digest, which may not be Digest::NONE.  If
+     *   Digest::NONE is specified, the begin() must return ErrorCode::INCOMPATIBLE_DIGEST.  In
+     *   addition, the size of the RSA key must be at least 2 + D bytes larger than the output size
+     *   of the digest, where D is the size of the digest, in bytes.  Otherwise begin() must
+     *   return ErrorCode::INCOMPATIBLE_DIGEST.  The salt size must be D.
+     *
+     * o PaddingMode::RSA_OAEP padding requires a digest, which may not be Digest::NONE.  If
+     *   Digest::NONE is specified, begin() must return ErrorCode::INCOMPATIBLE_DIGEST.  The OAEP
+     *   mask generation function must be MGF1 and the MGF1 digest must be SHA1, regardless of the
+     *   OAEP digest specified.
+     *
+     * -- EC Keys --
+     *
+     * EC key operations must specify exactly one padding mode in inParams.  If unspecified or
+     * specified more than once, begin() must return ErrorCode::UNSUPPORTED_PADDING_MODE.
+     *
+     * Private key operations (KeyPurpose::SIGN) need authorization of digest and padding, which
+     * means that the key authorizations must contain the specified values.  If not, begin() must
+     * return ErrorCode::INCOMPATIBLE_DIGEST.  Public key operations (KeyPurpose::VERIFY) are
+     * permitted with unauthorized digest or padding.
+     *
+     * -- AES Keys --
+     *
+     * AES key operations must specify exactly one block mode (Tag::BLOCK_MODE) and one padding mode
+     * (Tag::PADDING) in inParams.  If either value is unspecified or specified more than once,
+     * begin() must return ErrorCode::UNSUPPORTED_BLOCK_MODE or
+     * ErrorCode::UNSUPPORTED_PADDING_MODE.  The specified modes must be authorized by the key,
+     * otherwise begin() must return ErrorCode::INCOMPATIBLE_BLOCK_MODE or
+     * ErrorCode::INCOMPATIBLE_PADDING_MODE.
+     *
+     * If the block mode is BlockMode::GCM, inParams must specify Tag::MAC_LENGTH, and the specified
+     * value must be a multiple of 8 that is not greater than 128 or less than the value of
+     * Tag::MIN_MAC_LENGTH in the key authorizations.  For MAC lengths greater than 128 or
+     * non-multiples of 8, begin() must return ErrorCode::UNSUPPORTED_MAC_LENGTH.  For values less
+     * than the key's minimum length, begin() must return ErrorCode::INVALID_MAC_LENGTH.
+     *
+     * If the block mode is BlockMode::GCM or BlockMode::CTR, the specified padding mode must be
+     * PaddingMode::NONE.  For BlockMode::ECB or BlockMode::CBC, the mode may be PaddingMode::NONE
+     * or PaddingMode::PKCS7.  If the padding mode doesn't meet these conditions, begin() must
+     * return ErrorCode::INCOMPATIBLE_PADDING_MODE.
+     *
+     * If the block mode is BlockMode::CBC, BlockMode::CTR, or BlockMode::GCM, an initialization
+     * vector or nonce is required.  In most cases, callers shouldn't provide an IV or nonce and the
+     * IKeyMintDevice implementation must generate a random IV or nonce and return it via
+     * Tag::NONCE in outParams.  CBC and CTR IVs are 16 bytes.  GCM nonces are 12 bytes.  If the key
+     * authorizations contain Tag::CALLER_NONCE, then the caller may provide an IV/nonce with
+     * Tag::NONCE in inParams.  If a nonce is provided when Tag::CALLER_NONCE is not authorized,
+     * begin() must return ErrorCode::CALLER_NONCE_PROHIBITED.  If a nonce is not provided when
+     * Tag::CALLER_NONCE is authorized, IKeyMintDevice msut generate a random IV/nonce.
+     *
+     * -- HMAC keys --
+     *
+     * HMAC key operations must specify Tag::MAC_LENGTH in inParams.  The specified value must be a
+     * multiple of 8 that is not greater than the digest length or less than the value of
+     * Tag::MIN_MAC_LENGTH in the key authorizations.  For MAC lengths greater than the digest
+     * length or non-multiples of 8, begin() must return ErrorCode::UNSUPPORTED_MAC_LENGTH.  For
+     * values less than the key's minimum length, begin() must return ErrorCode::INVALID_MAC_LENGTH.
+     *
+     * @param inPurpose The purpose of the operation, one of KeyPurpose::ENCRYPT,
+     *        KeyPurpose::DECRYPT, KeyPurpose::SIGN or KeyPurpose::VERIFY.  Note that for AEAD
+     *        modes, encryption and decryption imply signing and verification, respectively, but
+     *        must be specified as KeyPurpose::ENCRYPT and KeyPurpose::DECRYPT.
+     *
+     * @param inKeyBlob The opaque key descriptor returned by generateKey() or importKey().  The key
+     *        must have a purpose compatible with purpose and all of its usage requirements must be
+     *        satisfied, or begin() must return an appropriate error code (see above).
+     *
+     * @param inParams Additional parameters for the operation.  If Tag::APPLICATION_ID or
+     *        Tag::APPLICATION_DATA were provided during generation, they must be provided here, or
+     *        the operation must fail with ErrorCode::INVALID_KEY_BLOB.  For operations that require
+     *        a nonce or IV, on keys that were generated with Tag::CALLER_NONCE, inParams may
+     *        contain a tag Tag::NONCE.  If Tag::NONCE is provided for a key without
+     *        Tag:CALLER_NONCE, ErrorCode::CALLER_NONCE_PROHIBITED must be returned.
+     *
+     * @param inAuthToken Authentication token.  Callers that provide no token must set all numeric
+     *        fields to zero and the MAC must be an empty vector.  TODO: make this field nullable.
+     *        b/173483024.
+     *
+     * @return BeginResult as output, which contains the challenge, KeyParameters which haves
+     *         additional data from the operation initialization, notably to return the IV or nonce
+     *         from operations that generate an IV or nonce, and IKeyMintOperation object pointer
+     *         which is used to perform update(), finish() or abort() operations.
+     */
+    BeginResult begin(in KeyPurpose inPurpose,
+               in byte[] inKeyBlob,
+               in KeyParameter[] inParams,
+               in HardwareAuthToken inAuthToken);
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
new file mode 100644
index 0000000..24960cc
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.ByteArray;
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameterArray;
+import android.hardware.security.keymint.VerificationToken;
+
+@VintfStability
+interface IKeyMintOperation {
+    /**
+     * Provides data to, and possibly receives output from, an ongoing cryptographic operation begun
+     * with begin().
+     *
+     * If operation is in an invalid state (was aborted or had an error) update() must return
+     * ErrorCode::INVALID_OPERATION_HANDLE.
+     *
+     * To provide more flexibility for buffer handling, implementations of this method have the
+     * option of consuming less data than was provided.  The caller is responsible for looping to
+     * feed the rest of the data in subsequent calls.  The amount of input consumed must be returned
+     * in the inputConsumed parameter.  Implementations must always consume at least one byte,
+     * unless the operation cannot accept any more; if more than zero bytes are provided and zero
+     * bytes are consumed, callers must consider this an error and abort the operation.
+     * TODO(seleneh) update the code to always consume alll the input data. b/168665179.
+     *
+     * Implementations may also choose how much data to return, as a result of the update.  This is
+     * only relevant for encryption and decryption operations, because signing and verification
+     * return no data until finish.  It is recommended to return data as early as possible, rather
+     * than buffer it.
+     *
+     * If this method returns an error code other than ErrorCode::OK, the operation is aborted and
+     * the operation handle must be invalidated.  Any future use of the handle, with this method,
+     * finish, or abort, must return ErrorCode::INVALID_OPERATION_HANDLE.
+     *
+     * == Authorization Enforcement ==
+     *
+     * Key authorization enforcement is performed primarily in begin().  The one exception is the
+     * case where the key has:
+     *
+     * o One or more Tag::USER_SECURE_IDs, and
+     *
+     * o Does not have a Tag::AUTH_TIMEOUT
+     *
+     * In this case, the key requires an authorization per operation, and the update method must
+     * receive a non-empty and valid HardwareAuthToken.  For the auth token to be valid, all of the
+     * following has to be true:
+     *
+     *   o The HMAC field must validate correctly.
+     *
+     *   o At least one of the Tag::USER_SECURE_ID values from the key must match at least one of
+     *     the secure ID values in the token.
+     *
+     *   o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
+     *
+     *   o The challenge field in the auth token must contain the operationHandle
+     *
+     *   If any of these conditions are not met, update() must return
+     *   ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * The caller must provide the auth token on every call to update() and finish().
+     *
+     * -- RSA keys --
+     *
+     * For signing and verification operations with Digest::NONE, this method must accept the entire
+     * block to be signed or verified in a single update.  It may not consume only a portion of the
+     * block in these cases.  However, the caller may choose to provide the data in multiple
+     * updates, and update() must accept the data this way as well.  If the caller provides more
+     * data to sign than can be used (length of data exceeds RSA key size), update() must return
+     * ErrorCode::INVALID_INPUT_LENGTH.
+     *
+     * -- ECDSA keys --
+     *
+     * For signing and verification operations with Digest::NONE, this method must accept the entire
+     * block to be signed or verified in a single update.  This method may not consume only a
+     * portion of the block.  However, the caller may choose to provide the data in multiple updates
+     * and update() must accept the data this way as well.  If the caller provides more data to sign
+     * than can be used, the data is silently truncated.  (This differs from the handling of excess
+     * data provided in similar RSA operations.  The reason for this is compatibility with legacy
+     * clients.)
+     *
+     * -- AES keys --
+     *
+     * AES GCM mode supports "associated authentication data," provided via the Tag::ASSOCIATED_DATA
+     * tag in the inParams argument.  The associated data may be provided in repeated calls
+     * (important if the data is too large to send in a single block) but must always precede data
+     * to be encrypted or decrypted.  An update call may receive both associated data and data to
+     * encrypt/decrypt, but subsequent updates must not include associated data.  If the caller
+     * provides associated data to an update call after a call that includes data to
+     * encrypt/decrypt, update() must return ErrorCode::INVALID_TAG.
+     *
+     * For GCM encryption, the AEAD tag must be appended to the ciphertext by finish().  During
+     * decryption, the last Tag::MAC_LENGTH bytes of the data provided to the last update call must
+     * be the AEAD tag.  Since a given invocation of update cannot know if it's the last invocation,
+     * it must process all but the tag length and buffer the possible tag data for processing during
+     * finish().
+     *
+     * TODO: update() needs to be refactored b/168665179.
+     *
+     * @param inParams Additional parameters for the operation.  For AEAD modes, this is used to
+     *        specify Tag::ADDITIONAL_DATA.  Note that additional data may be provided in multiple
+     *        calls to update(), but only until input data has been provided.
+     *
+     * @param input Data to be processed.  Note that update() may or may not consume all of the data
+     *        provided.  See return value.
+     *
+     * @param verificationToken Verification token, used to prove that another IKeymasterDevice HAL
+     *        has verified some parameters, and to deliver the other HAL's current timestamp, if
+     *        needed.  If not provided, all fields must be initialized to zero and vectors must be
+     *        empty.
+     *
+     * @return error Returns ErrorCode encountered in keymint as service specific errors. See the
+     *         ErrorCode enum in ErrorCode.aidl.
+     *
+     * @return int Amount of data that was consumed by update().  If this is less than the
+     *         amount provided, the caller may provide the remainder in a subsequent call to
+     *         update() or finish().  Every call to update must consume at least one byte, unless
+     *         the input is empty, and implementations should consume as much data as reasonably
+     *         possible for each call.
+     *
+     * @return outParams returns the updated key parameters from the blob, if needed.
+     * operation.
+     *
+     * @return out variable output The output data, if any.
+     */
+    int update(in @nullable KeyParameterArray inParams,
+               in @nullable byte[] input,
+               in @nullable HardwareAuthToken inAuthToken,
+               in @nullable VerificationToken inVerificationToken,
+               out @nullable KeyParameterArray outParams,
+               out @nullable ByteArray output);
+
+    /**
+     * Finalizes a cryptographic operation begun with begin() and invalidates operation.
+     *
+     * This method is the last one called in an operation, so all processed data must be returned.
+     *
+     * Whether it completes successfully or returns an error, this method finalizes the operation.
+     * Any future use of the operation, with finish(), update(), or abort(), must return
+     * ErrorCode::INVALID_OPERATION_HANDLE.
+     *
+     * Signing operations return the signature as the output.  Verification operations accept the
+     * signature in the signature parameter, and return no output.
+     *
+     * == Authorization enforcement ==
+     *
+     * Key authorization enforcement is performed primarily in begin().  The exceptions are
+     * authorization per operation keys and confirmation-required keys.
+     *
+     * Authorization per operation keys are the case where the key has one or more
+     * Tag::USER_SECURE_IDs, and does not have a Tag::AUTH_TIMEOUT.  In this case, the key requires
+     * an authorization per operation, and the finish method must receive a non-empty and valid
+     * authToken.  For the auth token to be valid, all of the following has to be true:
+     *
+     *   o The HMAC field must validate correctly.
+     *
+     *   o At least one of the Tag::USER_SECURE_ID values from the key must match at least one of
+     *     the secure ID values in the token.
+     *
+     *   o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
+     *
+     *   o The challenge field in the auth token must contain the operation challenge.
+     *
+     *   If any of these conditions are not met, update() must return
+     *   ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * The caller must provide the auth token on every call to update() and finish().
+     *
+     * Confirmation-required keys are keys that were generated with
+     * Tag::TRUSTED_CONFIRMATION_REQUIRED.  For these keys, when doing a signing operation the
+     * caller must pass a KeyParameter Tag::CONFIRMATION_TOKEN to finish().  Implementations must
+     * check the confirmation token by computing the 32-byte HMAC-SHA256 over all of the
+     * to-be-signed data, prefixed with the 18-byte UTF-8 encoded string "confirmation token". If
+     * the computed value does not match the Tag::CONFIRMATION_TOKEN parameter, finish() must not
+     * produce a signature and must return ErrorCode::NO_USER_CONFIRMATION.
+     *
+     * -- RSA keys --
+     *
+     * Some additional requirements, depending on the padding mode:
+     *
+     * o PaddingMode::NONE.  For unpadded signing and encryption operations, if the provided data is
+     *   shorter than the key, the data must be zero-padded on the left before
+     *   signing/encryption.  If the data is the same length as the key, but numerically larger,
+     *   finish() must return ErrorCode::INVALID_ARGUMENT.  For verification and decryption
+     *   operations, the data must be exactly as long as the key.  Otherwise, return
+     *   ErrorCode::INVALID_INPUT_LENGTH.
+     *
+     * o PaddingMode::RSA_PSS.  For PSS-padded signature operations, the PSS salt length must match
+     *   the size of the PSS digest selected.  The digest specified with Tag::DIGEST in inputParams
+     *   on begin() must be used as the PSS digest algorithm, MGF1 must be used as the mask
+     *   generation function and SHA1 must be used as the MGF1 digest algorithm.
+     *
+     * o PaddingMode::RSA_OAEP.  The digest specified with Tag::DIGEST in inputParams on begin is
+     *   used as the OAEP digest algorithm, MGF1 must be used as the mask generation function and
+     *   and SHA1 must be used as the MGF1 digest algorithm.
+     *
+     * -- ECDSA keys --
+     *
+     * If the data provided for unpadded signing or verification is too long, truncate it.
+     *
+     * -- AES keys --
+     *
+     * Some additional conditions, depending on block mode:
+     *
+     * o BlockMode::ECB or BlockMode::CBC.  If padding is PaddingMode::NONE and the data length is
+     *  not a multiple of the AES block size, finish() must return
+     *  ErrorCode::INVALID_INPUT_LENGTH.  If padding is PaddingMode::PKCS7, pad the data per the
+     *  PKCS#7 specification, including adding an additional padding block if the data is a multiple
+     *  of the block length.
+     *
+     * o BlockMode::GCM.  During encryption, after processing all plaintext, compute the tag
+     *   (Tag::MAC_LENGTH bytes) and append it to the returned ciphertext.  During decryption,
+     *   process the last Tag::MAC_LENGTH bytes as the tag.  If tag verification fails, finish()
+     *   must return ErrorCode::VERIFICATION_FAILED.
+     *
+     * TODO: update() will need to be refactored into 2 function. b/168665179.
+     *
+     * @param inParams Additional parameters for the operation.  For AEAD modes, this is used to
+     *        specify Tag::ADDITIONAL_DATA, but only if no input data was provided to update().
+     *
+     * @param input Data to be processed, per the parameters established in the call to begin().
+     *        finish() must consume all provided data or return ErrorCode::INVALID_INPUT_LENGTH.
+     *
+     * @param signature The signature to be verified if the purpose specified in the begin() call
+     *        was KeyPurpose::VERIFY.
+     *
+     * @param authToken Authentication token. Can be nullable if not provided.
+     *
+     * @param verificationToken Verification token, used to prove that another IKeyMintDevice HAL
+     *        has verified some parameters, and to deliver the other HAL's current timestamp, if
+     *        needed. Can be nullable if not needed.
+     *
+     * @return outParams Any output parameters generated by finish().
+     *
+     * @return The output data, if any.
+     */
+    byte[] finish(in @nullable KeyParameterArray inParams, in @nullable byte[] input,
+                in @nullable byte[] inSignature,
+                in @nullable HardwareAuthToken authToken,
+                in @nullable VerificationToken inVerificationToken,
+                out @nullable KeyParameterArray outParams);
+
+    /**
+     * Aborts a cryptographic operation begun with begin(), freeing all internal resources. If an
+     * operation was finalized, calling update, finish, or abort yields
+     * ErrorCode::INVALID_OPERATION_HANDLE. An operation is finalized if finish or abort was
+     * called on it, or if update returned an ErrorCode.
+     *
+     * @param operationHandle The operation handle returned by begin().  This handle must be
+     *        invalid when abort() returns.
+     *
+     * @return error See the ErrorCode enum in ErrorCode.aidl.
+     */
+    void abort();
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl
new file mode 100644
index 0000000..0801868
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.KeyParameter;
+
+/**
+ * KeyCharacteristics defines the attributes of a key, including cryptographic parameters, and usage
+ * restrictions.  It consits of two vectors of KeyParameters, one for "softwareEnforced" attributes
+ * and one for "hardwareEnforced" attributes.
+ *
+ * KeyCharacteristics objects are returned by generateKey, importKey, importWrappedKey and
+ * getKeyCharacteristics.  The IKeyMintDevice secure environment is responsible for allocating the
+ * parameters, all of which are Tags with associated values, to the correct vector.  The
+ * hardwareEnforced vector must contain only those attributes which are enforced by secure hardware.
+ * All others should be in the softwareEnforced vector.  See the definitions of individual Tag enums
+ * for specification of which must be hardware-enforced, which may be software-enforced and which
+ * must never appear in KeyCharacteristics.
+ */
+@VintfStability
+parcelable KeyCharacteristics {
+    /* TODO(seleneh) get rid of the software enforced in keymint.  replace hardware enforced with
+     * tee enforced and strongbox enforced.
+     */
+    KeyParameter[] softwareEnforced;
+    KeyParameter[] hardwareEnforced;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyDerivationFunction.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyDerivationFunction.aidl
new file mode 100644
index 0000000..e166ab6
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyDerivationFunction.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Key derivation functions, mostly used in ECIES.
+ */
+@VintfStability
+@Backing(type="int")
+enum KeyDerivationFunction {
+    /** Do not apply a key derivation function; use the raw agreed key */
+    NONE = 0,
+    /** HKDF defined in RFC 5869 with SHA256 */
+    RFC5869_SHA256 = 1,
+    /** KDF1 defined in ISO 18033-2 with SHA1 */
+    ISO18033_2_KDF1_SHA1 = 2,
+    /** KDF1 defined in ISO 18033-2 with SHA256 */
+    ISO18033_2_KDF1_SHA256 = 3,
+    /** KDF2 defined in ISO 18033-2 with SHA1 */
+    ISO18033_2_KDF2_SHA1 = 4,
+    /** KDF2 defined in ISO 18033-2 with SHA256 */
+    ISO18033_2_KDF2_SHA256 = 5,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl
new file mode 100644
index 0000000..6ad8e3d
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Formats for key import and export.
+ */
+@VintfStability
+@Backing(type="int")
+enum KeyFormat {
+    /** X.509 certificate format, for public key export. */
+    X509 = 0,
+    /** PCKS#8 format, asymmetric key pair import. */
+    PKCS8 = 1,
+    /** Raw bytes, for symmetric key import. */
+    RAW = 3,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
new file mode 100644
index 0000000..d3d7368
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.SecurityLevel;
+
+/**
+ * KeyMintHardwareInfo is the hardware information returned by calling KeyMint getHardwareInfo()
+ */
+@VintfStability
+parcelable KeyMintHardwareInfo {
+    /**
+     * Implementation version of the keymint hardware.  The version number is implementation
+     * defined, and not necessarily globally meaningful.  The version is used to distinguish
+     * between different versions of a given implementation.
+     * TODO(seleneh) add the version related info to the code.
+     */
+    int versionNumber;
+
+    /* securityLevel is the security level of the IKeyMintDevice implementation accessed
+     * through this aidl package.  */
+    SecurityLevel securityLevel;
+
+    /* keyMintName is the name of the IKeyMintDevice implementation.  */
+    @utf8InCpp String keyMintName;
+
+    /* keyMintAuthorName is the name of the author of the IKeyMintDevice implementation
+     *         (organization name, not individual). This name is implementation defined,
+     *         so it can be used to distinguish between different implementations from the
+     *         same author.
+     */
+    @utf8InCpp String keyMintAuthorName;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
new file mode 100644
index 0000000..0cd53c2
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * The origin of a key (or pair), i.e. where it was generated.  Note that ORIGIN can be found in
+ * either the hardware-enforced or software-enforced list for a key, indicating whether the key is
+ * hardware or software-based.  Specifically, a key with GENERATED in the hardware-enforced list
+ * must be guaranteed never to have existed outide the secure hardware.
+ */
+@VintfStability
+@Backing(type="int")
+enum KeyOrigin {
+    /** Generated in keyMint.  Should not exist outside the TEE. */
+    GENERATED = 0,
+
+    /** Derived inside keyMint.  Likely exists off-device. */
+    DERIVED = 1,
+
+    /** Imported into keyMint.  Existed as cleartext in Android. */
+    IMPORTED = 2,
+
+    /** Previously used for another purpose that is now obsolete. */
+    RESERVED = 3,
+
+    /**
+     * Securely imported into KeyMint.  Was created elsewhere, and passed securely through Android
+     * to secure hardware.
+     */
+    SECURELY_IMPORTED = 4,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl
new file mode 100644
index 0000000..938064c
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.Algorithm;
+import android.hardware.security.keymint.BlockMode;
+import android.hardware.security.keymint.Digest;
+import android.hardware.security.keymint.EcCurve;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
+import android.hardware.security.keymint.KeyDerivationFunction;
+import android.hardware.security.keymint.KeyOrigin;
+import android.hardware.security.keymint.KeyPurpose;
+import android.hardware.security.keymint.PaddingMode;
+import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.keymint.Tag;
+
+
+/**
+ * Identifies the key authorization parameters to be used with keyMint.  This is usually
+ * provided as an array of KeyParameters to IKeyMintDevice or Operation.
+ *
+ * TODO(seleneh): Union was not supported in aidl when this cl is first drafted.  So we just had
+ * the Tags, and bool, int, long, int[], and we will cast to the appropate types base on the
+ * Tag value.  We need to update this defination to distingish Algorithm, BlockMode,
+ * PaddingMode, KeyOrigin...etc later, as union support is recently added to aidl.
+ * b/173253030
+ */
+@VintfStability
+parcelable KeyParameter {
+    /**
+     * Identify what type of key parameter this parcelable actually holds, and based on the type
+     * of tag is int, long, bool, or byte[], one of the fields below will be referenced.
+     */
+    Tag tag;
+
+    boolean boolValue;
+    int integer;
+    long longInteger;
+    // TODO: change this to nullable.
+    byte[] blob;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyParameterArray.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyParameterArray.aidl
new file mode 100644
index 0000000..acab435
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyParameterArray.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.KeyParameter;
+
+/**
+ * Identifies the key authorization parameters to be used with keyMint.  This is usually
+ * provided as an array of KeyParameters to IKeyMintDevice or Operation.
+ */
+@VintfStability
+parcelable KeyParameterArray {
+    /**
+     * Identify list of key parameters corresponding to a particular key blob.
+     */
+    KeyParameter[] params;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
new file mode 100644
index 0000000..cb4682e
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+
+/**
+ * Possible purposes of a key (or pair).
+ */
+@VintfStability
+@Backing(type = "int")
+enum KeyPurpose {
+    /* Usable with RSA, EC and AES keys. */
+    ENCRYPT = 0,
+
+    /* Usable with RSA, EC and AES keys. */
+    DECRYPT = 1,
+
+    /* Usable with RSA, EC and HMAC keys. */
+    SIGN = 2,
+
+    /* Usable with RSA, EC and HMAC keys. */
+    VERIFY = 3,
+
+    /* 4 is reserved */
+    /* Usable with wrapping keys. */
+    WRAP_KEY = 5,
+
+    /* TODO(seleneh) add AGREE_KEY and ATTEST_KEY and their corresponding codes and tests later*/
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl b/security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl
new file mode 100644
index 0000000..80b73bd
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * TODO(seleneh) update the description.
+ *
+ * Padding modes that may be applied to plaintext for encryption operations.  This list includes
+ * padding modes for both symmetric and asymmetric algorithms.  Note that implementations should not
+ * provide all possible combinations of algorithm and padding, only the
+ * cryptographically-appropriate pairs.
+ */
+@VintfStability
+@Backing(type="int")
+enum PaddingMode {
+    NONE = 1, /* deprecated */
+    RSA_OAEP = 2,
+    RSA_PSS = 3,
+    RSA_PKCS1_1_5_ENCRYPT = 4,
+    RSA_PKCS1_1_5_SIGN = 5,
+    PKCS7 = 64,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl b/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl
new file mode 100644
index 0000000..10363e9
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Device security levels.
+ */
+@VintfStability
+@Backing(type="int")
+enum SecurityLevel {
+    SOFTWARE = 0,
+    TRUSTED_ENVIRONMENT = 1,
+    /**
+     * STRONGBOX specifies that the secure hardware satisfies the requirements specified in CDD
+     * 9.11.2.
+     */
+    STRONGBOX = 2,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
new file mode 100644
index 0000000..532bc5d
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -0,0 +1,892 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.TagType;
+
+// TODO(seleneh) : note aidl currently does not support double nested enum definitions such as
+// ROOT_OF_TRUST = TagType:BYTES | 704.  So we are forced to write definations as
+// ROOT_OF_TRUST = (9 << 28) for now.  Will need to flip this back later when aidl support is added.
+
+/**
+ * Tag specifies various kinds of tags that can be set in KeyParameter to identify what kind of
+ * data are stored in KeyParameter.
+ */
+@VintfStability
+@Backing(type = "int")
+enum Tag {
+    /**
+     * Tag::INVALID should never be set.  It means you hit an error.
+     */
+    INVALID = (0 << 28) | 0,
+
+    /**
+     * Tag::PURPOSE specifies the set of purposes for which the key may be used.  Possible values
+     * are defined in the KeyPurpose enumeration.
+     *
+     * This tag is repeatable; keys may be generated with multiple values, although an operation has
+     * a single purpose.  When begin() is called to start an operation, the purpose of the operation
+     * is specified.  If the purpose specified for the operation is not authorized by the key (the
+     * key didn't have a corresponding Tag::PURPOSE provided during generation/import), the
+     * operation must fail with ErrorCode::INCOMPATIBLE_PURPOSE.
+     *
+     * Must be hardware-enforced.
+     */
+    PURPOSE = (2 << 28) | 1, /* TagType:ENUM_REP */
+
+    /**
+     * Tag::ALGORITHM specifies the cryptographic algorithm with which the key is used.  This tag
+     * must be provided to generateKey and importKey, and must be specified in the wrapped key
+     * provided to importWrappedKey.
+     *
+     * Must be hardware-enforced.
+     */
+    ALGORITHM = (1 << 28) | 2, /* TagType:ENUM */
+
+    /**
+     * Tag::KEY_SIZE pecifies the size, in bits, of the key, measuring in the normal way for the
+     * key's algorithm.  For example, for RSA keys, Tag::KEY_SIZE specifies the size of the public
+     * modulus.  For AES keys it specifies the length of the secret key material.  For 3DES keys it
+     * specifies the length of the key material, not counting parity bits (though parity bits must
+     * be provided for import, etc.).  Since only three-key 3DES keys are supported, 3DES
+     * Tag::KEY_SIZE must be 168.
+     *
+     * Must be hardware-enforced.
+     */
+    KEY_SIZE = (3 << 28) | 3, /* TagType:UINT */
+
+    /**
+     * Tag::BLOCK_MODE specifies the block cipher mode(s) with which the key may be used.  This tag
+     * is only relevant to AES and 3DES keys.  Possible values are defined by the BlockMode enum.
+     *
+     * This tag is repeatable for key generation/import.  For AES and 3DES operations the caller
+     * must specify a Tag::BLOCK_MODE in the additionalParams argument of begin().  If the mode is
+     * missing or the specified mode is not in the modes specified for the key during
+     * generation/import, the operation must fail with ErrorCode::INCOMPATIBLE_BLOCK_MODE.
+     *
+     * Must be hardware-enforced.
+     */
+    BLOCK_MODE = (2 << 28) | 4,
+    /* BlockMode. */ /* TagType:ENUM_REP */
+
+    /**
+     * Tag::DIGEST specifies the digest algorithms that may be used with the key to perform signing
+     * and verification operations.  This tag is relevant to RSA, ECDSA and HMAC keys.  Possible
+     * values are defined by the Digest enum.
+     *
+     * This tag is repeatable for key generation/import.  For signing and verification operations,
+     * the caller must specify a digest in the additionalParams argument of begin().  If the digest
+     * is missing or the specified digest is not in the digests associated with the key, the
+     * operation must fail with ErrorCode::INCOMPATIBLE_DIGEST.
+     *
+     * Must be hardware-enforced.
+     */
+    DIGEST = (2 << 28) | 5, /* TagType:ENUM_REP */
+
+    /**
+     * Tag::PADDING specifies the padding modes that may be used with the key.  This tag is relevant
+     * to RSA, AES and 3DES keys.  Possible values are defined by the PaddingMode enum.
+     *
+     * PaddingMode::RSA_OAEP and PaddingMode::RSA_PKCS1_1_5_ENCRYPT are used only for RSA
+     * encryption/decryption keys and specify RSA OAEP padding and RSA PKCS#1 v1.5 randomized
+     * padding, respectively.  PaddingMode::RSA_PSS and PaddingMode::RSA_PKCS1_1_5_SIGN are used
+     * only for RSA signing/verification keys and specify RSA PSS padding and RSA PKCS#1 v1.5
+     * deterministic padding, respectively.
+     *
+     * PaddingMode::NONE may be used with either RSA, AES or 3DES keys.  For AES or 3DES keys, if
+     * PaddingMode::NONE is used with block mode ECB or CBC and the data to be encrypted or
+     * decrypted is not a multiple of the AES block size in length, the call to finish() must fail
+     * with ErrorCode::INVALID_INPUT_LENGTH.
+     *
+     * PaddingMode::PKCS7 may only be used with AES and 3DES keys, and only with ECB and CBC modes.
+     *
+     * In any case, if the caller specifies a padding mode that is not usable with the key's
+     * algorithm, the generation or import method must return ErrorCode::INCOMPATIBLE_PADDING_MODE.
+     *
+     * This tag is repeatable.  A padding mode must be specified in the call to begin().  If the
+     * specified mode is not authorized for the key, the operation must fail with
+     * ErrorCode::INCOMPATIBLE_BLOCK_MODE.
+     *
+     * Must be hardware-enforced.
+     */
+    PADDING = (2 << 28) | 6, /* TagType:ENUM_REP */
+
+    /**
+     * Tag::CALLER_NONCE specifies that the caller can provide a nonce for nonce-requiring
+     * operations.  This tag is boolean, so the possible values are true (if the tag is present) and
+     * false (if the tag is not present).
+     *
+     * This tag is used only for AES and 3DES keys, and is only relevant for CBC, CTR and GCM block
+     * modes.  If the tag is not present in a key's authorization list, implementations must reject
+     * any operation that provides Tag::NONCE to begin() with ErrorCode::CALLER_NONCE_PROHIBITED.
+     *
+     * Must be hardware-enforced.
+     */
+    CALLER_NONCE = (7 << 28) | 7, /* TagType:BOOL */
+
+    /**
+     * Tag::MIN_MAC_LENGTH specifies the minimum length of MAC that can be requested or verified
+     * with this key for HMAC keys and AES keys that support GCM mode.
+     *
+     * This value is the minimum MAC length, in bits.  It must be a multiple of 8 bits.  For HMAC
+     * keys, the value must be least 64 and no more than 512.  For GCM keys, the value must be at
+     * least 96 and no more than 128.  If the provided value violates these requirements,
+     * generateKey() or importKey() must return ErrorCode::UNSUPPORTED_KEY_SIZE.
+     *
+     * Must be hardware-enforced.
+     */
+    MIN_MAC_LENGTH = (3 << 28) | 8, /* TagType:UINT */
+
+    // Tag 9 reserved
+
+    /**
+     * Tag::EC_CURVE specifies the elliptic curve.  EC key generation requests may have
+     * Tag:EC_CURVE, Tag::KEY_SIZE, or both.  If both are provided and the size and curve do not
+     * match, IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
+     *
+     * Must be hardware-enforced.
+     */
+    EC_CURVE = (1 << 28) | 10, /* TagType:ENUM */
+
+    /**
+     * Tag::RSA_PUBLIC_EXPONENT specifies the value of the public exponent for an RSA key pair.
+     * This tag is relevant only to RSA keys, and is required for all RSA keys.
+     *
+     * The value is a 64-bit unsigned integer that satisfies the requirements of an RSA public
+     * exponent.  This value must be a prime number.  IKeyMintDevice implementations must support
+     * the value 2^16+1 and may support other reasonable values.  If no exponent is specified or if
+     * the specified exponent is not supported, key generation must fail with
+     * ErrorCode::INVALID_ARGUMENT.
+     *
+     * Must be hardware-enforced.
+     */
+    RSA_PUBLIC_EXPONENT = (5 << 28) | 200, /* TagType:ULONG */
+
+    // Tag 201 reserved
+
+    /**
+     * Tag::INCLUDE_UNIQUE_ID is specified during key generation to indicate that an attestation
+     * certificate for the generated key should contain an application-scoped and time-bounded
+     * device-unique ID.  See Tag::UNIQUE_ID.
+     *
+     * Must be hardware-enforced.
+     */
+    INCLUDE_UNIQUE_ID = (7 << 28) | 202, /* TagType:BOOL */
+
+    /**
+     * TODO(seleneh) this tag needs to be deleted from all codes.
+     *
+     * Tag::BLOB_USAGE_REQUIREMENTS specifies the necessary system environment conditions for the
+     * generated key to be used.  Possible values are defined by the KeyBlobUsageRequirements enum.
+     *
+     * This tag is specified by the caller during key generation or import to require that the key
+     * is usable in the specified condition.  If the caller specifies Tag::BLOB_USAGE_REQUIREMENTS
+     * with value KeyBlobUsageRequirements::STANDALONE the IKeyMintDevice must return a key blob
+     * that can be used without file system support.  This is critical for devices with encrypted
+     * disks, where the file system may not be available until after a KeyMint key is used to
+     * decrypt the disk.
+     *
+     * Must be hardware-enforced.
+     */
+    BLOB_USAGE_REQUIREMENTS = (1 << 28) | 301, /* TagType:ENUM */
+
+    /**
+     * Tag::BOOTLOADER_ONLY specifies only the bootloader can use the key.
+     *
+     * Any attempt to use a key with Tag::BOOTLOADER_ONLY from the Android system must fail with
+     * ErrorCode::INVALID_KEY_BLOB.
+     *
+     * Must be hardware-enforced.
+     */
+    BOOTLOADER_ONLY = (7 << 28) | 302, /* TagType:BOOL */
+
+    /**
+     * Tag::ROLLBACK_RESISTANCE specifies that the key has rollback resistance, meaning that when
+     * deleted with deleteKey() or deleteAllKeys(), the key is guaranteed to be permanently deleted
+     * and unusable.  It's possible that keys without this tag could be deleted and then restored
+     * from backup.
+     *
+     * This tag is specified by the caller during key generation or import to require.  If the
+     * IKeyMintDevice cannot guarantee rollback resistance for the specified key, it must return
+     * ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE.  IKeyMintDevice implementations are not
+     * required to support rollback resistance.
+     *
+     * Must be hardwared-enforced.
+     */
+    ROLLBACK_RESISTANCE = (7 << 28) | 303, /* TagType:BOOL */
+
+    // Reserved for future use.
+    HARDWARE_TYPE = (1 << 28) | 304, /* TagType:ENUM */
+
+    /**
+     * Keys tagged with EARLY_BOOT_ONLY may only be used, or created, during early boot, until
+     * IKeyMintDevice::earlyBootEnded() is called.
+     */
+    EARLY_BOOT_ONLY = (7 << 28) | 305, /* TagType:BOOL */
+
+    /**
+     * Tag::ACTIVE_DATETIME specifies the date and time at which the key becomes active, in
+     * milliseconds since Jan 1, 1970.  If a key with this tag is used prior to the specified date
+     * and time, IKeyMintDevice::begin() must return ErrorCode::KEY_NOT_YET_VALID;
+     *
+     * Need not be hardware-enforced.
+     */
+    ACTIVE_DATETIME = (6 << 28) | 400,
+    /* Start of validity. */ /* TagType:DATE */
+
+    /**
+     * Tag::ORIGINATION_EXPIRE_DATETIME specifies the date and time at which the key expires for
+     * signing and encryption purposes.  After this time, any attempt to use a key with
+     * KeyPurpose::SIGN or KeyPurpose::ENCRYPT provided to begin() must fail with
+     * ErrorCode::KEY_EXPIRED.
+     *
+     * The value is a 64-bit integer representing milliseconds since January 1, 1970.
+     *
+     * Need not be hardware-enforced.
+     */
+    ORIGINATION_EXPIRE_DATETIME = (6 << 28) | 401, /* TagType:DATE */
+
+    /**
+     * Tag::USAGE_EXPIRE_DATETIME specifies the date and time at which the key expires for
+     * verification and decryption purposes.  After this time, any attempt to use a key with
+     * KeyPurpose::VERIFY or KeyPurpose::DECRYPT provided to begin() must fail with
+     * ErrorCode::KEY_EXPIRED.
+     *
+     * The value is a 64-bit integer representing milliseconds since January 1, 1970.
+     *
+     * Need not be hardware-enforced.
+     */
+    USAGE_EXPIRE_DATETIME = (6 << 28) | 402, /* TagType:DATE */
+
+    /**
+     * TODO(seleneh) this tag need to be deleted.
+     *
+     * TODO(seleneh) this tag need to be deleted.
+     *
+     * Tag::MIN_SECONDS_BETWEEN_OPS specifies the minimum amount of time that elapses between
+     * allowed operations using a key.  This can be used to rate-limit uses of keys in contexts
+     * where unlimited use may enable brute force attacks.
+     *
+     * The value is a 32-bit integer representing seconds between allowed operations.
+     *
+     * When a key with this tag is used in an operation, the IKeyMintDevice must start a timer
+     * during the finish() or abort() call.  Any call to begin() that is received before the timer
+     * indicates that the interval specified by Tag::MIN_SECONDS_BETWEEN_OPS has elapsed must fail
+     * with ErrorCode::KEY_RATE_LIMIT_EXCEEDED.  This implies that the IKeyMintDevice must keep a
+     * table of use counters for keys with this tag.  Because memory is often limited, this table
+     * may have a fixed maximum size and KeyMint may fail operations that attempt to use keys with
+     * this tag when the table is full.  The table must acommodate at least 8 in-use keys and
+     * aggressively reuse table slots when key minimum-usage intervals expire.  If an operation
+     * fails because the table is full, KeyMint returns ErrorCode::TOO_MANY_OPERATIONS.
+     *
+     * Must be hardware-enforced.
+     */
+    MIN_SECONDS_BETWEEN_OPS = (3 << 28) | 403, /* TagType:UINT */
+
+    /**
+     * Tag::MAX_USES_PER_BOOT specifies the maximum number of times that a key may be used between
+     * system reboots.  This is another mechanism to rate-limit key use.
+     *
+     * The value is a 32-bit integer representing uses per boot.
+     *
+     * When a key with this tag is used in an operation, a key-associated counter must be
+     * incremented during the begin() call.  After the key counter has exceeded this value, all
+     * subsequent attempts to use the key must fail with ErrorCode::MAX_OPS_EXCEEDED, until the
+     * device is restarted.  This implies that the IKeyMintDevice must keep a table of use
+     * counters for keys with this tag.  Because KeyMint memory is often limited, this table can
+     * have a fixed maximum size and KeyMint can fail operations that attempt to use keys with
+     * this tag when the table is full.  The table needs to acommodate at least 8 keys.  If an
+     * operation fails because the table is full, IKeyMintDevice must
+     * ErrorCode::TOO_MANY_OPERATIONS.
+     *
+     * Must be hardware-enforced.
+     */
+    MAX_USES_PER_BOOT = (3 << 28) | 404, /* TagType:UINT */
+
+    /**
+     * Tag::USER_ID specifies the ID of the Android user that is permitted to use the key.
+     *
+     * Must not be hardware-enforced.
+     */
+    USER_ID = (3 << 28) | 501, /* TagType:UINT */
+
+    /**
+     * Tag::USER_SECURE_ID specifies that a key may only be used under a particular secure user
+     * authentication state.  This tag is mutually exclusive with Tag::NO_AUTH_REQUIRED.
+     *
+     * The value is a 64-bit integer specifying the authentication policy state value which must be
+     * present in the userId or authenticatorId field of a HardwareAuthToken provided to begin(),
+     * update(), or finish().  If a key with Tag::USER_SECURE_ID is used without a HardwareAuthToken
+     * with the matching userId or authenticatorId, the IKeyMintDevice must return
+     * ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * Tag::USER_SECURE_ID interacts with Tag::AUTH_TIMEOUT in a very important way.  If
+     * Tag::AUTH_TIMEOUT is present in the key's characteristics then the key is a "timeout-based"
+     * key, and may only be used if the difference between the current time when begin() is called
+     * and the timestamp in the HardwareAuthToken is less than the value in Tag::AUTH_TIMEOUT * 1000
+     * (the multiplier is because Tag::AUTH_TIMEOUT is in seconds, but the HardwareAuthToken
+     * timestamp is in milliseconds).  Otherwise the IKeyMintDevice must returrn
+     * ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * If Tag::AUTH_TIMEOUT is not present, then the key is an "auth-per-operation" key.  In this
+     * case, begin() must not require a HardwareAuthToken with appropriate contents.  Instead,
+     * update() and finish() must receive a HardwareAuthToken with Tag::USER_SECURE_ID value in
+     * userId or authenticatorId fields, and the current operation's operation handle in the
+     * challenge field.  Otherwise the IKeyMintDevice must returrn
+     * ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * This tag is repeatable.  If repeated, and any one of the values matches the HardwareAuthToken
+     * as described above, the key is authorized for use.  Otherwise the operation must fail with
+     * ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * Must be hardware-enforced.
+     */
+    USER_SECURE_ID = (10 << 28) | 502, /* TagType:ULONG_REP */
+
+    /**
+     * Tag::NO_AUTH_REQUIRED specifies that no authentication is required to use this key.  This tag
+     * is mutually exclusive with Tag::USER_SECURE_ID.
+     *
+     * Must be hardware-enforced.
+     */
+    NO_AUTH_REQUIRED = (7 << 28) | 503, /* TagType:BOOL */
+
+    /**
+     * Tag::USER_AUTH_TYPE specifies the types of user authenticators that may be used to authorize
+     * this key.
+     *
+     * The value is one or more values from HardwareAuthenticatorType, ORed together.
+     *
+     * When IKeyMintDevice is requested to perform an operation with a key with this tag, it must
+     * receive a HardwareAuthToken and one or more bits must be set in both the HardwareAuthToken's
+     * authenticatorType field and the Tag::USER_AUTH_TYPE value.  That is, it must be true that
+     *
+     *    (token.authenticatorType & tag_user_auth_type) != 0
+     *
+     * where token.authenticatorType is the authenticatorType field of the HardwareAuthToken and
+     * tag_user_auth_type is the value of Tag:USER_AUTH_TYPE.
+     *
+     * Must be hardware-enforced.
+     */
+    USER_AUTH_TYPE = (1 << 28) | 504, /* TagType:ENUM */
+
+    /**
+     * Tag::AUTH_TIMEOUT specifies the time in seconds for which the key is authorized for use,
+     * after user authentication.  If
+     * Tag::USER_SECURE_ID is present and this tag is not, then the key requies authentication for
+     * every usage (see begin() for the details of the authentication-per-operation flow).
+     *
+     * The value is a 32-bit integer specifying the time in seconds after a successful
+     * authentication of the user specified by Tag::USER_SECURE_ID with the authentication method
+     * specified by Tag::USER_AUTH_TYPE that the key can be used.
+     *
+     * Must be hardware-enforced.
+     */
+    AUTH_TIMEOUT = (3 << 28) | 505, /* TagType:UINT */
+
+    /**
+     * Tag::ALLOW_WHILE_ON_BODY specifies that the key may be used after authentication timeout if
+     * device is still on-body (requires on-body sensor).
+     *
+     * Cannot be hardware-enforced.
+     */
+    ALLOW_WHILE_ON_BODY = (7 << 28) | 506, /* TagType:BOOL */
+
+    /**
+     * TRUSTED_USER_PRESENCE_REQUIRED is an optional feature that specifies that this key must be
+     * unusable except when the user has provided proof of physical presence.  Proof of physical
+     * presence must be a signal that cannot be triggered by an attacker who doesn't have one of:
+     *
+     *    a) Physical control of the device or
+     *
+     *    b) Control of the secure environment that holds the key.
+     *
+     * For instance, proof of user identity may be considered proof of presence if it meets the
+     * requirements.  However, proof of identity established in one security domain (e.g. TEE) does
+     * not constitute proof of presence in another security domain (e.g. StrongBox), and no
+     * mechanism analogous to the authentication token is defined for communicating proof of
+     * presence across security domains.
+     *
+     * Some examples:
+     *
+     *     A hardware button hardwired to a pin on a StrongBox device in such a way that nothing
+     *     other than a button press can trigger the signal constitutes proof of physical presence
+     *     for StrongBox keys.
+     *
+     *     Fingerprint authentication provides proof of presence (and identity) for TEE keys if the
+     *     TEE has exclusive control of the fingerprint scanner and performs fingerprint matching.
+     *
+     *     Password authentication does not provide proof of presence to either TEE or StrongBox,
+     *     even if TEE or StrongBox does the password matching, because password input is handled by
+     *     the non-secure world, which means an attacker who has compromised Android can spoof
+     *     password authentication.
+     *
+     * Note that no mechanism is defined for delivering proof of presence to an IKeyMintDevice,
+     * except perhaps as implied by an auth token.  This means that KeyMint must be able to check
+     * proof of presence some other way.  Further, the proof of presence must be performed between
+     * begin() and the first call to update() or finish().  If the first update() or the finish()
+     * call is made without proof of presence, the keyMint method must return
+     * ErrorCode::PROOF_OF_PRESENCE_REQUIRED and abort the operation.  The caller must delay the
+     * update() or finish() call until proof of presence has been provided, which means the caller
+     * must also have some mechanism for verifying that the proof has been provided.
+     *
+     * Only one operation requiring TUP may be in flight at a time.  If begin() has already been
+     * called on one key with TRUSTED_USER_PRESENCE_REQUIRED, and another begin() comes in for that
+     * key or another with TRUSTED_USER_PRESENCE_REQUIRED, KeyMint must return
+     * ErrorCode::CONCURRENT_PROOF_OF_PRESENCE_REQUESTED.
+     *
+     * Must be hardware-enforced.
+     */
+    TRUSTED_USER_PRESENCE_REQUIRED = (7 << 28) | 507, /* TagType:BOOL */
+
+    /** Tag::TRUSTED_CONFIRMATION_REQUIRED is only applicable to keys with KeyPurpose SIGN, and
+     *  specifies that this key must not be usable unless the user provides confirmation of the data
+     *  to be signed.  Confirmation is proven to keyMint via an approval token.  See
+     *  CONFIRMATION_TOKEN, as well as the ConfirmatinUI HAL.
+     *
+     * If an attempt to use a key with this tag does not have a cryptographically valid
+     * CONFIRMATION_TOKEN provided to finish() or if the data provided to update()/finish() does not
+     * match the data described in the token, keyMint must return NO_USER_CONFIRMATION.
+     *
+     * Must be hardware-enforced.
+     */
+    TRUSTED_CONFIRMATION_REQUIRED = (7 << 28) | 508, /* TagType:BOOL */
+
+    /**
+     * Tag::UNLOCKED_DEVICE_REQUIRED specifies that the key may only be used when the device is
+     * unlocked.
+     *
+     * Must be software-enforced.
+     */
+    UNLOCKED_DEVICE_REQUIRED = (7 << 28) | 509, /* TagType:BOOL */
+
+    /**
+     * Tag::APPLICATION_ID.  When provided to generateKey or importKey, this tag specifies data
+     * that is necessary during all uses of the key.  In particular, calls to exportKey() and
+     * getKeyCharacteristics() must provide the same value to the clientId parameter, and calls to
+     * begin must provide this tag and the same associated data as part of the inParams set.  If
+     * the correct data is not provided, the method must return ErrorCode::INVALID_KEY_BLOB.
+     *
+     * The content of this tag must be bound to the key cryptographically, meaning it must not be
+     * possible for an adversary who has access to all of the secure world secrets but does not have
+     * access to the tag content to decrypt the key without brute-forcing the tag content, which
+     * applications can prevent by specifying sufficiently high-entropy content.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    APPLICATION_ID = (9 << 28) | 601, /* TagType:BYTES */
+
+    /*
+     * Semantically unenforceable tags, either because they have no specific meaning or because
+     * they're informational only.
+     */
+
+    /**
+     * Tag::APPLICATION_DATA.  When provided to generateKey or importKey, this tag specifies data
+     * that is necessary during all uses of the key.  In particular, calls to exportKey() and
+     * getKeyCharacteristics() must provide the same value to the appData parameter, and calls to
+     * begin must provide this tag and the same associated data as part of the inParams set.  If
+     * the correct data is not provided, the method must return ErrorCode::INVALID_KEY_BLOB.
+     *
+     * The content of this tag msut be bound to the key cryptographically, meaning it must not be
+     * possible for an adversary who has access to all of the secure world secrets but does not have
+     * access to the tag content to decrypt the key without brute-forcing the tag content, which
+     * applications can prevent by specifying sufficiently high-entropy content.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    APPLICATION_DATA = (9 << 28) | 700, /* TagType:BYTES */
+
+    /**
+     * Tag::CREATION_DATETIME specifies the date and time the key was created, in milliseconds since
+     * January 1, 1970.  This tag is optional and informational only.
+     *
+     * Tag::CREATED is informational only, and not enforced by anything.  Must be in the
+     * software-enforced list, if provided.
+     */
+    CREATION_DATETIME = (6 << 28) | 701, /* TagType:DATE */
+
+    /**
+     * Tag::ORIGIN specifies where the key was created, if known.  This tag must not be specified
+     * during key generation or import, and must be added to the key characteristics by the
+     * IKeyMintDevice.  The possible values are defined in the KeyOrigin enum.
+     *
+     * Must be hardware-enforced.
+     */
+    ORIGIN = (1 << 28) | 702, /* TagType:ENUM */
+
+    // 703 is unused.
+
+    /**
+     * Tag::ROOT_OF_TRUST specifies the root of trust associated with the key used by verified boot
+     * to validate the system.  It describes the boot key, verified boot state, boot hash, and
+     * whether device is locked.  This tag is never provided to or returned from KeyMint in the
+     * key characteristics.  It exists only to define the tag for use in the attestation record.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ROOT_OF_TRUST = (9 << 28) | 704, /* TagType:BYTES */
+
+    /**
+     * Tag::OS_VERSION specifies the system OS version with which the key may be used.  This tag is
+     * never sent to the IKeyMintDevice, but is added to the hardware-enforced authorization list
+     * by the TA.  Any attempt to use a key with a Tag::OS_VERSION value different from the
+     * currently-running OS version must cause begin(), getKeyCharacteristics() or exportKey() to
+     * return ErrorCode::KEY_REQUIRES_UPGRADE.  See upgradeKey() for details.
+     *
+     * The value of the tag is an integer of the form MMmmss, where MM is the major version number,
+     * mm is the minor version number, and ss is the sub-minor version number.  For example, for a
+     * key generated on Android version 4.0.3, the value would be 040003.
+     *
+     * The IKeyMintDevice HAL must read the current OS version from the system property
+     * ro.build.version.release and deliver it to the secure environment when the HAL is first
+     * loaded (mechanism is implementation-defined).  The secure environment must not accept another
+     * version until after the next boot.  If the content of ro.build.version.release has additional
+     * version information after the sub-minor version number, it must not be included in
+     * Tag::OS_VERSION.  If the content is non-numeric, the secure environment must use 0 as the
+     * system version.
+     *
+     * Must be hardware-enforced.
+     */
+    OS_VERSION = (3 << 28) | 705, /* TagType:UINT */
+
+    /**
+     * Tag::OS_PATCHLEVEL specifies the system security patch level with which the key may be used.
+     * This tag is never sent to the keyMint TA, but is added to the hardware-enforced
+     * authorization list by the TA.  Any attempt to use a key with a Tag::OS_PATCHLEVEL value
+     * different from the currently-running system patchlevel must cause begin(),
+     * getKeyCharacteristics() or exportKey() to return ErrorCode::KEY_REQUIRES_UPGRADE.  See
+     * upgradeKey() for details.
+     *
+     * The value of the tag is an integer of the form YYYYMM, where YYYY is the four-digit year of
+     * the last update and MM is the two-digit month of the last update.  For example, for a key
+     * generated on an Android device last updated in December 2015, the value would be 201512.
+     *
+     * The IKeyMintDevice HAL must read the current system patchlevel from the system property
+     * ro.build.version.security_patch and deliver it to the secure environment when the HAL is
+     * first loaded (mechanism is implementation-defined).  The secure environment must not accept
+     * another patchlevel until after the next boot.
+     *
+     * Must be hardware-enforced.
+     */
+    OS_PATCHLEVEL = (3 << 28) | 706, /* TagType:UINT */
+
+    /**
+     * Tag::UNIQUE_ID specifies a unique, time-based identifier.  This tag is never provided to or
+     * returned from KeyMint in the key characteristics.  It exists only to define the tag for use
+     * in the attestation record.
+     *
+     * When a key with Tag::INCLUDE_UNIQUE_ID is attested, the unique ID is added to the attestation
+     * record.  The value is a 128-bit hash that is unique per device and per calling application,
+     * and changes monthly and on most password resets.  It is computed with:
+     *
+     *    HMAC_SHA256(T || C || R, HBK)
+     *
+     * Where:
+     *
+     *    T is the "temporal counter value", computed by dividing the value of
+     *      Tag::CREATION_DATETIME by 2592000000, dropping any remainder.  T changes every 30 days
+     *      (2592000000 = 30 * 24 * 60 * 60 * 1000).
+     *
+     *    C is the value of Tag::ATTESTATION_APPLICATION_ID that is provided to attestKey().
+     *
+     *    R is 1 if Tag::RESET_SINCE_ID_ROTATION was provided to attestKey or 0 if the tag was not
+     *      provided.
+     *
+     *    HBK is a unique hardware-bound secret known to the secure environment and never revealed
+     *    by it.  The secret must contain at least 128 bits of entropy and be unique to the
+     *    individual device (probabilistic uniqueness is acceptable).
+     *
+     *    HMAC_SHA256 is the HMAC function, with SHA-2-256 as the hash.
+     *
+     * The output of the HMAC function must be truncated to 128 bits.
+     *
+     * Must be hardware-enforced.
+     */
+    UNIQUE_ID = (9 << 28) | 707, /* TagType:BYTES */
+
+    /**
+     * Tag::ATTESTATION_CHALLENGE is used to deliver a "challenge" value to the attestKey() method,
+     * which must place the value in the KeyDescription SEQUENCE of the attestation extension.  See
+     * attestKey().
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_CHALLENGE = (9 << 28) | 708, /* TagType:BYTES */
+
+    /**
+     * Tag::ATTESTATION_APPLICATION_ID identifies the set of applications which may use a key, used
+     * only with attestKey().
+     *
+     * The content of Tag::ATTESTATION_APPLICATION_ID is a DER-encoded ASN.1 structure, with the
+     * following schema:
+     *
+     * AttestationApplicationId ::= SEQUENCE {
+     *     packageInfoRecords SET OF PackageInfoRecord,
+     *     signatureDigests   SET OF OCTET_STRING,
+     * }
+     *
+     * PackageInfoRecord ::= SEQUENCE {
+     *     packageName        OCTET_STRING,
+     *     version            INTEGER,
+     * }
+     *
+     * See system/security/keystore/keystore_attestation_id.cpp for details of construction.
+     * IKeyMintDevice implementers do not need to create or parse the ASN.1 structure, but only
+     * copy the tag value into the attestation record.  The DER-encoded string must not exceed 1 KiB
+     * in length.
+     *
+     * Cannot be hardware-enforced.
+     */
+    ATTESTATION_APPLICATION_ID = (9 << 28) | 709, /* TagType:BYTES */
+
+    /**
+     * Tag::ATTESTATION_ID_BRAND provides the device's brand name, as returned by Build.BRAND in
+     * Android, to attestKey().  This field must be set only when requesting attestation of the
+     * device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_BRAND = (9 << 28) | 710, /* TagType:BYTES */
+
+    /**
+     * Tag::ATTESTATION_ID_DEVICE provides the device's device name, as returned by Build.DEVICE in
+     * Android, to attestKey().  This field must be set only when requesting attestation of the
+     * device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_DEVICE = (9 << 28) | 711, /* TagType:BYTES */
+
+    /**
+     * Tag::ATTESTATION_ID_PRODUCT provides the device's product name, as returned by Build.PRODUCT
+     * in Android, to attestKey().  This field must be set only when requesting attestation of the
+     * device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_PRODUCT = (9 << 28) | 712, /* TagType:BYTES */
+
+    /**
+     * Tag::ATTESTATION_ID_SERIAL the device's serial number.  This field must be set only when
+     * requesting attestation of the device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_SERIAL = (9 << 28) | 713, /* TagType:BYTES */
+
+    /**
+     * Tag::ATTESTATION_ID_IMEI provides the IMEIs for all radios on the device to attestKey().
+     * This field must be set only when requesting attestation of the device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_IMEI = (9 << 28) | 714, /* TagType:BYTES */
+
+    /**
+     * Tag::ATTESTATION_ID_MEID provides the MEIDs for all radios on the device to attestKey().
+     * This field must be set only when requesting attestation of the device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_MEID = (9 << 28) | 715, /* TagType:BYTES */
+
+    /**
+     * Tag::ATTESTATION_ID_MANUFACTURER provides the device's manufacturer name, as returned by
+     * Build.MANUFACTURER in Android, to attstKey().  This field must be set only when requesting
+     * attestation of the device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_MANUFACTURER = (9 << 28) | 716, /* TagType:BYTES */
+
+    /**
+     * Tag::ATTESTATION_ID_MODEL provides the device's model name, as returned by Build.MODEL in
+     * Android, to attestKey().  This field must be set only when requesting attestation of the
+     * device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_MODEL = (9 << 28) | 717, /* TagType:BYTES */
+
+    /**
+     * Tag::VENDOR_PATCHLEVEL specifies the vendor image security patch level with which the key may
+     * be used.  This tag is never sent to the keyMint TA, but is added to the hardware-enforced
+     * authorization list by the TA.  Any attempt to use a key with a Tag::VENDOR_PATCHLEVEL value
+     * different from the currently-running system patchlevel must cause begin(),
+     * getKeyCharacteristics() or exportKey() to return ErrorCode::KEY_REQUIRES_UPGRADE.  See
+     * upgradeKey() for details.
+     *
+     * The value of the tag is an integer of the form YYYYMMDD, where YYYY is the four-digit year of
+     * the last update, MM is the two-digit month and DD is the two-digit day of the last
+     * update.  For example, for a key generated on an Android device last updated on June 5, 2018,
+     * the value would be 20180605.
+     *
+     * The IKeyMintDevice HAL must read the current vendor patchlevel from the system property
+     * ro.vendor.build.security_patch and deliver it to the secure environment when the HAL is first
+     * loaded (mechanism is implementation-defined).  The secure environment must not accept another
+     * patchlevel until after the next boot.
+     *
+     * Must be hardware-enforced.
+     */
+    VENDOR_PATCHLEVEL = (3 << 28) | 718, /* TagType:UINT */
+
+    /**
+     * Tag::BOOT_PATCHLEVEL specifies the boot image (kernel) security patch level with which the
+     * key may be used.  This tag is never sent to the keyMint TA, but is added to the
+     * hardware-enforced authorization list by the TA.  Any attempt to use a key with a
+     * Tag::BOOT_PATCHLEVEL value different from the currently-running system patchlevel must
+     * cause begin(), getKeyCharacteristics() or exportKey() to return
+     * ErrorCode::KEY_REQUIRES_UPGRADE.  See upgradeKey() for details.
+     *
+     * The value of the tag is an integer of the form YYYYMMDD, where YYYY is the four-digit year of
+     * the last update, MM is the two-digit month and DD is the two-digit day of the last
+     * update.  For example, for a key generated on an Android device last updated on June 5, 2018,
+     * the value would be 20180605.  If the day is not known, 00 may be substituted.
+     *
+     * During each boot, the bootloader must provide the patch level of the boot image to the secure
+     * envirionment (mechanism is implementation-defined).
+     *
+     * Must be hardware-enforced.
+     */
+    BOOT_PATCHLEVEL = (3 << 28) | 719, /* TagType:UINT */
+
+    /**
+     * DEVICE_UNIQUE_ATTESTATION is an argument to IKeyMintDevice::attestKey().  It indicates that
+     * attestation using a device-unique key is requested, rather than a batch key.  When a
+     * device-unique key is used, only the attestation certificate is returned; no additional
+     * chained certificates are provided.  It's up to the caller to recognize the device-unique
+     * signing key.  Only SecurityLevel::STRONGBOX IKeyMintDevices may support device-unique
+     * attestations.  SecurityLevel::TRUSTED_ENVIRONMENT IKeyMintDevices must return
+     * ErrorCode::INVALID_ARGUMENT if they receive DEVICE_UNIQUE_ATTESTATION.
+     * SecurityLevel::STRONGBOX IKeyMintDevices need not support DEVICE_UNIQUE_ATTESTATION, and
+     * return ErrorCode::CANNOT_ATTEST_IDS if they do not support it.
+     *
+     * IKeyMintDevice implementations that support device-unique attestation MUST add the
+     * DEVICE_UNIQUE_ATTESTATION tag to device-unique attestations.
+     */
+    DEVICE_UNIQUE_ATTESTATION = (7 << 28) | 720, /* TagType:BOOL */
+
+    /**
+     * IDENTITY_CREDENTIAL_KEY is never used by IKeyMintDevice, is not a valid argument to key
+     * generation or any operation, is never returned by any method and is never used in a key
+     * attestation.  It is used in attestations produced by the IIdentityCredential HAL when that
+     * HAL attests to Credential Keys.  IIdentityCredential produces KeyMint-style attestations.
+     */
+    IDENTITY_CREDENTIAL_KEY = (7 << 28) | 721, /* TagType:BOOL */
+
+    /**
+     * To prevent keys from being compromised if an attacker acquires read access to system / kernel
+     * memory, some inline encryption hardware supports protecting storage encryption keys in
+     * hardware without software having access to or the ability to set the plaintext keys.
+     * Instead, software only sees wrapped version of these keys.
+     *
+     * STORAGE_KEY is used to denote that a key generated or imported is a key used for storage
+     * encryption. Keys of this type can either be generated or imported or secure imported using
+     * keyMint. exportKey() can be used to re-wrap storage key with a per-boot ephemeral key
+     * wrapped key once the key characteristics are enforced.
+     *
+     * Keys with this tag cannot be used for any operation within keyMint.
+     * ErrorCode::INVALID_OPERATION is returned when a key with Tag::STORAGE_KEY is provided to
+     * begin().
+     */
+    STORAGE_KEY = (7 << 28) | 722, /* TagType:BOOL */
+
+    /**
+     * Tag::ASSOCIATED_DATA Provides "associated data" for AES-GCM encryption or decryption.  This
+     * tag is provided to update and specifies data that is not encrypted/decrypted, but is used in
+     * computing the GCM tag.
+     *
+     * Must never appear KeyCharacteristics.
+     */
+    ASSOCIATED_DATA = (9 << 28) | 1000, /* TagType:BYTES */
+
+    /**
+     * Tag::NONCE is used to provide or return a nonce or Initialization Vector (IV) for AES-GCM,
+     * AES-CBC, AES-CTR, or 3DES-CBC encryption or decryption.  This tag is provided to begin during
+     * encryption and decryption operations.  It is only provided to begin if the key has
+     * Tag::CALLER_NONCE.  If not provided, an appropriate nonce or IV must be randomly generated by
+     * KeyMint and returned from begin.
+     *
+     * The value is a blob, an arbitrary-length array of bytes.  Allowed lengths depend on the mode:
+     * GCM nonces are 12 bytes in length; AES-CBC and AES-CTR IVs are 16 bytes in length, 3DES-CBC
+     * IVs are 8 bytes in length.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    NONCE = (9 << 28) | 1001, /* TagType:BYTES */
+
+    /**
+     * Tag::MAC_LENGTH provides the requested length of a MAC or GCM authentication tag, in bits.
+     *
+     * The value is the MAC length in bits.  It must be a multiple of 8 and at least as large as the
+     * value of Tag::MIN_MAC_LENGTH associated with the key.  Otherwise, begin() must return
+     * ErrorCode::INVALID_MAC_LENGTH.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    MAC_LENGTH = (3 << 28) | 1003, /* TagType:UINT */
+
+    /**
+     * Tag::RESET_SINCE_ID_ROTATION specifies whether the device has been factory reset since the
+     * last unique ID rotation.  Used for key attestation.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    RESET_SINCE_ID_ROTATION = (7 << 28) | 1004, /* TagType:BOOL */
+
+    /**
+     * Tag::CONFIRMATION_TOKEN is used to deliver a cryptographic token proving that the user
+     * confirmed a signing request.  The content is a full-length HMAC-SHA256 value.  See the
+     * ConfirmationUI HAL for details of token computation.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    CONFIRMATION_TOKEN = (9 << 28) | 1005, /* TagType:BYTES */
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl b/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl
new file mode 100644
index 0000000..a273af3
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * TagType classifies Tags in Tag.aidl into various groups of data.
+ */
+@VintfStability
+@Backing(type="int")
+enum TagType {
+    /** Invalid type, used to designate a tag as uninitialized. */
+    INVALID = 0 << 28,
+    /** Enumeration value. */
+    ENUM = 1 << 28,
+    /** Repeatable enumeration value. */
+    ENUM_REP = 2 << 28,
+    /** 32-bit unsigned integer. */
+    UINT = 3 << 28,
+    /** Repeatable 32-bit unsigned integer. */
+    UINT_REP = 4 << 28,
+    /** 64-bit unsigned integer. */
+    ULONG = 5 << 28,
+    /** 64-bit unsigned integer representing a date and time, in milliseconds since 1 Jan 1970. */
+    DATE = 6 << 28,
+    /** Boolean.  If a tag with this type is present, the value is "true".  If absent, "false". */
+    BOOL = 7 << 28,
+    /** Byte string containing an arbitrary-length integer, big-endian ordering. */
+    BIGNUM = 8 << 28,
+    /** Byte string */
+    BYTES = 9 << 28,
+    /** Repeatable 64-bit unsigned integer */
+    ULONG_REP = 10 << 28,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Timestamp.aidl b/security/keymint/aidl/android/hardware/security/keymint/Timestamp.aidl
new file mode 100644
index 0000000..ebb3684
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/Timestamp.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Time in milliseconds since some arbitrary point in time.  Time must be monotonically increasing,
+ * and a secure environment's notion of "current time" must not repeat until the Android device
+ * reboots, or until at least 50 million years have elapsed (note that this requirement is satisfied
+ * by setting the clock to zero during each boot, and then counting time accurately).
+ */
+@VintfStability
+parcelable Timestamp {
+    long milliSeconds;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/VerificationToken.aidl b/security/keymint/aidl/android/hardware/security/keymint/VerificationToken.aidl
new file mode 100644
index 0000000..f76e6a8
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/VerificationToken.aidl
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.keymint.Timestamp;
+
+/**
+ * VerificationToken instances are used for secure environments to authenticate one another.
+ *
+ * This version of the parcelable currently don't use the parametersVerified field since it's not
+ * needed for time-based verification. This can be added in a later version, if needed.
+ */
+@VintfStability
+parcelable VerificationToken {
+    /**
+     * The operation handle, used to ensure freshness.
+     */
+    long challenge;
+
+    /**
+     * The current time of the secure environment that generates the VerificationToken.  This can be
+     * checked against auth tokens generated by the same secure environment, which avoids needing to
+     * synchronize clocks.
+     */
+    Timestamp timestamp;
+
+    /**
+     * SecurityLevel of the secure environment that generated the token.
+     */
+    SecurityLevel securityLevel;
+
+    /**
+     * 32-byte HMAC-SHA256 of the above values, computed as:
+     *
+     *    HMAC(H,
+     *         "Auth Verification" || challenge || timestamp || securityLevel)
+     *
+     * where:
+     *
+     *   ``HMAC'' is the shared HMAC key (see computeSharedHmac() in IKeyMint).
+     *
+     *   ``||'' represents concatenation
+     *
+     * The representation of challenge and timestamp is as 64-bit unsigned integers in big-endian
+     * order.  securityLevel is represented as a 32-bit unsigned integer in big-endian order.
+     */
+    byte[] mac;
+}
diff --git a/security/keymint/aidl/default/Android.bp b/security/keymint/aidl/default/Android.bp
new file mode 100644
index 0000000..491a2c1
--- /dev/null
+++ b/security/keymint/aidl/default/Android.bp
@@ -0,0 +1,26 @@
+cc_binary {
+    name: "android.hardware.security.keymint-service",
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.security.keymint-service.rc"],
+    vintf_fragments: ["android.hardware.security.keymint-service.xml"],
+    vendor: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "android.hardware.security.keymint-ndk_platform",
+        "libbase",
+        "libbinder_ndk",
+        "libcppbor",
+        "libcrypto",
+        "libkeymaster_portable",
+        "libkeymint",
+        "liblog",
+        "libpuresoftkeymasterdevice",
+        "libutils",
+    ],
+    srcs: [
+        "service.cpp",
+    ],
+}
diff --git a/security/keymint/aidl/default/android.hardware.security.keymint-service.rc b/security/keymint/aidl/default/android.hardware.security.keymint-service.rc
new file mode 100644
index 0000000..0c3a6e1
--- /dev/null
+++ b/security/keymint/aidl/default/android.hardware.security.keymint-service.rc
@@ -0,0 +1,3 @@
+service vendor.keymint-default /vendor/bin/hw/android.hardware.security.keymint-service
+    class early_hal
+    user nobody
diff --git a/security/keymint/aidl/default/android.hardware.security.keymint-service.xml b/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
new file mode 100644
index 0000000..73d15a8
--- /dev/null
+++ b/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.security.keymint</name>
+        <fqname>IKeyMintDevice/default</fqname>
+    </hal>
+</manifest>
diff --git a/security/keymint/aidl/default/service.cpp b/security/keymint/aidl/default/service.cpp
new file mode 100644
index 0000000..a710535
--- /dev/null
+++ b/security/keymint/aidl/default/service.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.security.keymint-service"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <AndroidKeyMintDevice.h>
+#include <keymaster/soft_keymaster_logger.h>
+
+using aidl::android::hardware::security::keymint::AndroidKeyMintDevice;
+using aidl::android::hardware::security::keymint::SecurityLevel;
+
+int main() {
+    // Zero threads seems like a useless pool, but below we'll join this thread to it, increasing
+    // the pool size to 1.
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    std::shared_ptr<AndroidKeyMintDevice> keyMint =
+            ndk::SharedRefBase::make<AndroidKeyMintDevice>(SecurityLevel::SOFTWARE);
+
+    keymaster::SoftKeymasterLogger logger;
+    const auto instanceName = std::string(AndroidKeyMintDevice::descriptor) + "/default";
+    LOG(INFO) << "instance: " << instanceName;
+    binder_status_t status =
+            AServiceManager_addService(keyMint->asBinder().get(), instanceName.c_str());
+    CHECK(status == STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;  // should not reach
+}
diff --git a/security/keymint/aidl/vts/functional/Android.bp b/security/keymint/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..ef7adb1
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/Android.bp
@@ -0,0 +1,66 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "VtsAidlKeyMintTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "KeyMintTest.cpp",
+        "VerificationTokenTest.cpp",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libcrypto",
+        "libkeymint",
+        "libkeymint_support",
+    ],
+    static_libs: [
+        "android.hardware.security.keymint-cpp",
+        "libcppbor_external",
+        "libkeymint_vts_test_utils",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
+
+cc_test_library {
+    name: "libkeymint_vts_test_utils",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "KeyMintAidlTestBase.cpp",
+    ],
+    export_include_dirs: [
+        ".",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libcrypto",
+        "libkeymint",
+        "libkeymint_support",
+    ],
+    static_libs: [
+        "android.hardware.security.keymint-cpp",
+        "libcppbor",
+    ],
+}
diff --git a/security/keymint/aidl/vts/functional/AndroidTest.xml b/security/keymint/aidl/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..43e7a8a
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs VtsAidlKeyMintV1_0TargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsAidlKeyMintV1_0TargetTest->/data/local/tmp/VtsAidlKeyMintV1_0TargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsAidlKeyMintV1_0TargetTest" />
+        <option name="native-test-timeout" value="900000"/>
+    </test>
+</configuration>
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
new file mode 100644
index 0000000..ea3a329
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyMintAidlTestBase.h"
+
+#include <chrono>
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include <keymint_support/key_param_output.h>
+#include <keymint_support/keymint_utils.h>
+
+namespace android::hardware::security::keymint {
+
+using namespace std::literals::chrono_literals;
+using std::endl;
+using std::optional;
+
+::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set) {
+    if (set.size() == 0)
+        os << "(Empty)" << ::std::endl;
+    else {
+        os << "\n";
+        for (size_t i = 0; i < set.size(); ++i) os << set[i] << ::std::endl;
+    }
+    return os;
+}
+
+namespace test {
+
+ErrorCode KeyMintAidlTestBase::GetReturnErrorCode(Status result) {
+    if (result.isOk()) return ErrorCode::OK;
+
+    if (result.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
+        return static_cast<ErrorCode>(result.serviceSpecificErrorCode());
+    }
+
+    return ErrorCode::UNKNOWN_ERROR;
+}
+
+void KeyMintAidlTestBase::InitializeKeyMint(sp<IKeyMintDevice> keyMint) {
+    ASSERT_NE(keyMint, nullptr);
+    keymint_ = keyMint;
+
+    KeyMintHardwareInfo info;
+    ASSERT_TRUE(keymint_->getHardwareInfo(&info).isOk());
+
+    securityLevel_ = info.securityLevel;
+    name_.assign(info.keyMintName.begin(), info.keyMintName.end());
+    author_.assign(info.keyMintAuthorName.begin(), info.keyMintAuthorName.end());
+
+    os_version_ = getOsVersion();
+    os_patch_level_ = getOsPatchlevel();
+}
+
+void KeyMintAidlTestBase::SetUp() {
+    InitializeKeyMint(
+            android::waitForDeclaredService<IKeyMintDevice>(String16(GetParam().c_str())));
+}
+
+ErrorCode KeyMintAidlTestBase::GenerateKey(const AuthorizationSet& key_desc,
+                                           vector<uint8_t>* keyBlob, KeyCharacteristics* keyChar) {
+    EXPECT_NE(keyBlob, nullptr) << "Key blob pointer must not be null.  Test bug";
+    EXPECT_NE(keyChar, nullptr)
+            << "Previous characteristics not deleted before generating key.  Test bug.";
+
+    // Aidl does not clear these output parameters if the function returns
+    // error.  This is different from hal where output parameter is always
+    // cleared due to hal returning void.  So now we need to do our own clearing
+    // of the output variables prior to calling keyMint aidl libraries.
+    keyBlob->clear();
+    keyChar->softwareEnforced.clear();
+    keyChar->hardwareEnforced.clear();
+    certChain_.clear();
+
+    Status result;
+    ByteArray blob;
+
+    result = keymint_->generateKey(key_desc.vector_data(), &blob, keyChar, &certChain_);
+
+    // On result, blob & characteristics should be empty.
+    if (result.isOk()) {
+        if (SecLevel() != SecurityLevel::SOFTWARE) {
+            EXPECT_GT(keyChar->hardwareEnforced.size(), 0);
+        }
+        EXPECT_GT(keyChar->softwareEnforced.size(), 0);
+        // TODO(seleneh) in a later version where we return @nullable
+        // single Certificate, check non-null single certificate is always
+        // non-empty.
+        *keyBlob = blob.data;
+    }
+
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::GenerateKey(const AuthorizationSet& key_desc) {
+    return GenerateKey(key_desc, &key_blob_, &key_characteristics_);
+}
+
+ErrorCode KeyMintAidlTestBase::ImportKey(const AuthorizationSet& key_desc, KeyFormat format,
+                                         const string& key_material, vector<uint8_t>* key_blob,
+                                         KeyCharacteristics* key_characteristics) {
+    Status result;
+
+    certChain_.clear();
+    key_characteristics->softwareEnforced.clear();
+    key_characteristics->hardwareEnforced.clear();
+    key_blob->clear();
+
+    ByteArray blob;
+    result = keymint_->importKey(key_desc.vector_data(), format,
+                                 vector<uint8_t>(key_material.begin(), key_material.end()), &blob,
+                                 key_characteristics, &certChain_);
+
+    if (result.isOk()) {
+        if (SecLevel() != SecurityLevel::SOFTWARE) {
+            EXPECT_GT(key_characteristics->hardwareEnforced.size(), 0);
+        }
+        EXPECT_GT(key_characteristics->softwareEnforced.size(), 0);
+        *key_blob = blob.data;
+    }
+
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::ImportKey(const AuthorizationSet& key_desc, KeyFormat format,
+                                         const string& key_material) {
+    return ImportKey(key_desc, format, key_material, &key_blob_, &key_characteristics_);
+}
+
+ErrorCode KeyMintAidlTestBase::ImportWrappedKey(string wrapped_key, string wrapping_key,
+                                                const AuthorizationSet& wrapping_key_desc,
+                                                string masking_key,
+                                                const AuthorizationSet& unwrapping_params) {
+    Status result;
+    EXPECT_EQ(ErrorCode::OK, ImportKey(wrapping_key_desc, KeyFormat::PKCS8, wrapping_key));
+
+    ByteArray outBlob;
+    key_characteristics_.softwareEnforced.clear();
+    key_characteristics_.hardwareEnforced.clear();
+
+    result = keymint_->importWrappedKey(vector<uint8_t>(wrapped_key.begin(), wrapped_key.end()),
+                                        key_blob_,
+                                        vector<uint8_t>(masking_key.begin(), masking_key.end()),
+                                        unwrapping_params.vector_data(), 0 /* passwordSid */,
+                                        0 /* biometricSid */, &outBlob, &key_characteristics_);
+
+    if (result.isOk()) {
+        key_blob_ = outBlob.data;
+        if (SecLevel() != SecurityLevel::SOFTWARE) {
+            EXPECT_GT(key_characteristics_.hardwareEnforced.size(), 0);
+        }
+        EXPECT_GT(key_characteristics_.softwareEnforced.size(), 0);
+    }
+
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::DeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob) {
+    Status result = keymint_->deleteKey(*key_blob);
+    if (!keep_key_blob) {
+        *key_blob = vector<uint8_t>();
+    }
+
+    EXPECT_TRUE(result.isOk()) << result.serviceSpecificErrorCode() << endl;
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::DeleteKey(bool keep_key_blob) {
+    return DeleteKey(&key_blob_, keep_key_blob);
+}
+
+ErrorCode KeyMintAidlTestBase::DeleteAllKeys() {
+    Status result = keymint_->deleteAllKeys();
+    EXPECT_TRUE(result.isOk()) << result.serviceSpecificErrorCode() << endl;
+    return GetReturnErrorCode(result);
+}
+
+void KeyMintAidlTestBase::CheckedDeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob) {
+    ErrorCode result = DeleteKey(key_blob, keep_key_blob);
+    EXPECT_TRUE(result == ErrorCode::OK || result == ErrorCode::UNIMPLEMENTED) << result << endl;
+}
+
+void KeyMintAidlTestBase::CheckedDeleteKey() {
+    CheckedDeleteKey(&key_blob_);
+}
+
+ErrorCode KeyMintAidlTestBase::Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob,
+                                     const AuthorizationSet& in_params,
+                                     AuthorizationSet* out_params, sp<IKeyMintOperation>& op) {
+    SCOPED_TRACE("Begin");
+    Status result;
+    BeginResult out;
+    result = keymint_->begin(purpose, key_blob, in_params.vector_data(), HardwareAuthToken(), &out);
+
+    if (result.isOk()) {
+        *out_params = out.params;
+        challenge_ = out.challenge;
+        op = out.operation;
+    }
+
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob,
+                                     const AuthorizationSet& in_params,
+                                     AuthorizationSet* out_params) {
+    SCOPED_TRACE("Begin");
+    Status result;
+    BeginResult out;
+
+    result = keymint_->begin(purpose, key_blob, in_params.vector_data(), HardwareAuthToken(), &out);
+
+    if (result.isOk()) {
+        *out_params = out.params;
+        challenge_ = out.challenge;
+        op_ = out.operation;
+    }
+
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::Begin(KeyPurpose purpose, const AuthorizationSet& in_params,
+                                     AuthorizationSet* out_params) {
+    SCOPED_TRACE("Begin");
+    EXPECT_EQ(nullptr, op_);
+    return Begin(purpose, key_blob_, in_params, out_params);
+}
+
+ErrorCode KeyMintAidlTestBase::Begin(KeyPurpose purpose, const AuthorizationSet& in_params) {
+    SCOPED_TRACE("Begin");
+    AuthorizationSet out_params;
+    ErrorCode result = Begin(purpose, in_params, &out_params);
+    EXPECT_TRUE(out_params.empty());
+    return result;
+}
+
+ErrorCode KeyMintAidlTestBase::Update(const AuthorizationSet& in_params, const string& input,
+                                      AuthorizationSet* out_params, string* output,
+                                      int32_t* input_consumed) {
+    SCOPED_TRACE("Update");
+
+    Status result;
+    EXPECT_NE(op_, nullptr);
+    if (!op_) {
+        return ErrorCode::UNEXPECTED_NULL_POINTER;
+    }
+
+    KeyParameterArray key_params;
+    key_params.params = in_params.vector_data();
+
+    KeyParameterArray in_keyParams;
+    in_keyParams.params = in_params.vector_data();
+
+    optional<KeyParameterArray> out_keyParams;
+    optional<ByteArray> o_put;
+    result = op_->update(in_keyParams, vector<uint8_t>(input.begin(), input.end()), {}, {},
+                         &out_keyParams, &o_put, input_consumed);
+
+    if (result.isOk()) {
+        if (o_put) {
+            output->append(o_put->data.begin(), o_put->data.end());
+        }
+
+        if (out_keyParams) {
+            out_params->push_back(AuthorizationSet(out_keyParams->params));
+        }
+    }
+
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::Update(const string& input, string* out, int32_t* input_consumed) {
+    SCOPED_TRACE("Update");
+    AuthorizationSet out_params;
+    ErrorCode result =
+            Update(AuthorizationSet() /* in_params */, input, &out_params, out, input_consumed);
+    EXPECT_TRUE(out_params.empty());
+    return result;
+}
+
+ErrorCode KeyMintAidlTestBase::Finish(const AuthorizationSet& in_params, const string& input,
+                                      const string& signature, AuthorizationSet* out_params,
+                                      string* output) {
+    SCOPED_TRACE("Finish");
+    Status result;
+
+    EXPECT_NE(op_, nullptr);
+    if (!op_) {
+        return ErrorCode::UNEXPECTED_NULL_POINTER;
+    }
+
+    KeyParameterArray key_params;
+    key_params.params = in_params.vector_data();
+
+    KeyParameterArray in_keyParams;
+    in_keyParams.params = in_params.vector_data();
+
+    optional<KeyParameterArray> out_keyParams;
+    optional<vector<uint8_t>> o_put;
+
+    vector<uint8_t> oPut;
+    result = op_->finish(in_keyParams, vector<uint8_t>(input.begin(), input.end()),
+                         vector<uint8_t>(signature.begin(), signature.end()), {}, {},
+                         &out_keyParams, &oPut);
+
+    if (result.isOk()) {
+        if (out_keyParams) {
+            out_params->push_back(AuthorizationSet(out_keyParams->params));
+        }
+
+        output->append(oPut.begin(), oPut.end());
+    }
+
+    op_.clear();  // So dtor doesn't Abort().
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::Finish(const string& message, string* output) {
+    SCOPED_TRACE("Finish");
+    AuthorizationSet out_params;
+    string finish_output;
+    ErrorCode result = Finish(AuthorizationSet() /* in_params */, message, "" /* signature */,
+                              &out_params, output);
+    if (result != ErrorCode::OK) {
+        return result;
+    }
+    EXPECT_EQ(0U, out_params.size());
+    return result;
+}
+
+ErrorCode KeyMintAidlTestBase::Finish(const string& message, const string& signature,
+                                      string* output) {
+    SCOPED_TRACE("Finish");
+    AuthorizationSet out_params;
+    ErrorCode result =
+            Finish(AuthorizationSet() /* in_params */, message, signature, &out_params, output);
+
+    if (result != ErrorCode::OK) {
+        return result;
+    }
+
+    EXPECT_EQ(0U, out_params.size());
+    return result;
+}
+
+ErrorCode KeyMintAidlTestBase::Abort(const sp<IKeyMintOperation>& op) {
+    SCOPED_TRACE("Abort");
+
+    EXPECT_NE(op, nullptr);
+    if (!op) {
+        return ErrorCode::UNEXPECTED_NULL_POINTER;
+    }
+
+    Status retval = op->abort();
+    EXPECT_TRUE(retval.isOk());
+    return static_cast<ErrorCode>(retval.serviceSpecificErrorCode());
+}
+
+ErrorCode KeyMintAidlTestBase::Abort() {
+    SCOPED_TRACE("Abort");
+
+    EXPECT_NE(op_, nullptr);
+    if (!op_) {
+        return ErrorCode::UNEXPECTED_NULL_POINTER;
+    }
+
+    Status retval = op_->abort();
+    return static_cast<ErrorCode>(retval.serviceSpecificErrorCode());
+}
+
+void KeyMintAidlTestBase::AbortIfNeeded() {
+    SCOPED_TRACE("AbortIfNeeded");
+    if (op_) {
+        EXPECT_EQ(ErrorCode::OK, Abort());
+        op_.clear();
+    }
+}
+
+string KeyMintAidlTestBase::ProcessMessage(const vector<uint8_t>& key_blob, KeyPurpose operation,
+                                           const string& message, const AuthorizationSet& in_params,
+                                           AuthorizationSet* out_params) {
+    SCOPED_TRACE("ProcessMessage");
+    AuthorizationSet begin_out_params;
+    ErrorCode result = Begin(operation, key_blob, in_params, &begin_out_params);
+    EXPECT_EQ(ErrorCode::OK, result);
+    if (result != ErrorCode::OK) {
+        return "";
+    }
+
+    string output;
+    int32_t consumed = 0;
+    AuthorizationSet update_params;
+    AuthorizationSet update_out_params;
+    result = Update(update_params, message, &update_out_params, &output, &consumed);
+    EXPECT_EQ(ErrorCode::OK, result);
+    if (result != ErrorCode::OK) {
+        return "";
+    }
+
+    string unused;
+    AuthorizationSet finish_params;
+    AuthorizationSet finish_out_params;
+    EXPECT_EQ(ErrorCode::OK,
+              Finish(finish_params, message.substr(consumed), unused, &finish_out_params, &output));
+
+    out_params->push_back(begin_out_params);
+    out_params->push_back(finish_out_params);
+    return output;
+}
+
+string KeyMintAidlTestBase::SignMessage(const vector<uint8_t>& key_blob, const string& message,
+                                        const AuthorizationSet& params) {
+    SCOPED_TRACE("SignMessage");
+    AuthorizationSet out_params;
+    string signature = ProcessMessage(key_blob, KeyPurpose::SIGN, message, params, &out_params);
+    EXPECT_TRUE(out_params.empty());
+    return signature;
+}
+
+string KeyMintAidlTestBase::SignMessage(const string& message, const AuthorizationSet& params) {
+    SCOPED_TRACE("SignMessage");
+    return SignMessage(key_blob_, message, params);
+}
+
+string KeyMintAidlTestBase::MacMessage(const string& message, Digest digest, size_t mac_length) {
+    SCOPED_TRACE("MacMessage");
+    return SignMessage(
+            key_blob_, message,
+            AuthorizationSetBuilder().Digest(digest).Authorization(TAG_MAC_LENGTH, mac_length));
+}
+
+void KeyMintAidlTestBase::CheckHmacTestVector(const string& key, const string& message,
+                                              Digest digest, const string& expected_mac) {
+    SCOPED_TRACE("CheckHmacTestVector");
+    ASSERT_EQ(ErrorCode::OK,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .HmacKey(key.size() * 8)
+                                .Authorization(TAG_MIN_MAC_LENGTH, expected_mac.size() * 8)
+                                .Digest(digest),
+                        KeyFormat::RAW, key));
+    string signature = MacMessage(message, digest, expected_mac.size() * 8);
+    EXPECT_EQ(expected_mac, signature)
+            << "Test vector didn't match for key of size " << key.size() << " message of size "
+            << message.size() << " and digest " << digest;
+    CheckedDeleteKey();
+}
+
+void KeyMintAidlTestBase::CheckAesCtrTestVector(const string& key, const string& nonce,
+                                                const string& message,
+                                                const string& expected_ciphertext) {
+    SCOPED_TRACE("CheckAesCtrTestVector");
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .AesEncryptionKey(key.size() * 8)
+                                               .BlockMode(BlockMode::CTR)
+                                               .Authorization(TAG_CALLER_NONCE)
+                                               .Padding(PaddingMode::NONE),
+                                       KeyFormat::RAW, key));
+
+    auto params = AuthorizationSetBuilder()
+                          .Authorization(TAG_NONCE, nonce.data(), nonce.size())
+                          .BlockMode(BlockMode::CTR)
+                          .Padding(PaddingMode::NONE);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(key_blob_, message, params, &out_params);
+    EXPECT_EQ(expected_ciphertext, ciphertext);
+}
+
+void KeyMintAidlTestBase::CheckTripleDesTestVector(KeyPurpose purpose, BlockMode block_mode,
+                                                   PaddingMode padding_mode, const string& key,
+                                                   const string& iv, const string& input,
+                                                   const string& expected_output) {
+    auto authset = AuthorizationSetBuilder()
+                           .TripleDesEncryptionKey(key.size() * 7)
+                           .BlockMode(block_mode)
+                           .Authorization(TAG_NO_AUTH_REQUIRED)
+                           .Padding(padding_mode);
+    if (iv.size()) authset.Authorization(TAG_CALLER_NONCE);
+    ASSERT_EQ(ErrorCode::OK, ImportKey(authset, KeyFormat::RAW, key));
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    auto begin_params = AuthorizationSetBuilder().BlockMode(block_mode).Padding(padding_mode);
+    if (iv.size()) begin_params.Authorization(TAG_NONCE, iv.data(), iv.size());
+    AuthorizationSet output_params;
+    string output = ProcessMessage(key_blob_, purpose, input, begin_params, &output_params);
+    EXPECT_EQ(expected_output, output);
+}
+
+void KeyMintAidlTestBase::VerifyMessage(const vector<uint8_t>& key_blob, const string& message,
+                                        const string& signature, const AuthorizationSet& params) {
+    SCOPED_TRACE("VerifyMessage");
+    AuthorizationSet begin_out_params;
+    ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::VERIFY, key_blob, params, &begin_out_params));
+
+    string output;
+    AuthorizationSet update_params;
+    AuthorizationSet update_out_params;
+    int32_t consumed;
+    ASSERT_EQ(ErrorCode::OK,
+              Update(update_params, message, &update_out_params, &output, &consumed));
+    EXPECT_TRUE(output.empty());
+    EXPECT_GT(consumed, 0U);
+
+    string unused;
+    AuthorizationSet finish_params;
+    AuthorizationSet finish_out_params;
+    EXPECT_EQ(ErrorCode::OK, Finish(finish_params, message.substr(consumed), signature,
+                                    &finish_out_params, &output));
+    op_.clear();
+    EXPECT_TRUE(output.empty());
+}
+
+void KeyMintAidlTestBase::VerifyMessage(const string& message, const string& signature,
+                                        const AuthorizationSet& params) {
+    SCOPED_TRACE("VerifyMessage");
+    VerifyMessage(key_blob_, message, signature, params);
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const vector<uint8_t>& key_blob, const string& message,
+                                           const AuthorizationSet& in_params,
+                                           AuthorizationSet* out_params) {
+    SCOPED_TRACE("EncryptMessage");
+    return ProcessMessage(key_blob, KeyPurpose::ENCRYPT, message, in_params, out_params);
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const string& message, const AuthorizationSet& params,
+                                           AuthorizationSet* out_params) {
+    SCOPED_TRACE("EncryptMessage");
+    return EncryptMessage(key_blob_, message, params, out_params);
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const string& message, const AuthorizationSet& params) {
+    SCOPED_TRACE("EncryptMessage");
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    EXPECT_TRUE(out_params.empty()) << "Output params should be empty. Contained: " << out_params;
+    return ciphertext;
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const string& message, BlockMode block_mode,
+                                           PaddingMode padding) {
+    SCOPED_TRACE("EncryptMessage");
+    auto params = AuthorizationSetBuilder().BlockMode(block_mode).Padding(padding);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    EXPECT_TRUE(out_params.empty()) << "Output params should be empty. Contained: " << out_params;
+    return ciphertext;
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const string& message, BlockMode block_mode,
+                                           PaddingMode padding, vector<uint8_t>* iv_out) {
+    SCOPED_TRACE("EncryptMessage");
+    auto params = AuthorizationSetBuilder().BlockMode(block_mode).Padding(padding);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    EXPECT_EQ(1U, out_params.size());
+    auto ivVal = out_params.GetTagValue(TAG_NONCE);
+    EXPECT_TRUE(ivVal.isOk());
+    if (ivVal.isOk()) *iv_out = ivVal.value();
+    return ciphertext;
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const string& message, BlockMode block_mode,
+                                           PaddingMode padding, const vector<uint8_t>& iv_in) {
+    SCOPED_TRACE("EncryptMessage");
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(block_mode)
+                          .Padding(padding)
+                          .Authorization(TAG_NONCE, iv_in);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    return ciphertext;
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const string& message, BlockMode block_mode,
+                                           PaddingMode padding, uint8_t mac_length_bits,
+                                           const vector<uint8_t>& iv_in) {
+    SCOPED_TRACE("EncryptMessage");
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(block_mode)
+                          .Padding(padding)
+                          .Authorization(TAG_MAC_LENGTH, mac_length_bits)
+                          .Authorization(TAG_NONCE, iv_in);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    return ciphertext;
+}
+
+string KeyMintAidlTestBase::DecryptMessage(const vector<uint8_t>& key_blob,
+                                           const string& ciphertext,
+                                           const AuthorizationSet& params) {
+    SCOPED_TRACE("DecryptMessage");
+    AuthorizationSet out_params;
+    string plaintext =
+            ProcessMessage(key_blob, KeyPurpose::DECRYPT, ciphertext, params, &out_params);
+    EXPECT_TRUE(out_params.empty());
+    return plaintext;
+}
+
+string KeyMintAidlTestBase::DecryptMessage(const string& ciphertext,
+                                           const AuthorizationSet& params) {
+    SCOPED_TRACE("DecryptMessage");
+    return DecryptMessage(key_blob_, ciphertext, params);
+}
+
+string KeyMintAidlTestBase::DecryptMessage(const string& ciphertext, BlockMode block_mode,
+                                           PaddingMode padding_mode, const vector<uint8_t>& iv) {
+    SCOPED_TRACE("DecryptMessage");
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(block_mode)
+                          .Padding(padding_mode)
+                          .Authorization(TAG_NONCE, iv);
+    return DecryptMessage(key_blob_, ciphertext, params);
+}
+
+std::pair<ErrorCode, vector<uint8_t>> KeyMintAidlTestBase::UpgradeKey(
+        const vector<uint8_t>& key_blob) {
+    std::pair<ErrorCode, vector<uint8_t>> retval;
+    vector<uint8_t> outKeyBlob;
+    Status result = keymint_->upgradeKey(key_blob, vector<KeyParameter>(), &outKeyBlob);
+    ErrorCode errorcode = GetReturnErrorCode(result);
+    retval = std::tie(errorcode, outKeyBlob);
+
+    return retval;
+}
+vector<uint32_t> KeyMintAidlTestBase::ValidKeySizes(Algorithm algorithm) {
+    switch (algorithm) {
+        case Algorithm::RSA:
+            switch (SecLevel()) {
+                case SecurityLevel::SOFTWARE:
+                case SecurityLevel::TRUSTED_ENVIRONMENT:
+                    return {2048, 3072, 4096};
+                case SecurityLevel::STRONGBOX:
+                    return {2048};
+                default:
+                    ADD_FAILURE() << "Invalid security level " << uint32_t(SecLevel());
+                    break;
+            }
+            break;
+        case Algorithm::EC:
+            switch (SecLevel()) {
+                case SecurityLevel::SOFTWARE:
+                case SecurityLevel::TRUSTED_ENVIRONMENT:
+                    return {224, 256, 384, 521};
+                case SecurityLevel::STRONGBOX:
+                    return {256};
+                default:
+                    ADD_FAILURE() << "Invalid security level " << uint32_t(SecLevel());
+                    break;
+            }
+            break;
+        case Algorithm::AES:
+            return {128, 256};
+        case Algorithm::TRIPLE_DES:
+            return {168};
+        case Algorithm::HMAC: {
+            vector<uint32_t> retval((512 - 64) / 8 + 1);
+            uint32_t size = 64 - 8;
+            std::generate(retval.begin(), retval.end(), [&]() { return (size += 8); });
+            return retval;
+        }
+        default:
+            ADD_FAILURE() << "Invalid Algorithm: " << algorithm;
+            return {};
+    }
+    ADD_FAILURE() << "Should be impossible to get here";
+    return {};
+}
+
+vector<uint32_t> KeyMintAidlTestBase::InvalidKeySizes(Algorithm algorithm) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        switch (algorithm) {
+            case Algorithm::RSA:
+                return {3072, 4096};
+            case Algorithm::EC:
+                return {224, 384, 521};
+            case Algorithm::AES:
+                return {192};
+            default:
+                return {};
+        }
+    }
+    return {};
+}
+
+vector<EcCurve> KeyMintAidlTestBase::ValidCurves() {
+    if (securityLevel_ == SecurityLevel::STRONGBOX) {
+        return {EcCurve::P_256};
+    } else {
+        return {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521};
+    }
+}
+
+vector<EcCurve> KeyMintAidlTestBase::InvalidCurves() {
+    if (SecLevel() == SecurityLevel::TRUSTED_ENVIRONMENT) return {};
+    CHECK(SecLevel() == SecurityLevel::STRONGBOX);
+    return {EcCurve::P_224, EcCurve::P_384, EcCurve::P_521};
+}
+
+vector<Digest> KeyMintAidlTestBase::ValidDigests(bool withNone, bool withMD5) {
+    switch (SecLevel()) {
+        case SecurityLevel::SOFTWARE:
+        case SecurityLevel::TRUSTED_ENVIRONMENT:
+            if (withNone) {
+                if (withMD5)
+                    return {Digest::NONE,      Digest::MD5,       Digest::SHA1,
+                            Digest::SHA_2_224, Digest::SHA_2_256, Digest::SHA_2_384,
+                            Digest::SHA_2_512};
+                else
+                    return {Digest::NONE,      Digest::SHA1,      Digest::SHA_2_224,
+                            Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512};
+            } else {
+                if (withMD5)
+                    return {Digest::MD5,       Digest::SHA1,      Digest::SHA_2_224,
+                            Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512};
+                else
+                    return {Digest::SHA1, Digest::SHA_2_224, Digest::SHA_2_256, Digest::SHA_2_384,
+                            Digest::SHA_2_512};
+            }
+            break;
+        case SecurityLevel::STRONGBOX:
+            if (withNone)
+                return {Digest::NONE, Digest::SHA_2_256};
+            else
+                return {Digest::SHA_2_256};
+            break;
+        default:
+            ADD_FAILURE() << "Invalid security level " << uint32_t(SecLevel());
+            break;
+    }
+    ADD_FAILURE() << "Should be impossible to get here";
+    return {};
+}
+
+}  // namespace test
+
+}  // namespace android::hardware::security::keymint
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
new file mode 100644
index 0000000..76effcf
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VTS_KEYMINT_AIDL_TEST_UTILS_H
+#define VTS_KEYMINT_AIDL_TEST_UTILS_H
+
+#pragma once
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android/hardware/security/keymint/ErrorCode.h>
+#include <android/hardware/security/keymint/IKeyMintDevice.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+
+#include <keymint_support/authorization_set.h>
+
+namespace android::hardware::security::keymint::test {
+
+using ::android::sp;
+using binder::Status;
+using ::std::shared_ptr;
+using ::std::string;
+using ::std::vector;
+
+constexpr uint64_t kOpHandleSentinel = 0xFFFFFFFFFFFFFFFF;
+
+::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set);
+
+class KeyMintAidlTestBase : public ::testing::TestWithParam<string> {
+  public:
+    void SetUp() override;
+    void TearDown() override {
+        if (key_blob_.size()) {
+            CheckedDeleteKey();
+        }
+        AbortIfNeeded();
+    }
+
+    void InitializeKeyMint(sp<IKeyMintDevice> keyMint);
+    IKeyMintDevice& keyMint() { return *keymint_; }
+    uint32_t os_version() { return os_version_; }
+    uint32_t os_patch_level() { return os_patch_level_; }
+
+    ErrorCode GetReturnErrorCode(Status result);
+    ErrorCode GenerateKey(const AuthorizationSet& key_desc, vector<uint8_t>* key_blob,
+                          KeyCharacteristics* key_characteristics);
+
+    ErrorCode GenerateKey(const AuthorizationSet& key_desc);
+
+    ErrorCode ImportKey(const AuthorizationSet& key_desc, KeyFormat format,
+                        const string& key_material, vector<uint8_t>* key_blob,
+                        KeyCharacteristics* key_characteristics);
+    ErrorCode ImportKey(const AuthorizationSet& key_desc, KeyFormat format,
+                        const string& key_material);
+
+    ErrorCode ImportWrappedKey(string wrapped_key, string wrapping_key,
+                               const AuthorizationSet& wrapping_key_desc, string masking_key,
+                               const AuthorizationSet& unwrapping_params);
+
+    ErrorCode DeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob = false);
+    ErrorCode DeleteKey(bool keep_key_blob = false);
+
+    ErrorCode DeleteAllKeys();
+
+    void CheckedDeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob = false);
+    void CheckedDeleteKey();
+
+    ErrorCode Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob,
+                    const AuthorizationSet& in_params, AuthorizationSet* out_params,
+                    sp<IKeyMintOperation>& op);
+    ErrorCode Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob,
+                    const AuthorizationSet& in_params, AuthorizationSet* out_params);
+    ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params,
+                    AuthorizationSet* out_params);
+    ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params);
+
+    ErrorCode Update(const AuthorizationSet& in_params, const string& input,
+                     AuthorizationSet* out_params, string* output, int32_t* input_consumed);
+    ErrorCode Update(const string& input, string* out, int32_t* input_consumed);
+
+    ErrorCode Finish(const AuthorizationSet& in_params, const string& input,
+                     const string& signature, AuthorizationSet* out_params, string* output);
+    ErrorCode Finish(const string& message, string* output);
+    ErrorCode Finish(const string& message, const string& signature, string* output);
+    ErrorCode Finish(string* output) { return Finish(string(), output); }
+
+    ErrorCode Abort();
+    ErrorCode Abort(const sp<IKeyMintOperation>& op);
+    void AbortIfNeeded();
+
+    string ProcessMessage(const vector<uint8_t>& key_blob, KeyPurpose operation,
+                          const string& message, const AuthorizationSet& in_params,
+                          AuthorizationSet* out_params);
+
+    string SignMessage(const vector<uint8_t>& key_blob, const string& message,
+                       const AuthorizationSet& params);
+    string SignMessage(const string& message, const AuthorizationSet& params);
+
+    string MacMessage(const string& message, Digest digest, size_t mac_length);
+
+    void CheckHmacTestVector(const string& key, const string& message, Digest digest,
+                             const string& expected_mac);
+
+    void CheckAesCtrTestVector(const string& key, const string& nonce, const string& message,
+                               const string& expected_ciphertext);
+
+    void CheckTripleDesTestVector(KeyPurpose purpose, BlockMode block_mode,
+                                  PaddingMode padding_mode, const string& key, const string& iv,
+                                  const string& input, const string& expected_output);
+
+    void VerifyMessage(const vector<uint8_t>& key_blob, const string& message,
+                       const string& signature, const AuthorizationSet& params);
+    void VerifyMessage(const string& message, const string& signature,
+                       const AuthorizationSet& params);
+
+    string EncryptMessage(const vector<uint8_t>& key_blob, const string& message,
+                          const AuthorizationSet& in_params, AuthorizationSet* out_params);
+    string EncryptMessage(const string& message, const AuthorizationSet& params,
+                          AuthorizationSet* out_params);
+    string EncryptMessage(const string& message, const AuthorizationSet& params);
+    string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding);
+    string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding,
+                          vector<uint8_t>* iv_out);
+    string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding,
+                          const vector<uint8_t>& iv_in);
+    string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding,
+                          uint8_t mac_length_bits, const vector<uint8_t>& iv_in);
+
+    string DecryptMessage(const vector<uint8_t>& key_blob, const string& ciphertext,
+                          const AuthorizationSet& params);
+    string DecryptMessage(const string& ciphertext, const AuthorizationSet& params);
+    string DecryptMessage(const string& ciphertext, BlockMode block_mode, PaddingMode padding_mode,
+                          const vector<uint8_t>& iv);
+
+    std::pair<ErrorCode, vector<uint8_t>> UpgradeKey(const vector<uint8_t>& key_blob);
+
+    bool IsSecure() { return securityLevel_ != SecurityLevel::SOFTWARE; }
+    SecurityLevel SecLevel() { return securityLevel_; }
+
+    vector<uint32_t> ValidKeySizes(Algorithm algorithm);
+    vector<uint32_t> InvalidKeySizes(Algorithm algorithm);
+
+    vector<EcCurve> ValidCurves();
+    vector<EcCurve> InvalidCurves();
+
+    vector<Digest> ValidDigests(bool withNone, bool withMD5);
+
+    static vector<string> build_params() {
+        auto params = android::getAidlHalInstanceNames(IKeyMintDevice::descriptor);
+        return params;
+    }
+
+    sp<IKeyMintOperation> op_;
+    vector<Certificate> certChain_;
+    vector<uint8_t> key_blob_;
+    KeyCharacteristics key_characteristics_;
+
+  private:
+    sp<IKeyMintDevice> keymint_;
+    uint32_t os_version_;
+    uint32_t os_patch_level_;
+
+    SecurityLevel securityLevel_;
+    string name_;
+    string author_;
+    long challenge_;
+};
+
+#define INSTANTIATE_KEYMINT_AIDL_TEST(name)                                          \
+    INSTANTIATE_TEST_SUITE_P(PerInstance, name,                                      \
+                             testing::ValuesIn(KeyMintAidlTestBase::build_params()), \
+                             android::PrintInstanceNameToString)
+
+}  // namespace android::hardware::security::keymint::test
+
+#endif  // VTS_KEYMINT_AIDL_TEST_UTILS_H
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
new file mode 100644
index 0000000..f9423a2
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -0,0 +1,4058 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "keymint_5_test"
+#include <cutils/log.h>
+
+#include <signal.h>
+#include <iostream>
+
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/x509.h>
+
+#include <cutils/properties.h>
+
+#include <android/hardware/security/keymint/KeyFormat.h>
+
+#include <keymint_support/attestation_record.h>
+#include <keymint_support/key_param_output.h>
+#include <keymint_support/openssl_utils.h>
+
+#include "KeyMintAidlTestBase.h"
+
+static bool arm_deleteAllKeys = false;
+static bool dump_Attestations = false;
+
+using android::hardware::security::keymint::AuthorizationSet;
+using android::hardware::security::keymint::KeyCharacteristics;
+using android::hardware::security::keymint::KeyFormat;
+
+namespace android::hardware::security::keymint {
+
+bool operator==(const keymint::AuthorizationSet& a, const keymint::AuthorizationSet& b) {
+    return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
+}
+
+}  // namespace android::hardware::security::keymint
+
+namespace std {
+
+using namespace android::hardware::security::keymint;
+
+template <>
+struct std::equal_to<KeyCharacteristics> {
+    bool operator()(const KeyCharacteristics& a, const KeyCharacteristics& b) const {
+        // This isn't very efficient. Oh, well.
+        AuthorizationSet a_sw(a.softwareEnforced);
+        AuthorizationSet b_sw(b.softwareEnforced);
+        AuthorizationSet a_tee(b.hardwareEnforced);
+        AuthorizationSet b_tee(b.hardwareEnforced);
+
+        a_sw.Sort();
+        b_sw.Sort();
+        a_tee.Sort();
+        b_tee.Sort();
+
+        return ((a_sw == b_sw) && (a_tee == b_tee));
+    }
+};
+
+}  // namespace std
+
+namespace android::hardware::security::keymint::test {
+
+namespace {
+
+template <TagType tag_type, Tag tag, typename ValueT>
+bool contains(vector<KeyParameter>& set, TypedTag<tag_type, tag> ttag, ValueT expected_value) {
+    auto it = std::find_if(set.begin(), set.end(), [&](const KeyParameter& param) {
+        return param.tag == tag && accessTagValue(ttag, param) == expected_value;
+    });
+    return (it != set.end());
+}
+
+template <TagType tag_type, Tag tag>
+bool contains(vector<KeyParameter>& set, TypedTag<tag_type, tag>) {
+    auto it = std::find_if(set.begin(), set.end(),
+                           [&](const KeyParameter& param) { return param.tag == tag; });
+    return (it != set.end());
+}
+
+constexpr char hex_value[256] = {0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 1,  2,  3,  4,  5,  6,  7, 8, 9, 0, 0, 0, 0, 0, 0,  // '0'..'9'
+                                 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 'A'..'F'
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 'a'..'f'
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+string hex2str(string a) {
+    string b;
+    size_t num = a.size() / 2;
+    b.resize(num);
+    for (size_t i = 0; i < num; i++) {
+        b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]);
+    }
+    return b;
+}
+
+string rsa_key =
+        hex2str("30820275020100300d06092a864886f70d01010105000482025f3082025b"
+                "02010002818100c6095409047d8634812d5a218176e45c41d60a75b13901"
+                "f234226cffe776521c5a77b9e389417b71c0b6a44d13afe4e4a2805d46c9"
+                "da2935adb1ff0c1f24ea06e62b20d776430a4d435157233c6f916783c30e"
+                "310fcbd89b85c2d56771169785ac12bca244abda72bfb19fc44d27c81e1d"
+                "92de284f4061edfd99280745ea6d2502030100010281801be0f04d9cae37"
+                "18691f035338308e91564b55899ffb5084d2460e6630257e05b3ceab0297"
+                "2dfabcd6ce5f6ee2589eb67911ed0fac16e43a444b8c861e544a05933657"
+                "72f8baf6b22fc9e3c5f1024b063ac080a7b2234cf8aee8f6c47bbf4fd3ac"
+                "e7240290bef16c0b3f7f3cdd64ce3ab5912cf6e32f39ab188358afcccd80"
+                "81024100e4b49ef50f765d3b24dde01aceaaf130f2c76670a91a61ae08af"
+                "497b4a82be6dee8fcdd5e3f7ba1cfb1f0c926b88f88c92bfab137fba2285"
+                "227b83c342ff7c55024100ddabb5839c4c7f6bf3d4183231f005b31aa58a"
+                "ffdda5c79e4cce217f6bc930dbe563d480706c24e9ebfcab28a6cdefd324"
+                "b77e1bf7251b709092c24ff501fd91024023d4340eda3445d8cd26c14411"
+                "da6fdca63c1ccd4b80a98ad52b78cc8ad8beb2842c1d280405bc2f6c1bea"
+                "214a1d742ab996b35b63a82a5e470fa88dbf823cdd02401b7b57449ad30d"
+                "1518249a5f56bb98294d4b6ac12ffc86940497a5a5837a6cf946262b4945"
+                "26d328c11e1126380fde04c24f916dec250892db09a6d77cdba351024077"
+                "62cd8f4d050da56bd591adb515d24d7ccd32cca0d05f866d583514bd7324"
+                "d5f33645e8ed8b4a1cb3cc4a1d67987399f2a09f5b3fb68c88d5e5d90ac3"
+                "3492d6");
+
+string ec_256_key =
+        hex2str("308187020100301306072a8648ce3d020106082a8648ce3d030107046d30"
+                "6b0201010420737c2ecd7b8d1940bf2930aa9b4ed3ff941eed09366bc032"
+                "99986481f3a4d859a14403420004bf85d7720d07c25461683bc648b4778a"
+                "9a14dd8a024e3bdd8c7ddd9ab2b528bbc7aa1b51f14ebbbb0bd0ce21bcc4"
+                "1c6eb00083cf3376d11fd44949e0b2183bfe");
+
+string ec_521_key =
+        hex2str("3081EE020100301006072A8648CE3D020106052B810400230481D63081D3"
+                "02010104420011458C586DB5DAA92AFAB03F4FE46AA9D9C3CE9A9B7A006A"
+                "8384BEC4C78E8E9D18D7D08B5BCFA0E53C75B064AD51C449BAE0258D54B9"
+                "4B1E885DED08ED4FB25CE9A1818903818600040149EC11C6DF0FA122C6A9"
+                "AFD9754A4FA9513A627CA329E349535A5629875A8ADFBE27DCB932C05198"
+                "6377108D054C28C6F39B6F2C9AF81802F9F326B842FF2E5F3C00AB7635CF"
+                "B36157FC0882D574A10D839C1A0C049DC5E0D775E2EE50671A208431BB45"
+                "E78E70BEFE930DB34818EE4D5C26259F5C6B8E28A652950F9F88D7B4B2C9"
+                "D9");
+
+string ec_256_key_rfc5915 =
+        hex2str("308193020100301306072a8648ce3d020106082a8648ce3d030107047930"
+                "770201010420782370a8c8ce5537baadd04dcff079c8158cfa9c67b818b3"
+                "8e8d21c9fa750c1da00a06082a8648ce3d030107a14403420004e2cc561e"
+                "e701da0ad0ef0d176bb0c919d42e79c393fdc1bd6c4010d85cf2cf8e68c9"
+                "05464666f98dad4f01573ba81078b3428570a439ba3229fbc026c550682f");
+
+string ec_256_key_sec1 =
+        hex2str("308187020100301306072a8648ce3d020106082a8648ce3d030107046d30"
+                "6b0201010420782370a8c8ce5537baadd04dcff079c8158cfa9c67b818b3"
+                "8e8d21c9fa750c1da14403420004e2cc561ee701da0ad0ef0d176bb0c919"
+                "d42e79c393fdc1bd6c4010d85cf2cf8e68c905464666f98dad4f01573ba8"
+                "1078b3428570a439ba3229fbc026c550682f");
+
+struct RSA_Delete {
+    void operator()(RSA* p) { RSA_free(p); }
+};
+
+/* TODO(seleneh) add attestation verification codes like verify_chain() and
+ * attestation tests after we decided on the keymint 1 attestation changes.
+ */
+
+std::string make_string(const uint8_t* data, size_t length) {
+    return std::string(reinterpret_cast<const char*>(data), length);
+}
+
+template <size_t N>
+std::string make_string(const uint8_t (&a)[N]) {
+    return make_string(a, N);
+}
+
+class AidlBuf : public vector<uint8_t> {
+    typedef vector<uint8_t> super;
+
+  public:
+    AidlBuf() {}
+    AidlBuf(const super& other) : super(other) {}
+    AidlBuf(super&& other) : super(std::move(other)) {}
+    explicit AidlBuf(const std::string& other) : AidlBuf() { *this = other; }
+
+    AidlBuf& operator=(const super& other) {
+        super::operator=(other);
+        return *this;
+    }
+
+    AidlBuf& operator=(super&& other) {
+        super::operator=(std::move(other));
+        return *this;
+    }
+
+    AidlBuf& operator=(const string& other) {
+        resize(other.size());
+        for (size_t i = 0; i < other.size(); ++i) {
+            (*this)[i] = static_cast<uint8_t>(other[i]);
+        }
+        return *this;
+    }
+
+    string to_string() const { return string(reinterpret_cast<const char*>(data()), size()); }
+};
+
+}  // namespace
+
+class NewKeyGenerationTest : public KeyMintAidlTestBase {
+  protected:
+    void CheckBaseParams(const KeyCharacteristics& keyCharacteristics) {
+        // TODO(swillden): Distinguish which params should be in which auth list.
+
+        AuthorizationSet auths(keyCharacteristics.hardwareEnforced);
+        auths.push_back(AuthorizationSet(keyCharacteristics.softwareEnforced));
+
+        EXPECT_TRUE(auths.Contains(TAG_ORIGIN, KeyOrigin::GENERATED));
+        EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
+        EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY));
+
+        // Verify that App ID, App data and ROT are NOT included.
+        EXPECT_FALSE(auths.Contains(TAG_ROOT_OF_TRUST));
+        EXPECT_FALSE(auths.Contains(TAG_APPLICATION_ID));
+        EXPECT_FALSE(auths.Contains(TAG_APPLICATION_DATA));
+
+        // Check that some unexpected tags/values are NOT present.
+        EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::ENCRYPT));
+        EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));
+        EXPECT_FALSE(auths.Contains(TAG_AUTH_TIMEOUT, 301U));
+
+        // Now check that unspecified, defaulted tags are correct.
+        EXPECT_TRUE(auths.Contains(TAG_CREATION_DATETIME));
+
+        EXPECT_TRUE(auths.Contains(TAG_OS_VERSION, os_version()))
+                << "OS version is " << os_version() << " key reported "
+                << auths.GetTagValue(TAG_OS_VERSION);
+        EXPECT_TRUE(auths.Contains(TAG_OS_PATCHLEVEL, os_patch_level()))
+                << "OS patch level is " << os_patch_level() << " key reported "
+                << auths.GetTagValue(TAG_OS_PATCHLEVEL);
+    }
+};
+
+/*
+ * NewKeyGenerationTest.Rsa
+ *
+ * Verifies that keymint can generate all required RSA key sizes, and that the resulting keys
+ * have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, Rsa) {
+    for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        vector<uint8_t> key_blob;
+        KeyCharacteristics key_characteristics;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .RsaSigningKey(key_size, 65537)
+                                                     .Digest(Digest::NONE)
+                                                     .Padding(PaddingMode::NONE),
+                                             &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+
+        AuthorizationSet crypto_params;
+        if (IsSecure()) {
+            crypto_params = key_characteristics.hardwareEnforced;
+        } else {
+            crypto_params = key_characteristics.softwareEnforced;
+        }
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "missing";
+        EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.NoInvalidRsaSizes
+ *
+ * Verifies that keymint cannot generate any RSA key sizes that are designated as invalid.
+ */
+TEST_P(NewKeyGenerationTest, NoInvalidRsaSizes) {
+    for (auto key_size : InvalidKeySizes(Algorithm::RSA)) {
+        vector<uint8_t> key_blob;
+        KeyCharacteristics key_characteristics;
+        ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(key_size, 65537)
+                                      .Digest(Digest::NONE)
+                                      .Padding(PaddingMode::NONE),
+                              &key_blob, &key_characteristics));
+    }
+}
+
+/*
+ * NewKeyGenerationTest.RsaNoDefaultSize
+ *
+ * Verifies that failing to specify a key size for RSA key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, RsaNoDefaultSize) {
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .Authorization(TAG_ALGORITHM, Algorithm::RSA)
+                                  .Authorization(TAG_RSA_PUBLIC_EXPONENT, 3U)
+                                  .SigningKey()));
+}
+
+/*
+ * NewKeyGenerationTest.Ecdsa
+ *
+ * Verifies that keymint can generate all required EC key sizes, and that the resulting keys
+ * have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, Ecdsa) {
+    for (auto key_size : ValidKeySizes(Algorithm::EC)) {
+        vector<uint8_t> key_blob;
+        KeyCharacteristics key_characteristics;
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(
+                          AuthorizationSetBuilder().EcdsaSigningKey(key_size).Digest(Digest::NONE),
+                          &key_blob, &key_characteristics));
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+
+        AuthorizationSet crypto_params;
+        if (IsSecure()) {
+            crypto_params = key_characteristics.hardwareEnforced;
+        } else {
+            crypto_params = key_characteristics.softwareEnforced;
+        }
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "missing";
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaDefaultSize
+ *
+ * Verifies that failing to specify a key size for EC key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaDefaultSize) {
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .Authorization(TAG_ALGORITHM, Algorithm::EC)
+                                  .SigningKey()
+                                  .Digest(Digest::NONE)));
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaInvalidSize
+ *
+ * Verifies that specifying an invalid key size for EC key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaInvalidSize) {
+    for (auto key_size : InvalidKeySizes(Algorithm::EC)) {
+        vector<uint8_t> key_blob;
+        KeyCharacteristics key_characteristics;
+        ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                  GenerateKey(
+                          AuthorizationSetBuilder().EcdsaSigningKey(key_size).Digest(Digest::NONE),
+                          &key_blob, &key_characteristics));
+    }
+
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(190).Digest(Digest::NONE)));
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaMismatchKeySize
+ *
+ * Verifies that specifying mismatched key size and curve for EC key generation returns
+ * INVALID_ARGUMENT.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaMismatchKeySize) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::INVALID_ARGUMENT,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .EcdsaSigningKey(224)
+                                  .Authorization(TAG_EC_CURVE, EcCurve::P_256)
+                                  .Digest(Digest::NONE)));
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaAllValidSizes
+ *
+ * Verifies that keymint supports all required EC key sizes.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaAllValidSizes) {
+    auto valid_sizes = ValidKeySizes(Algorithm::EC);
+    for (size_t size : valid_sizes) {
+        EXPECT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(size).Digest(Digest::NONE)))
+                << "Failed to generate size: " << size;
+        CheckedDeleteKey();
+    }
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaInvalidCurves
+ *
+ * Verifies that keymint does not support any curve designated as unsupported.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaAllValidCurves) {
+    Digest digest;
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        digest = Digest::SHA_2_256;
+    } else {
+        digest = Digest::SHA_2_512;
+    }
+    for (auto curve : ValidCurves()) {
+        EXPECT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(curve).Digest(digest)))
+                << "Failed to generate key on curve: " << curve;
+        CheckedDeleteKey();
+    }
+}
+
+/*
+ * NewKeyGenerationTest.Hmac
+ *
+ * Verifies that keymint supports all required digests, and that the resulting keys have correct
+ * characteristics.
+ */
+TEST_P(NewKeyGenerationTest, Hmac) {
+    for (auto digest : ValidDigests(false /* withNone */, true /* withMD5 */)) {
+        vector<uint8_t> key_blob;
+        KeyCharacteristics key_characteristics;
+        constexpr size_t key_size = 128;
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(
+                          AuthorizationSetBuilder().HmacKey(key_size).Digest(digest).Authorization(
+                                  TAG_MIN_MAC_LENGTH, 128),
+                          &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+
+        AuthorizationSet hardwareEnforced = key_characteristics.hardwareEnforced;
+        AuthorizationSet softwareEnforced = key_characteristics.softwareEnforced;
+        if (IsSecure()) {
+            EXPECT_TRUE(hardwareEnforced.Contains(TAG_ALGORITHM, Algorithm::HMAC));
+            EXPECT_TRUE(hardwareEnforced.Contains(TAG_KEY_SIZE, key_size))
+                    << "Key size " << key_size << "missing";
+        } else {
+            EXPECT_TRUE(softwareEnforced.Contains(TAG_ALGORITHM, Algorithm::HMAC));
+            EXPECT_TRUE(softwareEnforced.Contains(TAG_KEY_SIZE, key_size))
+                    << "Key size " << key_size << "missing";
+        }
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.HmacCheckKeySizes
+ *
+ * Verifies that keymint supports all key sizes, and rejects all invalid key sizes.
+ */
+TEST_P(NewKeyGenerationTest, HmacCheckKeySizes) {
+    for (size_t key_size = 0; key_size <= 512; ++key_size) {
+        if (key_size < 64 || key_size % 8 != 0) {
+            // To keep this test from being very slow, we only test a random fraction of
+            // non-byte key sizes.  We test only ~10% of such cases. Since there are 392 of
+            // them, we expect to run ~40 of them in each run.
+            if (key_size % 8 == 0 || random() % 10 == 0) {
+                EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                          GenerateKey(AuthorizationSetBuilder()
+                                              .HmacKey(key_size)
+                                              .Digest(Digest::SHA_2_256)
+                                              .Authorization(TAG_MIN_MAC_LENGTH, 256)))
+                        << "HMAC key size " << key_size << " invalid";
+            }
+        } else {
+            EXPECT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                         .HmacKey(key_size)
+                                                         .Digest(Digest::SHA_2_256)
+                                                         .Authorization(TAG_MIN_MAC_LENGTH, 256)))
+                    << "Failed to generate HMAC key of size " << key_size;
+            CheckedDeleteKey();
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.HmacCheckMinMacLengths
+ *
+ * Verifies that keymint supports all required MAC lengths and rejects all invalid lengths. This
+ * test is probabilistic in order to keep the runtime down, but any failure prints out the
+ * specific MAC length that failed, so reproducing a failed run will be easy.
+ */
+TEST_P(NewKeyGenerationTest, HmacCheckMinMacLengths) {
+    for (size_t min_mac_length = 0; min_mac_length <= 256; ++min_mac_length) {
+        if (min_mac_length < 64 || min_mac_length % 8 != 0) {
+            // To keep this test from being very long, we only test a random fraction of
+            // non-byte lengths.  We test only ~10% of such cases. Since there are 172 of them,
+            // we expect to run ~17 of them in each run.
+            if (min_mac_length % 8 == 0 || random() % 10 == 0) {
+                EXPECT_EQ(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH,
+                          GenerateKey(AuthorizationSetBuilder()
+                                              .HmacKey(128)
+                                              .Digest(Digest::SHA_2_256)
+                                              .Authorization(TAG_MIN_MAC_LENGTH, min_mac_length)))
+                        << "HMAC min mac length " << min_mac_length << " invalid.";
+            }
+        } else {
+            EXPECT_EQ(ErrorCode::OK,
+                      GenerateKey(AuthorizationSetBuilder()
+                                          .HmacKey(128)
+                                          .Digest(Digest::SHA_2_256)
+                                          .Authorization(TAG_MIN_MAC_LENGTH, min_mac_length)))
+                    << "Failed to generate HMAC key with min MAC length " << min_mac_length;
+            CheckedDeleteKey();
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.HmacMultipleDigests
+ *
+ * Verifies that keymint rejects HMAC key generation with multiple specified digest algorithms.
+ */
+TEST_P(NewKeyGenerationTest, HmacMultipleDigests) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .HmacKey(128)
+                                  .Digest(Digest::SHA1)
+                                  .Digest(Digest::SHA_2_256)
+                                  .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+}
+
+/*
+ * NewKeyGenerationTest.HmacDigestNone
+ *
+ * Verifies that keymint rejects HMAC key generation with no digest or Digest::NONE
+ */
+TEST_P(NewKeyGenerationTest, HmacDigestNone) {
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+              GenerateKey(AuthorizationSetBuilder().HmacKey(128).Authorization(TAG_MIN_MAC_LENGTH,
+                                                                               128)));
+
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .HmacKey(128)
+                                  .Digest(Digest::NONE)
+                                  .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(NewKeyGenerationTest);
+
+typedef KeyMintAidlTestBase SigningOperationsTest;
+
+/*
+ * SigningOperationsTest.RsaSuccess
+ *
+ * Verifies that raw RSA signature operations succeed.
+ */
+TEST_P(SigningOperationsTest, RsaSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)));
+    string message = "12345678901234567890123456789012";
+    string signature = SignMessage(
+            message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+}
+
+/*
+ * SigningOperationsTest.RsaUseRequiresCorrectAppIdAppData
+ *
+ * Verifies that using an RSA key requires the correct app ID/data.
+ */
+TEST_P(SigningOperationsTest, RsaUseRequiresCorrectAppIdAppData) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_APPLICATION_ID, "clientid")
+                                                 .Authorization(TAG_APPLICATION_DATA, "appdata")));
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              Begin(KeyPurpose::SIGN,
+                    AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
+    AbortIfNeeded();
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::NONE)
+                                              .Authorization(TAG_APPLICATION_ID, "clientid")));
+    AbortIfNeeded();
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::NONE)
+                                              .Authorization(TAG_APPLICATION_DATA, "appdata")));
+    AbortIfNeeded();
+    EXPECT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::NONE)
+                                              .Authorization(TAG_APPLICATION_DATA, "appdata")
+                                              .Authorization(TAG_APPLICATION_ID, "clientid")));
+    AbortIfNeeded();
+}
+
+/*
+ * SigningOperationsTest.RsaPssSha256Success
+ *
+ * Verifies that RSA-PSS signature operations succeed.
+ */
+TEST_P(SigningOperationsTest, RsaPssSha256Success) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Padding(PaddingMode::RSA_PSS)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)));
+    // Use large message, which won't work without digesting.
+    string message(1024, 'a');
+    string signature = SignMessage(
+            message,
+            AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS));
+}
+
+/*
+ * SigningOperationsTest.RsaPaddingNoneDoesNotAllowOther
+ *
+ * Verifies that keymint rejects signature operations that specify a padding mode when the key
+ * supports only unpadded operations.
+ */
+TEST_P(SigningOperationsTest, RsaPaddingNoneDoesNotAllowOther) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+    string message = "12345678901234567890123456789012";
+    string signature;
+
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+}
+
+/*
+ * SigningOperationsTest.NoUserConfirmation
+ *
+ * Verifies that keymint rejects signing operations for keys with
+ * TRUSTED_CONFIRMATION_REQUIRED and no valid confirmation token
+ * presented.
+ */
+TEST_P(SigningOperationsTest, NoUserConfirmation) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+    ASSERT_EQ(ErrorCode::OK,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .RsaSigningKey(1024, 65537)
+                                  .Digest(Digest::NONE)
+                                  .Padding(PaddingMode::NONE)
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED)));
+
+    const string message = "12345678901234567890123456789012";
+    EXPECT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN,
+                    AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
+    string signature;
+    EXPECT_EQ(ErrorCode::NO_USER_CONFIRMATION, Finish(message, &signature));
+}
+
+/*
+ * SigningOperationsTest.RsaPkcs1Sha256Success
+ *
+ * Verifies that digested RSA-PKCS1 signature operations succeed.
+ */
+TEST_P(SigningOperationsTest, RsaPkcs1Sha256Success) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+    string message(1024, 'a');
+    string signature = SignMessage(message, AuthorizationSetBuilder()
+                                                    .Digest(Digest::SHA_2_256)
+                                                    .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN));
+}
+
+/*
+ * SigningOperationsTest.RsaPkcs1NoDigestSuccess
+ *
+ * Verifies that undigested RSA-PKCS1 signature operations succeed.
+ */
+TEST_P(SigningOperationsTest, RsaPkcs1NoDigestSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+    string message(53, 'a');
+    string signature = SignMessage(message, AuthorizationSetBuilder()
+                                                    .Digest(Digest::NONE)
+                                                    .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN));
+}
+
+/*
+ * SigningOperationsTest.RsaPkcs1NoDigestTooLarge
+ *
+ * Verifies that undigested RSA-PKCS1 signature operations fail with the correct error code when
+ * given a too-long message.
+ */
+TEST_P(SigningOperationsTest, RsaPkcs1NoDigestTooLong) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+    string message(257, 'a');
+
+    EXPECT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+    string signature;
+    EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &signature));
+}
+
+/*
+ * SigningOperationsTest.RsaPssSha512TooSmallKey
+ *
+ * Verifies that undigested RSA-PSS signature operations fail with the correct error code when
+ * used with a key that is too small for the message.
+ *
+ * A PSS-padded message is of length salt_size + digest_size + 16 (sizes in bits), and the
+ * keymint specification requires that salt_size == digest_size, so the message will be
+ * digest_size * 2 +
+ * 16. Such a message can only be signed by a given key if the key is at least that size. This
+ * test uses SHA512, which has a digest_size == 512, so the message size is 1040 bits, too large
+ * for a 1024-bit key.
+ */
+TEST_P(SigningOperationsTest, RsaPssSha512TooSmallKey) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(1024, 65537)
+                                                 .Digest(Digest::SHA_2_512)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::RSA_PSS)));
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::SHA_2_512)
+                                              .Padding(PaddingMode::RSA_PSS)));
+}
+
+/*
+ * SigningOperationsTest.RsaNoPaddingTooLong
+ *
+ * Verifies that raw RSA signature operations fail with the correct error code when
+ * given a too-long message.
+ */
+TEST_P(SigningOperationsTest, RsaNoPaddingTooLong) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+    // One byte too long
+    string message(2048 / 8 + 1, 'a');
+    ASSERT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+    string result;
+    ErrorCode finish_error_code = Finish(message, &result);
+    EXPECT_TRUE(finish_error_code == ErrorCode::INVALID_INPUT_LENGTH ||
+                finish_error_code == ErrorCode::INVALID_ARGUMENT);
+
+    // Very large message that should exceed the transfer buffer size of any reasonable TEE.
+    message = string(128 * 1024, 'a');
+    ASSERT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+    finish_error_code = Finish(message, &result);
+    EXPECT_TRUE(finish_error_code == ErrorCode::INVALID_INPUT_LENGTH ||
+                finish_error_code == ErrorCode::INVALID_ARGUMENT);
+}
+
+/*
+ * SigningOperationsTest.RsaAbort
+ *
+ * Verifies that operations can be aborted correctly.  Uses an RSA signing operation for the
+ * test, but the behavior should be algorithm and purpose-independent.
+ */
+TEST_P(SigningOperationsTest, RsaAbort) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+
+    ASSERT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN,
+                    AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
+    EXPECT_EQ(ErrorCode::OK, Abort());
+
+    // Another abort should fail
+    EXPECT_EQ(ErrorCode::INVALID_OPERATION_HANDLE, Abort());
+
+    // Set to sentinel, so TearDown() doesn't try to abort again.
+    op_.clear();
+}
+
+/*
+ * SigningOperationsTest.RsaUnsupportedPadding
+ *
+ * Verifies that RSA operations fail with the correct error (but key gen succeeds) when used
+ * with a padding mode inappropriate for RSA.
+ */
+TEST_P(SigningOperationsTest, RsaUnsupportedPadding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Digest(Digest::SHA_2_256 /* supported digest */)
+                                                 .Padding(PaddingMode::PKCS7)));
+    ASSERT_EQ(
+            ErrorCode::UNSUPPORTED_PADDING_MODE,
+            Begin(KeyPurpose::SIGN,
+                  AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::PKCS7)));
+}
+
+/*
+ * SigningOperationsTest.RsaPssNoDigest
+ *
+ * Verifies that RSA PSS operations fail when no digest is used.  PSS requires a digest.
+ */
+TEST_P(SigningOperationsTest, RsaNoDigest) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::RSA_PSS)));
+    ASSERT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
+              Begin(KeyPurpose::SIGN,
+                    AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::RSA_PSS)));
+
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Padding(PaddingMode::RSA_PSS)));
+}
+
+/*
+ * SigningOperationsTest.RsaPssNoDigest
+ *
+ * Verifies that RSA operations fail when no padding mode is specified.  PaddingMode::NONE is
+ * supported in some cases (as validated in other tests), but a mode must be specified.
+ */
+TEST_P(SigningOperationsTest, RsaNoPadding) {
+    // Padding must be specified
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaKey(2048, 65537)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .SigningKey()
+                                                 .Digest(Digest::NONE)));
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::NONE)));
+}
+
+/*
+ * SigningOperationsTest.RsaShortMessage
+ *
+ * Verifies that raw RSA signatures succeed with a message shorter than the key size.
+ */
+TEST_P(SigningOperationsTest, RsaTooShortMessage) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::NONE)));
+
+    // Barely shorter
+    string message(2048 / 8 - 1, 'a');
+    SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+
+    // Much shorter
+    message = "a";
+    SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+}
+
+/*
+ * SigningOperationsTest.RsaSignWithEncryptionKey
+ *
+ * Verifies that RSA encryption keys cannot be used to sign.
+ */
+TEST_P(SigningOperationsTest, RsaSignWithEncryptionKey) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::NONE)));
+    ASSERT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
+              Begin(KeyPurpose::SIGN,
+                    AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
+}
+
+/*
+ * SigningOperationsTest.RsaSignTooLargeMessage
+ *
+ * Verifies that attempting a raw signature of a message which is the same length as the key,
+ * but numerically larger than the public modulus, fails with the correct error.
+ */
+TEST_P(SigningOperationsTest, RsaSignTooLargeMessage) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::NONE)));
+
+    // Largest possible message will always be larger than the public modulus.
+    string message(2048 / 8, static_cast<char>(0xff));
+    ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                                             .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                             .Digest(Digest::NONE)
+                                                             .Padding(PaddingMode::NONE)));
+    string signature;
+    ASSERT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(message, &signature));
+}
+
+/*
+ * SigningOperationsTest.EcdsaAllSizesAndHashes
+ *
+ * Verifies that ECDSA operations succeed with all possible key sizes and hashes.
+ */
+TEST_P(SigningOperationsTest, EcdsaAllSizesAndHashes) {
+    for (auto key_size : ValidKeySizes(Algorithm::EC)) {
+        for (auto digest : ValidDigests(false /* withNone */, false /* withMD5 */)) {
+            ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                  .EcdsaSigningKey(key_size)
+                                                  .Digest(digest));
+            EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with size " << key_size
+                                            << " and digest " << digest;
+            if (error != ErrorCode::OK) continue;
+
+            string message(1024, 'a');
+            if (digest == Digest::NONE) message.resize(key_size / 8);
+            SignMessage(message, AuthorizationSetBuilder().Digest(digest));
+            CheckedDeleteKey();
+        }
+    }
+}
+
+/*
+ * SigningOperationsTest.EcdsaAllCurves
+ *
+ * Verifies that ECDSA operations succeed with all possible curves.
+ */
+TEST_P(SigningOperationsTest, EcdsaAllCurves) {
+    for (auto curve : ValidCurves()) {
+        ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+                                              .Authorization(TAG_NO_AUTH_REQUIRED)
+                                              .EcdsaSigningKey(curve)
+                                              .Digest(Digest::SHA_2_256));
+        EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
+        if (error != ErrorCode::OK) continue;
+
+        string message(1024, 'a');
+        SignMessage(message, AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+        CheckedDeleteKey();
+    }
+}
+
+/*
+ * SigningOperationsTest.EcdsaNoDigestHugeData
+ *
+ * Verifies that ECDSA operations support very large messages, even without digesting.  This
+ * should work because ECDSA actually only signs the leftmost L_n bits of the message, however
+ * large it may be.  Not using digesting is a bad idea, but in some cases digesting is done by
+ * the framework.
+ */
+TEST_P(SigningOperationsTest, EcdsaNoDigestHugeData) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .EcdsaSigningKey(256)
+                                                 .Digest(Digest::NONE)));
+    string message(1 * 1024, 'a');
+    SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE));
+}
+
+/*
+ * SigningOperationsTest.EcUseRequiresCorrectAppIdAppData
+ *
+ * Verifies that using an EC key requires the correct app ID/data.
+ */
+TEST_P(SigningOperationsTest, EcUseRequiresCorrectAppIdAppData) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .EcdsaSigningKey(256)
+                                                 .Digest(Digest::NONE)
+                                                 .Authorization(TAG_APPLICATION_ID, "clientid")
+                                                 .Authorization(TAG_APPLICATION_DATA, "appdata")));
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::NONE)));
+    AbortIfNeeded();
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Authorization(TAG_APPLICATION_ID, "clientid")));
+    AbortIfNeeded();
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Authorization(TAG_APPLICATION_DATA, "appdata")));
+    AbortIfNeeded();
+    EXPECT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Authorization(TAG_APPLICATION_DATA, "appdata")
+                                              .Authorization(TAG_APPLICATION_ID, "clientid")));
+    AbortIfNeeded();
+}
+
+/*
+ * SigningOperationsTest.AesEcbSign
+ *
+ * Verifies that attempts to use AES keys to sign fail in the correct way.
+ */
+TEST_P(SigningOperationsTest, AesEcbSign) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .SigningKey()
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)));
+
+    AuthorizationSet out_params;
+    EXPECT_EQ(ErrorCode::UNSUPPORTED_PURPOSE,
+              Begin(KeyPurpose::SIGN, AuthorizationSet() /* in_params */, &out_params));
+    EXPECT_EQ(ErrorCode::UNSUPPORTED_PURPOSE,
+              Begin(KeyPurpose::VERIFY, AuthorizationSet() /* in_params */, &out_params));
+}
+
+/*
+ * SigningOperationsTest.HmacAllDigests
+ *
+ * Verifies that HMAC works with all digests.
+ */
+TEST_P(SigningOperationsTest, HmacAllDigests) {
+    for (auto digest : ValidDigests(false /* withNone */, false /* withMD5 */)) {
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                     .HmacKey(128)
+                                                     .Digest(digest)
+                                                     .Authorization(TAG_MIN_MAC_LENGTH, 160)))
+                << "Failed to create HMAC key with digest " << digest;
+        string message = "12345678901234567890123456789012";
+        string signature = MacMessage(message, digest, 160);
+        EXPECT_EQ(160U / 8U, signature.size())
+                << "Failed to sign with HMAC key with digest " << digest;
+        CheckedDeleteKey();
+    }
+}
+
+/*
+ * SigningOperationsTest.HmacSha256TooLargeMacLength
+ *
+ * Verifies that HMAC fails in the correct way when asked to generate a MAC larger than the
+ * digest size.
+ */
+TEST_P(SigningOperationsTest, HmacSha256TooLargeMacLength) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .HmacKey(128)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 256)));
+    AuthorizationSet output_params;
+    EXPECT_EQ(ErrorCode::UNSUPPORTED_MAC_LENGTH, Begin(KeyPurpose::SIGN, key_blob_,
+                                                       AuthorizationSetBuilder()
+                                                               .Digest(Digest::SHA_2_256)
+                                                               .Authorization(TAG_MAC_LENGTH, 264),
+                                                       &output_params));
+}
+
+/*
+ * SigningOperationsTest.HmacSha256TooSmallMacLength
+ *
+ * Verifies that HMAC fails in the correct way when asked to generate a MAC smaller than the
+ * specified minimum MAC length.
+ */
+TEST_P(SigningOperationsTest, HmacSha256TooSmallMacLength) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .HmacKey(128)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+    AuthorizationSet output_params;
+    EXPECT_EQ(ErrorCode::INVALID_MAC_LENGTH, Begin(KeyPurpose::SIGN, key_blob_,
+                                                   AuthorizationSetBuilder()
+                                                           .Digest(Digest::SHA_2_256)
+                                                           .Authorization(TAG_MAC_LENGTH, 120),
+                                                   &output_params));
+}
+
+/*
+ * SigningOperationsTest.HmacRfc4231TestCase3
+ *
+ * Validates against the test vectors from RFC 4231 test case 3.
+ */
+TEST_P(SigningOperationsTest, HmacRfc4231TestCase3) {
+    string key(20, 0xaa);
+    string message(50, 0xdd);
+    uint8_t sha_224_expected[] = {
+            0x7f, 0xb3, 0xcb, 0x35, 0x88, 0xc6, 0xc1, 0xf6, 0xff, 0xa9, 0x69, 0x4d, 0x7d, 0x6a,
+            0xd2, 0x64, 0x93, 0x65, 0xb0, 0xc1, 0xf6, 0x5d, 0x69, 0xd1, 0xec, 0x83, 0x33, 0xea,
+    };
+    uint8_t sha_256_expected[] = {
+            0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8,
+            0xeb, 0xd0, 0x91, 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8,
+            0xc1, 0x22, 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe,
+    };
+    uint8_t sha_384_expected[] = {
+            0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a, 0x0a, 0xa2, 0xac, 0xe0,
+            0x14, 0xc8, 0xa8, 0x6f, 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb,
+            0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b, 0x2a, 0x5a, 0xb3, 0x9d,
+            0xc1, 0x38, 0x14, 0xb9, 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27,
+    };
+    uint8_t sha_512_expected[] = {
+            0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84, 0xef, 0xb0, 0xf0, 0x75, 0x6c,
+            0x89, 0x0b, 0xe9, 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36, 0x55, 0xf8,
+            0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39, 0xbf, 0x3e, 0x84, 0x82, 0x79, 0xa7, 0x22,
+            0xc8, 0x06, 0xb4, 0x85, 0xa4, 0x7e, 0x67, 0xc8, 0x07, 0xb9, 0x46, 0xa3, 0x37,
+            0xbe, 0xe8, 0x94, 0x26, 0x74, 0x27, 0x88, 0x59, 0xe1, 0x32, 0x92, 0xfb,
+    };
+
+    CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected));
+    if (SecLevel() != SecurityLevel::STRONGBOX) {
+        CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected));
+        CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected));
+        CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected));
+    }
+}
+
+/*
+ * SigningOperationsTest.HmacRfc4231TestCase5
+ *
+ * Validates against the test vectors from RFC 4231 test case 5.
+ */
+TEST_P(SigningOperationsTest, HmacRfc4231TestCase5) {
+    string key(20, 0x0c);
+    string message = "Test With Truncation";
+
+    uint8_t sha_224_expected[] = {
+            0x0e, 0x2a, 0xea, 0x68, 0xa9, 0x0c, 0x8d, 0x37,
+            0xc9, 0x88, 0xbc, 0xdb, 0x9f, 0xca, 0x6f, 0xa8,
+    };
+    uint8_t sha_256_expected[] = {
+            0xa3, 0xb6, 0x16, 0x74, 0x73, 0x10, 0x0e, 0xe0,
+            0x6e, 0x0c, 0x79, 0x6c, 0x29, 0x55, 0x55, 0x2b,
+    };
+    uint8_t sha_384_expected[] = {
+            0x3a, 0xbf, 0x34, 0xc3, 0x50, 0x3b, 0x2a, 0x23,
+            0xa4, 0x6e, 0xfc, 0x61, 0x9b, 0xae, 0xf8, 0x97,
+    };
+    uint8_t sha_512_expected[] = {
+            0x41, 0x5f, 0xad, 0x62, 0x71, 0x58, 0x0a, 0x53,
+            0x1d, 0x41, 0x79, 0xbc, 0x89, 0x1d, 0x87, 0xa6,
+    };
+
+    CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected));
+    if (SecLevel() != SecurityLevel::STRONGBOX) {
+        CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected));
+        CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected));
+        CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected));
+    }
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(SigningOperationsTest);
+
+typedef KeyMintAidlTestBase VerificationOperationsTest;
+
+/*
+ * VerificationOperationsTest.RsaSuccess
+ *
+ * Verifies that a simple RSA signature/verification sequence succeeds.
+ */
+TEST_P(VerificationOperationsTest, RsaSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::NONE)));
+    string message = "12345678901234567890123456789012";
+    string signature = SignMessage(
+            message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+    VerifyMessage(message, signature,
+                  AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+}
+
+/*
+ * VerificationOperationsTest.RsaSuccess
+ *
+ * Verifies RSA signature/verification for all padding modes and digests.
+ */
+TEST_P(VerificationOperationsTest, RsaAllPaddingsAndDigests) {
+    auto authorizations = AuthorizationSetBuilder()
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .RsaSigningKey(2048, 65537)
+                                  .Digest(ValidDigests(true /* withNone */, true /* withMD5 */))
+                                  .Padding(PaddingMode::NONE)
+                                  .Padding(PaddingMode::RSA_PSS)
+                                  .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(authorizations));
+
+    string message(128, 'a');
+    string corrupt_message(message);
+    ++corrupt_message[corrupt_message.size() / 2];
+
+    for (auto padding :
+         {PaddingMode::NONE, PaddingMode::RSA_PSS, PaddingMode::RSA_PKCS1_1_5_SIGN}) {
+        for (auto digest : ValidDigests(true /* withNone */, true /* withMD5 */)) {
+            if (padding == PaddingMode::NONE && digest != Digest::NONE) {
+                // Digesting only makes sense with padding.
+                continue;
+            }
+
+            if (padding == PaddingMode::RSA_PSS && digest == Digest::NONE) {
+                // PSS requires digesting.
+                continue;
+            }
+
+            string signature =
+                    SignMessage(message, AuthorizationSetBuilder().Digest(digest).Padding(padding));
+            VerifyMessage(message, signature,
+                          AuthorizationSetBuilder().Digest(digest).Padding(padding));
+
+            /* TODO(seleneh) add exportkey tests back later when we have decided on
+             * the new api.
+                        if (digest != Digest::NONE) {
+                            // Verify with OpenSSL.
+                            vector<uint8_t> pubkey;
+                            ASSERT_EQ(ErrorCode::OK, ExportKey(KeyFormat::X509, &pubkey));
+
+                            const uint8_t* p = pubkey.data();
+                            EVP_PKEY_Ptr pkey(d2i_PUBKEY(nullptr, &p, pubkey.size()));
+                            ASSERT_TRUE(pkey.get());
+
+                            EVP_MD_CTX digest_ctx;
+                            EVP_MD_CTX_init(&digest_ctx);
+                            EVP_PKEY_CTX* pkey_ctx;
+                            const EVP_MD* md = openssl_digest(digest);
+                            ASSERT_NE(md, nullptr);
+                            EXPECT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, md,
+             nullptr, pkey.get()));
+
+                            switch (padding) {
+                                case PaddingMode::RSA_PSS:
+                                    EXPECT_GT(EVP_PKEY_CTX_set_rsa_padding(pkey_ctx,
+               RSA_PKCS1_PSS_PADDING), 0); EXPECT_GT(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx,
+               EVP_MD_size(md)), 0); break; case PaddingMode::RSA_PKCS1_1_5_SIGN:
+                                    // PKCS1 is the default; don't need to set anything.
+                                    break;
+                                default:
+                                    FAIL();
+                                    break;
+                            }
+
+                            EXPECT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx, message.data(),
+               message.size())); EXPECT_EQ(1, EVP_DigestVerifyFinal(&digest_ctx,
+                                                            reinterpret_cast<const
+               uint8_t*>(signature.data()), signature.size())); EVP_MD_CTX_cleanup(&digest_ctx);
+                        }
+            */
+
+            // Corrupt signature shouldn't verify.
+            string corrupt_signature(signature);
+            ++corrupt_signature[corrupt_signature.size() / 2];
+
+            EXPECT_EQ(ErrorCode::OK,
+                      Begin(KeyPurpose::VERIFY,
+                            AuthorizationSetBuilder().Digest(digest).Padding(padding)));
+            string result;
+            EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(message, corrupt_signature, &result));
+
+            // Corrupt message shouldn't verify
+            EXPECT_EQ(ErrorCode::OK,
+                      Begin(KeyPurpose::VERIFY,
+                            AuthorizationSetBuilder().Digest(digest).Padding(padding)));
+            EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(corrupt_message, signature, &result));
+        }
+    }
+}
+
+/*
+ * VerificationOperationsTest.RsaSuccess
+ *
+ * Verifies ECDSA signature/verification for all digests and curves.
+ */
+TEST_P(VerificationOperationsTest, EcdsaAllDigestsAndCurves) {
+    auto digests = ValidDigests(true /* withNone */, false /* withMD5 */);
+
+    string message = "1234567890";
+    string corrupt_message = "2234567890";
+    for (auto curve : ValidCurves()) {
+        ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+                                              .Authorization(TAG_NO_AUTH_REQUIRED)
+                                              .EcdsaSigningKey(curve)
+                                              .Digest(digests));
+        EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate key for EC curve " << curve;
+        if (error != ErrorCode::OK) {
+            continue;
+        }
+
+        for (auto digest : digests) {
+            string signature = SignMessage(message, AuthorizationSetBuilder().Digest(digest));
+            VerifyMessage(message, signature, AuthorizationSetBuilder().Digest(digest));
+
+            /* TODO(seleneh) add exportkey tests back later when we have decided on
+             * the new api.
+
+                        // Verify with OpenSSL
+                        if (digest != Digest::NONE) {
+                            vector<uint8_t> pubkey;
+                            ASSERT_EQ(ErrorCode::OK, ExportKey(KeyFormat::X509, &pubkey))
+                                    << curve << ' ' << digest;
+
+                            const uint8_t* p = pubkey.data();
+                            EVP_PKEY_Ptr pkey(d2i_PUBKEY(nullptr, &p, pubkey.size()));
+                            ASSERT_TRUE(pkey.get());
+
+                            EVP_MD_CTX digest_ctx;
+                            EVP_MD_CTX_init(&digest_ctx);
+                            EVP_PKEY_CTX* pkey_ctx;
+                            const EVP_MD* md = openssl_digest(digest);
+
+                            EXPECT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, md,
+             nullptr, pkey.get()))
+                                    << curve << ' ' << digest;
+
+                            EXPECT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx, message.data(),
+               message.size()))
+                                    << curve << ' ' << digest;
+
+                            EXPECT_EQ(1,
+                                      EVP_DigestVerifyFinal(&digest_ctx,
+                                                            reinterpret_cast<const
+               uint8_t*>(signature.data()), signature.size()))
+                                    << curve << ' ' << digest;
+
+                            EVP_MD_CTX_cleanup(&digest_ctx);
+                        }
+            */
+            // Corrupt signature shouldn't verify.
+            string corrupt_signature(signature);
+            ++corrupt_signature[corrupt_signature.size() / 2];
+
+            EXPECT_EQ(ErrorCode::OK,
+                      Begin(KeyPurpose::VERIFY, AuthorizationSetBuilder().Digest(digest)))
+                    << curve << ' ' << digest;
+
+            string result;
+            EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(message, corrupt_signature, &result))
+                    << curve << ' ' << digest;
+
+            // Corrupt message shouldn't verify
+            EXPECT_EQ(ErrorCode::OK,
+                      Begin(KeyPurpose::VERIFY, AuthorizationSetBuilder().Digest(digest)))
+                    << curve << ' ' << digest;
+
+            EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(corrupt_message, signature, &result))
+                    << curve << ' ' << digest;
+        }
+
+        auto rc = DeleteKey();
+        ASSERT_TRUE(rc == ErrorCode::OK || rc == ErrorCode::UNIMPLEMENTED);
+    }
+}
+
+/*
+ * VerificationOperationsTest.HmacSigningKeyCannotVerify
+ *
+ * Verifies HMAC signing and verification, but that a signing key cannot be used to verify.
+ */
+TEST_P(VerificationOperationsTest, HmacSigningKeyCannotVerify) {
+    string key_material = "HelloThisIsAKey";
+
+    vector<uint8_t> signing_key, verification_key;
+    KeyCharacteristics signing_key_chars, verification_key_chars;
+    EXPECT_EQ(ErrorCode::OK,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .Authorization(TAG_ALGORITHM, Algorithm::HMAC)
+                                .Authorization(TAG_PURPOSE, KeyPurpose::SIGN)
+                                .Digest(Digest::SHA_2_256)
+                                .Authorization(TAG_MIN_MAC_LENGTH, 160),
+                        KeyFormat::RAW, key_material, &signing_key, &signing_key_chars));
+    EXPECT_EQ(ErrorCode::OK,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .Authorization(TAG_ALGORITHM, Algorithm::HMAC)
+                                .Authorization(TAG_PURPOSE, KeyPurpose::VERIFY)
+                                .Digest(Digest::SHA_2_256)
+                                .Authorization(TAG_MIN_MAC_LENGTH, 160),
+                        KeyFormat::RAW, key_material, &verification_key, &verification_key_chars));
+
+    string message = "This is a message.";
+    string signature = SignMessage(
+            signing_key, message,
+            AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Authorization(TAG_MAC_LENGTH, 160));
+
+    // Signing key should not work.
+    AuthorizationSet out_params;
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
+              Begin(KeyPurpose::VERIFY, signing_key,
+                    AuthorizationSetBuilder().Digest(Digest::SHA_2_256), &out_params));
+
+    // Verification key should work.
+    VerifyMessage(verification_key, message, signature,
+                  AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+
+    CheckedDeleteKey(&signing_key);
+    CheckedDeleteKey(&verification_key);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(VerificationOperationsTest);
+
+typedef KeyMintAidlTestBase ExportKeyTest;
+
+/*
+ * ExportKeyTest.RsaUnsupportedKeyFormat
+ *
+ * Verifies that attempting to export RSA keys in PKCS#8 format fails with the correct error.
+ */
+// TODO(seleneh) add ExportKey to GenerateKey
+// check result
+
+class ImportKeyTest : public KeyMintAidlTestBase {
+  public:
+    template <TagType tag_type, Tag tag, typename ValueT>
+    void CheckCryptoParam(TypedTag<tag_type, tag> ttag, ValueT expected) {
+        SCOPED_TRACE("CheckCryptoParam");
+        if (IsSecure()) {
+            EXPECT_TRUE(contains(key_characteristics_.hardwareEnforced, ttag, expected))
+                    << "Tag " << tag << " with value " << expected << " not found";
+            EXPECT_FALSE(contains(key_characteristics_.softwareEnforced, ttag))
+                    << "Tag " << tag << " found";
+        } else {
+            EXPECT_TRUE(contains(key_characteristics_.softwareEnforced, ttag, expected))
+                    << "Tag " << tag << " with value " << expected << " not found";
+            EXPECT_FALSE(contains(key_characteristics_.hardwareEnforced, ttag))
+                    << "Tag " << tag << " found";
+        }
+    }
+
+    void CheckOrigin() {
+        SCOPED_TRACE("CheckOrigin");
+        if (IsSecure()) {
+            EXPECT_TRUE(contains(key_characteristics_.hardwareEnforced, TAG_ORIGIN,
+                                 KeyOrigin::IMPORTED));
+        } else {
+            EXPECT_TRUE(contains(key_characteristics_.softwareEnforced, TAG_ORIGIN,
+                                 KeyOrigin::IMPORTED));
+        }
+    }
+};
+
+/*
+ * ImportKeyTest.RsaSuccess
+ *
+ * Verifies that importing and using an RSA key pair works correctly.
+ */
+TEST_P(ImportKeyTest, RsaSuccess) {
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .RsaSigningKey(1024, 65537)
+                                               .Digest(Digest::SHA_2_256)
+                                               .Padding(PaddingMode::RSA_PSS),
+                                       KeyFormat::PKCS8, rsa_key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::RSA);
+    CheckCryptoParam(TAG_KEY_SIZE, 1024U);
+    CheckCryptoParam(TAG_RSA_PUBLIC_EXPONENT, 65537U);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_PADDING, PaddingMode::RSA_PSS);
+    CheckOrigin();
+
+    string message(1024 / 8, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS);
+    string signature = SignMessage(message, params);
+    VerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.RsaKeySizeMismatch
+ *
+ * Verifies that importing an RSA key pair with a size that doesn't match the key fails in the
+ * correct way.
+ */
+TEST_P(ImportKeyTest, RsaKeySizeMismatch) {
+    ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
+              ImportKey(AuthorizationSetBuilder()
+                                .RsaSigningKey(2048 /* Doesn't match key */, 65537)
+                                .Digest(Digest::NONE)
+                                .Padding(PaddingMode::NONE),
+                        KeyFormat::PKCS8, rsa_key));
+}
+
+/*
+ * ImportKeyTest.RsaPublicExponentMismatch
+ *
+ * Verifies that importing an RSA key pair with a public exponent that doesn't match the key
+ * fails in the correct way.
+ */
+TEST_P(ImportKeyTest, RsaPublicExponentMismatch) {
+    ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
+              ImportKey(AuthorizationSetBuilder()
+                                .RsaSigningKey(1024, 3 /* Doesn't match key */)
+                                .Digest(Digest::NONE)
+                                .Padding(PaddingMode::NONE),
+                        KeyFormat::PKCS8, rsa_key));
+}
+
+/*
+ * ImportKeyTest.EcdsaSuccess
+ *
+ * Verifies that importing and using an ECDSA P-256 key pair works correctly.
+ */
+TEST_P(ImportKeyTest, EcdsaSuccess) {
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .EcdsaSigningKey(256)
+                                               .Digest(Digest::SHA_2_256),
+                                       KeyFormat::PKCS8, ec_256_key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+    CheckCryptoParam(TAG_KEY_SIZE, 256U);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_256);
+
+    CheckOrigin();
+
+    string message(32, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
+    string signature = SignMessage(message, params);
+    VerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.EcdsaP256RFC5915Success
+ *
+ * Verifies that importing and using an ECDSA P-256 key pair encoded using RFC5915 works
+ * correctly.
+ */
+TEST_P(ImportKeyTest, EcdsaP256RFC5915Success) {
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .EcdsaSigningKey(256)
+                                               .Digest(Digest::SHA_2_256),
+                                       KeyFormat::PKCS8, ec_256_key_rfc5915));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+    CheckCryptoParam(TAG_KEY_SIZE, 256U);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_256);
+
+    CheckOrigin();
+
+    string message(32, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
+    string signature = SignMessage(message, params);
+    VerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.EcdsaP256SEC1Success
+ *
+ * Verifies that importing and using an ECDSA P-256 key pair encoded using SEC1 works correctly.
+ */
+TEST_P(ImportKeyTest, EcdsaP256SEC1Success) {
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .EcdsaSigningKey(256)
+                                               .Digest(Digest::SHA_2_256),
+                                       KeyFormat::PKCS8, ec_256_key_sec1));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+    CheckCryptoParam(TAG_KEY_SIZE, 256U);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_256);
+
+    CheckOrigin();
+
+    string message(32, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
+    string signature = SignMessage(message, params);
+    VerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.Ecdsa521Success
+ *
+ * Verifies that importing and using an ECDSA P-521 key pair works correctly.
+ */
+TEST_P(ImportKeyTest, Ecdsa521Success) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .EcdsaSigningKey(521)
+                                               .Digest(Digest::SHA_2_256),
+                                       KeyFormat::PKCS8, ec_521_key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+    CheckCryptoParam(TAG_KEY_SIZE, 521U);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_521);
+    CheckOrigin();
+
+    string message(32, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
+    string signature = SignMessage(message, params);
+    VerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.EcdsaSizeMismatch
+ *
+ * Verifies that importing an ECDSA key pair with a size that doesn't match the key fails in the
+ * correct way.
+ */
+TEST_P(ImportKeyTest, EcdsaSizeMismatch) {
+    ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
+              ImportKey(AuthorizationSetBuilder()
+                                .EcdsaSigningKey(224 /* Doesn't match key */)
+                                .Digest(Digest::NONE),
+                        KeyFormat::PKCS8, ec_256_key));
+}
+
+/*
+ * ImportKeyTest.EcdsaCurveMismatch
+ *
+ * Verifies that importing an ECDSA key pair with a curve that doesn't match the key fails in
+ * the correct way.
+ */
+TEST_P(ImportKeyTest, EcdsaCurveMismatch) {
+    ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
+              ImportKey(AuthorizationSetBuilder()
+                                .EcdsaSigningKey(EcCurve::P_224 /* Doesn't match key */)
+                                .Digest(Digest::NONE),
+                        KeyFormat::PKCS8, ec_256_key));
+}
+
+/*
+ * ImportKeyTest.AesSuccess
+ *
+ * Verifies that importing and using an AES key works.
+ */
+TEST_P(ImportKeyTest, AesSuccess) {
+    string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .AesEncryptionKey(key.size() * 8)
+                                               .EcbMode()
+                                               .Padding(PaddingMode::PKCS7),
+                                       KeyFormat::RAW, key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::AES);
+    CheckCryptoParam(TAG_KEY_SIZE, 128U);
+    CheckCryptoParam(TAG_PADDING, PaddingMode::PKCS7);
+    CheckCryptoParam(TAG_BLOCK_MODE, BlockMode::ECB);
+    CheckOrigin();
+
+    string message = "Hello World!";
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+    string ciphertext = EncryptMessage(message, params);
+    string plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * ImportKeyTest.AesSuccess
+ *
+ * Verifies that importing and using an HMAC key works.
+ */
+TEST_P(ImportKeyTest, HmacKeySuccess) {
+    string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .HmacKey(key.size() * 8)
+                                               .Digest(Digest::SHA_2_256)
+                                               .Authorization(TAG_MIN_MAC_LENGTH, 256),
+                                       KeyFormat::RAW, key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::HMAC);
+    CheckCryptoParam(TAG_KEY_SIZE, 128U);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckOrigin();
+
+    string message = "Hello World!";
+    string signature = MacMessage(message, Digest::SHA_2_256, 256);
+    VerifyMessage(message, signature, AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(ImportKeyTest);
+
+auto wrapped_key = hex2str(
+        "3082017902010004820100934bf94e2aa28a3f83c9f79297250262fbe3276b5a1c91159bbfa3ef8957aac8"
+        "4b59b30b455a79c2973480823d8b3863c3deef4a8e243590268d80e18751a0e130f67ce6a1ace9f79b95e0"
+        "97474febc981195b1d13a69086c0863f66a7b7fdb48792227b1ac5e2489febdf087ab5486483033a6f001c"
+        "a5d1ec1e27f5c30f4cec2642074a39ae68aee552e196627a8e3d867e67a8c01b11e75f13cca0a97ab668b5"
+        "0cda07a8ecb7cd8e3dd7009c9636534f6f239cffe1fc8daa466f78b676c7119efb96bce4e69ca2a25d0b34"
+        "ed9c3ff999b801597d5220e307eaa5bee507fb94d1fa69f9e519b2de315bac92c36f2ea1fa1df4478c0dde"
+        "deae8c70e0233cd098040cd796b02c370f1fa4cc0124f1302e0201033029a1083106020100020101a20302"
+        "0120a30402020100a4053103020101a6053103020140bf83770205000420ccd540855f833a5e1480bfd2d3"
+        "6faf3aeee15df5beabe2691bc82dde2a7aa910041064c9f689c60ff6223ab6e6999e0eb6e5");
+
+auto wrapped_key_masked = hex2str(
+        "3082017902010004820100aad93ed5924f283b4bb5526fbe7a1412f9d9749ec30db9062b29e574a8546f33"
+        "c88732452f5b8e6a391ee76c39ed1712c61d8df6213dec1cffbc17a8c6d04c7b30893d8daa9b2015213e21"
+        "946821553207f8f9931c4caba23ed3bee28b36947e47f10e0a5c3dc51c988a628daad3e5e1f4005e79c2d5"
+        "a96c284b4b8d7e4948f331e5b85dd5a236f85579f3ea1d1b848487470bdb0ab4f81a12bee42c99fe0df4be"
+        "e3759453e69ad1d68a809ce06b949f7694a990429b2fe81e066ff43e56a21602db70757922a4bcc23ab89f"
+        "1e35da77586775f423e519c2ea394caf48a28d0c8020f1dcf6b3a68ec246f615ae96dae9a079b1f6eb9590"
+        "33c1af5c125fd94168040c6d9721d08589581ab49204a3302e0201033029a1083106020100020101a20302"
+        "0120a30402020100a4053103020101a6053103020140bf83770205000420a61c6e247e25b3e6e69aa78eb0"
+        "3c2d4ac20d1f99a9a024a76f35c8e2cab9b68d04102560c70109ae67c030f00b98b512a670");
+
+auto wrapping_key = hex2str(
+        "308204be020100300d06092a864886f70d0101010500048204a8308204a40201000282010100aec367931d"
+        "8900ce56b0067f7d70e1fc653f3f34d194c1fed50018fb43db937b06e673a837313d56b1c725150a3fef86"
+        "acbddc41bb759c2854eae32d35841efb5c18d82bc90a1cb5c1d55adf245b02911f0b7cda88c421ff0ebafe"
+        "7c0d23be312d7bd5921ffaea1347c157406fef718f682643e4e5d33c6703d61c0cf7ac0bf4645c11f5c137"
+        "4c3886427411c449796792e0bef75dec858a2123c36753e02a95a96d7c454b504de385a642e0dfc3e60ac3"
+        "a7ee4991d0d48b0172a95f9536f02ba13cecccb92b727db5c27e5b2f5cec09600b286af5cf14c42024c61d"
+        "dfe71c2a8d7458f185234cb00e01d282f10f8fc6721d2aed3f4833cca2bd8fa62821dd5502030100010282"
+        "0100431447b6251908112b1ee76f99f3711a52b6630960046c2de70de188d833f8b8b91e4d785caeeeaf4f"
+        "0f74414e2cda40641f7fe24f14c67a88959bdb27766df9e710b630a03adc683b5d2c43080e52bee71e9eae"
+        "b6de297a5fea1072070d181c822bccff087d63c940ba8a45f670feb29fb4484d1c95e6d2579ba02aae0a00"
+        "900c3ebf490e3d2cd7ee8d0e20c536e4dc5a5097272888cddd7e91f228b1c4d7474c55b8fcd618c4a957bb"
+        "ddd5ad7407cc312d8d98a5caf7e08f4a0d6b45bb41c652659d5a5ba05b663737a8696281865ba20fbdd7f8"
+        "51e6c56e8cbe0ddbbf24dc03b2d2cb4c3d540fb0af52e034a2d06698b128e5f101e3b51a34f8d8b4f86181"
+        "02818100de392e18d682c829266cc3454e1d6166242f32d9a1d10577753e904ea7d08bff841be5bac82a16"
+        "4c5970007047b8c517db8f8f84e37bd5988561bdf503d4dc2bdb38f885434ae42c355f725c9a60f91f0788"
+        "e1f1a97223b524b5357fdf72e2f696bab7d78e32bf92ba8e1864eab1229e91346130748a6e3c124f9149d7"
+        "1c743502818100c95387c0f9d35f137b57d0d65c397c5e21cc251e47008ed62a542409c8b6b6ac7f8967b3"
+        "863ca645fcce49582a9aa17349db6c4a95affdae0dae612e1afac99ed39a2d934c880440aed8832f984316"
+        "3a47f27f392199dc1202f9a0f9bd08308007cb1e4e7f58309366a7de25f7c3c9b880677c068e1be936e812"
+        "88815252a8a102818057ff8ca1895080b2cae486ef0adfd791fb0235c0b8b36cd6c136e52e4085f4ea5a06"
+        "3212a4f105a3764743e53281988aba073f6e0027298e1c4378556e0efca0e14ece1af76ad0b030f27af6f0"
+        "ab35fb73a060d8b1a0e142fa2647e93b32e36d8282ae0a4de50ab7afe85500a16f43a64719d6e2b9439823"
+        "719cd08bcd03178102818100ba73b0bb28e3f81e9bd1c568713b101241acc607976c4ddccc90e65b6556ca"
+        "31516058f92b6e09f3b160ff0e374ec40d78ae4d4979fde6ac06a1a400c61dd31254186af30b22c10582a8"
+        "a43e34fe949c5f3b9755bae7baa7b7b7a6bd03b38cef55c86885fc6c1978b9cee7ef33da507c9df6b9277c"
+        "ff1e6aaa5d57aca528466102818100c931617c77829dfb1270502be9195c8f2830885f57dba869536811e6"
+        "864236d0c4736a0008a145af36b8357a7c3d139966d04c4e00934ea1aede3bb6b8ec841dc95e3f579751e2"
+        "bfdfe27ae778983f959356210723287b0affcc9f727044d48c373f1babde0724fa17a4fd4da0902c7c9b9b"
+        "f27ba61be6ad02dfddda8f4e6822");
+
+string zero_masking_key =
+        hex2str("0000000000000000000000000000000000000000000000000000000000000000");
+string masking_key = hex2str("D796B02C370F1FA4CC0124F14EC8CBEBE987E825246265050F399A51FD477DFC");
+
+class ImportWrappedKeyTest : public KeyMintAidlTestBase {};
+
+TEST_P(ImportWrappedKeyTest, Success) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP)
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY);
+
+    ASSERT_EQ(ErrorCode::OK,
+              ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
+                               AuthorizationSetBuilder()
+                                       .Digest(Digest::SHA_2_256)
+                                       .Padding(PaddingMode::RSA_OAEP)));
+
+    string message = "Hello World!";
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+    string ciphertext = EncryptMessage(message, params);
+    string plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+}
+
+TEST_P(ImportWrappedKeyTest, SuccessMasked) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP)
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY);
+
+    ASSERT_EQ(ErrorCode::OK,
+              ImportWrappedKey(wrapped_key_masked, wrapping_key, wrapping_key_desc, masking_key,
+                               AuthorizationSetBuilder()
+                                       .Digest(Digest::SHA_2_256)
+                                       .Padding(PaddingMode::RSA_OAEP)));
+}
+
+TEST_P(ImportWrappedKeyTest, WrongMask) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP)
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY);
+
+    ASSERT_EQ(
+            ErrorCode::VERIFICATION_FAILED,
+            ImportWrappedKey(wrapped_key_masked, wrapping_key, wrapping_key_desc, zero_masking_key,
+                             AuthorizationSetBuilder()
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP)));
+}
+
+TEST_P(ImportWrappedKeyTest, WrongPurpose) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP);
+
+    ASSERT_EQ(
+            ErrorCode::INCOMPATIBLE_PURPOSE,
+            ImportWrappedKey(wrapped_key_masked, wrapping_key, wrapping_key_desc, zero_masking_key,
+                             AuthorizationSetBuilder()
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP)));
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(ImportWrappedKeyTest);
+
+typedef KeyMintAidlTestBase EncryptionOperationsTest;
+
+/*
+ * EncryptionOperationsTest.RsaNoPaddingSuccess
+ *
+ * Verifies that raw RSA encryption works.
+ */
+TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::NONE)));
+
+    string message = string(2048 / 8, 'a');
+    auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+    string ciphertext1 = EncryptMessage(message, params);
+    EXPECT_EQ(2048U / 8, ciphertext1.size());
+
+    string ciphertext2 = EncryptMessage(message, params);
+    EXPECT_EQ(2048U / 8, ciphertext2.size());
+
+    // Unpadded RSA is deterministic
+    EXPECT_EQ(ciphertext1, ciphertext2);
+}
+
+/*
+ * EncryptionOperationsTest.RsaNoPaddingShortMessage
+ *
+ * Verifies that raw RSA encryption of short messages works.
+ */
+TEST_P(EncryptionOperationsTest, RsaNoPaddingShortMessage) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::NONE)));
+
+    string message = "1";
+    auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+
+    string ciphertext = EncryptMessage(message, params);
+    EXPECT_EQ(2048U / 8, ciphertext.size());
+
+    string expected_plaintext = string(2048U / 8 - 1, 0) + message;
+    string plaintext = DecryptMessage(ciphertext, params);
+
+    EXPECT_EQ(expected_plaintext, plaintext);
+
+    // Degenerate case, encrypting a numeric 1 yields 0x00..01 as the ciphertext.
+    message = static_cast<char>(1);
+    ciphertext = EncryptMessage(message, params);
+    EXPECT_EQ(2048U / 8, ciphertext.size());
+    EXPECT_EQ(ciphertext, string(2048U / 8 - 1, 0) + message);
+}
+
+/*
+ * EncryptionOperationsTest.RsaNoPaddingTooLong
+ *
+ * Verifies that raw RSA encryption of too-long messages fails in the expected way.
+ */
+TEST_P(EncryptionOperationsTest, RsaNoPaddingTooLong) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::NONE)));
+
+    string message(2048 / 8 + 1, 'a');
+
+    auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
+
+    string result;
+    EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &result));
+}
+
+/*
+ * EncryptionOperationsTest.RsaNoPaddingTooLarge
+ *
+ * Verifies that raw RSA encryption of too-large (numerically) messages fails in the expected
+ * way.
+ */
+// TODO(seleneh) add RsaNoPaddingTooLarge test back after decided and implemented new
+// version of ExportKey inside generateKey
+
+/*
+ * EncryptionOperationsTest.RsaOaepSuccess
+ *
+ * Verifies that RSA-OAEP encryption operations work, with all digests.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepSuccess) {
+    auto digests = ValidDigests(false /* withNone */, true /* withMD5 */);
+
+    size_t key_size = 2048;  // Need largish key for SHA-512 test.
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(key_size, 65537)
+                                                 .Padding(PaddingMode::RSA_OAEP)
+                                                 .Digest(digests)));
+
+    string message = "Hello";
+
+    for (auto digest : digests) {
+        auto params = AuthorizationSetBuilder().Digest(digest).Padding(PaddingMode::RSA_OAEP);
+        string ciphertext1 = EncryptMessage(message, params);
+        if (HasNonfatalFailure()) std::cout << "-->" << digest << std::endl;
+        EXPECT_EQ(key_size / 8, ciphertext1.size());
+
+        string ciphertext2 = EncryptMessage(message, params);
+        EXPECT_EQ(key_size / 8, ciphertext2.size());
+
+        // OAEP randomizes padding so every result should be different (with astronomically high
+        // probability).
+        EXPECT_NE(ciphertext1, ciphertext2);
+
+        string plaintext1 = DecryptMessage(ciphertext1, params);
+        EXPECT_EQ(message, plaintext1) << "RSA-OAEP failed with digest " << digest;
+        string plaintext2 = DecryptMessage(ciphertext2, params);
+        EXPECT_EQ(message, plaintext2) << "RSA-OAEP failed with digest " << digest;
+
+        // Decrypting corrupted ciphertext should fail.
+        size_t offset_to_corrupt = random() % ciphertext1.size();
+        char corrupt_byte;
+        do {
+            corrupt_byte = static_cast<char>(random() % 256);
+        } while (corrupt_byte == ciphertext1[offset_to_corrupt]);
+        ciphertext1[offset_to_corrupt] = corrupt_byte;
+
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+        string result;
+        EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result));
+        EXPECT_EQ(0U, result.size());
+    }
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepInvalidDigest
+ *
+ * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to operate
+ * without a digest.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepInvalidDigest) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::RSA_OAEP)
+                                                 .Digest(Digest::NONE)));
+    string message = "Hello World!";
+
+    auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_OAEP).Digest(Digest::NONE);
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepInvalidDigest
+ *
+ * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to decrypt
+ * with a different digest than was used to encrypt.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepDecryptWithWrongDigest) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(1024, 65537)
+                                                 .Padding(PaddingMode::RSA_OAEP)
+                                                 .Digest(Digest::SHA_2_224, Digest::SHA_2_256)));
+    string message = "Hello World!";
+    string ciphertext = EncryptMessage(
+            message,
+            AuthorizationSetBuilder().Digest(Digest::SHA_2_224).Padding(PaddingMode::RSA_OAEP));
+
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, AuthorizationSetBuilder()
+                                                                .Digest(Digest::SHA_2_256)
+                                                                .Padding(PaddingMode::RSA_OAEP)));
+    string result;
+    EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext, &result));
+    EXPECT_EQ(0U, result.size());
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepTooLarge
+ *
+ * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to encrypt a
+ * too-large message.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepTooLarge) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::RSA_OAEP)
+                                                 .Digest(Digest::SHA_2_256)));
+    constexpr size_t digest_size = 256 /* SHA_2_256 */ / 8;
+    constexpr size_t oaep_overhead = 2 * digest_size + 2;
+    string message(2048 / 8 - oaep_overhead + 1, 'a');
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
+                                                                .Padding(PaddingMode::RSA_OAEP)
+                                                                .Digest(Digest::SHA_2_256)));
+    string result;
+    ErrorCode error = Finish(message, &result);
+    EXPECT_TRUE(error == ErrorCode::INVALID_INPUT_LENGTH || error == ErrorCode::INVALID_ARGUMENT);
+    EXPECT_EQ(0U, result.size());
+}
+
+/*
+ * EncryptionOperationsTest.RsaPkcs1Success
+ *
+ * Verifies that RSA PKCS encryption/decrypts works.
+ */
+TEST_P(EncryptionOperationsTest, RsaPkcs1Success) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT)));
+
+    string message = "Hello World!";
+    auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT);
+    string ciphertext1 = EncryptMessage(message, params);
+    EXPECT_EQ(2048U / 8, ciphertext1.size());
+
+    string ciphertext2 = EncryptMessage(message, params);
+    EXPECT_EQ(2048U / 8, ciphertext2.size());
+
+    // PKCS1 v1.5 randomizes padding so every result should be different.
+    EXPECT_NE(ciphertext1, ciphertext2);
+
+    string plaintext = DecryptMessage(ciphertext1, params);
+    EXPECT_EQ(message, plaintext);
+
+    // Decrypting corrupted ciphertext should fail.
+    size_t offset_to_corrupt = random() % ciphertext1.size();
+    char corrupt_byte;
+    do {
+        corrupt_byte = static_cast<char>(random() % 256);
+    } while (corrupt_byte == ciphertext1[offset_to_corrupt]);
+    ciphertext1[offset_to_corrupt] = corrupt_byte;
+
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+    string result;
+    EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result));
+    EXPECT_EQ(0U, result.size());
+}
+
+/*
+ * EncryptionOperationsTest.RsaPkcs1TooLarge
+ *
+ * Verifies that RSA PKCS encryption fails in the correct way when the mssage is too large.
+ */
+TEST_P(EncryptionOperationsTest, RsaPkcs1TooLarge) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT)));
+    string message(2048 / 8 - 10, 'a');
+
+    auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT);
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
+    string result;
+    ErrorCode error = Finish(message, &result);
+    EXPECT_TRUE(error == ErrorCode::INVALID_INPUT_LENGTH || error == ErrorCode::INVALID_ARGUMENT);
+    EXPECT_EQ(0U, result.size());
+}
+
+/*
+ * EncryptionOperationsTest.EcdsaEncrypt
+ *
+ * Verifies that attempting to use ECDSA keys to encrypt fails in the correct way.
+ */
+TEST_P(EncryptionOperationsTest, EcdsaEncrypt) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .EcdsaSigningKey(256)
+                                                 .Digest(Digest::NONE)));
+    auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::ENCRYPT, params));
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::DECRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.HmacEncrypt
+ *
+ * Verifies that attempting to use HMAC keys to encrypt fails in the correct way.
+ */
+TEST_P(EncryptionOperationsTest, HmacEncrypt) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .HmacKey(128)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+    auto params = AuthorizationSetBuilder()
+                          .Digest(Digest::SHA_2_256)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_MAC_LENGTH, 128);
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::ENCRYPT, params));
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::DECRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbRoundTripSuccess
+ *
+ * Verifies that AES ECB mode works.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbRoundTripSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::NONE)));
+
+    ASSERT_GT(key_blob_.size(), 0U);
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+
+    // Two-block message.
+    string message = "12345678901234567890123456789012";
+    string ciphertext1 = EncryptMessage(message, params);
+    EXPECT_EQ(message.size(), ciphertext1.size());
+
+    string ciphertext2 = EncryptMessage(string(message), params);
+    EXPECT_EQ(message.size(), ciphertext2.size());
+
+    // ECB is deterministic.
+    EXPECT_EQ(ciphertext1, ciphertext2);
+
+    string plaintext = DecryptMessage(ciphertext1, params);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbRoundTripSuccess
+ *
+ * Verifies that AES encryption fails in the correct way when an unauthorized mode is specified.
+ */
+TEST_P(EncryptionOperationsTest, AesWrongMode) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+                                                 .Padding(PaddingMode::NONE)));
+
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    // Two-block message.
+    string message = "12345678901234567890123456789012";
+    EXPECT_EQ(
+            ErrorCode::INCOMPATIBLE_BLOCK_MODE,
+            Begin(KeyPurpose::ENCRYPT,
+                  AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE)));
+}
+
+/*
+ * EncryptionOperationsTest.AesWrongPurpose
+ *
+ * Verifies that AES encryption fails in the correct way when an unauthorized purpose is
+ * specified.
+ */
+TEST_P(EncryptionOperationsTest, AesWrongPurpose) {
+    auto err = GenerateKey(AuthorizationSetBuilder()
+                                   .Authorization(TAG_NO_AUTH_REQUIRED)
+                                   .AesKey(128)
+                                   .Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT)
+                                   .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                                   .Authorization(TAG_MIN_MAC_LENGTH, 128)
+                                   .Padding(PaddingMode::NONE));
+    ASSERT_EQ(ErrorCode::OK, err) << "Got " << err;
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    err = Begin(KeyPurpose::DECRYPT, AuthorizationSetBuilder()
+                                             .BlockMode(BlockMode::GCM)
+                                             .Padding(PaddingMode::NONE)
+                                             .Authorization(TAG_MAC_LENGTH, 128));
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, err) << "Got " << err;
+
+    CheckedDeleteKey();
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesKey(128)
+                                                 .Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)
+                                                 .Padding(PaddingMode::NONE)));
+
+    err = Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
+                                             .BlockMode(BlockMode::GCM)
+                                             .Padding(PaddingMode::NONE)
+                                             .Authorization(TAG_MAC_LENGTH, 128));
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, err) << "Got " << err;
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbNoPaddingWrongInputSize
+ *
+ * Verifies that AES encryption fails in the correct way when provided an input that is not a
+ * multiple of the block size and no padding is specified.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbNoPaddingWrongInputSize) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::NONE)));
+    // Message is slightly shorter than two blocks.
+    string message(16 * 2 - 1, 'a');
+
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
+    string ciphertext;
+    EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &ciphertext));
+    EXPECT_EQ(0U, ciphertext.size());
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbPkcs7Padding
+ *
+ * Verifies that AES PKCS7 padding works for any message length.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbPkcs7Padding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::PKCS7)));
+
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+
+    // Try various message lengths; all should work.
+    for (size_t i = 0; i < 32; ++i) {
+        string message(i, 'a');
+        string ciphertext = EncryptMessage(message, params);
+        EXPECT_EQ(i + 16 - (i % 16), ciphertext.size());
+        string plaintext = DecryptMessage(ciphertext, params);
+        EXPECT_EQ(message, plaintext);
+    }
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbWrongPadding
+ *
+ * Verifies that AES enryption fails in the correct way when an unauthorized padding mode is
+ * specified.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbWrongPadding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::NONE)));
+
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+
+    // Try various message lengths; all should fail
+    for (size_t i = 0; i < 32; ++i) {
+        string message(i, 'a');
+        EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, params));
+    }
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbPkcs7PaddingCorrupted
+ *
+ * Verifies that AES decryption fails in the correct way when the padding is corrupted.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbPkcs7PaddingCorrupted) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::PKCS7)));
+
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+
+    string message = "a";
+    string ciphertext = EncryptMessage(message, params);
+    EXPECT_EQ(16U, ciphertext.size());
+    EXPECT_NE(ciphertext, message);
+    ++ciphertext[ciphertext.size() / 2];
+
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+    string plaintext;
+    EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &plaintext));
+}
+
+vector<uint8_t> CopyIv(const AuthorizationSet& set) {
+    auto iv = set.GetTagValue(TAG_NONCE);
+    EXPECT_TRUE(iv.isOk());
+    return iv.value();
+}
+
+/*
+ * EncryptionOperationsTest.AesCtrRoundTripSuccess
+ *
+ * Verifies that AES CTR mode works.
+ */
+TEST_P(EncryptionOperationsTest, AesCtrRoundTripSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CTR)
+                                                 .Padding(PaddingMode::NONE)));
+
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CTR).Padding(PaddingMode::NONE);
+
+    string message = "123";
+    AuthorizationSet out_params;
+    string ciphertext1 = EncryptMessage(message, params, &out_params);
+    vector<uint8_t> iv1 = CopyIv(out_params);
+    EXPECT_EQ(16U, iv1.size());
+
+    EXPECT_EQ(message.size(), ciphertext1.size());
+
+    out_params.Clear();
+    string ciphertext2 = EncryptMessage(message, params, &out_params);
+    vector<uint8_t> iv2 = CopyIv(out_params);
+    EXPECT_EQ(16U, iv2.size());
+
+    // IVs should be random, so ciphertexts should differ.
+    EXPECT_NE(ciphertext1, ciphertext2);
+
+    auto params_iv1 =
+            AuthorizationSetBuilder().Authorizations(params).Authorization(TAG_NONCE, iv1);
+    auto params_iv2 =
+            AuthorizationSetBuilder().Authorizations(params).Authorization(TAG_NONCE, iv2);
+
+    string plaintext = DecryptMessage(ciphertext1, params_iv1);
+    EXPECT_EQ(message, plaintext);
+    plaintext = DecryptMessage(ciphertext2, params_iv2);
+    EXPECT_EQ(message, plaintext);
+
+    // Using the wrong IV will result in a "valid" decryption, but the data will be garbage.
+    plaintext = DecryptMessage(ciphertext1, params_iv2);
+    EXPECT_NE(message, plaintext);
+    plaintext = DecryptMessage(ciphertext2, params_iv1);
+    EXPECT_NE(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesIncremental
+ *
+ * Verifies that AES works, all modes, when provided data in various size increments.
+ */
+TEST_P(EncryptionOperationsTest, AesIncremental) {
+    auto block_modes = {
+            BlockMode::ECB,
+            BlockMode::CBC,
+            BlockMode::CTR,
+            BlockMode::GCM,
+    };
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(block_modes)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    for (int increment = 1; increment <= 240; ++increment) {
+        for (auto block_mode : block_modes) {
+            string message(240, 'a');
+            auto params = AuthorizationSetBuilder()
+                                  .BlockMode(block_mode)
+                                  .Padding(PaddingMode::NONE)
+                                  .Authorization(TAG_MAC_LENGTH, 128) /* for GCM */;
+
+            AuthorizationSet output_params;
+            EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &output_params));
+
+            string ciphertext;
+            int32_t input_consumed;
+            string to_send;
+            for (size_t i = 0; i < message.size(); i += increment) {
+                to_send.append(message.substr(i, increment));
+                EXPECT_EQ(ErrorCode::OK, Update(to_send, &ciphertext, &input_consumed));
+                EXPECT_EQ(to_send.length(), input_consumed);
+                to_send = to_send.substr(input_consumed);
+                EXPECT_EQ(0U, to_send.length());
+
+                switch (block_mode) {
+                    case BlockMode::ECB:
+                    case BlockMode::CBC:
+                        // Implementations must take as many blocks as possible, leaving less
+                        // than a block.
+                        EXPECT_LE(to_send.length(), 16U);
+                        break;
+                    case BlockMode::GCM:
+                    case BlockMode::CTR:
+                        // Implementations must always take all the data.
+                        EXPECT_EQ(0U, to_send.length());
+                        break;
+                }
+            }
+            EXPECT_EQ(ErrorCode::OK, Finish(to_send, &ciphertext)) << "Error sending " << to_send;
+
+            switch (block_mode) {
+                case BlockMode::GCM:
+                    EXPECT_EQ(message.size() + 16, ciphertext.size());
+                    break;
+                case BlockMode::CTR:
+                    EXPECT_EQ(message.size(), ciphertext.size());
+                    break;
+                case BlockMode::CBC:
+                case BlockMode::ECB:
+                    EXPECT_EQ(message.size() + message.size() % 16, ciphertext.size());
+                    break;
+            }
+
+            auto iv = output_params.GetTagValue(TAG_NONCE);
+            switch (block_mode) {
+                case BlockMode::CBC:
+                case BlockMode::GCM:
+                case BlockMode::CTR:
+                    ASSERT_TRUE(iv.isOk()) << "No IV for block mode " << block_mode;
+                    EXPECT_EQ(block_mode == BlockMode::GCM ? 12U : 16U, iv.value().size());
+                    params.push_back(TAG_NONCE, iv.value());
+                    break;
+
+                case BlockMode::ECB:
+                    EXPECT_FALSE(iv.isOk()) << "ECB mode should not generate IV";
+                    break;
+            }
+
+            EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params))
+                    << "Decrypt begin() failed for block mode " << block_mode;
+
+            string plaintext;
+            for (size_t i = 0; i < ciphertext.size(); i += increment) {
+                to_send.append(ciphertext.substr(i, increment));
+                EXPECT_EQ(ErrorCode::OK, Update(to_send, &plaintext, &input_consumed));
+                to_send = to_send.substr(input_consumed);
+            }
+            ErrorCode error = Finish(to_send, &plaintext);
+            ASSERT_EQ(ErrorCode::OK, error) << "Decryption failed for block mode " << block_mode
+                                            << " and increment " << increment;
+            if (error == ErrorCode::OK) {
+                ASSERT_EQ(message, plaintext) << "Decryption didn't match for block mode "
+                                              << block_mode << " and increment " << increment;
+            }
+        }
+    }
+}
+
+struct AesCtrSp80038aTestVector {
+    const char* key;
+    const char* nonce;
+    const char* plaintext;
+    const char* ciphertext;
+};
+
+// These test vectors are taken from
+// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, section F.5.
+static const AesCtrSp80038aTestVector kAesCtrSp80038aTestVectors[] = {
+        // AES-128
+        {
+                "2b7e151628aed2a6abf7158809cf4f3c",
+                "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+                "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
+                "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+                "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff"
+                "5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee",
+        },
+        // AES-192
+        {
+                "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
+                "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+                "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
+                "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+                "1abc932417521ca24f2b0459fe7e6e0b090339ec0aa6faefd5ccc2c6f4ce8e94"
+                "1e36b26bd1ebc670d1bd1d665620abf74f78a7f6d29809585a97daec58c6b050",
+        },
+        // AES-256
+        {
+                "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
+                "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+                "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
+                "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+                "601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5"
+                "2b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6",
+        },
+};
+
+/*
+ * EncryptionOperationsTest.AesCtrSp80038aTestVector
+ *
+ * Verifies AES CTR implementation against SP800-38A test vectors.
+ */
+TEST_P(EncryptionOperationsTest, AesCtrSp80038aTestVector) {
+    std::vector<uint32_t> InvalidSizes = InvalidKeySizes(Algorithm::AES);
+    for (size_t i = 0; i < 3; i++) {
+        const AesCtrSp80038aTestVector& test(kAesCtrSp80038aTestVectors[i]);
+        const string key = hex2str(test.key);
+        if (std::find(InvalidSizes.begin(), InvalidSizes.end(), (key.size() * 8)) !=
+            InvalidSizes.end())
+            continue;
+        const string nonce = hex2str(test.nonce);
+        const string plaintext = hex2str(test.plaintext);
+        const string ciphertext = hex2str(test.ciphertext);
+        CheckAesCtrTestVector(key, nonce, plaintext, ciphertext);
+    }
+}
+
+/*
+ * EncryptionOperationsTest.AesCtrIncompatiblePaddingMode
+ *
+ * Verifies that keymint rejects use of CTR mode with PKCS7 padding in the correct way.
+ */
+TEST_P(EncryptionOperationsTest, AesCtrIncompatiblePaddingMode) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CTR)
+                                                 .Padding(PaddingMode::PKCS7)));
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CTR).Padding(PaddingMode::NONE);
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesCtrInvalidCallerNonce
+ *
+ * Verifies that keymint fails correctly when the user supplies an incorrect-size nonce.
+ */
+TEST_P(EncryptionOperationsTest, AesCtrInvalidCallerNonce) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CTR)
+                                                 .Authorization(TAG_CALLER_NONCE)
+                                                 .Padding(PaddingMode::NONE)));
+
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::CTR)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_NONCE, AidlBuf(string(1, 'a')));
+    EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
+
+    params = AuthorizationSetBuilder()
+                     .BlockMode(BlockMode::CTR)
+                     .Padding(PaddingMode::NONE)
+                     .Authorization(TAG_NONCE, AidlBuf(string(15, 'a')));
+    EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
+
+    params = AuthorizationSetBuilder()
+                     .BlockMode(BlockMode::CTR)
+                     .Padding(PaddingMode::NONE)
+                     .Authorization(TAG_NONCE, AidlBuf(string(17, 'a')));
+    EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesCtrInvalidCallerNonce
+ *
+ * Verifies that keymint fails correctly when the user supplies an incorrect-size nonce.
+ */
+TEST_P(EncryptionOperationsTest, AesCbcRoundTripSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+                                                 .Padding(PaddingMode::NONE)));
+    // Two-block message.
+    string message = "12345678901234567890123456789012";
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+    AuthorizationSet out_params;
+    string ciphertext1 = EncryptMessage(message, params, &out_params);
+    vector<uint8_t> iv1 = CopyIv(out_params);
+    EXPECT_EQ(message.size(), ciphertext1.size());
+
+    out_params.Clear();
+
+    string ciphertext2 = EncryptMessage(message, params, &out_params);
+    vector<uint8_t> iv2 = CopyIv(out_params);
+    EXPECT_EQ(message.size(), ciphertext2.size());
+
+    // IVs should be random, so ciphertexts should differ.
+    EXPECT_NE(ciphertext1, ciphertext2);
+
+    params.push_back(TAG_NONCE, iv1);
+    string plaintext = DecryptMessage(ciphertext1, params);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesCallerNonce
+ *
+ * Verifies that AES caller-provided nonces work correctly.
+ */
+TEST_P(EncryptionOperationsTest, AesCallerNonce) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+                                                 .Authorization(TAG_CALLER_NONCE)
+                                                 .Padding(PaddingMode::NONE)));
+
+    string message = "12345678901234567890123456789012";
+
+    // Don't specify nonce, should get a random one.
+    AuthorizationSetBuilder params =
+            AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    EXPECT_EQ(message.size(), ciphertext.size());
+    EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE).value().size());
+
+    params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE).value());
+    string plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+
+    // Now specify a nonce, should also work.
+    params = AuthorizationSetBuilder()
+                     .BlockMode(BlockMode::CBC)
+                     .Padding(PaddingMode::NONE)
+                     .Authorization(TAG_NONCE, AidlBuf("abcdefghijklmnop"));
+    out_params.Clear();
+    ciphertext = EncryptMessage(message, params, &out_params);
+
+    // Decrypt with correct nonce.
+    plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+
+    // Try with wrong nonce.
+    params = AuthorizationSetBuilder()
+                     .BlockMode(BlockMode::CBC)
+                     .Padding(PaddingMode::NONE)
+                     .Authorization(TAG_NONCE, AidlBuf("aaaaaaaaaaaaaaaa"));
+    plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_NE(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesCallerNonceProhibited
+ *
+ * Verifies that caller-provided nonces are not permitted when not specified in the key
+ * authorizations.
+ */
+TEST_P(EncryptionOperationsTest, AesCallerNonceProhibited) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+                                                 .Padding(PaddingMode::NONE)));
+
+    string message = "12345678901234567890123456789012";
+
+    // Don't specify nonce, should get a random one.
+    AuthorizationSetBuilder params =
+            AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    EXPECT_EQ(message.size(), ciphertext.size());
+    EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE).value().size());
+
+    params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE).value());
+    string plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+
+    // Now specify a nonce, should fail
+    params = AuthorizationSetBuilder()
+                     .BlockMode(BlockMode::CBC)
+                     .Padding(PaddingMode::NONE)
+                     .Authorization(TAG_NONCE, AidlBuf("abcdefghijklmnop"));
+    out_params.Clear();
+    EXPECT_EQ(ErrorCode::CALLER_NONCE_PROHIBITED, Begin(KeyPurpose::ENCRYPT, params, &out_params));
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmRoundTripSuccess
+ *
+ * Verifies that AES GCM mode works.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmRoundTripSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string aad = "foobar";
+    string message = "123456789012345678901234567890123456";
+
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::GCM)
+                                .Padding(PaddingMode::NONE)
+                                .Authorization(TAG_MAC_LENGTH, 128);
+
+    auto update_params =
+            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params))
+            << "Begin encrypt";
+    string ciphertext;
+    AuthorizationSet update_out_params;
+    ASSERT_EQ(ErrorCode::OK, Finish(update_params, message, "", &update_out_params, &ciphertext));
+
+    ASSERT_EQ(ciphertext.length(), message.length() + 16);
+
+    // Grab nonce
+    begin_params.push_back(begin_out_params);
+
+    // Decrypt.
+    ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)) << "Begin decrypt";
+    string plaintext;
+    int32_t input_consumed;
+    ASSERT_EQ(ErrorCode::OK,
+              Update(update_params, ciphertext, &update_out_params, &plaintext, &input_consumed));
+    EXPECT_EQ(ciphertext.size(), input_consumed);
+    EXPECT_EQ(ErrorCode::OK, Finish("", &plaintext));
+    EXPECT_EQ(message.length(), plaintext.length());
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmRoundTripWithDelaySuccess
+ *
+ * Verifies that AES GCM mode works, even when there's a long delay
+ * between operations.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmRoundTripWithDelaySuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string aad = "foobar";
+    string message = "123456789012345678901234567890123456";
+
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::GCM)
+                                .Padding(PaddingMode::NONE)
+                                .Authorization(TAG_MAC_LENGTH, 128);
+
+    auto update_params =
+            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params))
+            << "Begin encrypt";
+    string ciphertext;
+    AuthorizationSet update_out_params;
+    sleep(5);
+    ASSERT_EQ(ErrorCode::OK, Finish(update_params, message, "", &update_out_params, &ciphertext));
+
+    ASSERT_EQ(ciphertext.length(), message.length() + 16);
+
+    // Grab nonce
+    begin_params.push_back(begin_out_params);
+
+    // Decrypt.
+    ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)) << "Begin decrypt";
+    string plaintext;
+    int32_t input_consumed;
+    sleep(5);
+    ASSERT_EQ(ErrorCode::OK,
+              Update(update_params, ciphertext, &update_out_params, &plaintext, &input_consumed));
+    EXPECT_EQ(ciphertext.size(), input_consumed);
+    sleep(5);
+    EXPECT_EQ(ErrorCode::OK, Finish("", &plaintext));
+    EXPECT_EQ(message.length(), plaintext.length());
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmDifferentNonces
+ *
+ * Verifies that encrypting the same data with different nonces produces different outputs.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmDifferentNonces) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)
+                                                 .Authorization(TAG_CALLER_NONCE)));
+
+    string aad = "foobar";
+    string message = "123456789012345678901234567890123456";
+    string nonce1 = "000000000000";
+    string nonce2 = "111111111111";
+    string nonce3 = "222222222222";
+
+    string ciphertext1 =
+            EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128, AidlBuf(nonce1));
+    string ciphertext2 =
+            EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128, AidlBuf(nonce2));
+    string ciphertext3 =
+            EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128, AidlBuf(nonce3));
+
+    ASSERT_NE(ciphertext1, ciphertext2);
+    ASSERT_NE(ciphertext1, ciphertext3);
+    ASSERT_NE(ciphertext2, ciphertext3);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmTooShortTag
+ *
+ * Verifies that AES GCM mode fails correctly when a too-short tag length is specified.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmTooShortTag) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+    string message = "123456789012345678901234567890123456";
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::GCM)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_MAC_LENGTH, 96);
+
+    EXPECT_EQ(ErrorCode::INVALID_MAC_LENGTH, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmTooShortTagOnDecrypt
+ *
+ * Verifies that AES GCM mode fails correctly when a too-short tag is provided to decryption.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmTooShortTagOnDecrypt) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+    string aad = "foobar";
+    string message = "123456789012345678901234567890123456";
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::GCM)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_MAC_LENGTH, 128);
+
+    auto finish_params =
+            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
+    EXPECT_EQ(1U, begin_out_params.size());
+    ASSERT_TRUE(begin_out_params.GetTagValue(TAG_NONCE).isOk());
+
+    AuthorizationSet finish_out_params;
+    string ciphertext;
+    EXPECT_EQ(ErrorCode::OK,
+              Finish(finish_params, message, "" /* signature */, &finish_out_params, &ciphertext));
+
+    params = AuthorizationSetBuilder()
+                     .Authorizations(begin_out_params)
+                     .BlockMode(BlockMode::GCM)
+                     .Padding(PaddingMode::NONE)
+                     .Authorization(TAG_MAC_LENGTH, 96);
+
+    // Decrypt.
+    EXPECT_EQ(ErrorCode::INVALID_MAC_LENGTH, Begin(KeyPurpose::DECRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmCorruptKey
+ *
+ * Verifies that AES GCM mode fails correctly when the decryption key is incorrect.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmCorruptKey) {
+    const uint8_t nonce_bytes[] = {
+            0xb7, 0x94, 0x37, 0xae, 0x08, 0xff, 0x35, 0x5d, 0x7d, 0x8a, 0x4d, 0x0f,
+    };
+    string nonce = make_string(nonce_bytes);
+    const uint8_t ciphertext_bytes[] = {
+            0xb3, 0xf6, 0x79, 0x9e, 0x8f, 0x93, 0x26, 0xf2, 0xdf, 0x1e, 0x80, 0xfc,
+            0xd2, 0xcb, 0x16, 0xd7, 0x8c, 0x9d, 0xc7, 0xcc, 0x14, 0xbb, 0x67, 0x78,
+            0x62, 0xdc, 0x6c, 0x63, 0x9b, 0x3a, 0x63, 0x38, 0xd2, 0x4b, 0x31, 0x2d,
+            0x39, 0x89, 0xe5, 0x92, 0x0b, 0x5d, 0xbf, 0xc9, 0x76, 0x76, 0x5e, 0xfb,
+            0xfe, 0x57, 0xbb, 0x38, 0x59, 0x40, 0xa7, 0xa4, 0x3b, 0xdf, 0x05, 0xbd,
+            0xda, 0xe3, 0xc9, 0xd6, 0xa2, 0xfb, 0xbd, 0xfc, 0xc0, 0xcb, 0xa0,
+    };
+    string ciphertext = make_string(ciphertext_bytes);
+
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::GCM)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_MAC_LENGTH, 128)
+                          .Authorization(TAG_NONCE, nonce.data(), nonce.size());
+
+    auto import_params = AuthorizationSetBuilder()
+                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                 .AesEncryptionKey(128)
+                                 .BlockMode(BlockMode::GCM)
+                                 .Padding(PaddingMode::NONE)
+                                 .Authorization(TAG_CALLER_NONCE)
+                                 .Authorization(TAG_MIN_MAC_LENGTH, 128);
+
+    // Import correct key and decrypt
+    const uint8_t key_bytes[] = {
+            0xba, 0x76, 0x35, 0x4f, 0x0a, 0xed, 0x6e, 0x8d,
+            0x91, 0xf4, 0x5c, 0x4f, 0xf5, 0xa0, 0x62, 0xdb,
+    };
+    string key = make_string(key_bytes);
+    ASSERT_EQ(ErrorCode::OK, ImportKey(import_params, KeyFormat::RAW, key));
+    string plaintext = DecryptMessage(ciphertext, params);
+    CheckedDeleteKey();
+
+    // Corrupt key and attempt to decrypt
+    key[0] = 0;
+    ASSERT_EQ(ErrorCode::OK, ImportKey(import_params, KeyFormat::RAW, key));
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+    EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext));
+    CheckedDeleteKey();
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmAadNoData
+ *
+ * Verifies that AES GCM mode works when provided additional authenticated data, but no data to
+ * encrypt.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmAadNoData) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string aad = "1234567890123456";
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::GCM)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_MAC_LENGTH, 128);
+
+    auto finish_params =
+            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
+    string ciphertext;
+    AuthorizationSet finish_out_params;
+    EXPECT_EQ(ErrorCode::OK, Finish(finish_params, "" /* input */, "" /* signature */,
+                                    &finish_out_params, &ciphertext));
+    EXPECT_TRUE(finish_out_params.empty());
+
+    // Grab nonce
+    params.push_back(begin_out_params);
+
+    // Decrypt.
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+    string plaintext;
+    EXPECT_EQ(ErrorCode::OK, Finish(finish_params, ciphertext, "" /* signature */,
+                                    &finish_out_params, &plaintext));
+
+    EXPECT_TRUE(finish_out_params.empty());
+
+    EXPECT_EQ("", plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmMultiPartAad
+ *
+ * Verifies that AES GCM mode works when provided additional authenticated data in multiple
+ * chunks.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmMultiPartAad) {
+    const size_t tag_bits = 128;
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string message = "123456789012345678901234567890123456";
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::GCM)
+                                .Padding(PaddingMode::NONE)
+                                .Authorization(TAG_MAC_LENGTH, tag_bits);
+    AuthorizationSet begin_out_params;
+
+    auto update_params =
+            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foo", (size_t)3);
+
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
+
+    // No data, AAD only.
+    string ciphertext;
+    int32_t input_consumed;
+    AuthorizationSet update_out_params;
+    EXPECT_EQ(ErrorCode::OK, Update(update_params, "" /* input */, &update_out_params, &ciphertext,
+                                    &input_consumed));
+    EXPECT_EQ(0U, input_consumed);
+    EXPECT_EQ(0U, ciphertext.size());
+    EXPECT_TRUE(update_out_params.empty());
+
+    // AAD and data.
+    EXPECT_EQ(ErrorCode::OK,
+              Update(update_params, message, &update_out_params, &ciphertext, &input_consumed));
+    EXPECT_EQ(message.size(), input_consumed);
+    EXPECT_TRUE(update_out_params.empty());
+
+    EXPECT_EQ(ErrorCode::OK, Finish("" /* input */, &ciphertext));
+    // Expect 128-bit (16-byte) tag appended to ciphertext.
+    EXPECT_EQ(message.size() + (tag_bits >> 3), ciphertext.size());
+
+    // Grab nonce.
+    begin_params.push_back(begin_out_params);
+
+    // Decrypt
+    update_params =
+            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foofoo", (size_t)6);
+
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
+    string plaintext;
+    EXPECT_EQ(ErrorCode::OK, Finish(update_params, ciphertext, "" /* signature */,
+                                    &update_out_params, &plaintext));
+    EXPECT_TRUE(update_out_params.empty());
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmAadOutOfOrder
+ *
+ * Verifies that AES GCM mode fails correctly when given AAD after data to encipher.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmAadOutOfOrder) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string message = "123456789012345678901234567890123456";
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::GCM)
+                                .Padding(PaddingMode::NONE)
+                                .Authorization(TAG_MAC_LENGTH, 128);
+    AuthorizationSet begin_out_params;
+
+    auto update_params =
+            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foo", (size_t)3);
+
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
+
+    // No data, AAD only.
+    string ciphertext;
+    int32_t input_consumed;
+    AuthorizationSet update_out_params;
+    EXPECT_EQ(ErrorCode::OK, Update(update_params, "" /* input */, &update_out_params, &ciphertext,
+                                    &input_consumed));
+    EXPECT_EQ(0U, input_consumed);
+    EXPECT_EQ(0U, ciphertext.size());
+    EXPECT_TRUE(update_out_params.empty());
+
+    // AAD and data.
+    EXPECT_EQ(ErrorCode::OK,
+              Update(update_params, message, &update_out_params, &ciphertext, &input_consumed));
+    EXPECT_EQ(message.size(), input_consumed);
+    EXPECT_TRUE(update_out_params.empty());
+
+    // More AAD
+    EXPECT_EQ(ErrorCode::INVALID_TAG,
+              Update(update_params, "", &update_out_params, &ciphertext, &input_consumed));
+
+    op_.clear();
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmBadAad
+ *
+ * Verifies that AES GCM decryption fails correctly when additional authenticated date is wrong.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmBadAad) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string message = "12345678901234567890123456789012";
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::GCM)
+                                .Padding(PaddingMode::NONE)
+                                .Authorization(TAG_MAC_LENGTH, 128);
+
+    auto finish_params =
+            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foobar", (size_t)6);
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
+    string ciphertext;
+    AuthorizationSet finish_out_params;
+    EXPECT_EQ(ErrorCode::OK,
+              Finish(finish_params, message, "" /* signature */, &finish_out_params, &ciphertext));
+
+    // Grab nonce
+    begin_params.push_back(begin_out_params);
+
+    finish_params = AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA,
+                                                            "barfoo" /* Wrong AAD */, (size_t)6);
+
+    // Decrypt.
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params, &begin_out_params));
+    string plaintext;
+    EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(finish_params, ciphertext, "" /* signature */,
+                                                     &finish_out_params, &plaintext));
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmWrongNonce
+ *
+ * Verifies that AES GCM decryption fails correctly when the nonce is incorrect.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmWrongNonce) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string message = "12345678901234567890123456789012";
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::GCM)
+                                .Padding(PaddingMode::NONE)
+                                .Authorization(TAG_MAC_LENGTH, 128);
+
+    auto finish_params =
+            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foobar", (size_t)6);
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
+    string ciphertext;
+    AuthorizationSet finish_out_params;
+    EXPECT_EQ(ErrorCode::OK,
+              Finish(finish_params, message, "" /* signature */, &finish_out_params, &ciphertext));
+
+    // Wrong nonce
+    begin_params.push_back(TAG_NONCE, AidlBuf("123456789012"));
+
+    // Decrypt.
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params, &begin_out_params));
+    string plaintext;
+    EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(finish_params, ciphertext, "" /* signature */,
+                                                     &finish_out_params, &plaintext));
+
+    // With wrong nonce, should have gotten garbage plaintext (or none).
+    EXPECT_NE(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmCorruptTag
+ *
+ * Verifies that AES GCM decryption fails correctly when the tag is wrong.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmCorruptTag) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string aad = "1234567890123456";
+    string message = "123456789012345678901234567890123456";
+
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::GCM)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_MAC_LENGTH, 128);
+
+    auto finish_params =
+            AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
+    string ciphertext;
+    AuthorizationSet finish_out_params;
+    EXPECT_EQ(ErrorCode::OK,
+              Finish(finish_params, message, "" /* signature */, &finish_out_params, &ciphertext));
+    EXPECT_TRUE(finish_out_params.empty());
+
+    // Corrupt tag
+    ++(*ciphertext.rbegin());
+
+    // Grab nonce
+    params.push_back(begin_out_params);
+
+    // Decrypt.
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+    string plaintext;
+    EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(finish_params, ciphertext, "" /* signature */,
+                                                     &finish_out_params, &plaintext));
+    EXPECT_TRUE(finish_out_params.empty());
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesEcbRoundTripSuccess
+ *
+ * Verifies that 3DES is basically functional.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesEcbRoundTripSuccess) {
+    auto auths = AuthorizationSetBuilder()
+                         .TripleDesEncryptionKey(168)
+                         .BlockMode(BlockMode::ECB)
+                         .Authorization(TAG_NO_AUTH_REQUIRED)
+                         .Padding(PaddingMode::NONE);
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(auths));
+    // Two-block message.
+    string message = "1234567890123456";
+    auto inParams = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+    string ciphertext1 = EncryptMessage(message, inParams);
+    EXPECT_EQ(message.size(), ciphertext1.size());
+
+    string ciphertext2 = EncryptMessage(string(message), inParams);
+    EXPECT_EQ(message.size(), ciphertext2.size());
+
+    // ECB is deterministic.
+    EXPECT_EQ(ciphertext1, ciphertext2);
+
+    string plaintext = DecryptMessage(ciphertext1, inParams);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesEcbNotAuthorized
+ *
+ * Verifies that CBC keys reject ECB usage.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesEcbNotAuthorized) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+
+    auto inParams = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_BLOCK_MODE, Begin(KeyPurpose::ENCRYPT, inParams));
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesEcbPkcs7Padding
+ *
+ * Tests ECB mode with PKCS#7 padding, various message sizes.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesEcbPkcs7Padding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::ECB)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::PKCS7)));
+
+    for (size_t i = 0; i < 32; ++i) {
+        string message(i, 'a');
+        auto inParams =
+                AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+        string ciphertext = EncryptMessage(message, inParams);
+        EXPECT_EQ(i + 8 - (i % 8), ciphertext.size());
+        string plaintext = DecryptMessage(ciphertext, inParams);
+        EXPECT_EQ(message, plaintext);
+    }
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesEcbNoPaddingKeyWithPkcs7Padding
+ *
+ * Verifies that keys configured for no padding reject PKCS7 padding
+ */
+TEST_P(EncryptionOperationsTest, TripleDesEcbNoPaddingKeyWithPkcs7Padding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::ECB)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+    for (size_t i = 0; i < 32; ++i) {
+        auto inParams =
+                AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+        EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, inParams));
+    }
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesEcbPkcs7PaddingCorrupted
+ *
+ * Verifies that corrupted padding is detected.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesEcbPkcs7PaddingCorrupted) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::ECB)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::PKCS7)));
+
+    string message = "a";
+    string ciphertext = EncryptMessage(message, BlockMode::ECB, PaddingMode::PKCS7);
+    EXPECT_EQ(8U, ciphertext.size());
+    EXPECT_NE(ciphertext, message);
+    ++ciphertext[ciphertext.size() / 2];
+
+    AuthorizationSetBuilder begin_params;
+    begin_params.push_back(TAG_BLOCK_MODE, BlockMode::ECB);
+    begin_params.push_back(TAG_PADDING, PaddingMode::PKCS7);
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
+    string plaintext;
+    int32_t input_consumed;
+    EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext, &input_consumed));
+    EXPECT_EQ(ciphertext.size(), input_consumed);
+    EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(&plaintext));
+}
+
+struct TripleDesTestVector {
+    const char* name;
+    const KeyPurpose purpose;
+    const BlockMode block_mode;
+    const PaddingMode padding_mode;
+    const char* key;
+    const char* iv;
+    const char* input;
+    const char* output;
+};
+
+// These test vectors are from NIST CAVP, plus a few custom variants to test padding, since all
+// of the NIST vectors are multiples of the block size.
+static const TripleDesTestVector kTripleDesTestVectors[] = {
+        {
+                "TECBMMT3 Encrypt 0", KeyPurpose::ENCRYPT, BlockMode::ECB, PaddingMode::NONE,
+                "a2b5bc67da13dc92cd9d344aa238544a0e1fa79ef76810cd",  // key
+                "",                                                  // IV
+                "329d86bdf1bc5af4",                                  // input
+                "d946c2756d78633f",                                  // output
+        },
+        {
+                "TECBMMT3 Encrypt 1", KeyPurpose::ENCRYPT, BlockMode::ECB, PaddingMode::NONE,
+                "49e692290d2a5e46bace79b9648a4c5d491004c262dc9d49",  // key
+                "",                                                  // IV
+                "6b1540781b01ce1997adae102dbf3c5b",                  // input
+                "4d0dc182d6e481ac4a3dc6ab6976ccae",                  // output
+        },
+        {
+                "TECBMMT3 Decrypt 0", KeyPurpose::DECRYPT, BlockMode::ECB, PaddingMode::NONE,
+                "52daec2ac7dc1958377392682f37860b2cc1ea2304bab0e9",  // key
+                "",                                                  // IV
+                "6daad94ce08acfe7",                                  // input
+                "660e7d32dcc90e79",                                  // output
+        },
+        {
+                "TECBMMT3 Decrypt 1", KeyPurpose::DECRYPT, BlockMode::ECB, PaddingMode::NONE,
+                "7f8fe3d3f4a48394fb682c2919926d6ddfce8932529229ce",  // key
+                "",                                                  // IV
+                "e9653a0a1f05d31b9acd12d73aa9879d",                  // input
+                "9b2ae9d998efe62f1b592e7e1df8ff38",                  // output
+        },
+        {
+                "TCBCMMT3 Encrypt 0", KeyPurpose::ENCRYPT, BlockMode::CBC, PaddingMode::NONE,
+                "b5cb1504802326c73df186e3e352a20de643b0d63ee30e37",  // key
+                "43f791134c5647ba",                                  // IV
+                "dcc153cef81d6f24",                                  // input
+                "92538bd8af18d3ba",                                  // output
+        },
+        {
+                "TCBCMMT3 Encrypt 1", KeyPurpose::ENCRYPT, BlockMode::CBC, PaddingMode::NONE,
+                "a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358",  // key
+                "c2e999cb6249023c",                                  // IV
+                "c689aee38a301bb316da75db36f110b5",                  // input
+                "e9afaba5ec75ea1bbe65506655bb4ecb",                  // output
+        },
+        {
+                "TCBCMMT3 Encrypt 1 PKCS7 variant", KeyPurpose::ENCRYPT, BlockMode::CBC,
+                PaddingMode::PKCS7,
+                "a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358",  // key
+                "c2e999cb6249023c",                                  // IV
+                "c689aee38a301bb316da75db36f110b500",                // input
+                "e9afaba5ec75ea1bbe65506655bb4ecb825aa27ec0656156",  // output
+        },
+        {
+                "TCBCMMT3 Encrypt 1 PKCS7 decrypted", KeyPurpose::DECRYPT, BlockMode::CBC,
+                PaddingMode::PKCS7,
+                "a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358",  // key
+                "c2e999cb6249023c",                                  // IV
+                "e9afaba5ec75ea1bbe65506655bb4ecb825aa27ec0656156",  // input
+                "c689aee38a301bb316da75db36f110b500",                // output
+        },
+        {
+                "TCBCMMT3 Decrypt 0", KeyPurpose::DECRYPT, BlockMode::CBC, PaddingMode::NONE,
+                "5eb6040d46082c7aa7d06dfd08dfeac8c18364c1548c3ba1",  // key
+                "41746c7e442d3681",                                  // IV
+                "c53a7b0ec40600fe",                                  // input
+                "d4f00eb455de1034",                                  // output
+        },
+        {
+                "TCBCMMT3 Decrypt 1", KeyPurpose::DECRYPT, BlockMode::CBC, PaddingMode::NONE,
+                "5b1cce7c0dc1ec49130dfb4af45785ab9179e567f2c7d549",  // key
+                "3982bc02c3727d45",                                  // IV
+                "6006f10adef52991fcc777a1238bbb65",                  // input
+                "edae09288e9e3bc05746d872b48e3b29",                  // output
+        },
+};
+
+/*
+ * EncryptionOperationsTest.TripleDesTestVector
+ *
+ * Verifies that NIST (plus a few extra) test vectors produce the correct results.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesTestVector) {
+    constexpr size_t num_tests = sizeof(kTripleDesTestVectors) / sizeof(TripleDesTestVector);
+    for (auto* test = kTripleDesTestVectors; test < kTripleDesTestVectors + num_tests; ++test) {
+        SCOPED_TRACE(test->name);
+        CheckTripleDesTestVector(test->purpose, test->block_mode, test->padding_mode,
+                                 hex2str(test->key), hex2str(test->iv), hex2str(test->input),
+                                 hex2str(test->output));
+    }
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesCbcRoundTripSuccess
+ *
+ * Validates CBC mode functionality.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCbcRoundTripSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    // Two-block message.
+    string message = "1234567890123456";
+    vector<uint8_t> iv1;
+    string ciphertext1 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, &iv1);
+    EXPECT_EQ(message.size(), ciphertext1.size());
+
+    vector<uint8_t> iv2;
+    string ciphertext2 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, &iv2);
+    EXPECT_EQ(message.size(), ciphertext2.size());
+
+    // IVs should be random, so ciphertexts should differ.
+    EXPECT_NE(iv1, iv2);
+    EXPECT_NE(ciphertext1, ciphertext2);
+
+    string plaintext = DecryptMessage(ciphertext1, BlockMode::CBC, PaddingMode::NONE, iv1);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesCallerIv
+ *
+ * Validates that 3DES keys can allow caller-specified IVs, and use them correctly.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCallerIv) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Authorization(TAG_CALLER_NONCE)
+                                                 .Padding(PaddingMode::NONE)));
+    string message = "1234567890123456";
+    vector<uint8_t> iv;
+    // Don't specify IV, should get a random one.
+    string ciphertext1 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, &iv);
+    EXPECT_EQ(message.size(), ciphertext1.size());
+    EXPECT_EQ(8U, iv.size());
+
+    string plaintext = DecryptMessage(ciphertext1, BlockMode::CBC, PaddingMode::NONE, iv);
+    EXPECT_EQ(message, plaintext);
+
+    // Now specify an IV, should also work.
+    iv = AidlBuf("abcdefgh");
+    string ciphertext2 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, iv);
+
+    // Decrypt with correct IV.
+    plaintext = DecryptMessage(ciphertext2, BlockMode::CBC, PaddingMode::NONE, iv);
+    EXPECT_EQ(message, plaintext);
+
+    // Now try with wrong IV.
+    plaintext = DecryptMessage(ciphertext2, BlockMode::CBC, PaddingMode::NONE, AidlBuf("aaaaaaaa"));
+    EXPECT_NE(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest, TripleDesCallerNonceProhibited.
+ *
+ * Verifies that 3DES keys without TAG_CALLER_NONCE do not allow caller-specified IVS.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCallerNonceProhibited) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+
+    string message = "12345678901234567890123456789012";
+    vector<uint8_t> iv;
+    // Don't specify nonce, should get a random one.
+    string ciphertext1 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, &iv);
+    EXPECT_EQ(message.size(), ciphertext1.size());
+    EXPECT_EQ(8U, iv.size());
+
+    string plaintext = DecryptMessage(ciphertext1, BlockMode::CBC, PaddingMode::NONE, iv);
+    EXPECT_EQ(message, plaintext);
+
+    // Now specify a nonce, should fail.
+    auto input_params = AuthorizationSetBuilder()
+                                .Authorization(TAG_NONCE, AidlBuf("abcdefgh"))
+                                .BlockMode(BlockMode::CBC)
+                                .Padding(PaddingMode::NONE);
+    AuthorizationSet output_params;
+    EXPECT_EQ(ErrorCode::CALLER_NONCE_PROHIBITED,
+              Begin(KeyPurpose::ENCRYPT, input_params, &output_params));
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesCbcNotAuthorized
+ *
+ * Verifies that 3DES ECB-only keys do not allow CBC usage.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCbcNotAuthorized) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::ECB)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+    // Two-block message.
+    string message = "1234567890123456";
+    auto begin_params =
+            AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_BLOCK_MODE, Begin(KeyPurpose::ENCRYPT, begin_params));
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesCbcNoPaddingWrongInputSize
+ *
+ * Verifies that unpadded CBC operations reject inputs that are not a multiple of block size.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCbcNoPaddingWrongInputSize) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+    // Message is slightly shorter than two blocks.
+    string message = "123456789012345";
+
+    auto begin_params =
+            AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+    AuthorizationSet output_params;
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &output_params));
+    string ciphertext;
+    EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, "", &ciphertext));
+}
+
+/*
+ * EncryptionOperationsTest, TripleDesCbcPkcs7Padding.
+ *
+ * Verifies that PKCS7 padding works correctly in CBC mode.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCbcPkcs7Padding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::PKCS7)));
+
+    // Try various message lengths; all should work.
+    for (size_t i = 0; i < 32; ++i) {
+        string message(i, 'a');
+        vector<uint8_t> iv;
+        string ciphertext = EncryptMessage(message, BlockMode::CBC, PaddingMode::PKCS7, &iv);
+        EXPECT_EQ(i + 8 - (i % 8), ciphertext.size());
+        string plaintext = DecryptMessage(ciphertext, BlockMode::CBC, PaddingMode::PKCS7, iv);
+        EXPECT_EQ(message, plaintext);
+    }
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesCbcNoPaddingKeyWithPkcs7Padding
+ *
+ * Verifies that a key that requires PKCS7 padding cannot be used in unpadded mode.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCbcNoPaddingKeyWithPkcs7Padding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+
+    // Try various message lengths; all should fail.
+    for (size_t i = 0; i < 32; ++i) {
+        auto begin_params =
+                AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::PKCS7);
+        EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, begin_params));
+    }
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesCbcPkcs7PaddingCorrupted
+ *
+ * Verifies that corrupted PKCS7 padding is rejected during decryption.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCbcPkcs7PaddingCorrupted) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::PKCS7)));
+
+    string message = "a";
+    vector<uint8_t> iv;
+    string ciphertext = EncryptMessage(message, BlockMode::CBC, PaddingMode::PKCS7, &iv);
+    EXPECT_EQ(8U, ciphertext.size());
+    EXPECT_NE(ciphertext, message);
+    ++ciphertext[ciphertext.size() / 2];
+
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::CBC)
+                                .Padding(PaddingMode::PKCS7)
+                                .Authorization(TAG_NONCE, iv);
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
+    string plaintext;
+    int32_t input_consumed;
+    EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext, &input_consumed));
+    EXPECT_EQ(ciphertext.size(), input_consumed);
+    EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(&plaintext));
+}
+
+/*
+ * EncryptionOperationsTest, TripleDesCbcIncrementalNoPadding.
+ *
+ * Verifies that 3DES CBC works with many different input sizes.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCbcIncrementalNoPadding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+
+    int increment = 7;
+    string message(240, 'a');
+    AuthorizationSet input_params =
+            AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+    AuthorizationSet output_params;
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, input_params, &output_params));
+
+    string ciphertext;
+    int32_t input_consumed;
+    for (size_t i = 0; i < message.size(); i += increment)
+        EXPECT_EQ(ErrorCode::OK,
+                  Update(message.substr(i, increment), &ciphertext, &input_consumed));
+    EXPECT_EQ(ErrorCode::OK, Finish(&ciphertext));
+    EXPECT_EQ(message.size(), ciphertext.size());
+
+    // Move TAG_NONCE into input_params
+    input_params = output_params;
+    input_params.push_back(TAG_BLOCK_MODE, BlockMode::CBC);
+    input_params.push_back(TAG_PADDING, PaddingMode::NONE);
+    output_params.Clear();
+
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, input_params, &output_params));
+    string plaintext;
+    for (size_t i = 0; i < ciphertext.size(); i += increment)
+        EXPECT_EQ(ErrorCode::OK,
+                  Update(ciphertext.substr(i, increment), &plaintext, &input_consumed));
+    EXPECT_EQ(ErrorCode::OK, Finish(&plaintext));
+    EXPECT_EQ(ciphertext.size(), plaintext.size());
+    EXPECT_EQ(message, plaintext);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(EncryptionOperationsTest);
+
+typedef KeyMintAidlTestBase MaxOperationsTest;
+
+/*
+ * MaxOperationsTest.TestLimitAes
+ *
+ * Verifies that the max uses per boot tag works correctly with AES keys.
+ */
+TEST_P(MaxOperationsTest, TestLimitAes) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .EcbMode()
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MAX_USES_PER_BOOT, 3)));
+
+    string message = "1234567890123456";
+
+    auto params = AuthorizationSetBuilder().EcbMode().Padding(PaddingMode::NONE);
+
+    EncryptMessage(message, params);
+    EncryptMessage(message, params);
+    EncryptMessage(message, params);
+
+    // Fourth time should fail.
+    EXPECT_EQ(ErrorCode::KEY_MAX_OPS_EXCEEDED, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * MaxOperationsTest.TestLimitAes
+ *
+ * Verifies that the max uses per boot tag works correctly with RSA keys.
+ */
+TEST_P(MaxOperationsTest, TestLimitRsa) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaSigningKey(1024, 65537)
+                                                 .NoDigestOrPadding()
+                                                 .Authorization(TAG_MAX_USES_PER_BOOT, 3)));
+
+    string message = "1234567890123456";
+
+    auto params = AuthorizationSetBuilder().NoDigestOrPadding();
+
+    SignMessage(message, params);
+    SignMessage(message, params);
+    SignMessage(message, params);
+
+    // Fourth time should fail.
+    EXPECT_EQ(ErrorCode::KEY_MAX_OPS_EXCEEDED, Begin(KeyPurpose::SIGN, params));
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(MaxOperationsTest);
+
+typedef KeyMintAidlTestBase AddEntropyTest;
+
+/*
+ * AddEntropyTest.AddEntropy
+ *
+ * Verifies that the addRngEntropy method doesn't blow up.  There's no way to test that entropy
+ * is actually added.
+ */
+TEST_P(AddEntropyTest, AddEntropy) {
+    string data = "foo";
+    EXPECT_TRUE(keyMint().addRngEntropy(vector<uint8_t>(data.begin(), data.end())).isOk());
+}
+
+/*
+ * AddEntropyTest.AddEmptyEntropy
+ *
+ * Verifies that the addRngEntropy method doesn't blow up when given an empty buffer.
+ */
+TEST_P(AddEntropyTest, AddEmptyEntropy) {
+    EXPECT_TRUE(keyMint().addRngEntropy(AidlBuf()).isOk());
+}
+
+/*
+ * AddEntropyTest.AddLargeEntropy
+ *
+ * Verifies that the addRngEntropy method doesn't blow up when given a largish amount of data.
+ */
+TEST_P(AddEntropyTest, AddLargeEntropy) {
+    EXPECT_TRUE(keyMint().addRngEntropy(AidlBuf(string(2 * 1024, 'a'))).isOk());
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(AddEntropyTest);
+
+typedef KeyMintAidlTestBase AttestationTest;
+
+/*
+ * AttestationTest.RsaAttestation
+ *
+ * Verifies that attesting to RSA keys works and generates the expected output.
+ */
+// TODO(seleneh) add attestation tests back after decided on the new attestation
+// behavior under generateKey and importKey
+
+typedef KeyMintAidlTestBase KeyDeletionTest;
+
+/**
+ * KeyDeletionTest.DeleteKey
+ *
+ * This test checks that if rollback protection is implemented, DeleteKey invalidates a formerly
+ * valid key blob.
+ */
+TEST_P(KeyDeletionTest, DeleteKey) {
+    auto error = GenerateKey(AuthorizationSetBuilder()
+                                     .RsaSigningKey(2048, 65537)
+                                     .Digest(Digest::NONE)
+                                     .Padding(PaddingMode::NONE)
+                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                     .Authorization(TAG_ROLLBACK_RESISTANCE));
+    ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK);
+
+    // Delete must work if rollback protection is implemented
+    if (error == ErrorCode::OK) {
+        AuthorizationSet hardwareEnforced(key_characteristics_.hardwareEnforced);
+        ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE));
+
+        ASSERT_EQ(ErrorCode::OK, DeleteKey(true /* keep key blob */));
+
+        string message = "12345678901234567890123456789012";
+        AuthorizationSet begin_out_params;
+        EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+                  Begin(KeyPurpose::SIGN, key_blob_,
+                        AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE),
+                        &begin_out_params));
+        AbortIfNeeded();
+        key_blob_ = AidlBuf();
+    }
+}
+
+/**
+ * KeyDeletionTest.DeleteInvalidKey
+ *
+ * This test checks that the HAL excepts invalid key blobs..
+ */
+TEST_P(KeyDeletionTest, DeleteInvalidKey) {
+    // Generate key just to check if rollback protection is implemented
+    auto error = GenerateKey(AuthorizationSetBuilder()
+                                     .RsaSigningKey(2048, 65537)
+                                     .Digest(Digest::NONE)
+                                     .Padding(PaddingMode::NONE)
+                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                     .Authorization(TAG_ROLLBACK_RESISTANCE));
+    ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK);
+
+    // Delete must work if rollback protection is implemented
+    if (error == ErrorCode::OK) {
+        AuthorizationSet hardwareEnforced(key_characteristics_.hardwareEnforced);
+        ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE));
+
+        // Delete the key we don't care about the result at this point.
+        DeleteKey();
+
+        // Now create an invalid key blob and delete it.
+        key_blob_ = AidlBuf("just some garbage data which is not a valid key blob");
+
+        ASSERT_EQ(ErrorCode::OK, DeleteKey());
+    }
+}
+
+/**
+ * KeyDeletionTest.DeleteAllKeys
+ *
+ * This test is disarmed by default. To arm it use --arm_deleteAllKeys.
+ *
+ * BEWARE: This test has serious side effects. All user keys will be lost! This includes
+ * FBE/FDE encryption keys, which means that the device will not even boot until after the
+ * device has been wiped manually (e.g., fastboot flashall -w), and new FBE/FDE keys have
+ * been provisioned. Use this test only on dedicated testing devices that have no valuable
+ * credentials stored in Keystore/Keymint.
+ */
+TEST_P(KeyDeletionTest, DeleteAllKeys) {
+    if (!arm_deleteAllKeys) return;
+    auto error = GenerateKey(AuthorizationSetBuilder()
+                                     .RsaSigningKey(2048, 65537)
+                                     .Digest(Digest::NONE)
+                                     .Padding(PaddingMode::NONE)
+                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                     .Authorization(TAG_ROLLBACK_RESISTANCE));
+    ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK);
+
+    // Delete must work if rollback protection is implemented
+    if (error == ErrorCode::OK) {
+        AuthorizationSet hardwareEnforced(key_characteristics_.hardwareEnforced);
+        ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE));
+
+        ASSERT_EQ(ErrorCode::OK, DeleteAllKeys());
+
+        string message = "12345678901234567890123456789012";
+        AuthorizationSet begin_out_params;
+
+        EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+                  Begin(KeyPurpose::SIGN, key_blob_,
+                        AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE),
+                        &begin_out_params));
+        AbortIfNeeded();
+        key_blob_ = AidlBuf();
+    }
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(KeyDeletionTest);
+
+using UpgradeKeyTest = KeyMintAidlTestBase;
+
+/*
+ * UpgradeKeyTest.UpgradeKey
+ *
+ * Verifies that calling upgrade key on an up-to-date key works (i.e. does nothing).
+ */
+TEST_P(UpgradeKeyTest, UpgradeKey) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .AesEncryptionKey(128)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)));
+
+    auto result = UpgradeKey(key_blob_);
+
+    // Key doesn't need upgrading.  Should get okay, but no new key blob.
+    EXPECT_EQ(result, std::make_pair(ErrorCode::OK, vector<uint8_t>()));
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(UpgradeKeyTest);
+
+using ClearOperationsTest = KeyMintAidlTestBase;
+
+/*
+ * ClearSlotsTest.TooManyOperations
+ *
+ * Verifies that TOO_MANY_OPERATIONS is returned after the max number of
+ * operations are started without being finished or aborted. Also verifies
+ * that aborting the operations clears the operations.
+ *
+ */
+TEST_P(ClearOperationsTest, TooManyOperations) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::NONE)));
+
+    auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+    constexpr size_t max_operations = 100;  // set to arbituary large number
+    sp<IKeyMintOperation> op_handles[max_operations];
+    AuthorizationSet out_params;
+    ErrorCode result;
+    size_t i;
+
+    for (i = 0; i < max_operations; i++) {
+        result = Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, op_handles[i]);
+        if (ErrorCode::OK != result) {
+            break;
+        }
+    }
+    EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, result);
+    // Try again just in case there's a weird overflow bug
+    EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS,
+              Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params));
+    for (size_t j = 0; j < i; j++) {
+        EXPECT_EQ(ErrorCode::OK, Abort(op_handles[j]))
+                << "Aboort failed for i = " << j << std::endl;
+    }
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params));
+    AbortIfNeeded();
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(ClearOperationsTest);
+
+typedef KeyMintAidlTestBase TransportLimitTest;
+
+/*
+ * TransportLimitTest.FinishInput
+ *
+ * Verifies that passing input data to finish succeeds as expected.
+ */
+TEST_P(TransportLimitTest, LargeFinishInput) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::ECB)
+                                                 .Padding(PaddingMode::NONE)));
+
+    for (int msg_size = 8 /* 256 bytes */; msg_size <= 11 /* 2 KiB */; msg_size++) {
+        auto cipher_params =
+                AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+
+        AuthorizationSet out_params;
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, cipher_params, &out_params));
+
+        string plain_message = std::string(1 << msg_size, 'x');
+        string encrypted_message;
+        auto rc = Finish(plain_message, &encrypted_message);
+
+        EXPECT_EQ(ErrorCode::OK, rc);
+        EXPECT_EQ(plain_message.size(), encrypted_message.size())
+                << "Encrypt finish returned OK, but did not consume all of the given input";
+        cipher_params.push_back(out_params);
+
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, cipher_params));
+
+        string decrypted_message;
+        rc = Finish(encrypted_message, &decrypted_message);
+        EXPECT_EQ(ErrorCode::OK, rc);
+        EXPECT_EQ(plain_message.size(), decrypted_message.size())
+                << "Decrypt finish returned OK, did not consume all of the given input";
+    }
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(TransportLimitTest);
+
+}  // namespace android::hardware::security::keymint::test
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    for (int i = 1; i < argc; ++i) {
+        if (argv[i][0] == '-') {
+            if (std::string(argv[i]) == "--arm_deleteAllKeys") {
+                arm_deleteAllKeys = true;
+            }
+            if (std::string(argv[i]) == "--dump_attestations") {
+                dump_Attestations = true;
+            }
+        }
+    }
+    return RUN_ALL_TESTS();
+}
diff --git a/security/keymint/aidl/vts/functional/VerificationTokenTest.cpp b/security/keymint/aidl/vts/functional/VerificationTokenTest.cpp
new file mode 100644
index 0000000..6d3a34e
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/VerificationTokenTest.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyMintAidlTestBase.h"
+
+namespace android::hardware::security::keymint::test {
+
+class VerificationTokenTest : public KeyMintAidlTestBase {
+  protected:
+    struct VerifyAuthorizationResult {
+        ErrorCode error;
+        VerificationToken token;
+    };
+
+    VerifyAuthorizationResult verifyAuthorization(uint64_t operationHandle,
+                                                  const HardwareAuthToken& authToken) {
+        VerifyAuthorizationResult result;
+
+        Status err;
+        err = keyMint().verifyAuthorization(operationHandle,  //
+                                            authToken,        //
+                                            &result.token);
+
+        result.error = GetReturnErrorCode(err);
+        return result;
+    }
+
+    uint64_t getTime() {
+        struct timespec timespec;
+        EXPECT_EQ(0, clock_gettime(CLOCK_BOOTTIME, &timespec));
+        return timespec.tv_sec * 1000 + timespec.tv_nsec / 1000000;
+    }
+
+    int sleep_ms(uint32_t milliseconds) {
+        struct timespec sleep_time = {static_cast<time_t>(milliseconds / 1000),
+                                      static_cast<long>(milliseconds % 1000) * 1000000};
+        while (sleep_time.tv_sec || sleep_time.tv_nsec) {
+            if (nanosleep(&sleep_time /* to wait */,
+                          &sleep_time /* remaining (on interrruption) */) == 0) {
+                sleep_time = {};
+            } else {
+                if (errno != EINTR) return errno;
+            }
+        }
+        return 0;
+    }
+};
+
+/*
+ * VerificationTokens exist to facilitate cross-KeyMint verification of requirements.  As
+ * such, the precise capabilities required will vary depending on the specific vendor
+ * implementations. Essentially, VerificationTokens are a "hook" to enable vendor
+ * implementations to communicate, so the precise usage is defined by those vendors.  The only
+ * thing we really can test is that tokens can be created by TEE keyMints, and that the
+ * timestamps increase as expected.
+ */
+TEST_P(VerificationTokenTest, TestCreation) {
+    auto result1 = verifyAuthorization(1 /* operation handle */, HardwareAuthToken());
+    auto result1_time = getTime();
+
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        // StrongBox should not implement verifyAuthorization.
+        EXPECT_EQ(ErrorCode::UNIMPLEMENTED, result1.error);
+        return;
+    }
+
+    ASSERT_EQ(ErrorCode::OK, result1.error);
+    EXPECT_EQ(1U, result1.token.challenge);
+    EXPECT_EQ(SecLevel(), result1.token.securityLevel);
+    EXPECT_GT(result1.token.timestamp.milliSeconds, 0U);
+
+    constexpr uint32_t time_to_sleep = 200;
+    sleep_ms(time_to_sleep);
+
+    auto result2 = verifyAuthorization(2 /* operation handle */, HardwareAuthToken());
+
+    auto result2_time = getTime();
+    ASSERT_EQ(ErrorCode::OK, result2.error);
+    EXPECT_EQ(2U, result2.token.challenge);
+    EXPECT_EQ(SecLevel(), result2.token.securityLevel);
+
+    auto host_time_delta = result2_time - result1_time;
+
+    EXPECT_GE(host_time_delta, time_to_sleep)
+            << "We slept for " << time_to_sleep << " ms, the clock must have advanced by that much";
+    EXPECT_LE(host_time_delta, time_to_sleep + 20)
+            << "The verifyAuthorization call took " << (host_time_delta - time_to_sleep)
+            << " ms?  That's awful!";
+
+    auto km_time_delta =
+            result2.token.timestamp.milliSeconds - result1.token.timestamp.milliSeconds;
+
+    // If not too much else is going on on the system, the time delta should be quite close.  Allow
+    // 2 ms of slop just to avoid test flakiness.
+    //
+    // TODO(swillden): see if we can output values so they can be gathered across many runs and
+    // report if times aren't nearly always <1ms apart.
+    EXPECT_LE(host_time_delta, km_time_delta + 2);
+    EXPECT_LE(km_time_delta, host_time_delta + 2);
+    ASSERT_EQ(result1.token.mac.size(), result2.token.mac.size());
+    ASSERT_NE(0,
+              memcmp(result1.token.mac.data(), result2.token.mac.data(), result1.token.mac.size()));
+}
+
+/*
+ * Test that the mac changes when the time stamp changes. This is does not guarantee that the time
+ * stamp is included in the mac but on failure we know that it is not. Other than in the test
+ * case above we call verifyAuthorization with the exact same set of parameters.
+ */
+TEST_P(VerificationTokenTest, MacChangesOnChangingTimestamp) {
+    auto result1 = verifyAuthorization(0 /* operation handle */, HardwareAuthToken());
+    auto result1_time = getTime();
+
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        // StrongBox should not implement verifyAuthorization.
+        EXPECT_EQ(ErrorCode::UNIMPLEMENTED, result1.error);
+        return;
+    }
+
+    EXPECT_EQ(ErrorCode::OK, result1.error);
+    EXPECT_EQ(0U, result1.token.challenge);
+    EXPECT_EQ(SecLevel(), result1.token.securityLevel);
+    EXPECT_GT(result1.token.timestamp.milliSeconds, 0U);
+
+    constexpr uint32_t time_to_sleep = 200;
+    sleep_ms(time_to_sleep);
+
+    auto result2 = verifyAuthorization(0 /* operation handle */, HardwareAuthToken());
+    // ASSERT_TRUE(result2.callSuccessful);
+    auto result2_time = getTime();
+    EXPECT_EQ(ErrorCode::OK, result2.error);
+    EXPECT_EQ(0U, result2.token.challenge);
+    EXPECT_EQ(SecLevel(), result2.token.securityLevel);
+
+    auto host_time_delta = result2_time - result1_time;
+
+    EXPECT_GE(host_time_delta, time_to_sleep)
+            << "We slept for " << time_to_sleep << " ms, the clock must have advanced by that much";
+    EXPECT_LE(host_time_delta, time_to_sleep + 20)
+            << "The verifyAuthorization call took " << (host_time_delta - time_to_sleep)
+            << " ms?  That's awful!";
+
+    auto km_time_delta =
+            result2.token.timestamp.milliSeconds - result1.token.timestamp.milliSeconds;
+
+    EXPECT_LE(host_time_delta, km_time_delta + 2);
+    EXPECT_LE(km_time_delta, host_time_delta + 2);
+    ASSERT_EQ(result1.token.mac.size(), result2.token.mac.size());
+    ASSERT_NE(0,
+              memcmp(result1.token.mac.data(), result2.token.mac.data(), result1.token.mac.size()));
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(VerificationTokenTest);
+
+}  // namespace android::hardware::security::keymint::test
diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp
new file mode 100644
index 0000000..ddac92f
--- /dev/null
+++ b/security/keymint/support/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library {
+    name: "libkeymint_support",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    srcs: [
+        "attestation_record.cpp",
+        "authorization_set.cpp",
+        "keymint_utils.cpp",
+        "key_param_output.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "android.hardware.security.keymint-cpp",
+        "libbase",
+        "libcrypto",
+        "libutils",
+    ],
+}
diff --git a/security/keymint/support/OWNERS b/security/keymint/support/OWNERS
new file mode 100644
index 0000000..a93b171
--- /dev/null
+++ b/security/keymint/support/OWNERS
@@ -0,0 +1,4 @@
+jbires@google.com
+jdanis@google.com
+seleneh@google.com
+swillden@google.com
diff --git a/security/keymint/support/attestation_record.cpp b/security/keymint/support/attestation_record.cpp
new file mode 100644
index 0000000..afdb208
--- /dev/null
+++ b/security/keymint/support/attestation_record.cpp
@@ -0,0 +1,384 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <keymint_support/attestation_record.h>
+
+#include <assert.h>
+
+#include <android-base/logging.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <android/hardware/security/keymint/Tag.h>
+#include <android/hardware/security/keymint/TagType.h>
+
+#include <keymint_support/authorization_set.h>
+#include <keymint_support/openssl_utils.h>
+
+#define AT __FILE__ ":" << __LINE__
+
+namespace android::hardware::security::keymint {
+
+struct stack_st_ASN1_TYPE_Delete {
+    void operator()(stack_st_ASN1_TYPE* p) { sk_ASN1_TYPE_free(p); }
+};
+
+struct ASN1_STRING_Delete {
+    void operator()(ASN1_STRING* p) { ASN1_STRING_free(p); }
+};
+
+struct ASN1_TYPE_Delete {
+    void operator()(ASN1_TYPE* p) { ASN1_TYPE_free(p); }
+};
+
+#define ASN1_INTEGER_SET STACK_OF(ASN1_INTEGER)
+
+typedef struct km_root_of_trust {
+    ASN1_OCTET_STRING* verified_boot_key;
+    ASN1_BOOLEAN device_locked;
+    ASN1_ENUMERATED* verified_boot_state;
+    ASN1_OCTET_STRING* verified_boot_hash;
+} KM_ROOT_OF_TRUST;
+
+ASN1_SEQUENCE(KM_ROOT_OF_TRUST) = {
+        ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_key, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(KM_ROOT_OF_TRUST, device_locked, ASN1_BOOLEAN),
+        ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_state, ASN1_ENUMERATED),
+        ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_hash, ASN1_OCTET_STRING),
+} ASN1_SEQUENCE_END(KM_ROOT_OF_TRUST);
+IMPLEMENT_ASN1_FUNCTIONS(KM_ROOT_OF_TRUST);
+
+typedef struct km_auth_list {
+    ASN1_INTEGER_SET* purpose;
+    ASN1_INTEGER* algorithm;
+    ASN1_INTEGER* key_size;
+    ASN1_INTEGER_SET* digest;
+    ASN1_INTEGER_SET* padding;
+    ASN1_INTEGER* ec_curve;
+    ASN1_INTEGER* rsa_public_exponent;
+    ASN1_INTEGER* active_date_time;
+    ASN1_INTEGER* origination_expire_date_time;
+    ASN1_INTEGER* usage_expire_date_time;
+    ASN1_NULL* no_auth_required;
+    ASN1_INTEGER* user_auth_type;
+    ASN1_INTEGER* auth_timeout;
+    ASN1_NULL* allow_while_on_body;
+    ASN1_NULL* all_applications;
+    ASN1_OCTET_STRING* application_id;
+    ASN1_INTEGER* creation_date_time;
+    ASN1_INTEGER* origin;
+    ASN1_NULL* rollback_resistance;
+    KM_ROOT_OF_TRUST* root_of_trust;
+    ASN1_INTEGER* os_version;
+    ASN1_INTEGER* os_patchlevel;
+    ASN1_OCTET_STRING* attestation_application_id;
+    ASN1_NULL* trusted_user_presence_required;
+    ASN1_NULL* trusted_confirmation_required;
+    ASN1_NULL* unlocked_device_required;
+    ASN1_INTEGER* vendor_patchlevel;
+    ASN1_INTEGER* boot_patchlevel;
+    ASN1_NULL* early_boot_only;
+    ASN1_NULL* device_unique_attestation;
+    ASN1_NULL* storage_key;
+    ASN1_NULL* identity_credential;
+} KM_AUTH_LIST;
+
+ASN1_SEQUENCE(KM_AUTH_LIST) = {
+        ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, purpose, ASN1_INTEGER, TAG_PURPOSE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, algorithm, ASN1_INTEGER, TAG_ALGORITHM.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, key_size, ASN1_INTEGER, TAG_KEY_SIZE.maskedTag()),
+        ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, digest, ASN1_INTEGER, TAG_DIGEST.maskedTag()),
+        ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, padding, ASN1_INTEGER, TAG_PADDING.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, ec_curve, ASN1_INTEGER, TAG_EC_CURVE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, rsa_public_exponent, ASN1_INTEGER,
+                     TAG_RSA_PUBLIC_EXPONENT.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, rollback_resistance, ASN1_NULL,
+                     TAG_ROLLBACK_RESISTANCE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, active_date_time, ASN1_INTEGER, TAG_ACTIVE_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, origination_expire_date_time, ASN1_INTEGER,
+                     TAG_ORIGINATION_EXPIRE_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, usage_expire_date_time, ASN1_INTEGER,
+                     TAG_USAGE_EXPIRE_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, no_auth_required, ASN1_NULL, TAG_NO_AUTH_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, user_auth_type, ASN1_INTEGER, TAG_USER_AUTH_TYPE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, auth_timeout, ASN1_INTEGER, TAG_AUTH_TIMEOUT.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, allow_while_on_body, ASN1_NULL,
+                     TAG_ALLOW_WHILE_ON_BODY.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, trusted_user_presence_required, ASN1_NULL,
+                     TAG_TRUSTED_USER_PRESENCE_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, trusted_confirmation_required, ASN1_NULL,
+                     TAG_TRUSTED_CONFIRMATION_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, unlocked_device_required, ASN1_NULL,
+                     TAG_UNLOCKED_DEVICE_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, creation_date_time, ASN1_INTEGER,
+                     TAG_CREATION_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, origin, ASN1_INTEGER, TAG_ORIGIN.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, root_of_trust, KM_ROOT_OF_TRUST, TAG_ROOT_OF_TRUST.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, os_version, ASN1_INTEGER, TAG_OS_VERSION.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, os_patchlevel, ASN1_INTEGER, TAG_OS_PATCHLEVEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, vendor_patchlevel, ASN1_INTEGER,
+                     TAG_VENDOR_PATCHLEVEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, boot_patchlevel, ASN1_INTEGER, TAG_BOOT_PATCHLEVEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_application_id, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_APPLICATION_ID.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, early_boot_only, ASN1_NULL, TAG_EARLY_BOOT_ONLY.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, device_unique_attestation, ASN1_NULL,
+                     TAG_DEVICE_UNIQUE_ATTESTATION.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, storage_key, ASN1_NULL, TAG_STORAGE_KEY.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, identity_credential, ASN1_NULL,
+                     TAG_IDENTITY_CREDENTIAL_KEY.maskedTag()),
+
+} ASN1_SEQUENCE_END(KM_AUTH_LIST);
+IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST);
+
+typedef struct km_key_description {
+    ASN1_INTEGER* attestation_version;
+    ASN1_ENUMERATED* attestation_security_level;
+    ASN1_INTEGER* keymint_version;
+    ASN1_ENUMERATED* keymint_security_level;
+    ASN1_OCTET_STRING* attestation_challenge;
+    KM_AUTH_LIST* software_enforced;
+    KM_AUTH_LIST* tee_enforced;
+    ASN1_INTEGER* unique_id;
+} KM_KEY_DESCRIPTION;
+
+ASN1_SEQUENCE(KM_KEY_DESCRIPTION) = {
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_version, ASN1_INTEGER),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_security_level, ASN1_ENUMERATED),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymint_version, ASN1_INTEGER),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymint_security_level, ASN1_ENUMERATED),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_challenge, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, unique_id, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, software_enforced, KM_AUTH_LIST),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, tee_enforced, KM_AUTH_LIST),
+} ASN1_SEQUENCE_END(KM_KEY_DESCRIPTION);
+IMPLEMENT_ASN1_FUNCTIONS(KM_KEY_DESCRIPTION);
+
+template <Tag tag>
+void copyAuthTag(const stack_st_ASN1_INTEGER* stack, TypedTag<TagType::ENUM_REP, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    typedef typename TypedTag2ValueType<decltype(ttag)>::type ValueT;
+    for (size_t i = 0; i < sk_ASN1_INTEGER_num(stack); ++i) {
+        auth_list->push_back(
+                ttag, static_cast<ValueT>(ASN1_INTEGER_get(sk_ASN1_INTEGER_value(stack, i))));
+    }
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::ENUM, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    typedef typename TypedTag2ValueType<decltype(ttag)>::type ValueT;
+    if (!asn1_int) return;
+    auth_list->push_back(ttag, static_cast<ValueT>(ASN1_INTEGER_get(asn1_int)));
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::UINT, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_int) return;
+    auth_list->push_back(ttag, ASN1_INTEGER_get(asn1_int));
+}
+
+BIGNUM* construct_uint_max() {
+    BIGNUM* value = BN_new();
+    BIGNUM_Ptr one(BN_new());
+    BN_one(one.get());
+    BN_lshift(value, one.get(), 32);
+    return value;
+}
+
+uint64_t BignumToUint64(BIGNUM* num) {
+    static_assert((sizeof(BN_ULONG) == sizeof(uint32_t)) || (sizeof(BN_ULONG) == sizeof(uint64_t)),
+                  "This implementation only supports 32 and 64-bit BN_ULONG");
+    if (sizeof(BN_ULONG) == sizeof(uint32_t)) {
+        BIGNUM_Ptr uint_max(construct_uint_max());
+        BIGNUM_Ptr hi(BN_new()), lo(BN_new());
+        BN_CTX_Ptr ctx(BN_CTX_new());
+        BN_div(hi.get(), lo.get(), num, uint_max.get(), ctx.get());
+        return static_cast<uint64_t>(BN_get_word(hi.get())) << 32 | BN_get_word(lo.get());
+    } else if (sizeof(BN_ULONG) == sizeof(uint64_t)) {
+        return BN_get_word(num);
+    } else {
+        return 0;
+    }
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::ULONG, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_int) return;
+    BIGNUM_Ptr num(ASN1_INTEGER_to_BN(asn1_int, nullptr));
+    auth_list->push_back(ttag, BignumToUint64(num.get()));
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::DATE, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_int) return;
+    BIGNUM_Ptr num(ASN1_INTEGER_to_BN(asn1_int, nullptr));
+    auth_list->push_back(ttag, BignumToUint64(num.get()));
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_NULL* asn1_null, TypedTag<TagType::BOOL, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_null) return;
+    auth_list->push_back(ttag);
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_OCTET_STRING* asn1_string, TypedTag<TagType::BYTES, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_string) return;
+    vector<uint8_t> buf(asn1_string->data, asn1_string->data + asn1_string->length);
+    auth_list->push_back(ttag, buf);
+}
+
+// Extract the values from the specified ASN.1 record and place them in auth_list.
+static ErrorCode extract_auth_list(const KM_AUTH_LIST* record, AuthorizationSet* auth_list) {
+    if (!record) return ErrorCode::OK;
+
+    copyAuthTag(record->active_date_time, TAG_ACTIVE_DATETIME, auth_list);
+    copyAuthTag(record->algorithm, TAG_ALGORITHM, auth_list);
+    copyAuthTag(record->application_id, TAG_APPLICATION_ID, auth_list);
+    copyAuthTag(record->auth_timeout, TAG_AUTH_TIMEOUT, auth_list);
+    copyAuthTag(record->creation_date_time, TAG_CREATION_DATETIME, auth_list);
+    copyAuthTag(record->digest, TAG_DIGEST, auth_list);
+    copyAuthTag(record->ec_curve, TAG_EC_CURVE, auth_list);
+    copyAuthTag(record->key_size, TAG_KEY_SIZE, auth_list);
+    copyAuthTag(record->no_auth_required, TAG_NO_AUTH_REQUIRED, auth_list);
+    copyAuthTag(record->origin, TAG_ORIGIN, auth_list);
+    copyAuthTag(record->origination_expire_date_time, TAG_ORIGINATION_EXPIRE_DATETIME, auth_list);
+    copyAuthTag(record->os_patchlevel, TAG_OS_PATCHLEVEL, auth_list);
+    copyAuthTag(record->os_version, TAG_OS_VERSION, auth_list);
+    copyAuthTag(record->padding, TAG_PADDING, auth_list);
+    copyAuthTag(record->purpose, TAG_PURPOSE, auth_list);
+    copyAuthTag(record->rollback_resistance, TAG_ROLLBACK_RESISTANCE, auth_list);
+    copyAuthTag(record->rsa_public_exponent, TAG_RSA_PUBLIC_EXPONENT, auth_list);
+    copyAuthTag(record->usage_expire_date_time, TAG_USAGE_EXPIRE_DATETIME, auth_list);
+    copyAuthTag(record->user_auth_type, TAG_USER_AUTH_TYPE, auth_list);
+    copyAuthTag(record->attestation_application_id, TAG_ATTESTATION_APPLICATION_ID, auth_list);
+    copyAuthTag(record->vendor_patchlevel, TAG_VENDOR_PATCHLEVEL, auth_list);
+    copyAuthTag(record->boot_patchlevel, TAG_BOOT_PATCHLEVEL, auth_list);
+    copyAuthTag(record->trusted_user_presence_required, TAG_TRUSTED_USER_PRESENCE_REQUIRED,
+                auth_list);
+    copyAuthTag(record->trusted_confirmation_required, TAG_TRUSTED_CONFIRMATION_REQUIRED,
+                auth_list);
+    copyAuthTag(record->unlocked_device_required, TAG_UNLOCKED_DEVICE_REQUIRED, auth_list);
+    copyAuthTag(record->early_boot_only, TAG_EARLY_BOOT_ONLY, auth_list);
+    copyAuthTag(record->device_unique_attestation, TAG_DEVICE_UNIQUE_ATTESTATION, auth_list);
+    copyAuthTag(record->storage_key, TAG_STORAGE_KEY, auth_list);
+    copyAuthTag(record->identity_credential, TAG_IDENTITY_CREDENTIAL_KEY, auth_list);
+
+    return ErrorCode::OK;
+}
+
+MAKE_OPENSSL_PTR_TYPE(KM_KEY_DESCRIPTION)
+
+// Parse the DER-encoded attestation record, placing the results in keymint_version,
+// attestation_challenge, software_enforced, tee_enforced and unique_id.
+ErrorCode parse_attestation_record(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
+                                   uint32_t* attestation_version,  //
+                                   SecurityLevel* attestation_security_level,
+                                   uint32_t* keymint_version, SecurityLevel* keymint_security_level,
+                                   vector<uint8_t>* attestation_challenge,
+                                   AuthorizationSet* software_enforced,
+                                   AuthorizationSet* tee_enforced,  //
+                                   vector<uint8_t>* unique_id) {
+    const uint8_t* p = asn1_key_desc;
+    KM_KEY_DESCRIPTION_Ptr record(d2i_KM_KEY_DESCRIPTION(nullptr, &p, asn1_key_desc_len));
+    if (!record.get()) return ErrorCode::UNKNOWN_ERROR;
+
+    *attestation_version = ASN1_INTEGER_get(record->attestation_version);
+    *attestation_security_level =
+            static_cast<SecurityLevel>(ASN1_ENUMERATED_get(record->attestation_security_level));
+    *keymint_version = ASN1_INTEGER_get(record->keymint_version);
+    *keymint_security_level =
+            static_cast<SecurityLevel>(ASN1_ENUMERATED_get(record->keymint_security_level));
+
+    auto& chall = record->attestation_challenge;
+    attestation_challenge->resize(chall->length);
+    memcpy(attestation_challenge->data(), chall->data, chall->length);
+    auto& uid = record->unique_id;
+    unique_id->resize(uid->length);
+    memcpy(unique_id->data(), uid->data, uid->length);
+
+    ErrorCode error = extract_auth_list(record->software_enforced, software_enforced);
+    if (error != ErrorCode::OK) return error;
+
+    return extract_auth_list(record->tee_enforced, tee_enforced);
+}
+
+ErrorCode parse_root_of_trust(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
+                              vector<uint8_t>* verified_boot_key,
+                              keymint_verified_boot_t* verified_boot_state, bool* device_locked,
+                              vector<uint8_t>* verified_boot_hash) {
+    if (!verified_boot_key || !verified_boot_state || !device_locked || !verified_boot_hash) {
+        LOG(ERROR) << AT << "null pointer input(s)";
+        return ErrorCode::INVALID_ARGUMENT;
+    }
+    const uint8_t* p = asn1_key_desc;
+    KM_KEY_DESCRIPTION_Ptr record(d2i_KM_KEY_DESCRIPTION(nullptr, &p, asn1_key_desc_len));
+    if (!record.get()) {
+        LOG(ERROR) << AT << "Failed record parsing";
+        return ErrorCode::UNKNOWN_ERROR;
+    }
+
+    KM_ROOT_OF_TRUST* root_of_trust = nullptr;
+    if (record->tee_enforced && record->tee_enforced->root_of_trust) {
+        root_of_trust = record->tee_enforced->root_of_trust;
+    } else if (record->software_enforced && record->software_enforced->root_of_trust) {
+        root_of_trust = record->software_enforced->root_of_trust;
+    } else {
+        LOG(ERROR) << AT << " Failed root of trust parsing";
+        return ErrorCode::INVALID_ARGUMENT;
+    }
+    if (!root_of_trust->verified_boot_key) {
+        LOG(ERROR) << AT << " Failed verified boot key parsing";
+        return ErrorCode::INVALID_ARGUMENT;
+    }
+
+    auto& vb_key = root_of_trust->verified_boot_key;
+    verified_boot_key->resize(vb_key->length);
+    memcpy(verified_boot_key->data(), vb_key->data, vb_key->length);
+
+    *verified_boot_state = static_cast<keymint_verified_boot_t>(
+            ASN1_ENUMERATED_get(root_of_trust->verified_boot_state));
+    if (!verified_boot_state) {
+        LOG(ERROR) << AT << " Failed verified boot state parsing";
+        return ErrorCode::INVALID_ARGUMENT;
+    }
+
+    *device_locked = root_of_trust->device_locked;
+    if (!device_locked) {
+        LOG(ERROR) << AT << " Failed device locked parsing";
+        return ErrorCode::INVALID_ARGUMENT;
+    }
+
+    auto& vb_hash = root_of_trust->verified_boot_hash;
+    if (!vb_hash) {
+        LOG(ERROR) << AT << " Failed verified boot hash parsing";
+        return ErrorCode::INVALID_ARGUMENT;
+    }
+    verified_boot_hash->resize(vb_hash->length);
+    memcpy(verified_boot_hash->data(), vb_hash->data, vb_hash->length);
+    return ErrorCode::OK;  // KM_ERROR_OK;
+}
+
+}  // namespace android::hardware::security::keymint
diff --git a/security/keymint/support/authorization_set.cpp b/security/keymint/support/authorization_set.cpp
new file mode 100644
index 0000000..aa9638f
--- /dev/null
+++ b/security/keymint/support/authorization_set.cpp
@@ -0,0 +1,526 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <keymint_support/authorization_set.h>
+
+#include <assert.h>
+#include <sstream>
+
+#include <android-base/logging.h>
+
+#include <android/hardware/security/keymint/Algorithm.h>
+#include <android/hardware/security/keymint/BlockMode.h>
+#include <android/hardware/security/keymint/Digest.h>
+#include <android/hardware/security/keymint/KeyParameter.h>
+#include <android/hardware/security/keymint/KeyPurpose.h>
+#include <android/hardware/security/keymint/TagType.h>
+
+namespace android::hardware::security::keymint {
+
+void AuthorizationSet::Sort() {
+    std::sort(data_.begin(), data_.end());
+}
+
+void AuthorizationSet::Deduplicate() {
+    if (data_.empty()) return;
+
+    Sort();
+    std::vector<KeyParameter> result;
+
+    auto curr = data_.begin();
+    auto prev = curr++;
+    for (; curr != data_.end(); ++prev, ++curr) {
+        if (prev->tag == Tag::INVALID) continue;
+
+        if (*prev != *curr) {
+            result.push_back(std::move(*prev));
+        }
+    }
+    result.push_back(std::move(*prev));
+
+    std::swap(data_, result);
+}
+
+void AuthorizationSet::Union(const AuthorizationSet& other) {
+    data_.insert(data_.end(), other.data_.begin(), other.data_.end());
+    Deduplicate();
+}
+
+void AuthorizationSet::Subtract(const AuthorizationSet& other) {
+    Deduplicate();
+
+    auto i = other.begin();
+    while (i != other.end()) {
+        int pos = -1;
+        do {
+            pos = find(i->tag, pos);
+            if (pos != -1 && (*i == data_[pos])) {
+                data_.erase(data_.begin() + pos);
+                break;
+            }
+        } while (pos != -1);
+        ++i;
+    }
+}
+
+void AuthorizationSet::Filter(std::function<bool(const KeyParameter&)> doKeep) {
+    std::vector<KeyParameter> result;
+    for (auto& param : data_) {
+        if (doKeep(param)) {
+            result.push_back(std::move(param));
+        }
+    }
+    std::swap(data_, result);
+}
+
+KeyParameter& AuthorizationSet::operator[](int at) {
+    return data_[at];
+}
+
+const KeyParameter& AuthorizationSet::operator[](int at) const {
+    return data_[at];
+}
+
+void AuthorizationSet::Clear() {
+    data_.clear();
+}
+
+size_t AuthorizationSet::GetTagCount(Tag tag) const {
+    size_t count = 0;
+    for (int pos = -1; (pos = find(tag, pos)) != -1;) ++count;
+    return count;
+}
+
+int AuthorizationSet::find(Tag tag, int begin) const {
+    auto iter = data_.begin() + (1 + begin);
+
+    while (iter != data_.end() && iter->tag != tag) ++iter;
+
+    if (iter != data_.end()) return iter - data_.begin();
+    return -1;
+}
+
+bool AuthorizationSet::erase(int index) {
+    auto pos = data_.begin() + index;
+    if (pos != data_.end()) {
+        data_.erase(pos);
+        return true;
+    }
+    return false;
+}
+
+NullOr<const KeyParameter&> AuthorizationSet::GetEntry(Tag tag) const {
+    int pos = find(tag);
+    if (pos == -1) return {};
+    return data_[pos];
+}
+
+/**
+ * Persistent format is:
+ * | 32 bit indirect_size         |
+ * --------------------------------
+ * | indirect_size bytes of data  | this is where the blob data is stored
+ * --------------------------------
+ * | 32 bit element_count         | number of entries
+ * | 32 bit elements_size         | total bytes used by entries (entries have variable length)
+ * --------------------------------
+ * | elementes_size bytes of data | where the elements are stored
+ */
+
+/**
+ * Persistent format of blobs and bignums:
+ * | 32 bit tag             |
+ * | 32 bit blob_length     |
+ * | 32 bit indirect_offset |
+ */
+
+struct OutStreams {
+    std::ostream& indirect;
+    std::ostream& elements;
+    size_t skipped;
+};
+
+OutStreams& serializeParamValue(OutStreams& out, const vector<uint8_t>& blob) {
+    uint32_t buffer;
+
+    // write blob_length
+    auto blob_length = blob.size();
+    if (blob_length > std::numeric_limits<uint32_t>::max()) {
+        out.elements.setstate(std::ios_base::badbit);
+        return out;
+    }
+    buffer = blob_length;
+    out.elements.write(reinterpret_cast<const char*>(&buffer), sizeof(uint32_t));
+
+    // write indirect_offset
+    auto offset = out.indirect.tellp();
+    if (offset < 0 || offset > std::numeric_limits<uint32_t>::max() ||
+        uint32_t(offset) + uint32_t(blob_length) < uint32_t(offset)) {  // overflow check
+        out.elements.setstate(std::ios_base::badbit);
+        return out;
+    }
+    buffer = offset;
+    out.elements.write(reinterpret_cast<const char*>(&buffer), sizeof(uint32_t));
+
+    // write blob to indirect stream
+    if (blob_length) out.indirect.write(reinterpret_cast<const char*>(&blob[0]), blob_length);
+
+    return out;
+}
+
+template <typename T>
+OutStreams& serializeParamValue(OutStreams& out, const T& value) {
+    out.elements.write(reinterpret_cast<const char*>(&value), sizeof(T));
+    return out;
+}
+
+OutStreams& serialize(TAG_INVALID_t&&, OutStreams& out, const KeyParameter&) {
+    // skip invalid entries.
+    ++out.skipped;
+    return out;
+}
+template <typename T>
+OutStreams& serialize(T ttag, OutStreams& out, const KeyParameter& param) {
+    out.elements.write(reinterpret_cast<const char*>(&param.tag), sizeof(int32_t));
+    return serializeParamValue(out, accessTagValue(ttag, param));
+}
+
+template <typename... T>
+struct choose_serializer;
+template <typename... Tags>
+struct choose_serializer<MetaList<Tags...>> {
+    static OutStreams& serialize(OutStreams& out, const KeyParameter& param) {
+        return choose_serializer<Tags...>::serialize(out, param);
+    }
+};
+
+template <>
+struct choose_serializer<> {
+    static OutStreams& serialize(OutStreams& out, const KeyParameter& param) {
+        LOG(WARNING) << "Trying to serialize unknown tag " << unsigned(param.tag)
+                     << ". Did you forget to add it to all_tags_t?";
+        ++out.skipped;
+        return out;
+    }
+};
+
+template <TagType tag_type, Tag tag, typename... Tail>
+struct choose_serializer<android::hardware::security::keymint::TypedTag<tag_type, tag>, Tail...> {
+    static OutStreams& serialize(OutStreams& out, const KeyParameter& param) {
+        if (param.tag == tag) {
+            return android::hardware::security::keymint::serialize(TypedTag<tag_type, tag>(), out,
+                                                                   param);
+        } else {
+            return choose_serializer<Tail...>::serialize(out, param);
+        }
+    }
+};
+
+OutStreams& serialize(OutStreams& out, const KeyParameter& param) {
+    return choose_serializer<all_tags_t>::serialize(out, param);
+}
+
+std::ostream& serialize(std::ostream& out, const std::vector<KeyParameter>& params) {
+    std::stringstream indirect;
+    std::stringstream elements;
+    OutStreams streams = {indirect, elements, 0};
+    for (const auto& param : params) {
+        serialize(streams, param);
+    }
+    if (indirect.bad() || elements.bad()) {
+        out.setstate(std::ios_base::badbit);
+        return out;
+    }
+    auto pos = indirect.tellp();
+    if (pos < 0 || pos > std::numeric_limits<uint32_t>::max()) {
+        out.setstate(std::ios_base::badbit);
+        return out;
+    }
+    uint32_t indirect_size = pos;
+    pos = elements.tellp();
+    if (pos < 0 || pos > std::numeric_limits<uint32_t>::max()) {
+        out.setstate(std::ios_base::badbit);
+        return out;
+    }
+    uint32_t elements_size = pos;
+    uint32_t element_count = params.size() - streams.skipped;
+
+    out.write(reinterpret_cast<const char*>(&indirect_size), sizeof(uint32_t));
+
+    pos = out.tellp();
+    if (indirect_size) out << indirect.rdbuf();
+    assert(out.tellp() - pos == indirect_size);
+
+    out.write(reinterpret_cast<const char*>(&element_count), sizeof(uint32_t));
+    out.write(reinterpret_cast<const char*>(&elements_size), sizeof(uint32_t));
+
+    pos = out.tellp();
+    if (elements_size) out << elements.rdbuf();
+    assert(out.tellp() - pos == elements_size);
+
+    return out;
+}
+
+struct InStreams {
+    std::istream& indirect;
+    std::istream& elements;
+    size_t invalids;
+};
+
+InStreams& deserializeParamValue(InStreams& in, vector<uint8_t>* blob) {
+    uint32_t blob_length = 0;
+    uint32_t offset = 0;
+    in.elements.read(reinterpret_cast<char*>(&blob_length), sizeof(uint32_t));
+    blob->resize(blob_length);
+    in.elements.read(reinterpret_cast<char*>(&offset), sizeof(uint32_t));
+    in.indirect.seekg(offset);
+    in.indirect.read(reinterpret_cast<char*>(&(*blob)[0]), blob->size());
+    return in;
+}
+
+template <typename T>
+InStreams& deserializeParamValue(InStreams& in, T* value) {
+    in.elements.read(reinterpret_cast<char*>(value), sizeof(T));
+    return in;
+}
+
+InStreams& deserialize(TAG_INVALID_t&&, InStreams& in, KeyParameter*) {
+    // there should be no invalid KeyParameters but if handle them as zero sized.
+    ++in.invalids;
+    return in;
+}
+
+template <typename T>
+InStreams& deserialize(T&& ttag, InStreams& in, KeyParameter* param) {
+    return deserializeParamValue(in, &accessTagValue(ttag, *param));
+}
+
+template <typename... T>
+struct choose_deserializer;
+template <typename... Tags>
+struct choose_deserializer<MetaList<Tags...>> {
+    static InStreams& deserialize(InStreams& in, KeyParameter* param) {
+        return choose_deserializer<Tags...>::deserialize(in, param);
+    }
+};
+template <>
+struct choose_deserializer<> {
+    static InStreams& deserialize(InStreams& in, KeyParameter*) {
+        // encountered an unknown tag -> fail parsing
+        in.elements.setstate(std::ios_base::badbit);
+        return in;
+    }
+};
+template <TagType tag_type, Tag tag, typename... Tail>
+struct choose_deserializer<TypedTag<tag_type, tag>, Tail...> {
+    static InStreams& deserialize(InStreams& in, KeyParameter* param) {
+        if (param->tag == tag) {
+            return android::hardware::security::keymint::deserialize(TypedTag<tag_type, tag>(), in,
+                                                                     param);
+        } else {
+            return choose_deserializer<Tail...>::deserialize(in, param);
+        }
+    }
+};
+
+InStreams& deserialize(InStreams& in, KeyParameter* param) {
+    in.elements.read(reinterpret_cast<char*>(&param->tag), sizeof(Tag));
+    return choose_deserializer<all_tags_t>::deserialize(in, param);
+}
+
+std::istream& deserialize(std::istream& in, std::vector<KeyParameter>* params) {
+    uint32_t indirect_size = 0;
+    in.read(reinterpret_cast<char*>(&indirect_size), sizeof(uint32_t));
+    std::string indirect_buffer(indirect_size, '\0');
+    if (indirect_buffer.size() != indirect_size) {
+        in.setstate(std::ios_base::badbit);
+        return in;
+    }
+    in.read(&indirect_buffer[0], indirect_buffer.size());
+
+    uint32_t element_count = 0;
+    in.read(reinterpret_cast<char*>(&element_count), sizeof(uint32_t));
+    uint32_t elements_size = 0;
+    in.read(reinterpret_cast<char*>(&elements_size), sizeof(uint32_t));
+
+    std::string elements_buffer(elements_size, '\0');
+    if (elements_buffer.size() != elements_size) {
+        in.setstate(std::ios_base::badbit);
+        return in;
+    }
+    in.read(&elements_buffer[0], elements_buffer.size());
+
+    if (in.bad()) return in;
+
+    // TODO write one-shot stream buffer to avoid copying here
+    std::stringstream indirect(indirect_buffer);
+    std::stringstream elements(elements_buffer);
+    InStreams streams = {indirect, elements, 0};
+
+    params->resize(element_count);
+
+    for (uint32_t i = 0; i < element_count; ++i) {
+        deserialize(streams, &(*params)[i]);
+    }
+
+    /*
+     * There are legacy blobs which have invalid tags in them due to a bug during serialization.
+     * This makes sure that invalid tags are filtered from the result before it is returned.
+     */
+    if (streams.invalids > 0) {
+        std::vector<KeyParameter> filtered(element_count - streams.invalids);
+        auto ifiltered = filtered.begin();
+        for (auto& p : *params) {
+            if (p.tag != Tag::INVALID) {
+                *ifiltered++ = std::move(p);
+            }
+        }
+        *params = std::move(filtered);
+    }
+    return in;
+}
+
+void AuthorizationSet::Serialize(std::ostream* out) const {
+    serialize(*out, data_);
+}
+
+void AuthorizationSet::Deserialize(std::istream* in) {
+    deserialize(*in, &data_);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::RsaKey(uint32_t key_size,
+                                                         uint64_t public_exponent) {
+    Authorization(TAG_ALGORITHM, Algorithm::RSA);
+    Authorization(TAG_KEY_SIZE, key_size);
+    Authorization(TAG_RSA_PUBLIC_EXPONENT, public_exponent);
+    return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(uint32_t key_size) {
+    Authorization(TAG_ALGORITHM, Algorithm::EC);
+    Authorization(TAG_KEY_SIZE, key_size);
+    return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(EcCurve curve) {
+    Authorization(TAG_ALGORITHM, Algorithm::EC);
+    Authorization(TAG_EC_CURVE, curve);
+    return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::AesKey(uint32_t key_size) {
+    Authorization(TAG_ALGORITHM, Algorithm::AES);
+    return Authorization(TAG_KEY_SIZE, key_size);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::TripleDesKey(uint32_t key_size) {
+    Authorization(TAG_ALGORITHM, Algorithm::TRIPLE_DES);
+    return Authorization(TAG_KEY_SIZE, key_size);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::HmacKey(uint32_t key_size) {
+    Authorization(TAG_ALGORITHM, Algorithm::HMAC);
+    Authorization(TAG_KEY_SIZE, key_size);
+    return SigningKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::RsaSigningKey(uint32_t key_size,
+                                                                uint64_t public_exponent) {
+    RsaKey(key_size, public_exponent);
+    return SigningKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::RsaEncryptionKey(uint32_t key_size,
+                                                                   uint64_t public_exponent) {
+    RsaKey(key_size, public_exponent);
+    return EncryptionKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(uint32_t key_size) {
+    EcdsaKey(key_size);
+    return SigningKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(EcCurve curve) {
+    EcdsaKey(curve);
+    return SigningKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::AesEncryptionKey(uint32_t key_size) {
+    AesKey(key_size);
+    return EncryptionKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::TripleDesEncryptionKey(uint32_t key_size) {
+    TripleDesKey(key_size);
+    return EncryptionKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::SigningKey() {
+    Authorization(TAG_PURPOSE, KeyPurpose::SIGN);
+    return Authorization(TAG_PURPOSE, KeyPurpose::VERIFY);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EncryptionKey() {
+    Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT);
+    return Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::NoDigestOrPadding() {
+    Authorization(TAG_DIGEST, Digest::NONE);
+    return Authorization(TAG_PADDING, PaddingMode::NONE);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcbMode() {
+    return Authorization(TAG_BLOCK_MODE, BlockMode::ECB);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMinMacLen(uint32_t minMacLength) {
+    return BlockMode(BlockMode::GCM)
+            .Padding(PaddingMode::NONE)
+            .Authorization(TAG_MIN_MAC_LENGTH, minMacLength);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMacLen(uint32_t macLength) {
+    return BlockMode(BlockMode::GCM)
+            .Padding(PaddingMode::NONE)
+            .Authorization(TAG_MAC_LENGTH, macLength);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::BlockMode(
+        std::initializer_list<android::hardware::security::keymint::BlockMode> blockModes) {
+    for (auto mode : blockModes) {
+        push_back(TAG_BLOCK_MODE, mode);
+    }
+    return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::Digest(std::vector<keymint::Digest> digests) {
+    for (auto digest : digests) {
+        push_back(TAG_DIGEST, digest);
+    }
+    return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::Padding(
+        std::initializer_list<PaddingMode> paddingModes) {
+    for (auto paddingMode : paddingModes) {
+        push_back(TAG_PADDING, paddingMode);
+    }
+    return *this;
+}
+
+}  // namespace android::hardware::security::keymint
diff --git a/security/keymint/support/include/keymint_support/attestation_record.h b/security/keymint/support/include/keymint_support/attestation_record.h
new file mode 100644
index 0000000..d71624c
--- /dev/null
+++ b/security/keymint/support/include/keymint_support/attestation_record.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/security/keymint/ErrorCode.h>
+#include <android/hardware/security/keymint/IKeyMintDevice.h>
+
+#include <keymint_support/attestation_record.h>
+#include <keymint_support/authorization_set.h>
+#include <keymint_support/openssl_utils.h>
+
+namespace android::hardware::security::keymint {
+
+class AuthorizationSet;
+
+/**
+ * The OID for Android attestation records.  For the curious, it breaks down as follows:
+ *
+ * 1 = ISO
+ * 3 = org
+ * 6 = DoD (Huh? OIDs are weird.)
+ * 1 = IANA
+ * 4 = Private
+ * 1 = Enterprises
+ * 11129 = Google
+ * 2 = Google security
+ * 1 = certificate extension
+ * 17 = Android attestation extension.
+ */
+static const char kAttestionRecordOid[] = "1.3.6.1.4.1.11129.2.1.17";
+
+enum keymint_verified_boot_t {
+    KM_VERIFIED_BOOT_VERIFIED = 0,
+    KM_VERIFIED_BOOT_SELF_SIGNED = 1,
+    KM_VERIFIED_BOOT_UNVERIFIED = 2,
+    KM_VERIFIED_BOOT_FAILED = 3,
+};
+
+struct RootOfTrust {
+    SecurityLevel security_level;
+    vector<uint8_t> verified_boot_key;
+    vector<uint8_t> verified_boot_hash;
+    keymint_verified_boot_t verified_boot_state;
+    bool device_locked;
+};
+
+struct AttestationRecord {
+    RootOfTrust root_of_trust;
+    uint32_t attestation_version;
+    SecurityLevel attestation_security_level;
+    uint32_t keymint_version;
+    SecurityLevel keymint_security_level;
+    std::vector<uint8_t> attestation_challenge;
+    AuthorizationSet software_enforced;
+    AuthorizationSet hardware_enforced;
+    std::vector<uint8_t> unique_id;
+};
+
+ErrorCode parse_attestation_record(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
+                                   uint32_t* attestation_version,  //
+                                   SecurityLevel* attestation_security_level,
+                                   uint32_t* keymint_version, SecurityLevel* keymint_security_level,
+                                   std::vector<uint8_t>* attestation_challenge,
+                                   AuthorizationSet* software_enforced,
+                                   AuthorizationSet* tee_enforced,  //
+                                   std::vector<uint8_t>* unique_id);
+
+ErrorCode parse_root_of_trust(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
+                              std::vector<uint8_t>* verified_boot_key,
+                              keymint_verified_boot_t* verified_boot_state, bool* device_locked,
+                              std::vector<uint8_t>* verified_boot_hash);
+
+}  // namespace android::hardware::security::keymint
diff --git a/security/keymint/support/include/keymint_support/authorization_set.h b/security/keymint/support/include/keymint_support/authorization_set.h
new file mode 100644
index 0000000..97e1022
--- /dev/null
+++ b/security/keymint/support/include/keymint_support/authorization_set.h
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_SECURITY_KEYSTORE_KM4_AUTHORIZATION_SET_H_
+#define SYSTEM_SECURITY_KEYSTORE_KM4_AUTHORIZATION_SET_H_
+
+#include <vector>
+
+#include <android/hardware/security/keymint/BlockMode.h>
+#include <android/hardware/security/keymint/Digest.h>
+#include <android/hardware/security/keymint/EcCurve.h>
+#include <android/hardware/security/keymint/PaddingMode.h>
+
+#include <keymint_support/keymint_tags.h>
+
+namespace android::hardware::security::keymint {
+
+using std::vector;
+
+class AuthorizationSetBuilder;
+
+/**
+ * An ordered collection of KeyParameters. It provides memory ownership and some convenient
+ * functionality for sorting, deduplicating, joining, and subtracting sets of KeyParameters.
+ * For serialization, wrap the backing store of this structure in a vector<KeyParameter>.
+ */
+class AuthorizationSet {
+  public:
+    typedef KeyParameter value_type;
+
+    /**
+     * Construct an empty, dynamically-allocated, growable AuthorizationSet.
+     */
+    AuthorizationSet(){};
+
+    // Copy constructor.
+    AuthorizationSet(const AuthorizationSet& other) : data_(other.data_) {}
+
+    // Move constructor.
+    AuthorizationSet(AuthorizationSet&& other) noexcept : data_(std::move(other.data_)) {}
+
+    // Constructor from vector<KeyParameter>
+    AuthorizationSet(const vector<KeyParameter>& other) { *this = other; }
+
+    // Copy assignment.
+    AuthorizationSet& operator=(const AuthorizationSet& other) {
+        data_ = other.data_;
+        return *this;
+    }
+
+    // Move assignment.
+    AuthorizationSet& operator=(AuthorizationSet&& other) noexcept {
+        data_ = std::move(other.data_);
+        return *this;
+    }
+
+    AuthorizationSet& operator=(const vector<KeyParameter>& other) {
+        if (other.size() > 0) {
+            data_.resize(other.size());
+            for (size_t i = 0; i < data_.size(); ++i) {
+                /* This makes a deep copy even of embedded blobs.
+                 * See assignment operator/copy constructor of vector.*/
+                data_[i] = other[i];
+            }
+        }
+        return *this;
+    }
+
+    /**
+     * Clear existing authorization set data
+     */
+    void Clear();
+
+    ~AuthorizationSet() = default;
+
+    /**
+     * Returns the size of the set.
+     */
+    size_t size() const { return data_.size(); }
+
+    /**
+     * Returns true if the set is empty.
+     */
+    bool empty() const { return size() == 0; }
+
+    /**
+     * Returns the data in the set, directly. Be careful with this.
+     */
+    const KeyParameter* data() const { return data_.data(); }
+
+    /**
+     * Sorts the set
+     */
+    void Sort();
+
+    /**
+     * Sorts the set and removes duplicates (inadvertently duplicating tags is easy to do with the
+     * AuthorizationSetBuilder).
+     */
+    void Deduplicate();
+
+    /**
+     * Adds all elements from \p set that are not already present in this AuthorizationSet.  As a
+     * side-effect, if \p set is not null this AuthorizationSet will end up sorted.
+     */
+    void Union(const AuthorizationSet& set);
+
+    /**
+     * Removes all elements in \p set from this AuthorizationSet.
+     */
+    void Subtract(const AuthorizationSet& set);
+
+    /**
+     * Returns the offset of the next entry that matches \p tag, starting from the element after \p
+     * begin.  If not found, returns -1.
+     */
+    int find(Tag tag, int begin = -1) const;
+
+    /**
+     * Removes the entry at the specified index. Returns true if successful, false if the index was
+     * out of bounds.
+     */
+    bool erase(int index);
+
+    /**
+     * Returns iterator (pointer) to beginning of elems array, to enable STL-style iteration
+     */
+    std::vector<KeyParameter>::const_iterator begin() const { return data_.begin(); }
+
+    /**
+     * Returns iterator (pointer) one past end of elems array, to enable STL-style iteration
+     */
+    std::vector<KeyParameter>::const_iterator end() const { return data_.end(); }
+
+    /**
+     * Modifies this Authorization set such that it only keeps the entries for which doKeep
+     * returns true.
+     */
+    void Filter(std::function<bool(const KeyParameter&)> doKeep);
+    /**
+     * Returns the nth element of the set.
+     * Like for std::vector::operator[] there is no range check performed. Use of out of range
+     * indices is undefined.
+     */
+    KeyParameter& operator[](int n);
+
+    /**
+     * Returns the nth element of the set.
+     * Like for std::vector::operator[] there is no range check performed. Use of out of range
+     * indices is undefined.
+     */
+    const KeyParameter& operator[](int n) const;
+
+    /**
+     * Returns true if the set contains at least one instance of \p tag
+     */
+    bool Contains(Tag tag) const { return find(tag) != -1; }
+
+    template <TagType tag_type, Tag tag, typename ValueT>
+    bool Contains(TypedTag<tag_type, tag> ttag, const ValueT& value) const {
+        for (const auto& param : data_) {
+            auto entry = authorizationValue(ttag, param);
+            if (entry.isOk() && static_cast<ValueT>(entry.value()) == value) return true;
+        }
+        return false;
+    }
+    /**
+     * Returns the number of \p tag entries.
+     */
+    size_t GetTagCount(Tag tag) const;
+
+    template <typename T>
+    inline NullOr<const typename TypedTag2ValueType<T>::type&> GetTagValue(T tag) const {
+        auto entry = GetEntry(tag);
+        if (entry.isOk()) return authorizationValue(tag, entry.value());
+        return {};
+    }
+
+    void push_back(const KeyParameter& param) { data_.push_back(param); }
+    void push_back(KeyParameter&& param) { data_.push_back(std::move(param)); }
+    void push_back(const AuthorizationSet& set) {
+        for (auto& entry : set) {
+            push_back(entry);
+        }
+    }
+    void push_back(AuthorizationSet&& set) {
+        std::move(set.begin(), set.end(), std::back_inserter(*this));
+    }
+
+    /**
+     * Append the tag and enumerated value to the set.
+     * "val" may be exactly one parameter unless a boolean parameter is added.
+     * In this case "val" is omitted. This condition is checked at compile time by Authorization()
+     */
+    template <typename TypedTagT, typename... Value>
+    void push_back(TypedTagT tag, Value&&... val) {
+        push_back(Authorization(tag, std::forward<Value>(val)...));
+    }
+
+    template <typename Iterator>
+    void append(Iterator begin, Iterator end) {
+        while (begin != end) {
+            push_back(*begin);
+            ++begin;
+        }
+    }
+
+    vector<KeyParameter> vector_data() const {
+        vector<KeyParameter> result(begin(), end());
+        return result;
+    }
+
+    void Serialize(std::ostream* out) const;
+    void Deserialize(std::istream* in);
+
+  private:
+    NullOr<const KeyParameter&> GetEntry(Tag tag) const;
+
+    std::vector<KeyParameter> data_;
+};
+
+class AuthorizationSetBuilder : public AuthorizationSet {
+  public:
+    template <typename TagType, typename... ValueType>
+    AuthorizationSetBuilder& Authorization(TagType ttag, ValueType&&... value) {
+        push_back(ttag, std::forward<ValueType>(value)...);
+        return *this;
+    }
+
+    template <Tag tag>
+    AuthorizationSetBuilder& Authorization(TypedTag<TagType::BYTES, tag> ttag, const uint8_t* data,
+                                           size_t data_length) {
+        vector<uint8_t> new_blob(data, data + data_length);
+        push_back(ttag, new_blob);
+        return *this;
+    }
+
+    template <Tag tag>
+    AuthorizationSetBuilder& Authorization(TypedTag<TagType::BYTES, tag> ttag, const char* data,
+                                           size_t data_length) {
+        return Authorization(ttag, reinterpret_cast<const uint8_t*>(data), data_length);
+    }
+
+    template <Tag tag>
+    AuthorizationSetBuilder& Authorization(TypedTag<TagType::BYTES, tag> ttag, char* data,
+                                           size_t data_length) {
+        return Authorization(ttag, reinterpret_cast<const uint8_t*>(data), data_length);
+    }
+
+    template <Tag tag, size_t size>
+    AuthorizationSetBuilder& Authorization(TypedTag<TagType::BYTES, tag> ttag,
+                                           const char (&data)[size]) {
+        return Authorization(ttag, reinterpret_cast<const uint8_t*>(&data[0]),
+                             size - 1);  // drop the terminating '\0'
+    }
+
+    AuthorizationSetBuilder& Authorizations(const AuthorizationSet& set) {
+        for (const auto& entry : set) {
+            push_back(entry);
+        }
+        return *this;
+    }
+
+    AuthorizationSetBuilder& RsaKey(uint32_t key_size, uint64_t public_exponent);
+    AuthorizationSetBuilder& EcdsaKey(uint32_t key_size);
+    AuthorizationSetBuilder& EcdsaKey(EcCurve curve);
+    AuthorizationSetBuilder& AesKey(uint32_t key_size);
+    AuthorizationSetBuilder& TripleDesKey(uint32_t key_size);
+    AuthorizationSetBuilder& HmacKey(uint32_t key_size);
+
+    AuthorizationSetBuilder& RsaSigningKey(uint32_t key_size, uint64_t public_exponent);
+    AuthorizationSetBuilder& RsaEncryptionKey(uint32_t key_size, uint64_t public_exponent);
+    AuthorizationSetBuilder& EcdsaSigningKey(uint32_t key_size);
+    AuthorizationSetBuilder& EcdsaSigningKey(EcCurve curve);
+    AuthorizationSetBuilder& AesEncryptionKey(uint32_t key_size);
+    AuthorizationSetBuilder& TripleDesEncryptionKey(uint32_t key_size);
+
+    AuthorizationSetBuilder& SigningKey();
+    AuthorizationSetBuilder& EncryptionKey();
+
+    AuthorizationSetBuilder& NoDigestOrPadding();
+
+    AuthorizationSetBuilder& EcbMode();
+    AuthorizationSetBuilder& GcmModeMinMacLen(uint32_t minMacLength);
+    AuthorizationSetBuilder& GcmModeMacLen(uint32_t macLength);
+
+    AuthorizationSetBuilder& BlockMode(std::initializer_list<BlockMode> blockModes);
+    AuthorizationSetBuilder& Digest(std::vector<Digest> digests);
+    AuthorizationSetBuilder& Padding(std::initializer_list<PaddingMode> paddings);
+
+    template <typename... T>
+    AuthorizationSetBuilder& BlockMode(T&&... a) {
+        return BlockMode({std::forward<T>(a)...});
+    }
+    template <typename... T>
+    AuthorizationSetBuilder& Digest(T&&... a) {
+        return Digest({std::forward<T>(a)...});
+    }
+    template <typename... T>
+    AuthorizationSetBuilder& Padding(T&&... a) {
+        return Padding({std::forward<T>(a)...});
+    }
+};
+
+}  // namespace android::hardware::security::keymint
+
+#endif  // SYSTEM_SECURITY_KEYSTORE_KM4_AUTHORIZATION_SET_H_
diff --git a/security/keymint/support/include/keymint_support/key_param_output.h b/security/keymint/support/include/keymint_support/key_param_output.h
new file mode 100644
index 0000000..82c9689
--- /dev/null
+++ b/security/keymint/support/include/keymint_support/key_param_output.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HARDWARE_INTERFACES_KEYMINT_SUPPORT_INCLUDE_KEY_PARAM_OUTPUT_H_
+#define HARDWARE_INTERFACES_KEYMINT_SUPPORT_INCLUDE_KEY_PARAM_OUTPUT_H_
+
+#include <iostream>
+#include <vector>
+
+#include <android/hardware/security/keymint/Algorithm.h>
+#include <android/hardware/security/keymint/BlockMode.h>
+#include <android/hardware/security/keymint/Digest.h>
+#include <android/hardware/security/keymint/EcCurve.h>
+#include <android/hardware/security/keymint/ErrorCode.h>
+#include <android/hardware/security/keymint/HardwareAuthenticatorType.h>
+#include <android/hardware/security/keymint/KeyCharacteristics.h>
+#include <android/hardware/security/keymint/KeyOrigin.h>
+#include <android/hardware/security/keymint/KeyParameter.h>
+#include <android/hardware/security/keymint/KeyPurpose.h>
+#include <android/hardware/security/keymint/PaddingMode.h>
+#include <android/hardware/security/keymint/SecurityLevel.h>
+#include <android/hardware/security/keymint/Tag.h>
+#include <android/hardware/security/keymint/TagType.h>
+
+#include "keymint_tags.h"
+
+namespace android::hardware::security::keymint {
+
+inline ::std::ostream& operator<<(::std::ostream& os, Algorithm value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, BlockMode value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, Digest value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, EcCurve value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, ErrorCode value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, KeyOrigin value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, PaddingMode value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, SecurityLevel value) {
+    return os << toString(value);
+}
+
+template <typename ValueT>
+::std::ostream& operator<<(::std::ostream& os, const NullOr<ValueT>& value) {
+    if (!value.isOk()) {
+        os << "(value not present)";
+    } else {
+        os << value.value();
+    }
+    return os;
+}
+
+::std::ostream& operator<<(::std::ostream& os, const ::std::vector<KeyParameter>& set);
+::std::ostream& operator<<(::std::ostream& os, const KeyParameter& param);
+
+inline ::std::ostream& operator<<(::std::ostream& os, const KeyCharacteristics& value) {
+    return os << "SW: " << value.softwareEnforced << ::std::endl
+              << "HW: " << value.hardwareEnforced << ::std::endl;
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, KeyPurpose value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, Tag tag) {
+    return os << toString(tag);
+}
+
+}  // namespace android::hardware::security::keymint
+
+#endif  // HARDWARE_INTERFACES_KEYMINT_SUPPORT_INCLUDE_KEY_PARAM_OUTPUT_H_
diff --git a/security/keymint/support/include/keymint_support/keymint_tags.h b/security/keymint/support/include/keymint_support/keymint_tags.h
new file mode 100644
index 0000000..f23e4f2
--- /dev/null
+++ b/security/keymint/support/include/keymint_support/keymint_tags.h
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef HARDWARE_INTERFACES_KEYMINT_SUPPORT_INCLUDE_KEYMINT_TAGS_H_
+#define HARDWARE_INTERFACES_KEYMINT_SUPPORT_INCLUDE_KEYMINT_TAGS_H_
+
+#include <android/hardware/security/keymint/Algorithm.h>
+#include <android/hardware/security/keymint/BlockMode.h>
+#include <android/hardware/security/keymint/Digest.h>
+#include <android/hardware/security/keymint/EcCurve.h>
+#include <android/hardware/security/keymint/HardwareAuthenticatorType.h>
+#include <android/hardware/security/keymint/KeyOrigin.h>
+#include <android/hardware/security/keymint/KeyParameter.h>
+#include <android/hardware/security/keymint/KeyPurpose.h>
+#include <android/hardware/security/keymint/PaddingMode.h>
+#include <android/hardware/security/keymint/SecurityLevel.h>
+#include <android/hardware/security/keymint/Tag.h>
+#include <android/hardware/security/keymint/TagType.h>
+
+namespace android::hardware::security::keymint {
+
+// The following create the numeric values that KM_TAG_PADDING and KM_TAG_DIGEST used to have.  We
+// need these old values to be able to support old keys that use them.
+// TODO(seleneh) we should delete this code when we stop supporting keymaster1
+// and deletes it.
+static const int32_t KM_TAG_DIGEST_OLD = static_cast<int32_t>(TagType::ENUM) | 5;
+static const int32_t KM_TAG_PADDING_OLD = static_cast<int32_t>(TagType::ENUM) | 7;
+
+constexpr TagType typeFromTag(Tag tag) {
+    return static_cast<TagType>(static_cast<uint32_t>(tag) & static_cast<uint32_t>(0xf0000000));
+}
+
+/**
+ * TypedTag is a templatized version of Tag, which provides compile-time checking of
+ * keymint tag types. Instances are convertible to Tag, so they can be used wherever
+ * Tag is expected, and because they encode the tag type it's possible to create
+ * function overloads that only operate on tags with a particular type.
+ */
+template <TagType tag_type, Tag tag>
+struct TypedTag {
+    inline TypedTag() {
+        // Ensure that it's impossible to create a TypedTag instance whose 'tag' doesn't have type
+        // 'tag_type'.  Attempting to instantiate a tag with the wrong type will result in a compile
+        // error (no match for template specialization StaticAssert<false>), with no run-time cost.
+        static_assert(typeFromTag(tag) == tag_type, "mismatch between tag and tag_type");
+    }
+    operator Tag() const { return tag; }
+    int32_t maskedTag() { return static_cast<uint32_t>(tag) & 0x0FFFFFFF; }
+};
+
+template <Tag tag>
+struct Tag2TypedTag {
+    typedef TypedTag<typeFromTag(tag), tag> type;
+};
+
+#define DECLARE_TYPED_TAG(name)                                    \
+    typedef typename Tag2TypedTag<Tag::name>::type TAG_##name##_t; \
+    static TAG_##name##_t TAG_##name;
+
+DECLARE_TYPED_TAG(ACTIVE_DATETIME);
+DECLARE_TYPED_TAG(ALGORITHM);
+DECLARE_TYPED_TAG(ALLOW_WHILE_ON_BODY);
+DECLARE_TYPED_TAG(APPLICATION_DATA);
+DECLARE_TYPED_TAG(APPLICATION_ID);
+DECLARE_TYPED_TAG(ASSOCIATED_DATA);
+DECLARE_TYPED_TAG(ATTESTATION_APPLICATION_ID);
+DECLARE_TYPED_TAG(ATTESTATION_CHALLENGE);
+DECLARE_TYPED_TAG(ATTESTATION_ID_BRAND);
+DECLARE_TYPED_TAG(ATTESTATION_ID_DEVICE);
+DECLARE_TYPED_TAG(ATTESTATION_ID_PRODUCT);
+DECLARE_TYPED_TAG(ATTESTATION_ID_MANUFACTURER);
+DECLARE_TYPED_TAG(ATTESTATION_ID_MODEL);
+DECLARE_TYPED_TAG(AUTH_TIMEOUT);
+DECLARE_TYPED_TAG(BLOCK_MODE);
+DECLARE_TYPED_TAG(BOOTLOADER_ONLY);
+DECLARE_TYPED_TAG(BOOT_PATCHLEVEL);
+DECLARE_TYPED_TAG(CALLER_NONCE);
+DECLARE_TYPED_TAG(CONFIRMATION_TOKEN);
+DECLARE_TYPED_TAG(CREATION_DATETIME);
+DECLARE_TYPED_TAG(DEVICE_UNIQUE_ATTESTATION);
+DECLARE_TYPED_TAG(DIGEST);
+DECLARE_TYPED_TAG(EARLY_BOOT_ONLY);
+DECLARE_TYPED_TAG(EC_CURVE);
+DECLARE_TYPED_TAG(HARDWARE_TYPE);
+DECLARE_TYPED_TAG(IDENTITY_CREDENTIAL_KEY);
+DECLARE_TYPED_TAG(INCLUDE_UNIQUE_ID);
+DECLARE_TYPED_TAG(INVALID);
+DECLARE_TYPED_TAG(KEY_SIZE);
+DECLARE_TYPED_TAG(MAC_LENGTH);
+DECLARE_TYPED_TAG(MAX_USES_PER_BOOT);
+DECLARE_TYPED_TAG(MIN_MAC_LENGTH);
+DECLARE_TYPED_TAG(MIN_SECONDS_BETWEEN_OPS);
+DECLARE_TYPED_TAG(NONCE);
+DECLARE_TYPED_TAG(NO_AUTH_REQUIRED);
+DECLARE_TYPED_TAG(ORIGIN);
+DECLARE_TYPED_TAG(ORIGINATION_EXPIRE_DATETIME);
+DECLARE_TYPED_TAG(OS_PATCHLEVEL);
+DECLARE_TYPED_TAG(OS_VERSION);
+DECLARE_TYPED_TAG(PADDING);
+DECLARE_TYPED_TAG(PURPOSE);
+DECLARE_TYPED_TAG(RESET_SINCE_ID_ROTATION);
+DECLARE_TYPED_TAG(ROLLBACK_RESISTANCE);
+DECLARE_TYPED_TAG(ROOT_OF_TRUST);
+DECLARE_TYPED_TAG(RSA_PUBLIC_EXPONENT);
+DECLARE_TYPED_TAG(STORAGE_KEY);
+DECLARE_TYPED_TAG(TRUSTED_CONFIRMATION_REQUIRED);
+DECLARE_TYPED_TAG(TRUSTED_USER_PRESENCE_REQUIRED);
+DECLARE_TYPED_TAG(UNIQUE_ID);
+DECLARE_TYPED_TAG(UNLOCKED_DEVICE_REQUIRED);
+DECLARE_TYPED_TAG(USAGE_EXPIRE_DATETIME);
+DECLARE_TYPED_TAG(USER_AUTH_TYPE);
+DECLARE_TYPED_TAG(USER_ID);
+DECLARE_TYPED_TAG(USER_SECURE_ID);
+DECLARE_TYPED_TAG(VENDOR_PATCHLEVEL);
+
+template <typename... Elems>
+struct MetaList {};
+
+using all_tags_t = MetaList<
+        TAG_INVALID_t, TAG_KEY_SIZE_t, TAG_MAC_LENGTH_t, TAG_CALLER_NONCE_t, TAG_MIN_MAC_LENGTH_t,
+        TAG_RSA_PUBLIC_EXPONENT_t, TAG_INCLUDE_UNIQUE_ID_t, TAG_ACTIVE_DATETIME_t,
+        TAG_ORIGINATION_EXPIRE_DATETIME_t, TAG_USAGE_EXPIRE_DATETIME_t,
+        TAG_MIN_SECONDS_BETWEEN_OPS_t, TAG_MAX_USES_PER_BOOT_t, TAG_USER_ID_t, TAG_USER_SECURE_ID_t,
+        TAG_NO_AUTH_REQUIRED_t, TAG_AUTH_TIMEOUT_t, TAG_ALLOW_WHILE_ON_BODY_t,
+        TAG_UNLOCKED_DEVICE_REQUIRED_t, TAG_APPLICATION_ID_t, TAG_APPLICATION_DATA_t,
+        TAG_CREATION_DATETIME_t, TAG_ROLLBACK_RESISTANCE_t, TAG_HARDWARE_TYPE_t,
+        TAG_ROOT_OF_TRUST_t, TAG_ASSOCIATED_DATA_t, TAG_NONCE_t, TAG_BOOTLOADER_ONLY_t,
+        TAG_OS_VERSION_t, TAG_OS_PATCHLEVEL_t, TAG_UNIQUE_ID_t, TAG_ATTESTATION_CHALLENGE_t,
+        TAG_ATTESTATION_APPLICATION_ID_t, TAG_ATTESTATION_ID_BRAND_t, TAG_ATTESTATION_ID_DEVICE_t,
+        TAG_ATTESTATION_ID_PRODUCT_t, TAG_ATTESTATION_ID_MANUFACTURER_t, TAG_ATTESTATION_ID_MODEL_t,
+        TAG_RESET_SINCE_ID_ROTATION_t, TAG_PURPOSE_t, TAG_ALGORITHM_t, TAG_BLOCK_MODE_t,
+        TAG_DIGEST_t, TAG_PADDING_t, TAG_ORIGIN_t, TAG_USER_AUTH_TYPE_t, TAG_EC_CURVE_t,
+        TAG_BOOT_PATCHLEVEL_t, TAG_VENDOR_PATCHLEVEL_t, TAG_TRUSTED_CONFIRMATION_REQUIRED_t,
+        TAG_TRUSTED_USER_PRESENCE_REQUIRED_t>;
+
+template <typename TypedTagType>
+struct TypedTag2ValueType;
+
+#define MAKE_TAG_VALUE_ACCESSOR(tag_type, field_name)                              \
+    template <Tag tag>                                                             \
+    struct TypedTag2ValueType<TypedTag<tag_type, tag>> {                           \
+        typedef decltype(static_cast<KeyParameter*>(nullptr)->field_name) type;    \
+    };                                                                             \
+    template <Tag tag>                                                             \
+    inline auto accessTagValue(TypedTag<tag_type, tag>, const KeyParameter& param) \
+            ->const decltype(param.field_name)& {                                  \
+        return param.field_name;                                                   \
+    }                                                                              \
+    template <Tag tag>                                                             \
+    inline auto accessTagValue(TypedTag<tag_type, tag>, KeyParameter& param)       \
+            ->decltype(param.field_name)& {                                        \
+        return param.field_name;                                                   \
+    }
+
+MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG, longInteger)
+MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG_REP, longInteger)
+MAKE_TAG_VALUE_ACCESSOR(TagType::DATE, longInteger)
+MAKE_TAG_VALUE_ACCESSOR(TagType::UINT, integer)
+MAKE_TAG_VALUE_ACCESSOR(TagType::UINT_REP, integer)
+MAKE_TAG_VALUE_ACCESSOR(TagType::BOOL, boolValue)
+MAKE_TAG_VALUE_ACCESSOR(TagType::BYTES, blob)
+MAKE_TAG_VALUE_ACCESSOR(TagType::BIGNUM, blob)
+
+//  TODO(seleneh) change these MAKE_TAG_ENUM_VALUE_ACCESSOR back to the 2 parameter
+//  version when aidl supports union
+#define MAKE_TAG_ENUM_VALUE_ACCESSOR(typed_tag, field_name, field_type)                 \
+    template <>                                                                         \
+    struct TypedTag2ValueType<decltype(typed_tag)> {                                    \
+        typedef field_type type;                                                        \
+    };                                                                                  \
+    inline auto accessTagValue(decltype(typed_tag), const KeyParameter& param)          \
+            ->const field_type& {                                                       \
+        return *reinterpret_cast<const field_type*>(&param.field_name);                 \
+    }                                                                                   \
+    inline auto accessTagValue(decltype(typed_tag), KeyParameter& param)->field_type& { \
+        return *reinterpret_cast<field_type*>(&param.field_name);                       \
+    }
+
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ALGORITHM, integer, Algorithm)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_BLOCK_MODE, integer, BlockMode)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_DIGEST, integer, Digest)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_EC_CURVE, integer, EcCurve)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ORIGIN, integer, KeyOrigin)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PADDING, integer, PaddingMode)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PURPOSE, integer, KeyPurpose)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_USER_AUTH_TYPE, integer, HardwareAuthenticatorType)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_HARDWARE_TYPE, integer, SecurityLevel)
+
+template <TagType tag_type, Tag tag, typename ValueT>
+inline KeyParameter makeKeyParameter(TypedTag<tag_type, tag> ttag, ValueT&& value) {
+    KeyParameter param;
+    param.tag = tag;
+    param.longInteger = 0;
+    accessTagValue(ttag, param) = std::forward<ValueT>(value);
+    return param;
+}
+
+// the boolean case
+template <Tag tag>
+inline KeyParameter makeKeyParameter(TypedTag<TagType::BOOL, tag>) {
+    KeyParameter param;
+    param.tag = tag;
+    param.boolValue = true;
+    return param;
+}
+
+template <typename... Pack>
+struct FirstOrNoneHelper;
+template <typename First>
+struct FirstOrNoneHelper<First> {
+    typedef First type;
+};
+template <>
+struct FirstOrNoneHelper<> {
+    struct type {};
+};
+
+template <typename... Pack>
+using FirstOrNone = typename FirstOrNoneHelper<Pack...>::type;
+
+template <TagType tag_type, Tag tag, typename... Args>
+inline KeyParameter Authorization(TypedTag<tag_type, tag> ttag, Args&&... args) {
+    static_assert(tag_type != TagType::BOOL || (sizeof...(args) == 0),
+                  "TagType::BOOL Authorizations do not take parameters. Presence is truth.");
+    static_assert(tag_type == TagType::BOOL || (sizeof...(args) == 1),
+                  "Authorization other then TagType::BOOL take exactly one parameter.");
+    static_assert(
+            tag_type == TagType::BOOL ||
+                    std::is_convertible<
+                            std::remove_cv_t<std::remove_reference_t<FirstOrNone<Args...>>>,
+                            typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type>::value,
+            "Invalid argument type for given tag.");
+
+    return makeKeyParameter(ttag, std::forward<Args>(args)...);
+}
+
+/**
+ * This class wraps a (mostly return) value and stores whether or not the wrapped value is valid out
+ * of band. Note that if the wrapped value is a reference it is unsafe to access the value if
+ * !isOk(). If the wrapped type is a pointer or value and !isOk(), it is still safe to access the
+ * wrapped value. In this case the pointer will be NULL though, and the value will be default
+ * constructed.
+ *
+ * TODO(seleneh) replace this with std::optional.
+ */
+template <typename ValueT>
+class NullOr {
+    using internal_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value,
+                                          std::remove_reference_t<ValueT>*, ValueT>;
+
+    struct pointer_initializer {
+        static std::nullptr_t init() { return nullptr; }
+    };
+    struct value_initializer {
+        static ValueT init() { return ValueT(); }
+    };
+    struct value_pointer_deref_t {
+        static ValueT& deref(ValueT& v) { return v; }
+    };
+    struct reference_deref_t {
+        static auto& deref(internal_t v) { return *v; }
+    };
+    using initializer_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value ||
+                                                     std::is_pointer<ValueT>::value,
+                                             pointer_initializer, value_initializer>;
+    using deref_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value, reference_deref_t,
+                                       value_pointer_deref_t>;
+
+  public:
+    NullOr() : value_(initializer_t::init()), null_(true) {}
+    template <typename T>
+    NullOr(T&& value, typename std::enable_if<
+                              !std::is_lvalue_reference<ValueT>::value &&
+                                      std::is_same<std::decay_t<ValueT>, std::decay_t<T>>::value,
+                              int>::type = 0)
+        : value_(std::forward<ValueT>(value)), null_(false) {}
+    template <typename T>
+    NullOr(T& value, typename std::enable_if<
+                             std::is_lvalue_reference<ValueT>::value &&
+                                     std::is_same<std::decay_t<ValueT>, std::decay_t<T>>::value,
+                             int>::type = 0)
+        : value_(&value), null_(false) {}
+
+    bool isOk() const { return !null_; }
+
+    const ValueT& value() const& { return deref_t::deref(value_); }
+    ValueT& value() & { return deref_t::deref(value_); }
+    ValueT&& value() && { return std::move(deref_t::deref(value_)); }
+
+  private:
+    internal_t value_;
+    bool null_;
+};
+
+template <typename T>
+std::remove_reference_t<T> NullOrOr(T&& v) {
+    if (v.isOk()) return v;
+    return {};
+}
+
+template <typename Head, typename... Tail>
+std::remove_reference_t<Head> NullOrOr(Head&& head, Tail&&... tail) {
+    if (head.isOk()) return head;
+    return NullOrOr(std::forward<Tail>(tail)...);
+}
+
+template <typename Default, typename Wrapped>
+std::remove_reference_t<Wrapped> defaultOr(NullOr<Wrapped>&& optional, Default&& def) {
+    static_assert(std::is_convertible<std::remove_reference_t<Default>,
+                                      std::remove_reference_t<Wrapped>>::value,
+                  "Type of default value must match the type wrapped by NullOr");
+    if (optional.isOk()) return optional.value();
+    return def;
+}
+
+template <TagType tag_type, Tag tag>
+inline NullOr<const typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type&> authorizationValue(
+        TypedTag<tag_type, tag> ttag, const KeyParameter& param) {
+    if (tag != param.tag) return {};
+    return accessTagValue(ttag, param);
+}
+
+}  // namespace android::hardware::security::keymint
+
+#endif  // HARDWARE_INTERFACES_KEYMINT_SUPPORT_INCLUDE_KEYMINT_TAGS_H_
diff --git a/security/keymint/support/include/keymint_support/keymint_utils.h b/security/keymint/support/include/keymint_support/keymint_utils.h
new file mode 100644
index 0000000..fda1b6c
--- /dev/null
+++ b/security/keymint/support/include/keymint_support/keymint_utils.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifndef HARDWARE_INTERFACES_KEYMINT_10_SUPPORT_KEYMINT_UTILS_H_
+#define HARDWARE_INTERFACES_KEYMINT_10_SUPPORT_KEYMINT_UTILS_H_
+
+#include <android/hardware/security/keymint/HardwareAuthToken.h>
+
+namespace android::hardware::security::keymint {
+
+using std::vector;
+
+inline static std::vector<uint8_t> blob2vector(const uint8_t* data, const size_t length) {
+    std::vector<uint8_t> result(data, data + length);
+    return result;
+}
+
+inline static std::vector<uint8_t> blob2vector(const std::string& value) {
+    vector<uint8_t> result(reinterpret_cast<const uint8_t*>(value.data()),
+                           reinterpret_cast<const uint8_t*>(value.data()) + value.size());
+    return result;
+}
+
+HardwareAuthToken vector2AuthToken(const vector<uint8_t>& buffer);
+vector<uint8_t> authToken2vector(const HardwareAuthToken& token);
+
+uint32_t getOsVersion();
+uint32_t getOsPatchlevel();
+
+}  // namespace android::hardware::security::keymint
+
+#endif  // HARDWARE_INTERFACES_KEYMINT_10_SUPPORT_KEYMINT_UTILS_H_
diff --git a/security/keymint/support/include/keymint_support/openssl_utils.h b/security/keymint/support/include/keymint_support/openssl_utils.h
new file mode 100644
index 0000000..cb09968
--- /dev/null
+++ b/security/keymint/support/include/keymint_support/openssl_utils.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#ifndef HARDWARE_INTERFACES_KEYMINT_1_0_SUPPORT_OPENSSL_UTILS_H_
+#define HARDWARE_INTERFACES_KEYMINT_1_0_SUPPORT_OPENSSL_UTILS_H_
+
+#include <android/hardware/security/keymint/Digest.h>
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+namespace android::hardware::security::keymint {
+
+template <typename T, void (*F)(T*)>
+struct UniquePtrDeleter {
+    void operator()(T* p) const { F(p); }
+};
+
+typedef UniquePtrDeleter<EVP_PKEY, EVP_PKEY_free> EVP_PKEY_Delete;
+
+#define MAKE_OPENSSL_PTR_TYPE(type) \
+    typedef std::unique_ptr<type, UniquePtrDeleter<type, type##_free>> type##_Ptr;
+
+MAKE_OPENSSL_PTR_TYPE(ASN1_OBJECT)
+MAKE_OPENSSL_PTR_TYPE(EVP_PKEY)
+MAKE_OPENSSL_PTR_TYPE(RSA)
+MAKE_OPENSSL_PTR_TYPE(X509)
+MAKE_OPENSSL_PTR_TYPE(BN_CTX)
+
+typedef std::unique_ptr<BIGNUM, UniquePtrDeleter<BIGNUM, BN_free>> BIGNUM_Ptr;
+
+inline const EVP_MD* openssl_digest(Digest digest) {
+    switch (digest) {
+        case Digest::NONE:
+            return nullptr;
+        case Digest::MD5:
+            return EVP_md5();
+        case Digest::SHA1:
+            return EVP_sha1();
+        case Digest::SHA_2_224:
+            return EVP_sha224();
+        case Digest::SHA_2_256:
+            return EVP_sha256();
+        case Digest::SHA_2_384:
+            return EVP_sha384();
+        case Digest::SHA_2_512:
+            return EVP_sha512();
+    }
+    return nullptr;
+}
+
+}  // namespace android::hardware::security::keymint
+
+#endif  // HARDWARE_INTERFACES_KEYMINT_1_0_SUPPORT_OPENSSL_UTILS_H_
diff --git a/security/keymint/support/key_param_output.cpp b/security/keymint/support/key_param_output.cpp
new file mode 100644
index 0000000..b699b22
--- /dev/null
+++ b/security/keymint/support/key_param_output.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <keymint_support/key_param_output.h>
+
+#include <iomanip>
+
+#include <keymint_support/keymint_tags.h>
+
+namespace android::hardware::security::keymint {
+
+using ::std::endl;
+using ::std::ostream;
+
+ostream& operator<<(ostream& os, const ::std::vector<KeyParameter>& set) {
+    if (set.size() == 0) {
+        os << "(Empty)" << endl;
+    } else {
+        os << "\n";
+        for (const auto& elem : set) os << elem << endl;
+    }
+    return os;
+}
+
+// TODO(seleneh) update this to a parsing that looks at each tags individually
+// such as ALGORITHM BLOCK_MODE when aidl union support is added.
+ostream& operator<<(ostream& os, const KeyParameter& param) {
+    os << param.tag << ": ";
+    switch (typeFromTag(param.tag)) {
+        case TagType::INVALID:
+            return os << " Invalid";
+        case TagType::ENUM_REP:
+        case TagType::ENUM:
+        case TagType::UINT_REP:
+        case TagType::UINT:
+            return os << param.integer;
+        case TagType::ULONG_REP:
+        case TagType::ULONG:
+        case TagType::DATE:
+            return os << param.longInteger;
+        case TagType::BOOL:
+            return os << "true";
+        case TagType::BIGNUM:
+            os << " Bignum: ";
+            for (size_t i = 0; i < param.blob.size(); ++i) {
+                os << std::hex << ::std::setw(2) << static_cast<int>(param.blob[i]) << ::std::dec;
+            }
+            return os;
+        case TagType::BYTES:
+            os << " Bytes: ";
+            for (size_t i = 0; i < param.blob.size(); ++i) {
+                os << ::std::hex << ::std::setw(2) << static_cast<int>(param.blob[i]) << ::std::dec;
+            }
+            return os;
+    }
+    return os << "UNKNOWN TAG TYPE!";
+}
+
+}  // namespace android::hardware::security::keymint
diff --git a/security/keymint/support/keymint_utils.cpp b/security/keymint/support/keymint_utils.cpp
new file mode 100644
index 0000000..cd4cca2
--- /dev/null
+++ b/security/keymint/support/keymint_utils.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <regex.h>
+
+#include <android-base/properties.h>
+#include <hardware/hw_auth_token.h>
+#include <keymint_support/keymint_utils.h>
+
+#include <arpa/inet.h>
+
+namespace android::hardware::security::keymint {
+
+namespace {
+
+constexpr char kPlatformVersionProp[] = "ro.build.version.release";
+constexpr char kPlatformVersionRegex[] = "^([0-9]{1,2})(\\.([0-9]{1,2}))?(\\.([0-9]{1,2}))?";
+constexpr size_t kMajorVersionMatch = 1;
+constexpr size_t kMinorVersionMatch = 3;
+constexpr size_t kSubminorVersionMatch = 5;
+constexpr size_t kPlatformVersionMatchCount = kSubminorVersionMatch + 1;
+
+constexpr char kPlatformPatchlevelProp[] = "ro.build.version.security_patch";
+constexpr char kPlatformPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-[0-9]{2}$";
+constexpr size_t kYearMatch = 1;
+constexpr size_t kMonthMatch = 2;
+constexpr size_t kPlatformPatchlevelMatchCount = kMonthMatch + 1;
+
+uint32_t match_to_uint32(const char* expression, const regmatch_t& match) {
+    if (match.rm_so == -1) return 0;
+
+    size_t len = match.rm_eo - match.rm_so;
+    std::string s(expression + match.rm_so, len);
+    return std::stoul(s);
+}
+
+std::string wait_and_get_property(const char* prop) {
+    std::string prop_value;
+    while (!::android::base::WaitForPropertyCreation(prop))
+        ;
+    prop_value = ::android::base::GetProperty(prop, "" /* default */);
+    return prop_value;
+}
+
+}  // anonymous namespace
+
+uint32_t getOsVersion(const char* version_str) {
+    regex_t regex;
+    if (regcomp(&regex, kPlatformVersionRegex, REG_EXTENDED)) {
+        return 0;
+    }
+
+    regmatch_t matches[kPlatformVersionMatchCount];
+    int not_match =
+            regexec(&regex, version_str, kPlatformVersionMatchCount, matches, 0 /* flags */);
+    regfree(&regex);
+    if (not_match) {
+        return 0;
+    }
+
+    uint32_t major = match_to_uint32(version_str, matches[kMajorVersionMatch]);
+    uint32_t minor = match_to_uint32(version_str, matches[kMinorVersionMatch]);
+    uint32_t subminor = match_to_uint32(version_str, matches[kSubminorVersionMatch]);
+
+    return (major * 100 + minor) * 100 + subminor;
+}
+
+uint32_t getOsVersion() {
+    std::string version = wait_and_get_property(kPlatformVersionProp);
+    return getOsVersion(version.c_str());
+}
+
+uint32_t getOsPatchlevel(const char* patchlevel_str) {
+    regex_t regex;
+    if (regcomp(&regex, kPlatformPatchlevelRegex, REG_EXTENDED) != 0) {
+        return 0;
+    }
+
+    regmatch_t matches[kPlatformPatchlevelMatchCount];
+    int not_match =
+            regexec(&regex, patchlevel_str, kPlatformPatchlevelMatchCount, matches, 0 /* flags */);
+    regfree(&regex);
+    if (not_match) {
+        return 0;
+    }
+
+    uint32_t year = match_to_uint32(patchlevel_str, matches[kYearMatch]);
+    uint32_t month = match_to_uint32(patchlevel_str, matches[kMonthMatch]);
+
+    if (month < 1 || month > 12) {
+        return 0;
+    }
+    return year * 100 + month;
+}
+
+uint32_t getOsPatchlevel() {
+    std::string patchlevel = wait_and_get_property(kPlatformPatchlevelProp);
+    return getOsPatchlevel(patchlevel.c_str());
+}
+
+}  // namespace android::hardware::security::keymint
diff --git a/sensors/2.0/default/SensorsV2_0.h b/sensors/2.0/default/SensorsV2_0.h
index 345835a..8270a35 100644
--- a/sensors/2.0/default/SensorsV2_0.h
+++ b/sensors/2.0/default/SensorsV2_0.h
@@ -28,6 +28,19 @@
 namespace implementation {
 
 struct SensorsV2_0 : public ::android::hardware::sensors::V2_X::implementation::Sensors<ISensors> {
+  Return<void>
+  getSensorsList(V2_0::ISensors::getSensorsList_cb _hidl_cb) override {
+    std::vector<V1_0::SensorInfo> sensors;
+    for (const auto &sensor : mSensors) {
+      sensors.push_back(V2_1::implementation::convertToOldSensorInfo(
+          sensor.second->getSensorInfo()));
+    }
+
+    // Call the HIDL callback with the SensorInfo
+    _hidl_cb(sensors);
+
+    return Void();
+  }
 };
 
 }  // namespace implementation
diff --git a/sensors/2.1/default/SensorsV2_1.cpp b/sensors/2.1/default/SensorsV2_1.cpp
index 2e3d315..4c5386a 100644
--- a/sensors/2.1/default/SensorsV2_1.cpp
+++ b/sensors/2.1/default/SensorsV2_1.cpp
@@ -45,7 +45,8 @@
         mSensorInfo.fifoReservedEventCount = 0;
         mSensorInfo.fifoMaxEventCount = 0;
         mSensorInfo.requiredPermission = "";
-        mSensorInfo.flags = static_cast<uint32_t>(V1_0::SensorFlagBits::ON_CHANGE_MODE);
+        mSensorInfo.flags = static_cast<uint32_t>(V1_0::SensorFlagBits::ON_CHANGE_MODE |
+                                                  V1_0::SensorFlagBits::WAKE_UP);
     }
 };
 
diff --git a/sensors/2.1/default/SensorsV2_1.h b/sensors/2.1/default/SensorsV2_1.h
index 9f7fe04..c748383 100644
--- a/sensors/2.1/default/SensorsV2_1.h
+++ b/sensors/2.1/default/SensorsV2_1.h
@@ -54,6 +54,23 @@
     // Methods from ::android::hardware::sensors::V2_1::ISensors follow.
     Return<void> getSensorsList_2_1(ISensors::getSensorsList_2_1_cb _hidl_cb) override;
 
+    Return<void>
+    getSensorsList(V2_0::ISensors::getSensorsList_cb _hidl_cb) override {
+      std::vector<V1_0::SensorInfo> sensors;
+      for (const auto &sensor : mSensors) {
+        auto &info = sensor.second->getSensorInfo();
+        if (info.type != SensorType::HINGE_ANGLE) {
+          sensors.push_back(V2_1::implementation::convertToOldSensorInfo(
+              sensor.second->getSensorInfo()));
+        }
+      }
+
+      // Call the HIDL callback with the SensorInfo
+      _hidl_cb(sensors);
+
+      return Void();
+    }
+
     Return<Result> initialize_2_1(
             const ::android::hardware::MQDescriptorSync<V2_1::Event>& eventQueueDescriptor,
             const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
@@ -71,4 +88,4 @@
 }  // namespace hardware
 }  // namespace android
 
-#endif  // ANDROID_HARDWARE_SENSORS_V2_1_H
\ No newline at end of file
+#endif // ANDROID_HARDWARE_SENSORS_V2_1_H
diff --git a/sensors/common/default/2.X/Sensor.cpp b/sensors/common/default/2.X/Sensor.cpp
index 1841dff..642fc89 100644
--- a/sensors/common/default/2.X/Sensor.cpp
+++ b/sensors/common/default/2.X/Sensor.cpp
@@ -26,6 +26,7 @@
 namespace V2_X {
 namespace implementation {
 
+using ::android::hardware::sensors::V1_0::EventPayload;
 using ::android::hardware::sensors::V1_0::MetaDataEventType;
 using ::android::hardware::sensors::V1_0::OperationMode;
 using ::android::hardware::sensors::V1_0::Result;
@@ -57,11 +58,11 @@
     return mSensorInfo;
 }
 
-void Sensor::batch(int32_t samplingPeriodNs) {
-    if (samplingPeriodNs < mSensorInfo.minDelay * 1000) {
-        samplingPeriodNs = mSensorInfo.minDelay * 1000;
-    } else if (samplingPeriodNs > mSensorInfo.maxDelay * 1000) {
-        samplingPeriodNs = mSensorInfo.maxDelay * 1000;
+void Sensor::batch(int64_t samplingPeriodNs) {
+    if (samplingPeriodNs < mSensorInfo.minDelay * 1000ll) {
+        samplingPeriodNs = mSensorInfo.minDelay * 1000ll;
+    } else if (samplingPeriodNs > mSensorInfo.maxDelay * 1000ll) {
+        samplingPeriodNs = mSensorInfo.maxDelay * 1000ll;
     }
 
     if (mSamplingPeriodNs != samplingPeriodNs) {
@@ -138,10 +139,8 @@
     event.sensorHandle = mSensorInfo.sensorHandle;
     event.sensorType = mSensorInfo.type;
     event.timestamp = ::android::elapsedRealtimeNano();
-    event.u.vec3.x = 0;
-    event.u.vec3.y = 0;
-    event.u.vec3.z = 0;
-    event.u.vec3.status = SensorStatus::ACCURACY_HIGH;
+    memset(&event.u, 0, sizeof(event.u));
+    readEventPayload(event.u);
     events.push_back(event);
     return events;
 }
@@ -189,7 +188,7 @@
 
     for (auto iter = events.begin(); iter != events.end(); ++iter) {
         Event ev = *iter;
-        if (ev.u.vec3 != mPreviousEvent.u.vec3 || !mPreviousEventSet) {
+        if (!mPreviousEventSet || memcmp(&mPreviousEvent.u, &ev.u, sizeof(ev.u)) != 0) {
             outputEvents.push_back(ev);
             mPreviousEvent = ev;
             mPreviousEventSet = true;
@@ -208,7 +207,7 @@
     mSensorInfo.maxRange = 78.4f;  // +/- 8g
     mSensorInfo.resolution = 1.52e-5;
     mSensorInfo.power = 0.001f;        // mA
-    mSensorInfo.minDelay = 20 * 1000;  // microseconds
+    mSensorInfo.minDelay = 10 * 1000;  // microseconds
     mSensorInfo.maxDelay = kDefaultMaxDelayUs;
     mSensorInfo.fifoReservedEventCount = 0;
     mSensorInfo.fifoMaxEventCount = 0;
@@ -216,6 +215,13 @@
     mSensorInfo.flags = static_cast<uint32_t>(SensorFlagBits::DATA_INJECTION);
 };
 
+void AccelSensor::readEventPayload(EventPayload& payload) {
+    payload.vec3.x = 0;
+    payload.vec3.y = 0;
+    payload.vec3.z = -9.8;
+    payload.vec3.status = SensorStatus::ACCURACY_HIGH;
+}
+
 PressureSensor::PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback)
     : Sensor(callback) {
     mSensorInfo.sensorHandle = sensorHandle;
@@ -235,6 +241,10 @@
     mSensorInfo.flags = 0;
 };
 
+void PressureSensor::readEventPayload(EventPayload& payload) {
+    payload.scalar = 1013.25f;
+}
+
 MagnetometerSensor::MagnetometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback)
     : Sensor(callback) {
     mSensorInfo.sensorHandle = sensorHandle;
@@ -330,25 +340,6 @@
     mSensorInfo.flags = static_cast<uint32_t>(SensorFlagBits::ON_CHANGE_MODE);
 };
 
-DeviceTempSensor::DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback)
-    : OnChangeSensor(callback) {
-    mSensorInfo.sensorHandle = sensorHandle;
-    mSensorInfo.name = "Device Temp Sensor";
-    mSensorInfo.vendor = "Vendor String";
-    mSensorInfo.version = 1;
-    mSensorInfo.type = SensorType::TEMPERATURE;
-    mSensorInfo.typeAsString = "";
-    mSensorInfo.maxRange = 80.0f;
-    mSensorInfo.resolution = 0.01f;
-    mSensorInfo.power = 0.001f;
-    mSensorInfo.minDelay = 40 * 1000;  // microseconds
-    mSensorInfo.maxDelay = kDefaultMaxDelayUs;
-    mSensorInfo.fifoReservedEventCount = 0;
-    mSensorInfo.fifoMaxEventCount = 0;
-    mSensorInfo.requiredPermission = "";
-    mSensorInfo.flags = static_cast<uint32_t>(SensorFlagBits::ON_CHANGE_MODE);
-}
-
 RelativeHumiditySensor::RelativeHumiditySensor(int32_t sensorHandle,
                                                ISensorsEventCallback* callback)
     : OnChangeSensor(callback) {
diff --git a/sensors/common/default/2.X/Sensor.h b/sensors/common/default/2.X/Sensor.h
index 2f8a143..8ef11be 100644
--- a/sensors/common/default/2.X/Sensor.h
+++ b/sensors/common/default/2.X/Sensor.h
@@ -32,7 +32,7 @@
 namespace V2_X {
 namespace implementation {
 
-static constexpr float kDefaultMaxDelayUs = 10 * 1000 * 1000;
+static constexpr int32_t kDefaultMaxDelayUs = 10 * 1000 * 1000;
 
 class ISensorsEventCallback {
   public:
@@ -47,6 +47,7 @@
     using OperationMode = ::android::hardware::sensors::V1_0::OperationMode;
     using Result = ::android::hardware::sensors::V1_0::Result;
     using Event = ::android::hardware::sensors::V2_1::Event;
+    using EventPayload = ::android::hardware::sensors::V1_0::EventPayload;
     using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
     using SensorType = ::android::hardware::sensors::V2_1::SensorType;
 
@@ -54,7 +55,7 @@
     virtual ~Sensor();
 
     const SensorInfo& getSensorInfo() const;
-    void batch(int32_t samplingPeriodNs);
+    void batch(int64_t samplingPeriodNs);
     virtual void activate(bool enable);
     Result flush();
 
@@ -65,6 +66,7 @@
   protected:
     void run();
     virtual std::vector<Event> readEvents();
+    virtual void readEventPayload(EventPayload&) {}
     static void startThread(Sensor* sensor);
 
     bool isWakeUpSensor();
@@ -101,6 +103,9 @@
 class AccelSensor : public Sensor {
   public:
     AccelSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
+
+  protected:
+    virtual void readEventPayload(EventPayload& payload) override;
 };
 
 class GyroSensor : public Sensor {
@@ -113,14 +118,12 @@
     AmbientTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
 };
 
-class DeviceTempSensor : public OnChangeSensor {
-  public:
-    DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
-};
-
 class PressureSensor : public Sensor {
   public:
     PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
+
+  protected:
+    virtual void readEventPayload(EventPayload& payload) override;
 };
 
 class MagnetometerSensor : public Sensor {
diff --git a/sensors/common/default/2.X/Sensors.h b/sensors/common/default/2.X/Sensors.h
index ee8240d..1124425 100644
--- a/sensors/common/default/2.X/Sensors.h
+++ b/sensors/common/default/2.X/Sensors.h
@@ -64,7 +64,6 @@
         AddSensor<AccelSensor>();
         AddSensor<GyroSensor>();
         AddSensor<AmbientTempSensor>();
-        AddSensor<DeviceTempSensor>();
         AddSensor<PressureSensor>();
         AddSensor<MagnetometerSensor>();
         AddSensor<LightSensor>();
@@ -79,19 +78,6 @@
     }
 
     // Methods from ::android::hardware::sensors::V2_0::ISensors follow.
-    Return<void> getSensorsList(V2_0::ISensors::getSensorsList_cb _hidl_cb) override {
-        std::vector<V1_0::SensorInfo> sensors;
-        for (const auto& sensor : mSensors) {
-            sensors.push_back(
-                    V2_1::implementation::convertToOldSensorInfo(sensor.second->getSensorInfo()));
-        }
-
-        // Call the HIDL callback with the SensorInfo
-        _hidl_cb(sensors);
-
-        return Void();
-    }
-
     Return<Result> setOperationMode(OperationMode mode) override {
         for (auto sensor : mSensors) {
             sensor.second->setOperationMode(mode);
diff --git a/sensors/common/default/2.X/multihal/HalProxy.cpp b/sensors/common/default/2.X/multihal/HalProxy.cpp
index 8dda0af..f5fc066 100644
--- a/sensors/common/default/2.X/multihal/HalProxy.cpp
+++ b/sensors/common/default/2.X/multihal/HalProxy.cpp
@@ -124,7 +124,9 @@
 Return<void> HalProxy::getSensorsList(ISensorsV2_0::getSensorsList_cb _hidl_cb) {
     std::vector<V1_0::SensorInfo> sensors;
     for (const auto& iter : mSensors) {
+      if (iter.second.type != SensorType::HINGE_ANGLE) {
         sensors.push_back(convertToOldSensorInfo(iter.second));
+      }
     }
     _hidl_cb(sensors);
     return Void();
diff --git a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp
index 1efd971..69debb6 100644
--- a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp
+++ b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp
@@ -71,9 +71,10 @@
     return mSensorInfo;
 }
 
-void Sensor::batch(int32_t samplingPeriodNs) {
-    samplingPeriodNs =
-            std::clamp(samplingPeriodNs, mSensorInfo.minDelay * 1000, mSensorInfo.maxDelay * 1000);
+void Sensor::batch(int64_t samplingPeriodNs) {
+    samplingPeriodNs = std::clamp(samplingPeriodNs,
+                                  static_cast<int64_t>(mSensorInfo.minDelay) * 1000,
+                                  static_cast<int64_t>(mSensorInfo.maxDelay) * 1000);
 
     if (mSamplingPeriodNs != samplingPeriodNs) {
         mSamplingPeriodNs = samplingPeriodNs;
@@ -323,17 +324,6 @@
     mSensorInfo.minDelay = 40 * 1000;  // microseconds
 }
 
-DeviceTempSensor::DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback)
-    : ContinuousSensor(sensorHandle, callback) {
-    mSensorInfo.name = "Device Temp Sensor";
-    mSensorInfo.type = SensorType::TEMPERATURE;
-    mSensorInfo.typeAsString = SENSOR_STRING_TYPE_TEMPERATURE;
-    mSensorInfo.maxRange = 80.0f;
-    mSensorInfo.resolution = 0.01f;
-    mSensorInfo.power = 0.001f;
-    mSensorInfo.minDelay = 40 * 1000;  // microseconds
-}
-
 RelativeHumiditySensor::RelativeHumiditySensor(int32_t sensorHandle,
                                                ISensorsEventCallback* callback)
     : OnChangeSensor(sensorHandle, callback) {
diff --git a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.h b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.h
index 5cf9f83..08c8647 100644
--- a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.h
+++ b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.h
@@ -49,7 +49,7 @@
     virtual ~Sensor();
 
     const SensorInfo& getSensorInfo() const;
-    void batch(int32_t samplingPeriodNs);
+    void batch(int64_t samplingPeriodNs);
     virtual void activate(bool enable);
     Result flush();
 
@@ -114,11 +114,6 @@
     std::vector<Event> readEvents() override;
 };
 
-class DeviceTempSensor : public ContinuousSensor {
-  public:
-    DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
-};
-
 class PressureSensor : public ContinuousSensor {
   public:
     PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
diff --git a/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.h b/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.h
index 1a78e84..353563c 100644
--- a/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.h
+++ b/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.h
@@ -206,7 +206,6 @@
         ISensorsSubHalBase::AddSensor<GyroSensor>();
         ISensorsSubHalBase::AddSensor<MagnetometerSensor>();
         ISensorsSubHalBase::AddSensor<PressureSensor>();
-        ISensorsSubHalBase::AddSensor<DeviceTempSensor>();
     }
 };
 
@@ -231,7 +230,6 @@
         ISensorsSubHalBase::AddSensor<GyroSensor>();
         ISensorsSubHalBase::AddSensor<MagnetometerSensor>();
         ISensorsSubHalBase::AddSensor<PressureSensor>();
-        ISensorsSubHalBase::AddSensor<DeviceTempSensor>();
         ISensorsSubHalBase::AddSensor<AmbientTempSensor>();
         ISensorsSubHalBase::AddSensor<LightSensor>();
         ISensorsSubHalBase::AddSensor<ProximitySensor>();
diff --git a/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
index e674ddb..8cf5003 100644
--- a/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
+++ b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
@@ -366,7 +366,7 @@
     for (const SensorInfoType& sensor : getSensorsList()) {
         maxHandle = std::max(maxHandle, sensor.sensorHandle);
     }
-    return maxHandle + 1;
+    return maxHandle + 42;
 }
 
 // Test if sensor list returned is valid
@@ -845,7 +845,11 @@
         std::shared_ptr<SensorsTestSharedMemory<SensorTypeVersion, EventType>> mem,
         int32_t* directChannelHandle, bool supportsSharedMemType, bool supportsAnyDirectChannel) {
     char* buffer = mem->getBuffer();
-    memset(buffer, 0xff, mem->getSize());
+    size_t size = mem->getSize();
+
+    if (supportsSharedMemType) {
+        memset(buffer, 0xff, size);
+    }
 
     registerDirectChannel(mem->getSharedMemInfo(), [&](Result result, int32_t channelHandle) {
         if (supportsSharedMemType) {
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
index 03bec87..a8e1996 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
@@ -109,7 +109,6 @@
         case SensorTypeT::MAGNETIC_FIELD:
         case SensorTypeT::ORIENTATION:
         case SensorTypeT::PRESSURE:
-        case SensorTypeT::TEMPERATURE:
         case SensorTypeT::GRAVITY:
         case SensorTypeT::LINEAR_ACCELERATION:
         case SensorTypeT::ROTATION_VECTOR:
@@ -145,6 +144,10 @@
         case SensorTypeT::DYNAMIC_SENSOR_META:
             return SensorFlagBits::SPECIAL_REPORTING_MODE;
 
+        case SensorTypeT::TEMPERATURE:
+            ALOGW("Device temperature sensor is deprecated, ignoring for test");
+            return (SensorFlagBits)-1;
+
         default:
             ALOGW("Type %d is not implemented in expectedReportModeForType", (int)type);
             return (SensorFlagBits)-1;
@@ -334,7 +337,7 @@
         usleep(500000);  // sleep 0.5 sec to wait for change rate to happen
         events1 = collectEvents(collectionTimeoutUs, minNEvent, getEnvironment());
 
-        // second collection, without stop sensor
+        // second collection, without stopping the sensor
         ASSERT_EQ(batch(handle, secondCollectionPeriod, batchingPeriodInNs), Result::OK);
 
         usleep(500000);  // sleep 0.5 sec to wait for change rate to happen
diff --git a/soundtrigger/2.1/ISoundTriggerHwCallback.hal b/soundtrigger/2.1/ISoundTriggerHwCallback.hal
index 42e851b..b7720b6 100644
--- a/soundtrigger/2.1/ISoundTriggerHwCallback.hal
+++ b/soundtrigger/2.1/ISoundTriggerHwCallback.hal
@@ -22,7 +22,6 @@
 /**
  * SoundTrigger HAL Callback interface. Obtained during SoundTrigger setup.
  */
-@hidl_callback
 interface ISoundTriggerHwCallback extends @2.0::ISoundTriggerHwCallback {
 
     /**
diff --git a/tests/extension/vibrator/aidl/default/Android.bp b/tests/extension/vibrator/aidl/default/Android.bp
index ed40d25..80f7727 100644
--- a/tests/extension/vibrator/aidl/default/Android.bp
+++ b/tests/extension/vibrator/aidl/default/Android.bp
@@ -19,7 +19,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.vibrator-ndk_platform",
-        "android.hardware.tests.extension.vibrator-ndk_platform",
+        "android.hardware.vibrator-unstable-ndk_platform",
+        "android.hardware.tests.extension.vibrator-unstable-ndk_platform",
     ],
 }
diff --git a/tests/foo/1.0/IFoo.hal b/tests/foo/1.0/IFoo.hal
index 4c54427..e242616 100644
--- a/tests/foo/1.0/IFoo.hal
+++ b/tests/foo/1.0/IFoo.hal
@@ -21,6 +21,7 @@
 import ISimple;
 import ITheirTypes.FloatArray;
 
+@SensitiveData // for test
 interface IFoo {
 
     enum SomeBaseEnum : uint8_t {
diff --git a/tetheroffload/control/1.0/vts/functional/Android.bp b/tetheroffload/control/1.0/vts/functional/Android.bp
index c51dd8b..c397df4 100644
--- a/tetheroffload/control/1.0/vts/functional/Android.bp
+++ b/tetheroffload/control/1.0/vts/functional/Android.bp
@@ -15,10 +15,18 @@
 cc_test {
     name: "VtsHalTetheroffloadControlV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
-    srcs: ["VtsHalTetheroffloadControlV1_0TargetTest.cpp"],
+    local_include_dirs: ["include"],
+    srcs: [
+        "VtsHalTetheroffloadControlV1_0TargetTest.cpp",
+        "OffloadControlTestBase.cpp",
+        "OffloadControlTestUtils.cpp",
+    ],
     static_libs: [
         "android.hardware.tetheroffload.config@1.0",
         "android.hardware.tetheroffload.control@1.0",
     ],
-    test_suites: ["general-tests", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
 }
diff --git a/tetheroffload/control/1.0/vts/functional/OffloadControlTestBase.cpp b/tetheroffload/control/1.0/vts/functional/OffloadControlTestBase.cpp
new file mode 100644
index 0000000..bd0dad7
--- /dev/null
+++ b/tetheroffload/control/1.0/vts/functional/OffloadControlTestBase.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <OffloadControlTestBase.h>
+
+void OffloadControlTestBase::TearDown() {
+    // For good measure, the teardown should try stopOffload() once more, since
+    // different HAL call test cycles might enter this function. Also the
+    // return code cannot be actually expected for all cases, hence ignore it.
+    stopOffload(ExpectBoolean::Ignored);
+}
+
+// The IOffloadConfig HAL is tested more thoroughly elsewhere. Here the class
+// just setup everything correctly and verify basic readiness.
+void OffloadControlTestBase::setupConfigHal() {
+    config = IOffloadConfig::getService(std::get<0>(GetParam()));
+    ASSERT_NE(nullptr, config.get()) << "Could not get HIDL instance";
+
+    unique_fd fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY));
+    if (fd1.get() < 0) {
+        ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
+        FAIL();
+    }
+    native_handle_t* const nativeHandle1 = native_handle_create(1, 0);
+    nativeHandle1->data[0] = fd1.release();
+    hidl_handle h1;
+    h1.setTo(nativeHandle1, true);
+
+    unique_fd fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY));
+    if (fd2.get() < 0) {
+        ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
+        FAIL();
+    }
+    native_handle_t* const nativeHandle2 = native_handle_create(1, 0);
+    nativeHandle2->data[0] = fd2.release();
+    hidl_handle h2;
+    h2.setTo(nativeHandle2, true);
+
+    const Return<void> ret = config->setHandles(h1, h2, ASSERT_TRUE_CALLBACK);
+    ASSERT_TRUE(ret.isOk());
+}
+
+void OffloadControlTestBase::prepareControlHal() {
+    control = createControl(std::get<1>(GetParam()));
+    ASSERT_NE(nullptr, control.get()) << "Could not get HIDL instance";
+
+    control_cb = new TetheringOffloadCallback();
+    ASSERT_NE(nullptr, control_cb.get()) << "Could not get get offload callback";
+}
+
+void OffloadControlTestBase::initOffload(const bool expected_result) {
+    auto init_cb = [&](bool success, std::string errMsg) {
+        std::string msg = StringPrintf("Unexpectedly %s to init offload: %s",
+                                       success ? "succeeded" : "failed", errMsg.c_str());
+        ASSERT_EQ(expected_result, success) << msg;
+    };
+    const Return<void> ret = control->initOffload(control_cb, init_cb);
+    ASSERT_TRUE(ret.isOk());
+}
+
+void OffloadControlTestBase::setupControlHal() {
+    prepareControlHal();
+    initOffload(true);
+}
+
+void OffloadControlTestBase::stopOffload(const ExpectBoolean value) {
+    auto cb = [&](bool success, const hidl_string& errMsg) {
+        switch (value) {
+            case ExpectBoolean::False:
+                ASSERT_EQ(false, success) << "Unexpectedly able to stop offload: " << errMsg;
+                break;
+            case ExpectBoolean::True:
+                ASSERT_EQ(true, success) << "Unexpectedly failed to stop offload: " << errMsg;
+                break;
+            case ExpectBoolean::Ignored:
+                break;
+        }
+    };
+    const Return<void> ret = control->stopOffload(cb);
+    ASSERT_TRUE(ret.isOk());
+}
diff --git a/tetheroffload/control/1.0/vts/functional/OffloadControlTestUtils.cpp b/tetheroffload/control/1.0/vts/functional/OffloadControlTestUtils.cpp
new file mode 100644
index 0000000..c784fe1
--- /dev/null
+++ b/tetheroffload/control/1.0/vts/functional/OffloadControlTestUtils.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <OffloadControlTestUtils.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+inline const sockaddr* asSockaddr(const sockaddr_nl* nladdr) {
+    return reinterpret_cast<const sockaddr*>(nladdr);
+}
+
+int conntrackSocket(unsigned groups) {
+    unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
+    if (s.get() < 0) {
+        return -errno;
+    }
+
+    const struct sockaddr_nl bind_addr = {
+            .nl_family = AF_NETLINK,
+            .nl_pad = 0,
+            .nl_pid = 0,
+            .nl_groups = groups,
+    };
+    if (::bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) < 0) {
+        return -errno;
+    }
+
+    const struct sockaddr_nl kernel_addr = {
+            .nl_family = AF_NETLINK,
+            .nl_pad = 0,
+            .nl_pid = 0,
+            .nl_groups = groups,
+    };
+    if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
+        return -errno;
+    }
+
+    return s.release();
+}
\ No newline at end of file
diff --git a/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp b/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
index d3a7020..ad4ef12 100644
--- a/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
+++ b/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
@@ -16,215 +16,24 @@
 
 #define LOG_TAG "VtsOffloadControlV1_0TargetTest"
 
-#include <VtsHalHidlTargetCallbackBase.h>
+#include <OffloadControlTestV1_0.h>
 #include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
-#include <android/hardware/tetheroffload/control/1.0/IOffloadControl.h>
-#include <android/hardware/tetheroffload/control/1.0/types.h>
 #include <gtest/gtest.h>
 #include <hidl/GtestPrinter.h>
 #include <hidl/ServiceManagement.h>
-#include <linux/netfilter/nfnetlink.h>
-#include <linux/netlink.h>
-#include <log/log.h>
 #include <net/if.h>
 #include <sys/socket.h>
-#include <unistd.h>
-#include <set>
 
 using android::base::StringPrintf;
-using android::base::unique_fd;
-using android::hardware::hidl_handle;
-using android::hardware::hidl_string;
-using android::hardware::hidl_vec;
 using android::hardware::Return;
 using android::hardware::tetheroffload::config::V1_0::IOffloadConfig;
 using android::hardware::tetheroffload::control::V1_0::IOffloadControl;
-using android::hardware::tetheroffload::control::V1_0::IPv4AddrPortPair;
-using android::hardware::tetheroffload::control::V1_0::ITetheringOffloadCallback;
-using android::hardware::tetheroffload::control::V1_0::OffloadCallbackEvent;
-using android::hardware::tetheroffload::control::V1_0::NatTimeoutUpdate;
-using android::hardware::tetheroffload::control::V1_0::NetworkProtocol;
 using android::hardware::Void;
-using android::sp;
-
-enum class ExpectBoolean {
-    Ignored = -1,
-    False = 0,
-    True = 1,
-};
 
 constexpr const char* TEST_IFACE = "rmnet_data0";
 
-// We use #defines here so as to get local lamba captures and error message line numbers
-#define ASSERT_TRUE_CALLBACK                                                    \
-    [&](bool success, std::string errMsg) {                                     \
-        std::string msg = StringPrintf("unexpected error: %s", errMsg.c_str()); \
-        ASSERT_TRUE(success) << msg;                                            \
-    }
-
-#define ASSERT_FALSE_CALLBACK                                                 \
-    [&](bool success, std::string errMsg) {                                   \
-        std::string msg = StringPrintf("expected error: %s", errMsg.c_str()); \
-        ASSERT_FALSE(success) << msg;                                         \
-    }
-
-#define ASSERT_ZERO_BYTES_CALLBACK            \
-    [&](uint64_t rxBytes, uint64_t txBytes) { \
-        EXPECT_EQ(0ULL, rxBytes);             \
-        EXPECT_EQ(0ULL, txBytes);             \
-    }
-
-inline const sockaddr* asSockaddr(const sockaddr_nl* nladdr) {
-    return reinterpret_cast<const sockaddr*>(nladdr);
-}
-
-int conntrackSocket(unsigned groups) {
-    unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
-    if (s.get() < 0) {
-        return -errno;
-    }
-
-    const struct sockaddr_nl bind_addr = {
-        .nl_family = AF_NETLINK, .nl_pad = 0, .nl_pid = 0, .nl_groups = groups,
-    };
-    if (::bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) < 0) {
-        return -errno;
-    }
-
-    const struct sockaddr_nl kernel_addr = {
-        .nl_family = AF_NETLINK, .nl_pad = 0, .nl_pid = 0, .nl_groups = groups,
-    };
-    if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
-        return -errno;
-    }
-
-    return s.release();
-}
-
-constexpr char kCallbackOnEvent[] = "onEvent";
-constexpr char kCallbackUpdateTimeout[] = "updateTimeout";
-
-class TetheringOffloadCallbackArgs {
-   public:
-    OffloadCallbackEvent last_event;
-    NatTimeoutUpdate last_params;
-};
-
-class OffloadControlHidlTestBase
-    : public testing::TestWithParam<std::tuple<std::string, std::string>> {
-   public:
-    virtual void SetUp() override {
-        setupConfigHal();
-        prepareControlHal();
-    }
-
-    virtual void TearDown() override {
-        // For good measure, we should try stopOffload() once more. Since we
-        // don't know where we are in HAL call test cycle we don't know what
-        // return code to actually expect, so we just ignore it.
-        stopOffload(ExpectBoolean::Ignored);
-    }
-
-    // The IOffloadConfig HAL is tested more thoroughly elsewhere. He we just
-    // setup everything correctly and verify basic readiness.
-    void setupConfigHal() {
-        config = IOffloadConfig::getService(std::get<0>(GetParam()));
-        ASSERT_NE(nullptr, config.get()) << "Could not get HIDL instance";
-
-        unique_fd fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY));
-        if (fd1.get() < 0) {
-            ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
-            FAIL();
-        }
-        native_handle_t* const nativeHandle1 = native_handle_create(1, 0);
-        nativeHandle1->data[0] = fd1.release();
-        hidl_handle h1;
-        h1.setTo(nativeHandle1, true);
-
-        unique_fd fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY));
-        if (fd2.get() < 0) {
-            ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
-            FAIL();
-        }
-        native_handle_t* const nativeHandle2 = native_handle_create(1, 0);
-        nativeHandle2->data[0] = fd2.release();
-        hidl_handle h2;
-        h2.setTo(nativeHandle2, true);
-
-        const Return<void> ret = config->setHandles(h1, h2, ASSERT_TRUE_CALLBACK);
-        ASSERT_TRUE(ret.isOk());
-    }
-
-    void prepareControlHal() {
-        control = IOffloadControl::getService(std::get<1>(GetParam()));
-        ASSERT_NE(nullptr, control.get()) << "Could not get HIDL instance";
-
-        control_cb = new TetheringOffloadCallback();
-        ASSERT_NE(nullptr, control_cb.get()) << "Could not get get offload callback";
-    }
-
-    void initOffload(const bool expected_result) {
-        auto init_cb = [&](bool success, std::string errMsg) {
-            std::string msg = StringPrintf("Unexpectedly %s to init offload: %s",
-                                           success ? "succeeded" : "failed", errMsg.c_str());
-            ASSERT_EQ(expected_result, success) << msg;
-        };
-        const Return<void> ret = control->initOffload(control_cb, init_cb);
-        ASSERT_TRUE(ret.isOk());
-    }
-
-    void setupControlHal() {
-        prepareControlHal();
-        initOffload(true);
-    }
-
-    void stopOffload(const ExpectBoolean value) {
-        auto cb = [&](bool success, const hidl_string& errMsg) {
-            switch (value) {
-                case ExpectBoolean::False:
-                    ASSERT_EQ(false, success) << "Unexpectedly able to stop offload: " << errMsg;
-                    break;
-                case ExpectBoolean::True:
-                    ASSERT_EQ(true, success) << "Unexpectedly failed to stop offload: " << errMsg;
-                    break;
-                case ExpectBoolean::Ignored:
-                    break;
-            }
-        };
-        const Return<void> ret = control->stopOffload(cb);
-        ASSERT_TRUE(ret.isOk());
-    }
-
-    // Callback class for both events and NAT timeout updates.
-    class TetheringOffloadCallback
-        : public testing::VtsHalHidlTargetCallbackBase<TetheringOffloadCallbackArgs>,
-          public ITetheringOffloadCallback {
-       public:
-        TetheringOffloadCallback() = default;
-        virtual ~TetheringOffloadCallback() = default;
-
-        Return<void> onEvent(OffloadCallbackEvent event) override {
-            const TetheringOffloadCallbackArgs args{.last_event = event};
-            NotifyFromCallback(kCallbackOnEvent, args);
-            return Void();
-        };
-
-        Return<void> updateTimeout(const NatTimeoutUpdate& params) override {
-            const TetheringOffloadCallbackArgs args{.last_params = params};
-            NotifyFromCallback(kCallbackUpdateTimeout, args);
-            return Void();
-        };
-    };
-
-    sp<IOffloadConfig> config;
-    sp<IOffloadControl> control;
-    sp<TetheringOffloadCallback> control_cb;
-};
-
 // Call initOffload() multiple times. Check that non-first initOffload() calls return false.
-TEST_P(OffloadControlHidlTestBase, AdditionalInitsWithoutStopReturnFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, AdditionalInitsWithoutStopReturnFalse) {
     initOffload(true);
     initOffload(false);
     initOffload(false);
@@ -232,7 +41,7 @@
 }
 
 // Check that calling stopOffload() without first having called initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, MultipleStopsWithoutInitReturnFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, MultipleStopsWithoutInitReturnFalse) {
     stopOffload(ExpectBoolean::False);
     stopOffload(ExpectBoolean::False);
     stopOffload(ExpectBoolean::False);
@@ -251,7 +60,7 @@
 }
 
 // Check that calling stopOffload() after a complete init/stop cycle returns false.
-TEST_P(OffloadControlHidlTestBase, AdditionalStopsWithInitReturnFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, AdditionalStopsWithInitReturnFalse) {
     initOffload(true);
     // Call setUpstreamParameters() so that "offload" can be reasonably said
     // to be both requested and operational.
@@ -273,7 +82,7 @@
 }
 
 // Check that calling setLocalPrefixes() without first having called initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, SetLocalPrefixesWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, SetLocalPrefixesWithoutInitReturnsFalse) {
     const vector<hidl_string> prefixes{hidl_string("2001:db8::/64")};
     const Return<void> ret = control->setLocalPrefixes(prefixes, ASSERT_FALSE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
@@ -281,14 +90,14 @@
 
 // Check that calling getForwardedStats() without first having called initOffload()
 // returns zero bytes statistics.
-TEST_P(OffloadControlHidlTestBase, GetForwardedStatsWithoutInitReturnsZeroValues) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, GetForwardedStatsWithoutInitReturnsZeroValues) {
     const hidl_string upstream(TEST_IFACE);
     const Return<void> ret = control->getForwardedStats(upstream, ASSERT_ZERO_BYTES_CALLBACK);
     EXPECT_TRUE(ret.isOk());
 }
 
 // Check that calling setDataLimit() without first having called initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, SetDataLimitWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, SetDataLimitWithoutInitReturnsFalse) {
     const hidl_string upstream(TEST_IFACE);
     const uint64_t limit = 5000ULL;
     const Return<void> ret = control->setDataLimit(upstream, limit, ASSERT_FALSE_CALLBACK);
@@ -297,7 +106,7 @@
 
 // Check that calling setUpstreamParameters() without first having called initOffload()
 // returns false.
-TEST_P(OffloadControlHidlTestBase, SetUpstreamParametersWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, SetUpstreamParametersWithoutInitReturnsFalse) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr("192.0.2.0/24");
     const hidl_string v4Gw("192.0.2.1");
@@ -309,7 +118,7 @@
 
 // Check that calling addDownstream() with an IPv4 prefix without first having called
 // initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, AddIPv4DownstreamWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, AddIPv4DownstreamWithoutInitReturnsFalse) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string prefix("192.0.2.0/24");
     const Return<void> ret = control->addDownstream(iface, prefix, ASSERT_FALSE_CALLBACK);
@@ -318,7 +127,7 @@
 
 // Check that calling addDownstream() with an IPv6 prefix without first having called
 // initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, AddIPv6DownstreamWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, AddIPv6DownstreamWithoutInitReturnsFalse) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string prefix("2001:db8::/64");
     const Return<void> ret = control->addDownstream(iface, prefix, ASSERT_FALSE_CALLBACK);
@@ -327,7 +136,7 @@
 
 // Check that calling removeDownstream() with an IPv4 prefix without first having called
 // initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, RemoveIPv4DownstreamWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, RemoveIPv4DownstreamWithoutInitReturnsFalse) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string prefix("192.0.2.0/24");
     const Return<void> ret = control->removeDownstream(iface, prefix, ASSERT_FALSE_CALLBACK);
@@ -336,48 +145,33 @@
 
 // Check that calling removeDownstream() with an IPv6 prefix without first having called
 // initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, RemoveIPv6DownstreamWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, RemoveIPv6DownstreamWithoutInitReturnsFalse) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string prefix("2001:db8::/64");
     const Return<void> ret = control->removeDownstream(iface, prefix, ASSERT_FALSE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
 }
 
-class OffloadControlHidlTest : public OffloadControlHidlTestBase {
-   public:
-    virtual void SetUp() override {
-        setupConfigHal();
-        setupControlHal();
-    }
-
-    virtual void TearDown() override {
-        // For good measure, we should try stopOffload() once more. Since we
-        // don't know where we are in HAL call test cycle we don't know what
-        // return code to actually expect, so we just ignore it.
-        stopOffload(ExpectBoolean::Ignored);
-    }
-};
-
 /*
  * Tests for IOffloadControl::setLocalPrefixes().
  */
 
 // Test setLocalPrefixes() accepts an IPv4 address.
-TEST_P(OffloadControlHidlTest, SetLocalPrefixesIPv4AddressOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetLocalPrefixesIPv4AddressOk) {
     const vector<hidl_string> prefixes{hidl_string("192.0.2.1")};
     const Return<void> ret = control->setLocalPrefixes(prefixes, ASSERT_TRUE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
 }
 
 // Test setLocalPrefixes() accepts an IPv6 address.
-TEST_P(OffloadControlHidlTest, SetLocalPrefixesIPv6AddressOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetLocalPrefixesIPv6AddressOk) {
     const vector<hidl_string> prefixes{hidl_string("fe80::1")};
     const Return<void> ret = control->setLocalPrefixes(prefixes, ASSERT_TRUE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
 }
 
 // Test setLocalPrefixes() accepts both IPv4 and IPv6 prefixes.
-TEST_P(OffloadControlHidlTest, SetLocalPrefixesIPv4v6PrefixesOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetLocalPrefixesIPv4v6PrefixesOk) {
     const vector<hidl_string> prefixes{hidl_string("192.0.2.0/24"), hidl_string("fe80::/64")};
     const Return<void> ret = control->setLocalPrefixes(prefixes, ASSERT_TRUE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
@@ -386,14 +180,14 @@
 // Test that setLocalPrefixes() fails given empty input. There is always
 // a non-empty set of local prefixes; when all networking interfaces are down
 // we still apply {127.0.0.0/8, ::1/128, fe80::/64} here.
-TEST_P(OffloadControlHidlTest, SetLocalPrefixesEmptyFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetLocalPrefixesEmptyFails) {
     const vector<hidl_string> prefixes{};
     const Return<void> ret = control->setLocalPrefixes(prefixes, ASSERT_FALSE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
 }
 
 // Test setLocalPrefixes() fails on incorrectly formed input strings.
-TEST_P(OffloadControlHidlTest, SetLocalPrefixesInvalidFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetLocalPrefixesInvalidFails) {
     const vector<hidl_string> prefixes{hidl_string("192.0.2.0/24"), hidl_string("invalid")};
     const Return<void> ret = control->setLocalPrefixes(prefixes, ASSERT_FALSE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
@@ -404,7 +198,7 @@
  */
 
 // Test that getForwardedStats() for a non-existent upstream yields zero bytes statistics.
-TEST_P(OffloadControlHidlTest, GetForwardedStatsInvalidUpstreamIface) {
+TEST_P(OffloadControlTestV1_0_HalStarted, GetForwardedStatsInvalidUpstreamIface) {
     const hidl_string upstream("invalid");
     const Return<void> ret = control->getForwardedStats(upstream, ASSERT_ZERO_BYTES_CALLBACK);
     EXPECT_TRUE(ret.isOk());
@@ -412,7 +206,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, GetForwardedStatsDummyIface) {
+TEST_P(OffloadControlTestV1_0_HalStarted, GetForwardedStatsDummyIface) {
     const hidl_string upstream(TEST_IFACE);
     const Return<void> ret = control->getForwardedStats(upstream, ASSERT_ZERO_BYTES_CALLBACK);
     EXPECT_TRUE(ret.isOk());
@@ -423,7 +217,7 @@
  */
 
 // Test that setDataLimit() for an empty interface name fails.
-TEST_P(OffloadControlHidlTest, SetDataLimitEmptyUpstreamIfaceFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetDataLimitEmptyUpstreamIfaceFails) {
     const hidl_string upstream("");
     const uint64_t limit = 5000ULL;
     const Return<void> ret = control->setDataLimit(upstream, limit, ASSERT_FALSE_CALLBACK);
@@ -432,7 +226,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, SetDataLimitNonZeroOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetDataLimitNonZeroOk) {
     const hidl_string upstream(TEST_IFACE);
     const uint64_t limit = 5000ULL;
     const Return<void> ret = control->setDataLimit(upstream, limit, ASSERT_TRUE_CALLBACK);
@@ -441,7 +235,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, SetDataLimitZeroOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetDataLimitZeroOk) {
     const hidl_string upstream(TEST_IFACE);
     const uint64_t limit = 0ULL;
     const Return<void> ret = control->setDataLimit(upstream, limit, ASSERT_TRUE_CALLBACK);
@@ -454,7 +248,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersIPv6OnlyOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersIPv6OnlyOk) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr("");
     const hidl_string v4Gw("");
@@ -466,7 +260,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersAlternateIPv6OnlyOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersAlternateIPv6OnlyOk) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr;
     const hidl_string v4Gw;
@@ -478,7 +272,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersIPv4OnlyOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersIPv4OnlyOk) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr("192.0.2.2");
     const hidl_string v4Gw("192.0.2.1");
@@ -490,7 +284,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersIPv4v6Ok) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersIPv4v6Ok) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr("192.0.2.2");
     const hidl_string v4Gw("192.0.2.1");
@@ -501,7 +295,7 @@
 }
 
 // Test that setUpstreamParameters() fails when all parameters are empty.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersEmptyFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersEmptyFails) {
     const hidl_string iface("");
     const hidl_string v4Addr("");
     const hidl_string v4Gw("");
@@ -512,7 +306,7 @@
 }
 
 // Test that setUpstreamParameters() fails when given empty or non-existent interface names.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersBogusIfaceFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersBogusIfaceFails) {
     const hidl_string v4Addr("192.0.2.2");
     const hidl_string v4Gw("192.0.2.1");
     const vector<hidl_string> v6Gws{hidl_string("fe80::db8:1")};
@@ -526,7 +320,7 @@
 }
 
 // Test that setUpstreamParameters() fails when given unparseable IPv4 addresses.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersInvalidIPv4AddrFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersInvalidIPv4AddrFails) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Gw("192.0.2.1");
     const vector<hidl_string> v6Gws{hidl_string("fe80::db8:1")};
@@ -540,7 +334,7 @@
 }
 
 // Test that setUpstreamParameters() fails when given unparseable IPv4 gateways.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersInvalidIPv4GatewayFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersInvalidIPv4GatewayFails) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr("192.0.2.2");
     const vector<hidl_string> v6Gws{hidl_string("fe80::db8:1")};
@@ -554,7 +348,7 @@
 }
 
 // Test that setUpstreamParameters() fails when given unparseable IPv6 gateways.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersBadIPv6GatewaysFail) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersBadIPv6GatewaysFail) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr("192.0.2.2");
     const hidl_string v4Gw("192.0.2.1");
@@ -572,7 +366,7 @@
  */
 
 // Test addDownstream() works given an IPv4 prefix.
-TEST_P(OffloadControlHidlTest, AddDownstreamIPv4) {
+TEST_P(OffloadControlTestV1_0_HalStarted, AddDownstreamIPv4) {
     const hidl_string iface("dummy0");
     const hidl_string prefix("192.0.2.0/24");
     const Return<void> ret = control->addDownstream(iface, prefix, ASSERT_TRUE_CALLBACK);
@@ -580,7 +374,7 @@
 }
 
 // Test addDownstream() works given an IPv6 prefix.
-TEST_P(OffloadControlHidlTest, AddDownstreamIPv6) {
+TEST_P(OffloadControlTestV1_0_HalStarted, AddDownstreamIPv6) {
     const hidl_string iface("dummy0");
     const hidl_string prefix("2001:db8::/64");
     const Return<void> ret = control->addDownstream(iface, prefix, ASSERT_TRUE_CALLBACK);
@@ -588,7 +382,7 @@
 }
 
 // Test addDownstream() fails given all empty parameters.
-TEST_P(OffloadControlHidlTest, AddDownstreamEmptyFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, AddDownstreamEmptyFails) {
     const hidl_string iface("");
     const hidl_string prefix("");
     const Return<void> ret = control->addDownstream(iface, prefix, ASSERT_FALSE_CALLBACK);
@@ -596,7 +390,7 @@
 }
 
 // Test addDownstream() fails given empty or non-existent interface names.
-TEST_P(OffloadControlHidlTest, AddDownstreamInvalidIfaceFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, AddDownstreamInvalidIfaceFails) {
     const hidl_string prefix("192.0.2.0/24");
     for (const auto& bogus : {"", "invalid"}) {
         SCOPED_TRACE(StringPrintf("iface='%s'", bogus));
@@ -607,7 +401,7 @@
 }
 
 // Test addDownstream() fails given unparseable prefix arguments.
-TEST_P(OffloadControlHidlTest, AddDownstreamBogusPrefixFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, AddDownstreamBogusPrefixFails) {
     const hidl_string iface("dummy0");
     for (const auto& bogus : {"", "192.0.2/24", "2001:db8/64"}) {
         SCOPED_TRACE(StringPrintf("prefix='%s'", bogus));
@@ -622,7 +416,7 @@
  */
 
 // Test removeDownstream() works given an IPv4 prefix.
-TEST_P(OffloadControlHidlTest, RemoveDownstreamIPv4) {
+TEST_P(OffloadControlTestV1_0_HalStarted, RemoveDownstreamIPv4) {
     const hidl_string iface("dummy0");
     const hidl_string prefix("192.0.2.0/24");
     // First add the downstream, otherwise removeDownstream logic can reasonably
@@ -634,7 +428,7 @@
 }
 
 // Test removeDownstream() works given an IPv6 prefix.
-TEST_P(OffloadControlHidlTest, RemoveDownstreamIPv6) {
+TEST_P(OffloadControlTestV1_0_HalStarted, RemoveDownstreamIPv6) {
     const hidl_string iface("dummy0");
     const hidl_string prefix("2001:db8::/64");
     // First add the downstream, otherwise removeDownstream logic can reasonably
@@ -646,7 +440,7 @@
 }
 
 // Test removeDownstream() fails given all empty parameters.
-TEST_P(OffloadControlHidlTest, RemoveDownstreamEmptyFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, RemoveDownstreamEmptyFails) {
     const hidl_string iface("");
     const hidl_string prefix("");
     const Return<void> ret = control->removeDownstream(iface, prefix, ASSERT_FALSE_CALLBACK);
@@ -654,7 +448,7 @@
 }
 
 // Test removeDownstream() fails given empty or non-existent interface names.
-TEST_P(OffloadControlHidlTest, RemoveDownstreamBogusIfaceFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, RemoveDownstreamBogusIfaceFails) {
     const hidl_string prefix("192.0.2.0/24");
     for (const auto& bogus : {"", "invalid"}) {
         SCOPED_TRACE(StringPrintf("iface='%s'", bogus));
@@ -665,7 +459,7 @@
 }
 
 // Test removeDownstream() fails given unparseable prefix arguments.
-TEST_P(OffloadControlHidlTest, RemoveDownstreamBogusPrefixFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, RemoveDownstreamBogusPrefixFails) {
     const hidl_string iface("dummy0");
     for (const auto& bogus : {"", "192.0.2/24", "2001:db8/64"}) {
         SCOPED_TRACE(StringPrintf("prefix='%s'", bogus));
@@ -677,21 +471,18 @@
 
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlHidlTestBase);
 INSTANTIATE_TEST_CASE_P(
-    PerInstance, OffloadControlHidlTestBase,
-    testing::Combine(
-        testing::ValuesIn(
-            android::hardware::getAllHalInstanceNames(IOffloadConfig::descriptor)),
-        testing::ValuesIn(
-            android::hardware::getAllHalInstanceNames(IOffloadControl::descriptor))),
-    android::hardware::PrintInstanceTupleNameToString<>);
+        PerInstance, OffloadControlTestV1_0_HalNotStarted,
+        testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IOffloadConfig::descriptor)),
+                         testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IOffloadControl::descriptor))),
+        android::hardware::PrintInstanceTupleNameToString<>);
 
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlHidlTest);
 INSTANTIATE_TEST_CASE_P(
-    PerInstance, OffloadControlHidlTest,
-    testing::Combine(
-        testing::ValuesIn(
-            android::hardware::getAllHalInstanceNames(IOffloadConfig::descriptor)),
-        testing::ValuesIn(
-            android::hardware::getAllHalInstanceNames(IOffloadControl::descriptor))),
-    android::hardware::PrintInstanceTupleNameToString<>);
-
+        PerInstance, OffloadControlTestV1_0_HalStarted,
+        testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IOffloadConfig::descriptor)),
+                         testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IOffloadControl::descriptor))),
+        android::hardware::PrintInstanceTupleNameToString<>);
diff --git a/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestBase.h b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestBase.h
new file mode 100644
index 0000000..004019a
--- /dev/null
+++ b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestBase.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <OffloadControlTestUtils.h>
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
+#include <android/hardware/tetheroffload/control/1.0/IOffloadControl.h>
+#include <android/hardware/tetheroffload/control/1.0/types.h>
+#include <gtest/gtest.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <log/log.h>
+
+using android::sp;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::hardware::hidl_handle;
+using android::hardware::hidl_string;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::tetheroffload::config::V1_0::IOffloadConfig;
+using android::hardware::tetheroffload::control::V1_0::IOffloadControl;
+using android::hardware::tetheroffload::control::V1_0::ITetheringOffloadCallback;
+using android::hardware::tetheroffload::control::V1_0::NatTimeoutUpdate;
+using android::hardware::tetheroffload::control::V1_0::OffloadCallbackEvent;
+
+constexpr char kCallbackOnEvent[] = "onEvent";
+constexpr char kCallbackUpdateTimeout[] = "updateTimeout";
+
+enum class ExpectBoolean {
+    Ignored = -1,
+    False = 0,
+    True = 1,
+};
+
+class TetheringOffloadCallbackArgs {
+  public:
+    OffloadCallbackEvent last_event;
+    NatTimeoutUpdate last_params;
+};
+
+class OffloadControlTestBase : public testing::TestWithParam<std::tuple<std::string, std::string>> {
+  public:
+    virtual void SetUp() = 0;
+
+    virtual void TearDown();
+
+    // Called once in setup stage to retrieve correct version of
+    // IOffloadControl object.
+    virtual sp<IOffloadControl> createControl(const std::string& serviceName) = 0;
+
+    // The IOffloadConfig HAL is tested more thoroughly elsewhere. Here the
+    // class just setup everything correctly and verify basic readiness.
+    void setupConfigHal();
+
+    void prepareControlHal();
+
+    void initOffload(const bool expected_result);
+
+    void setupControlHal();
+
+    void stopOffload(const ExpectBoolean value);
+
+    // Callback class for both events and NAT timeout updates.
+    class TetheringOffloadCallback
+        : public testing::VtsHalHidlTargetCallbackBase<TetheringOffloadCallbackArgs>,
+          public ITetheringOffloadCallback {
+      public:
+        TetheringOffloadCallback() = default;
+        virtual ~TetheringOffloadCallback() = default;
+
+        Return<void> onEvent(OffloadCallbackEvent event) override {
+            const TetheringOffloadCallbackArgs args{.last_event = event};
+            NotifyFromCallback(kCallbackOnEvent, args);
+            return Void();
+        };
+
+        Return<void> updateTimeout(const NatTimeoutUpdate& params) override {
+            const TetheringOffloadCallbackArgs args{.last_params = params};
+            NotifyFromCallback(kCallbackUpdateTimeout, args);
+            return Void();
+        };
+    };
+
+    sp<IOffloadConfig> config;
+    sp<IOffloadControl> control;
+    sp<TetheringOffloadCallback> control_cb;
+};
\ No newline at end of file
diff --git a/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestUtils.h b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestUtils.h
new file mode 100644
index 0000000..f9e5783
--- /dev/null
+++ b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestUtils.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <linux/netlink.h>
+#include <sys/socket.h>
+
+// We use #defines here so as to get local lamba captures and error message line numbers
+#define ASSERT_TRUE_CALLBACK                                    \
+    [&](bool success, std::string errMsg) {                     \
+        ASSERT_TRUE(success) << "unexpected error: " << errMsg; \
+    }
+
+#define ASSERT_FALSE_CALLBACK \
+    [&](bool success, std::string errMsg) { ASSERT_FALSE(success) << "expected error: " << errMsg; }
+
+#define ASSERT_ZERO_BYTES_CALLBACK            \
+    [&](uint64_t rxBytes, uint64_t txBytes) { \
+        EXPECT_EQ(0ULL, rxBytes);             \
+        EXPECT_EQ(0ULL, txBytes);             \
+    }
+
+inline const sockaddr* asSockaddr(const sockaddr_nl* nladdr);
+
+int conntrackSocket(unsigned groups);
\ No newline at end of file
diff --git a/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestV1_0.h b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestV1_0.h
new file mode 100644
index 0000000..7492f8a
--- /dev/null
+++ b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestV1_0.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <OffloadControlTestBase.h>
+
+class OffloadControlTestV1_0_HalNotStarted : public OffloadControlTestBase {
+  public:
+    virtual void SetUp() override {
+        setupConfigHal();
+        // Create tether offload control object without calling its initOffload.
+        prepareControlHal();
+    }
+
+    virtual sp<IOffloadControl> createControl(const std::string& serviceName) override {
+        return IOffloadControl::getService(serviceName);
+    }
+};
+
+class OffloadControlTestV1_0_HalStarted : public OffloadControlTestV1_0_HalNotStarted {
+  public:
+    virtual void SetUp() override {
+        setupConfigHal();
+        setupControlHal();
+    }
+};
diff --git a/tv/tuner/1.0/vts/functional/Android.bp b/tv/tuner/1.0/vts/functional/Android.bp
index 1765915..7b130ea 100644
--- a/tv/tuner/1.0/vts/functional/Android.bp
+++ b/tv/tuner/1.0/vts/functional/Android.bp
@@ -41,6 +41,9 @@
     shared_libs: [
         "libbinder",
     ],
+    data: [
+        ":tuner_frontend_input_ts",
+    ],
     test_suites: [
         "general-tests",
         "vts",
diff --git a/tv/tuner/1.0/vts/functional/AndroidTest.xml b/tv/tuner/1.0/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..3a2db27
--- /dev/null
+++ b/tv/tuner/1.0/vts/functional/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs VtsHalTvTunerV1_0TargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalTvTunerV1_0TargetTest->/data/local/tmp/VtsHalTvTunerV1_0TargetTest" />
+        <option name="push" value="test.es->/data/local/tmp/test.es" />
+        <option name="push" value="segment000000.ts->/data/local/tmp/segment000000.ts" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalTvTunerV1_0TargetTest" />
+    </test>
+</configuration>
diff --git a/tv/tuner/1.1/IFilter.hal b/tv/tuner/1.1/IFilter.hal
index df736aa..1e94114 100644
--- a/tv/tuner/1.1/IFilter.hal
+++ b/tv/tuner/1.1/IFilter.hal
@@ -26,6 +26,8 @@
  * To access the v1.1 IFilter APIs, the implementation can cast the IFilter
  * interface returned from the @1.0::IDemux openFilter into a v1.1 IFiler
  * using V1_1::IFilter::castFrom(V1_0::IFilter).
+ *
+ * Note that reconfiguring Filter must happen after the Filter is stopped.
  */
 interface IFilter extends @1.0::IFilter {
     /**
@@ -83,19 +85,23 @@
     configureAvStreamType(AvStreamType avStreamType) generates (Result result);
 
     /**
-     * Configure the filter to monitor specific Scrambling Status.
+     * Configure the monitor event.
      *
-     * Scrambling Status should be sent through the filer callback at the following two scenarios:
+     * The event for Scrambling Status should be sent at the following two scenarios:
      *   1. When this method is called, the first detected scrambling status should be sent.
-     *   2. When the filter transits into the monitored statuses configured through this method,
-     *      event should be sent.
+     *   2. When the Scrambling status transits into different status, event should be sent.
      *
-     * @param statuses Scrambling Statuses to monitor. Set corresponding bit to monitor. Reset to
-     *        stop monitoring.
+     * The event for IP CID change should be sent at the following two scenarios:
+     *   1. When this method is called, the first detected CID for the IP should be sent.
+     *   2. When the CID is changed to different value for the IP filter, event should be sent.
+     *
+     * @param monitorEventypes the events to monitor. Set corresponding bit of the event to monitor.
+     *                         Reset to stop monitoring.
      * @return result Result status of the operation.
-     *         SUCCESS if successful,
-     *         INVALID_STATE if configure can't be applied,
-     *         UNKNOWN_ERROR if failed for other reasons.
+     *                SUCCESS if successful,
+     *                INVALID_STATE if configure can't be applied,
+     *                UNKNOWN_ERROR if failed for other reasons.
      */
-    configureScramblingEvent(bitfield<ScramblingStatus> statuses) generates (Result result);
+    configureMonitorEvent(bitfield<DemuxFilterMonitorEventType> monitorEventTypes)
+            generates (Result result);
 };
diff --git a/tv/tuner/1.1/IFilterCallback.hal b/tv/tuner/1.1/IFilterCallback.hal
index 9960a23..23ae844 100644
--- a/tv/tuner/1.1/IFilterCallback.hal
+++ b/tv/tuner/1.1/IFilterCallback.hal
@@ -25,7 +25,8 @@
      * Notify the client that a new filter event happened.
      *
      * @param filterEvent a v1_0 filter event.
-     * @param filterEventExt a v1_1 extended filter event.
+     * @param filterEventExt a v1_1 extended filter event. Send an empty filterEvent along with
+     *                       startId or scramblingStatus filterEventExt
      */
     oneway onFilterEvent_1_1(DemuxFilterEvent filterEvent, DemuxFilterEventExt filterEventExt);
 };
diff --git a/tv/tuner/1.1/default/Filter.cpp b/tv/tuner/1.1/default/Filter.cpp
index 139e98a..6b2413c 100644
--- a/tv/tuner/1.1/default/Filter.cpp
+++ b/tv/tuner/1.1/default/Filter.cpp
@@ -131,6 +131,7 @@
             break;
     }
 
+    mConfigured = true;
     return Result::SUCCESS;
 }
 
@@ -145,8 +146,6 @@
 
     mFilterThreadRunning = false;
 
-    std::lock_guard<std::mutex> lock(mFilterThreadLock);
-
     return Result::SUCCESS;
 }
 
@@ -166,8 +165,9 @@
 Return<Result> Filter::releaseAvHandle(const hidl_handle& avMemory, uint64_t avDataId) {
     ALOGV("%s", __FUNCTION__);
 
-    if ((avMemory.getNativeHandle()->numFds > 0) &&
+    if (mSharedAvMemHandle != NULL && avMemory != NULL &&
         (mSharedAvMemHandle.getNativeHandle()->numFds > 0) &&
+        (avMemory.getNativeHandle()->numFds > 0) &&
         (sameFile(avMemory.getNativeHandle()->data[0],
                   mSharedAvMemHandle.getNativeHandle()->data[0]))) {
         freeSharedAvHandle();
@@ -251,25 +251,49 @@
     return Result::SUCCESS;
 }
 
-Return<Result> Filter::configureScramblingEvent(uint32_t statuses) {
+Return<Result> Filter::configureMonitorEvent(uint32_t monitorEventTypes) {
     ALOGV("%s", __FUNCTION__);
 
-    mStatuses = statuses;
-    if (mCallback_1_1 != nullptr) {
-        // Assuming current status is always NOT_SCRAMBLED
-        V1_1::DemuxFilterEventExt filterEventExt;
-        V1_1::DemuxFilterEventExt::Event event;
-        event.scramblingStatus(V1_1::ScramblingStatus::NOT_SCRAMBLED);
-        int size = filterEventExt.events.size();
-        filterEventExt.events.resize(size + 1);
-        filterEventExt.events[size] = event;
-        DemuxFilterEvent emptyFilterEvent;
+    DemuxFilterEvent emptyFilterEvent;
+    V1_1::DemuxFilterMonitorEvent monitorEvent;
+    V1_1::DemuxFilterEventExt eventExt;
 
-        mCallback_1_1->onFilterEvent_1_1(emptyFilterEvent, filterEventExt);
-        mFilterEventExt.events.resize(0);
-    } else {
-        return Result::INVALID_STATE;
+    uint32_t newScramblingStatus =
+            monitorEventTypes & V1_1::DemuxFilterMonitorEventType::SCRAMBLING_STATUS;
+    uint32_t newIpCid = monitorEventTypes & V1_1::DemuxFilterMonitorEventType::IP_CID_CHANGE;
+
+    // if scrambling status monitoring flipped, record the new state and send msg on enabling
+    if (newScramblingStatus ^ mScramblingStatusMonitored) {
+        mScramblingStatusMonitored = newScramblingStatus;
+        if (mScramblingStatusMonitored) {
+            if (mCallback_1_1 != nullptr) {
+                // Assuming current status is always NOT_SCRAMBLED
+                monitorEvent.scramblingStatus(V1_1::ScramblingStatus::NOT_SCRAMBLED);
+                eventExt.events.resize(1);
+                eventExt.events[0].monitorEvent(monitorEvent);
+                mCallback_1_1->onFilterEvent_1_1(emptyFilterEvent, eventExt);
+            } else {
+                return Result::INVALID_STATE;
+            }
+        }
     }
+
+    // if ip cid monitoring flipped, record the new state and send msg on enabling
+    if (newIpCid ^ mIpCidMonitored) {
+        mIpCidMonitored = newIpCid;
+        if (mIpCidMonitored) {
+            if (mCallback_1_1 != nullptr) {
+                // Return random cid
+                monitorEvent.cid(1);
+                eventExt.events.resize(1);
+                eventExt.events[0].monitorEvent(monitorEvent);
+                mCallback_1_1->onFilterEvent_1_1(emptyFilterEvent, eventExt);
+            } else {
+                return Result::INVALID_STATE;
+            }
+        }
+    }
+
     return Result::SUCCESS;
 }
 
@@ -321,8 +345,17 @@
             usleep(1000 * 1000);
             continue;
         }
+
         // After successfully write, send a callback and wait for the read to be done
         if (mCallback_1_1 != nullptr) {
+            if (mConfigured) {
+                DemuxFilterEvent emptyEvent;
+                V1_1::DemuxFilterEventExt startEvent;
+                startEvent.events.resize(1);
+                startEvent.events[0].startId(mStartId++);
+                mCallback_1_1->onFilterEvent_1_1(emptyEvent, startEvent);
+                mConfigured = false;
+            }
             mCallback_1_1->onFilterEvent_1_1(mFilterEvent, mFilterEventExt);
             mFilterEventExt.events.resize(0);
         } else if (mCallback != nullptr) {
diff --git a/tv/tuner/1.1/default/Filter.h b/tv/tuner/1.1/default/Filter.h
index a7b3fd2..3a4246e 100644
--- a/tv/tuner/1.1/default/Filter.h
+++ b/tv/tuner/1.1/default/Filter.h
@@ -84,7 +84,7 @@
 
     virtual Return<Result> configureAvStreamType(const V1_1::AvStreamType& avStreamType) override;
 
-    virtual Return<Result> configureScramblingEvent(uint32_t statuses) override;
+    virtual Return<Result> configureMonitorEvent(uint32_t monitorEventTypes) override;
 
     /**
      * To create a FilterMQ and its Event Flag.
@@ -235,6 +235,11 @@
 
     // Scrambling status to be monitored
     uint32_t mStatuses = 0;
+
+    bool mConfigured = false;
+    int mStartId = 0;
+    uint8_t mScramblingStatusMonitored = 0;
+    uint8_t mIpCidMonitored = 0;
 };
 
 }  // namespace implementation
diff --git a/tv/tuner/1.1/types.hal b/tv/tuner/1.1/types.hal
index 006e597..938cb6e 100644
--- a/tv/tuner/1.1/types.hal
+++ b/tv/tuner/1.1/types.hal
@@ -151,16 +151,44 @@
 struct DemuxFilterEventExt {
     safe_union Event {
         /**
-         * No extended record filter Event. This is used by the tsRecord or mmtpRecord filter event
-         * that does not contain the DemuxFilterTs/MmtpRecordEventExt information.
+         * No extended filter Event.
          */
         Monostate noinit;
 
+        /**
+         * Extended TS Record event sent along with @1.0::DemuxFilterEvent::Event::tsRecord.
+         * DemuxFilterEventExt.events[i] is corresponding to @1.0::DemuxFilterEvent.events[i]. If
+         * @1.0::DemuxFilterEvent.events[i] does not have extended event,
+         * DemuxFilterEventExt.events[i] should use Monostate.
+         */
         DemuxFilterTsRecordEventExt tsRecord;
 
+        /**
+         * Extended MMTP Record event sent along with @1.0::DemuxFilterEvent::Event::mmtpRecord.
+         * DemuxFilterEventExt.events[i] is corresponding to @1.0::DemuxFilterEvent.events[i]. If
+         * @1.0::DemuxFilterEvent.events[i] does not have extended event,
+         * DemuxFilterEventExt.events[i] should use Monostate.
+         */
         DemuxFilterMmtpRecordEventExt mmtpRecord;
 
-        ScramblingStatus scramblingStatus;
+        /**
+         * Monitor event to notify monitored status change.
+         *
+         * When sending monitorEvent, DemuxFilterEventExt.events should only contain one
+         * monitorEvent. MonitorEvent should be sent with an empty @1.0::DemuxFilterEvent.
+         */
+        DemuxFilterMonitorEvent monitorEvent;
+
+        /**
+         * An unique ID to mark the start point of receiving the valid filter events after
+         * reconfiguring the filter. It must be sent at least once in the first event after the
+         * filter is restarted. 0 is reserved for the newly opened filter's first start, which is
+         * optional for HAL to send.
+         *
+         * When sending starId, DemuxFilterEventExt.events should only contain one startId event.
+         * StardId event should be sent with an empty @1.0::DemuxFilterEvent.
+         */
+        uint32_t startId;
     };
 
     /**
@@ -188,6 +216,24 @@
     SCRAMBLED = 1 << 2,
 };
 
+@export
+enum DemuxFilterMonitorEventType : uint32_t {
+    SCRAMBLING_STATUS = 1 << 0,
+    IP_CID_CHANGE = 1 << 1,
+};
+
+safe_union DemuxFilterMonitorEvent {
+    /**
+     * New scrambling status.
+     */
+    ScramblingStatus scramblingStatus;
+
+    /**
+     * New cid for the IP filter.
+     */
+    uint32_t cid;
+};
+
 typedef FrontendDvbcSpectralInversion FrontendSpectralInversion;
 
 /**
diff --git a/tv/tuner/1.1/vts/functional/Android.bp b/tv/tuner/1.1/vts/functional/Android.bp
index 1fc36f1..73cd057 100644
--- a/tv/tuner/1.1/vts/functional/Android.bp
+++ b/tv/tuner/1.1/vts/functional/Android.bp
@@ -40,6 +40,9 @@
     shared_libs: [
         "libbinder",
     ],
+    data: [
+        ":tuner_frontend_input_es",
+    ],
     test_suites: [
         "general-tests",
         "vts",
diff --git a/tv/tuner/1.1/vts/functional/AndroidTest.xml b/tv/tuner/1.1/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..28f95db
--- /dev/null
+++ b/tv/tuner/1.1/vts/functional/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs VtsHalTvTunerV1_1TargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalTvTunerV1_1TargetTest->/data/local/tmp/VtsHalTvTunerV1_1TargetTest" />
+        <option name="push" value="test.es->/data/local/tmp/test.es" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalTvTunerV1_1TargetTest" />
+    </test>
+</configuration>
diff --git a/tv/tuner/1.1/vts/functional/FilterTests.cpp b/tv/tuner/1.1/vts/functional/FilterTests.cpp
index d2535e5..d8fad3d 100644
--- a/tv/tuner/1.1/vts/functional/FilterTests.cpp
+++ b/tv/tuner/1.1/vts/functional/FilterTests.cpp
@@ -40,6 +40,30 @@
     ALOGW("[vts] pass and stop");
 }
 
+void FilterCallback::testFilterIpCidEvent() {
+    android::Mutex::Autolock autoLock(mMsgLock);
+    while (mIpCidEvent < 1) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            EXPECT_TRUE(false) << "ip cid change event does not output within timeout";
+            return;
+        }
+    }
+    mIpCidEvent = 0;
+    ALOGW("[vts] pass and stop");
+}
+
+void FilterCallback::testStartIdAfterReconfigure() {
+    android::Mutex::Autolock autoLock(mMsgLock);
+    while (!mStartIdReceived) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            EXPECT_TRUE(false) << "does not receive start id within timeout";
+            return;
+        }
+    }
+    mStartIdReceived = false;
+    ALOGW("[vts] pass and stop");
+}
+
 void FilterCallback::readFilterEventData() {
     ALOGW("[vts] reading filter event");
     // todo separate filter handlers
@@ -68,8 +92,21 @@
                       eventExt.mmtpRecord().pts, eventExt.mmtpRecord().firstMbInSlice,
                       eventExt.mmtpRecord().mpuSequenceNumber, eventExt.mmtpRecord().tsIndexMask);
                 break;
-            case DemuxFilterEventExt::Event::hidl_discriminator::scramblingStatus:
-                mScramblingStatusEvent++;
+            case DemuxFilterEventExt::Event::hidl_discriminator::monitorEvent:
+                switch (eventExt.monitorEvent().getDiscriminator()) {
+                    case DemuxFilterMonitorEvent::hidl_discriminator::scramblingStatus:
+                        mScramblingStatusEvent++;
+                        break;
+                    case DemuxFilterMonitorEvent::hidl_discriminator::cid:
+                        mIpCidEvent++;
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            case DemuxFilterEventExt::Event::hidl_discriminator::startId:
+                ALOGD("[vts] Extended restart filter event, startId=%d", eventExt.startId());
+                mStartIdReceived = true;
                 break;
             default:
                 break;
@@ -90,8 +127,8 @@
     }
 
     int av_fd = handle.getNativeHandle()->data[0];
-    uint8_t* buffer =
-            static_cast<uint8_t*>(mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, av_fd, 0));
+    uint8_t* buffer = static_cast<uint8_t*>(
+            mmap(NULL, length + offset, PROT_READ | PROT_WRITE, MAP_SHARED, av_fd, 0));
     if (buffer == MAP_FAILED) {
         ALOGE("[vts] fail to allocate av buffer, errno=%d", errno);
         return false;
@@ -256,18 +293,25 @@
     return AssertionResult(status == Result::SUCCESS);
 }
 
-AssertionResult FilterTests::configureScramblingEvent(uint64_t filterId, uint32_t statuses) {
+AssertionResult FilterTests::configureMonitorEvent(uint64_t filterId, uint32_t monitorEventTypes) {
     EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
     Result status;
 
     sp<android::hardware::tv::tuner::V1_1::IFilter> filter_v1_1 =
             android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilters[filterId]);
     if (filter_v1_1 != NULL) {
-        status = filter_v1_1->configureScramblingEvent(statuses);
+        status = filter_v1_1->configureMonitorEvent(monitorEventTypes);
         mFilterCallbacks[filterId]->testFilterScramblingEvent();
+        mFilterCallbacks[filterId]->testFilterIpCidEvent();
     } else {
         ALOGW("[vts] Can't cast IFilter into v1_1.");
         return failure();
     }
     return AssertionResult(status == Result::SUCCESS);
 }
+
+AssertionResult FilterTests::startIdTest(uint64_t filterId) {
+    EXPECT_TRUE(mFilterCallbacks[filterId]) << "Test with getNewlyOpenedFilterId first.";
+    mFilterCallbacks[filterId]->testStartIdAfterReconfigure();
+    return AssertionResult(true);
+}
diff --git a/tv/tuner/1.1/vts/functional/FilterTests.h b/tv/tuner/1.1/vts/functional/FilterTests.h
index d88f171..6749265 100644
--- a/tv/tuner/1.1/vts/functional/FilterTests.h
+++ b/tv/tuner/1.1/vts/functional/FilterTests.h
@@ -56,6 +56,7 @@
 using android::hardware::tv::tuner::V1_0::Result;
 using android::hardware::tv::tuner::V1_1::AvStreamType;
 using android::hardware::tv::tuner::V1_1::DemuxFilterEventExt;
+using android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEvent;
 using android::hardware::tv::tuner::V1_1::IFilterCallback;
 using android::hardware::tv::tuner::V1_1::ITuner;
 
@@ -118,6 +119,8 @@
 
     void testFilterDataOutput();
     void testFilterScramblingEvent();
+    void testFilterIpCidEvent();
+    void testStartIdAfterReconfigure();
 
     void readFilterEventData();
     bool dumpAvData(DemuxFilterMediaEvent event);
@@ -138,6 +141,8 @@
 
     int mPidFilterOutputCount = 0;
     int mScramblingStatusEvent = 0;
+    int mIpCidEvent = 0;
+    bool mStartIdReceived = false;
 };
 
 class FilterTests {
@@ -155,11 +160,12 @@
     AssertionResult configFilter(DemuxFilterSettings setting, uint64_t filterId);
     AssertionResult configAvFilterStreamType(AvStreamType type, uint64_t filterId);
     AssertionResult configIpFilterCid(uint32_t ipCid, uint64_t filterId);
-    AssertionResult configureScramblingEvent(uint64_t filterId, uint32_t statuses);
+    AssertionResult configureMonitorEvent(uint64_t filterId, uint32_t monitorEventTypes);
     AssertionResult getFilterMQDescriptor(uint64_t filterId);
     AssertionResult startFilter(uint64_t filterId);
     AssertionResult stopFilter(uint64_t filterId);
     AssertionResult closeFilter(uint64_t filterId);
+    AssertionResult startIdTest(uint64_t filterId);
 
     FilterEventType getFilterEventType(DemuxFilterType type) {
         FilterEventType eventType = FilterEventType::UNDEFINED;
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
index 17abf49..2dcb9a1 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.cpp
@@ -46,8 +46,8 @@
     if (filterConf.type.mainType == DemuxFilterMainType::IP) {
         ASSERT_TRUE(mFilterTests.configIpFilterCid(filterConf.ipCid, filterId));
     }
-    if (filterConf.statuses > 0) {
-        ASSERT_TRUE(mFilterTests.configureScramblingEvent(filterId, filterConf.statuses));
+    if (filterConf.monitorEventTypes > 0) {
+        ASSERT_TRUE(mFilterTests.configureMonitorEvent(filterId, filterConf.monitorEventTypes));
     }
     ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
@@ -57,6 +57,39 @@
     ASSERT_TRUE(mFrontendTests.closeFrontend());
 }
 
+void TunerFilterHidlTest::reconfigSingleFilterInDemuxTest(FilterConfig filterConf,
+                                                          FilterConfig filterReconf,
+                                                          FrontendConfig frontendConf) {
+    uint32_t feId;
+    uint32_t demuxId;
+    sp<IDemux> demux;
+    uint64_t filterId;
+
+    mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+    ASSERT_TRUE(feId != INVALID_ID);
+    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+    mFrontendTests.setDemux(demux);
+    mFilterTests.setDemux(demux);
+    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
+    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
+    ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+    ASSERT_TRUE(mFilterTests.startFilter(filterId));
+    ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+    ASSERT_TRUE(mFilterTests.configFilter(filterReconf.settings, filterId));
+    ASSERT_TRUE(mFilterTests.startFilter(filterId));
+    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+    ASSERT_TRUE(mFilterTests.startIdTest(filterId));
+    ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+    ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+    ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+    ASSERT_TRUE(mDemuxTests.closeDemux());
+    ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
+
 void TunerBroadcastHidlTest::mediaFilterUsingSharedMemoryTest(FilterConfig filterConf,
                                                               FrontendConfig frontendConf) {
     uint32_t feId;
@@ -146,6 +179,13 @@
     configSingleFilterInDemuxTest(filterArray[IP_IP0], frontendArray[DVBT]);
 }
 
+TEST_P(TunerFilterHidlTest, ReconfigFilterToReceiveStartId) {
+    description("Recofigure and restart a filter to test start id.");
+    // TODO use parameterized tests
+    reconfigSingleFilterInDemuxTest(filterArray[TS_VIDEO0], filterArray[TS_VIDEO1],
+                                    frontendArray[DVBT]);
+}
+
 TEST_P(TunerRecordHidlTest, RecordDataFlowWithTsRecordFilterTest) {
     description("Feed ts data from frontend to recording and test with ts record filter");
     recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[DVBT], dvrArray[DVR_RECORD0]);
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h
index 773224e..d14a2e8 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TargetTest.h
@@ -58,7 +58,8 @@
     }
 
     void configSingleFilterInDemuxTest(FilterConfig filterConf, FrontendConfig frontendConf);
-
+    void reconfigSingleFilterInDemuxTest(FilterConfig filterConf, FilterConfig filterReconf,
+                                         FrontendConfig frontendConf);
     sp<ITuner> mService;
     FrontendTests mFrontendTests;
     DemuxTests mDemuxTests;
diff --git a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h
index 76bf765..beae223 100644
--- a/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h
+++ b/tv/tuner/1.1/vts/functional/VtsHalTvTunerV1_1TestConfigurations.h
@@ -96,7 +96,7 @@
     DemuxFilterSettings settings;
     AvStreamType streamType;
     uint32_t ipCid;
-    uint32_t statuses;
+    uint32_t monitorEventTypes;
 
     bool operator<(const FilterConfig& /*c*/) const { return false; }
 };
@@ -188,7 +188,9 @@
     filterArray[TS_VIDEO0].bufferSize = FMQ_SIZE_16M;
     filterArray[TS_VIDEO0].settings.ts().tpid = 256;
     filterArray[TS_VIDEO0].settings.ts().filterSettings.av({.isPassthrough = false});
-    filterArray[TS_VIDEO0].statuses = 1;
+    filterArray[TS_VIDEO0].monitorEventTypes =
+            android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEventType::SCRAMBLING_STATUS |
+            android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEventType::IP_CID_CHANGE;
     filterArray[TS_VIDEO1].type.mainType = DemuxFilterMainType::TS;
     filterArray[TS_VIDEO1].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
     filterArray[TS_VIDEO1].bufferSize = FMQ_SIZE_16M;
diff --git a/tv/tuner/assets/Android.bp b/tv/tuner/assets/Android.bp
new file mode 100644
index 0000000..b58b645
--- /dev/null
+++ b/tv/tuner/assets/Android.bp
@@ -0,0 +1,17 @@
+genrule {
+    name: "tuner_frontend_input_es",
+    srcs: ["tuner_frontend_input.es"],
+    out: [
+        "test.es",
+    ],
+    cmd: "cp -f $(in) $(genDir)/test.es",
+}
+
+genrule {
+    name: "tuner_frontend_input_ts",
+    srcs: ["tuner_frontend_input.ts"],
+    out: [
+        "segment000000.ts",
+    ],
+    cmd: "cp -f $(in) $(genDir)/segment000000.ts",
+}
diff --git a/tv/tuner/assets/tuner_frontend_input.es b/tv/tuner/assets/tuner_frontend_input.es
new file mode 100644
index 0000000..b53c957
--- /dev/null
+++ b/tv/tuner/assets/tuner_frontend_input.es
Binary files differ
diff --git a/tv/tuner/assets/tuner_frontend_input.ts b/tv/tuner/assets/tuner_frontend_input.ts
new file mode 100644
index 0000000..8992c7b
--- /dev/null
+++ b/tv/tuner/assets/tuner_frontend_input.ts
Binary files differ
diff --git a/usb/gadget/1.2/Android.bp b/usb/gadget/1.2/Android.bp
new file mode 100644
index 0000000..386f00f
--- /dev/null
+++ b/usb/gadget/1.2/Android.bp
@@ -0,0 +1,17 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.usb.gadget@1.2",
+    root: "android.hardware",
+    srcs: [
+        "types.hal",
+        "IUsbGadget.hal",
+        "IUsbGadgetCallback.hal",
+    ],
+    interfaces: [
+        "android.hardware.usb.gadget@1.0",
+        "android.hardware.usb.gadget@1.1",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/usb/gadget/1.2/IUsbGadget.hal b/usb/gadget/1.2/IUsbGadget.hal
new file mode 100644
index 0000000..5c6e930
--- /dev/null
+++ b/usb/gadget/1.2/IUsbGadget.hal
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb.gadget@1.2;
+
+import IUsbGadgetCallback;
+import android.hardware.usb.gadget@1.1::IUsbGadget;
+
+interface IUsbGadget extends @1.1::IUsbGadget {
+    /**
+     * The function is used to query current USB speed.
+     *
+     * @param callback IUsbGadgetCallback::getUsbSpeedCb used to propagate
+     *                 current USB speed.
+     */
+    oneway getUsbSpeed(IUsbGadgetCallback callback);
+};
diff --git a/usb/gadget/1.2/IUsbGadgetCallback.hal b/usb/gadget/1.2/IUsbGadgetCallback.hal
new file mode 100644
index 0000000..4170573
--- /dev/null
+++ b/usb/gadget/1.2/IUsbGadgetCallback.hal
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb.gadget@1.2;
+
+import UsbSpeed;
+import android.hardware.usb.gadget@1.0::IUsbGadgetCallback;
+
+interface IUsbGadgetCallback extends @1.0::IUsbGadgetCallback {
+    /**
+     * Used to convey the current USB speed to the caller.
+     * Must be called either when USB state changes due to USB enumeration or
+     * when caller requested for USB speed through getUsbSpeed.
+     *
+     * @param speed USB Speed defined by UsbSpeed showed current USB
+     *              connection speed.
+     */
+    oneway getUsbSpeedCb(UsbSpeed speed);
+};
+
diff --git a/usb/gadget/1.2/default/Android.bp b/usb/gadget/1.2/default/Android.bp
new file mode 100644
index 0000000..9c73a59
--- /dev/null
+++ b/usb/gadget/1.2/default/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_binary {
+    name: "android.hardware.usb.gadget@1.2-service",
+    defaults: ["hidl_defaults"],
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.usb.gadget@1.2-service.rc"],
+    vintf_fragments: ["android.hardware.usb.gadget@1.2-service.xml"],
+    vendor: true,
+    srcs: [
+        "service.cpp",
+        "UsbGadget.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.usb.gadget@1.0",
+        "android.hardware.usb.gadget@1.1",
+        "android.hardware.usb.gadget@1.2",
+        "libbase",
+        "libcutils",
+        "libhardware",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: ["libusbconfigfs-2"],
+}
diff --git a/usb/gadget/1.2/default/UsbGadget.cpp b/usb/gadget/1.2/default/UsbGadget.cpp
new file mode 100644
index 0000000..8dd229e
--- /dev/null
+++ b/usb/gadget/1.2/default/UsbGadget.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.usb.gadget@1.2-service"
+
+#include "UsbGadget.h"
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+namespace V1_2 {
+namespace implementation {
+
+UsbGadget::UsbGadget() {
+    if (access(OS_DESC_PATH, R_OK) != 0) {
+        ALOGE("configfs setup not done yet");
+        abort();
+    }
+}
+
+void currentFunctionsAppliedCallback(bool functionsApplied, void* payload) {
+    UsbGadget* gadget = (UsbGadget*)payload;
+    gadget->mCurrentUsbFunctionsApplied = functionsApplied;
+}
+
+Return<void> UsbGadget::getCurrentUsbFunctions(const sp<V1_0::IUsbGadgetCallback>& callback) {
+    Return<void> ret = callback->getCurrentUsbFunctionsCb(
+            mCurrentUsbFunctions, mCurrentUsbFunctionsApplied ? Status::FUNCTIONS_APPLIED
+                                                              : Status::FUNCTIONS_NOT_APPLIED);
+    if (!ret.isOk()) ALOGE("Call to getCurrentUsbFunctionsCb failed %s", ret.description().c_str());
+
+    return Void();
+}
+
+Return<void> UsbGadget::getUsbSpeed(const sp<V1_2::IUsbGadgetCallback>& callback) {
+    std::string current_speed;
+    if (ReadFileToString(SPEED_PATH, &current_speed)) {
+        current_speed = Trim(current_speed);
+        ALOGI("current USB speed is %s", current_speed.c_str());
+        if (current_speed == "low-speed")
+            mUsbSpeed = UsbSpeed::LOWSPEED;
+        else if (current_speed == "full-speed")
+            mUsbSpeed = UsbSpeed::FULLSPEED;
+        else if (current_speed == "high-speed")
+            mUsbSpeed = UsbSpeed::HIGHSPEED;
+        else if (current_speed == "super-speed")
+            mUsbSpeed = UsbSpeed::SUPERSPEED;
+        else if (current_speed == "super-speed-plus")
+            mUsbSpeed = UsbSpeed::SUPERSPEED_10Gb;
+        else if (current_speed == "UNKNOWN")
+            mUsbSpeed = UsbSpeed::UNKNOWN;
+        else {
+            /**
+             * This part is used for USB4 or reserved speed.
+             *
+             * If reserved speed is detected, it needs to convert to other speeds.
+             * For example:
+             * If the bandwidth of new speed is 7G, adding new if
+             * statement and set mUsbSpeed to SUPERSPEED.
+             * If the bandwidth of new speed is 80G, adding new if
+             * statement and set mUsbSpeed to USB4_GEN3_40Gb.
+             */
+            mUsbSpeed = UsbSpeed::RESERVED_SPEED;
+        }
+    } else {
+        ALOGE("Fail to read current speed");
+        mUsbSpeed = UsbSpeed::UNKNOWN;
+    }
+
+    if (callback) {
+        Return<void> ret = callback->getUsbSpeedCb(mUsbSpeed);
+
+        if (!ret.isOk()) ALOGE("Call to getUsbSpeedCb failed %s", ret.description().c_str());
+    }
+
+    return Void();
+}
+
+V1_0::Status UsbGadget::tearDownGadget() {
+    if (resetGadget() != V1_0::Status::SUCCESS) return V1_0::Status::ERROR;
+
+    if (monitorFfs.isMonitorRunning()) {
+        monitorFfs.reset();
+    } else {
+        ALOGI("mMonitor not running");
+    }
+    return V1_0::Status::SUCCESS;
+}
+
+Return<Status> UsbGadget::reset() {
+    if (!WriteStringToFile("none", PULLUP_PATH)) {
+        ALOGI("Gadget cannot be pulled down");
+        return Status::ERROR;
+    }
+
+    usleep(kDisconnectWaitUs);
+
+    if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) {
+        ALOGI("Gadget cannot be pulled up");
+        return Status::ERROR;
+    }
+
+    return Status::SUCCESS;
+}
+
+static V1_0::Status validateAndSetVidPid(uint64_t functions) {
+    V1_0::Status ret = V1_0::Status::SUCCESS;
+
+    switch (functions) {
+        case static_cast<uint64_t>(V1_2::GadgetFunction::MTP):
+            ret = setVidPid("0x18d1", "0x4ee1");
+            break;
+        case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::MTP:
+            ret = setVidPid("0x18d1", "0x4ee2");
+            break;
+        case static_cast<uint64_t>(V1_2::GadgetFunction::RNDIS):
+            ret = setVidPid("0x18d1", "0x4ee3");
+            break;
+        case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::RNDIS:
+            ret = setVidPid("0x18d1", "0x4ee4");
+            break;
+        case static_cast<uint64_t>(V1_2::GadgetFunction::PTP):
+            ret = setVidPid("0x18d1", "0x4ee5");
+            break;
+        case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::PTP:
+            ret = setVidPid("0x18d1", "0x4ee6");
+            break;
+        case static_cast<uint64_t>(V1_2::GadgetFunction::ADB):
+            ret = setVidPid("0x18d1", "0x4ee7");
+            break;
+        case static_cast<uint64_t>(V1_2::GadgetFunction::MIDI):
+            ret = setVidPid("0x18d1", "0x4ee8");
+            break;
+        case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::MIDI:
+            ret = setVidPid("0x18d1", "0x4ee9");
+            break;
+        case static_cast<uint64_t>(V1_2::GadgetFunction::NCM):
+            ret = setVidPid("0x18d1", "0x4eeb");
+            break;
+        case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::NCM:
+            ret = setVidPid("0x18d1", "0x4eec");
+            break;
+        case static_cast<uint64_t>(V1_2::GadgetFunction::ACCESSORY):
+            ret = setVidPid("0x18d1", "0x2d00");
+            break;
+        case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::ACCESSORY:
+            ret = setVidPid("0x18d1", "0x2d01");
+            break;
+        case static_cast<uint64_t>(V1_2::GadgetFunction::AUDIO_SOURCE):
+            ret = setVidPid("0x18d1", "0x2d02");
+            break;
+        case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::AUDIO_SOURCE:
+            ret = setVidPid("0x18d1", "0x2d03");
+            break;
+        case V1_2::GadgetFunction::ACCESSORY | V1_2::GadgetFunction::AUDIO_SOURCE:
+            ret = setVidPid("0x18d1", "0x2d04");
+            break;
+        case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::ACCESSORY |
+                V1_2::GadgetFunction::AUDIO_SOURCE:
+            ret = setVidPid("0x18d1", "0x2d05");
+            break;
+        default:
+            ALOGE("Combination not supported");
+            ret = V1_0::Status::CONFIGURATION_NOT_SUPPORTED;
+    }
+    return ret;
+}
+
+V1_0::Status UsbGadget::setupFunctions(uint64_t functions,
+                                       const sp<V1_0::IUsbGadgetCallback>& callback,
+                                       uint64_t timeout) {
+    bool ffsEnabled = false;
+    int i = 0;
+
+    if (addGenericAndroidFunctions(&monitorFfs, functions, &ffsEnabled, &i) !=
+        V1_0::Status::SUCCESS)
+        return V1_0::Status::ERROR;
+
+    if ((functions & V1_2::GadgetFunction::ADB) != 0) {
+        ffsEnabled = true;
+        if (addAdb(&monitorFfs, &i) != V1_0::Status::SUCCESS) return V1_0::Status::ERROR;
+    }
+
+    // Pull up the gadget right away when there are no ffs functions.
+    if (!ffsEnabled) {
+        if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) return V1_0::Status::ERROR;
+        mCurrentUsbFunctionsApplied = true;
+        if (callback) callback->setCurrentUsbFunctionsCb(functions, V1_0::Status::SUCCESS);
+        return V1_0::Status::SUCCESS;
+    }
+
+    monitorFfs.registerFunctionsAppliedCallback(&currentFunctionsAppliedCallback, this);
+    // Monitors the ffs paths to pull up the gadget when descriptors are written.
+    // Also takes of the pulling up the gadget again if the userspace process
+    // dies and restarts.
+    monitorFfs.startMonitor();
+
+    if (kDebug) ALOGI("Mainthread in Cv");
+
+    if (callback) {
+        bool pullup = monitorFfs.waitForPullUp(timeout);
+        Return<void> ret = callback->setCurrentUsbFunctionsCb(
+                functions, pullup ? V1_0::Status::SUCCESS : V1_0::Status::ERROR);
+        if (!ret.isOk()) ALOGE("setCurrentUsbFunctionsCb error %s", ret.description().c_str());
+    }
+
+    return V1_0::Status::SUCCESS;
+}
+
+Return<void> UsbGadget::setCurrentUsbFunctions(uint64_t functions,
+                                               const sp<V1_0::IUsbGadgetCallback>& callback,
+                                               uint64_t timeout) {
+    std::unique_lock<std::mutex> lk(mLockSetCurrentFunction);
+
+    mCurrentUsbFunctions = functions;
+    mCurrentUsbFunctionsApplied = false;
+
+    // Unlink the gadget and stop the monitor if running.
+    V1_0::Status status = tearDownGadget();
+    if (status != V1_0::Status::SUCCESS) {
+        goto error;
+    }
+
+    ALOGI("Returned from tearDown gadget");
+
+    // Leave the gadget pulled down to give time for the host to sense disconnect.
+    usleep(kDisconnectWaitUs);
+
+    if (functions == static_cast<uint64_t>(V1_2::GadgetFunction::NONE)) {
+        if (callback == NULL) return Void();
+        Return<void> ret = callback->setCurrentUsbFunctionsCb(functions, V1_0::Status::SUCCESS);
+        if (!ret.isOk())
+            ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.description().c_str());
+        return Void();
+    }
+
+    status = validateAndSetVidPid(functions);
+
+    if (status != V1_0::Status::SUCCESS) {
+        goto error;
+    }
+
+    status = setupFunctions(functions, callback, timeout);
+    if (status != V1_0::Status::SUCCESS) {
+        goto error;
+    }
+
+    ALOGI("Usb Gadget setcurrent functions called successfully");
+    return Void();
+
+error:
+    ALOGI("Usb Gadget setcurrent functions failed");
+    if (callback == NULL) return Void();
+    Return<void> ret = callback->setCurrentUsbFunctionsCb(functions, status);
+    if (!ret.isOk())
+        ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.description().c_str());
+    return Void();
+}
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace gadget
+}  // namespace usb
+}  // namespace hardware
+}  // namespace android
diff --git a/usb/gadget/1.2/default/UsbGadget.h b/usb/gadget/1.2/default/UsbGadget.h
new file mode 100644
index 0000000..12ad8ee
--- /dev/null
+++ b/usb/gadget/1.2/default/UsbGadget.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_USB_GADGET_V1_2_USBGADGET_H
+#define ANDROID_HARDWARE_USB_GADGET_V1_2_USBGADGET_H
+
+#include <UsbGadgetCommon.h>
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <android/hardware/usb/gadget/1.2/IUsbGadget.h>
+#include <android/hardware/usb/gadget/1.2/types.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <utils/Log.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+namespace V1_2 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::base::GetProperty;
+using ::android::base::ReadFileToString;
+using ::android::base::SetProperty;
+using ::android::base::Trim;
+using ::android::base::unique_fd;
+using ::android::base::WriteStringToFile;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::usb::gadget::addAdb;
+using ::android::hardware::usb::gadget::addEpollFd;
+using ::android::hardware::usb::gadget::getVendorFunctions;
+using ::android::hardware::usb::gadget::kDebug;
+using ::android::hardware::usb::gadget::kDisconnectWaitUs;
+using ::android::hardware::usb::gadget::linkFunction;
+using ::android::hardware::usb::gadget::MonitorFfs;
+using ::android::hardware::usb::gadget::resetGadget;
+using ::android::hardware::usb::gadget::setVidPid;
+using ::android::hardware::usb::gadget::unlinkFunctions;
+using ::std::string;
+
+constexpr char kGadgetName[] = "11110000.usb";
+static MonitorFfs monitorFfs(kGadgetName);
+
+#define UDC_PATH "/sys/class/udc/11110000.usb/"
+#define SPEED_PATH UDC_PATH "current_speed"
+
+struct UsbGadget : public IUsbGadget {
+    UsbGadget();
+
+    // Makes sure that only one request is processed at a time.
+    std::mutex mLockSetCurrentFunction;
+    uint64_t mCurrentUsbFunctions;
+    bool mCurrentUsbFunctionsApplied;
+    UsbSpeed mUsbSpeed;
+
+    Return<void> setCurrentUsbFunctions(uint64_t functions,
+                                        const sp<V1_0::IUsbGadgetCallback>& callback,
+                                        uint64_t timeout) override;
+
+    Return<void> getCurrentUsbFunctions(const sp<V1_0::IUsbGadgetCallback>& callback) override;
+
+    Return<Status> reset() override;
+
+    Return<void> getUsbSpeed(const sp<V1_2::IUsbGadgetCallback>& callback) override;
+
+  private:
+    V1_0::Status tearDownGadget();
+    V1_0::Status setupFunctions(uint64_t functions, const sp<V1_0::IUsbGadgetCallback>& callback,
+                                uint64_t timeout);
+};
+
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace gadget
+}  // namespace usb
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_USB_V1_2_USBGADGET_H
diff --git a/usb/gadget/1.2/default/android.hardware.usb.gadget@1.2-service.rc b/usb/gadget/1.2/default/android.hardware.usb.gadget@1.2-service.rc
new file mode 100644
index 0000000..650b085
--- /dev/null
+++ b/usb/gadget/1.2/default/android.hardware.usb.gadget@1.2-service.rc
@@ -0,0 +1,7 @@
+service vendor.usb-gadget-hal-1-2 /vendor/bin/hw/android.hardware.usb.gadget@1.2-service
+    interface android.hardware.usb.gadget@1.0::IUsbGadget default
+    interface android.hardware.usb.gadget@1.1::IUsbGadget default
+    interface android.hardware.usb.gadget@1.2::IUsbGadget default
+    class hal
+    user system
+    group system shell mtp
diff --git a/usb/gadget/1.2/default/android.hardware.usb.gadget@1.2-service.xml b/usb/gadget/1.2/default/android.hardware.usb.gadget@1.2-service.xml
new file mode 100644
index 0000000..8557f6f
--- /dev/null
+++ b/usb/gadget/1.2/default/android.hardware.usb.gadget@1.2-service.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.usb.gadget</name>
+        <transport>hwbinder</transport>
+        <version>1.2</version>
+        <interface>
+            <name>IUsbGadget</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/usb/gadget/1.2/default/lib/Android.bp b/usb/gadget/1.2/default/lib/Android.bp
new file mode 100644
index 0000000..727de13
--- /dev/null
+++ b/usb/gadget/1.2/default/lib/Android.bp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_library_static {
+    name: "libusbconfigfs-2",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+
+    srcs: [
+        "UsbGadgetUtils.cpp",
+        "MonitorFfs.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "android.hardware.usb.gadget@1.0",
+        "android.hardware.usb.gadget@1.1",
+        "android.hardware.usb.gadget@1.2",
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "libutils",
+    ],
+}
diff --git a/usb/gadget/1.2/default/lib/MonitorFfs.cpp b/usb/gadget/1.2/default/lib/MonitorFfs.cpp
new file mode 100644
index 0000000..0cdf038
--- /dev/null
+++ b/usb/gadget/1.2/default/lib/MonitorFfs.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libusbconfigfs"
+
+#include "include/UsbGadgetCommon.h"
+
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+
+static volatile bool gadgetPullup;
+
+MonitorFfs::MonitorFfs(const char* const gadget)
+    : mWatchFd(),
+      mEndpointList(),
+      mLock(),
+      mCv(),
+      mLockFd(),
+      mCurrentUsbFunctionsApplied(false),
+      mMonitor(),
+      mCallback(NULL),
+      mPayload(NULL),
+      mGadgetName(gadget),
+      mMonitorRunning(false) {
+    unique_fd eventFd(eventfd(0, 0));
+    if (eventFd == -1) {
+        ALOGE("mEventFd failed to create %d", errno);
+        abort();
+    }
+
+    unique_fd epollFd(epoll_create(2));
+    if (epollFd == -1) {
+        ALOGE("mEpollFd failed to create %d", errno);
+        abort();
+    }
+
+    unique_fd inotifyFd(inotify_init());
+    if (inotifyFd < 0) {
+        ALOGE("inotify init failed");
+        abort();
+    }
+
+    if (addEpollFd(epollFd, inotifyFd) == -1) abort();
+
+    if (addEpollFd(epollFd, eventFd) == -1) abort();
+
+    mEpollFd = move(epollFd);
+    mInotifyFd = move(inotifyFd);
+    mEventFd = move(eventFd);
+    gadgetPullup = false;
+}
+
+static void displayInotifyEvent(struct inotify_event* i) {
+    ALOGE("    wd =%2d; ", i->wd);
+    if (i->cookie > 0) ALOGE("cookie =%4d; ", i->cookie);
+
+    ALOGE("mask = ");
+    if (i->mask & IN_ACCESS) ALOGE("IN_ACCESS ");
+    if (i->mask & IN_ATTRIB) ALOGE("IN_ATTRIB ");
+    if (i->mask & IN_CLOSE_NOWRITE) ALOGE("IN_CLOSE_NOWRITE ");
+    if (i->mask & IN_CLOSE_WRITE) ALOGE("IN_CLOSE_WRITE ");
+    if (i->mask & IN_CREATE) ALOGE("IN_CREATE ");
+    if (i->mask & IN_DELETE) ALOGE("IN_DELETE ");
+    if (i->mask & IN_DELETE_SELF) ALOGE("IN_DELETE_SELF ");
+    if (i->mask & IN_IGNORED) ALOGE("IN_IGNORED ");
+    if (i->mask & IN_ISDIR) ALOGE("IN_ISDIR ");
+    if (i->mask & IN_MODIFY) ALOGE("IN_MODIFY ");
+    if (i->mask & IN_MOVE_SELF) ALOGE("IN_MOVE_SELF ");
+    if (i->mask & IN_MOVED_FROM) ALOGE("IN_MOVED_FROM ");
+    if (i->mask & IN_MOVED_TO) ALOGE("IN_MOVED_TO ");
+    if (i->mask & IN_OPEN) ALOGE("IN_OPEN ");
+    if (i->mask & IN_Q_OVERFLOW) ALOGE("IN_Q_OVERFLOW ");
+    if (i->mask & IN_UNMOUNT) ALOGE("IN_UNMOUNT ");
+    ALOGE("\n");
+
+    if (i->len > 0) ALOGE("        name = %s\n", i->name);
+}
+
+void* MonitorFfs::startMonitorFd(void* param) {
+    MonitorFfs* monitorFfs = (MonitorFfs*)param;
+    char buf[kBufferSize];
+    bool writeUdc = true, stopMonitor = false;
+    struct epoll_event events[kEpollEvents];
+    steady_clock::time_point disconnect;
+
+    bool descriptorWritten = true;
+    for (int i = 0; i < static_cast<int>(monitorFfs->mEndpointList.size()); i++) {
+        if (access(monitorFfs->mEndpointList.at(i).c_str(), R_OK)) {
+            descriptorWritten = false;
+            break;
+        }
+    }
+
+    // notify here if the endpoints are already present.
+    if (descriptorWritten) {
+        usleep(kPullUpDelay);
+        if (!!WriteStringToFile(monitorFfs->mGadgetName, PULLUP_PATH)) {
+            lock_guard<mutex> lock(monitorFfs->mLock);
+            monitorFfs->mCurrentUsbFunctionsApplied = true;
+            monitorFfs->mCallback(monitorFfs->mCurrentUsbFunctionsApplied, monitorFfs->mPayload);
+            gadgetPullup = true;
+            writeUdc = false;
+            ALOGI("GADGET pulled up");
+            monitorFfs->mCv.notify_all();
+        }
+    }
+
+    while (!stopMonitor) {
+        int nrEvents = epoll_wait(monitorFfs->mEpollFd, events, kEpollEvents, -1);
+
+        if (nrEvents <= 0) {
+            ALOGE("epoll wait did not return descriptor number");
+            continue;
+        }
+
+        for (int i = 0; i < nrEvents; i++) {
+            ALOGI("event=%u on fd=%d\n", events[i].events, events[i].data.fd);
+
+            if (events[i].data.fd == monitorFfs->mInotifyFd) {
+                // Process all of the events in buffer returned by read().
+                int numRead = read(monitorFfs->mInotifyFd, buf, kBufferSize);
+                for (char* p = buf; p < buf + numRead;) {
+                    struct inotify_event* event = (struct inotify_event*)p;
+                    if (kDebug) displayInotifyEvent(event);
+
+                    p += sizeof(struct inotify_event) + event->len;
+
+                    bool descriptorPresent = true;
+                    for (int j = 0; j < static_cast<int>(monitorFfs->mEndpointList.size()); j++) {
+                        if (access(monitorFfs->mEndpointList.at(j).c_str(), R_OK)) {
+                            if (kDebug) ALOGI("%s absent", monitorFfs->mEndpointList.at(j).c_str());
+                            descriptorPresent = false;
+                            break;
+                        }
+                    }
+
+                    if (!descriptorPresent && !writeUdc) {
+                        if (kDebug) ALOGI("endpoints not up");
+                        writeUdc = true;
+                        disconnect = std::chrono::steady_clock::now();
+                    } else if (descriptorPresent && writeUdc) {
+                        steady_clock::time_point temp = steady_clock::now();
+
+                        if (std::chrono::duration_cast<microseconds>(temp - disconnect).count() <
+                            kPullUpDelay)
+                            usleep(kPullUpDelay);
+
+                        if (!!WriteStringToFile(monitorFfs->mGadgetName, PULLUP_PATH)) {
+                            lock_guard<mutex> lock(monitorFfs->mLock);
+                            monitorFfs->mCurrentUsbFunctionsApplied = true;
+                            monitorFfs->mCallback(monitorFfs->mCurrentUsbFunctionsApplied,
+                                                  monitorFfs->mPayload);
+                            ALOGI("GADGET pulled up");
+                            writeUdc = false;
+                            gadgetPullup = true;
+                            // notify the main thread to signal userspace.
+                            monitorFfs->mCv.notify_all();
+                        }
+                    }
+                }
+            } else {
+                uint64_t flag;
+                read(monitorFfs->mEventFd, &flag, sizeof(flag));
+                if (flag == 100) {
+                    stopMonitor = true;
+                    break;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+void MonitorFfs::reset() {
+    lock_guard<mutex> lock(mLockFd);
+    uint64_t flag = 100;
+    unsigned long ret;
+
+    if (mMonitorRunning) {
+        // Stop the monitor thread by writing into signal fd.
+        ret = TEMP_FAILURE_RETRY(write(mEventFd, &flag, sizeof(flag)));
+        if (ret < 0) ALOGE("Error writing eventfd errno=%d", errno);
+
+        ALOGI("mMonitor signalled to exit");
+        mMonitor->join();
+        ALOGI("mMonitor destroyed");
+        mMonitorRunning = false;
+    }
+
+    for (std::vector<int>::size_type i = 0; i != mWatchFd.size(); i++)
+        inotify_rm_watch(mInotifyFd, mWatchFd[i]);
+
+    mEndpointList.clear();
+    gadgetPullup = false;
+    mCallback = NULL;
+    mPayload = NULL;
+}
+
+bool MonitorFfs::startMonitor() {
+    mMonitor = unique_ptr<thread>(new thread(this->startMonitorFd, this));
+    mMonitorRunning = true;
+    return true;
+}
+
+bool MonitorFfs::isMonitorRunning() {
+    return mMonitorRunning;
+}
+
+bool MonitorFfs::waitForPullUp(int timeout_ms) {
+    std::unique_lock<std::mutex> lk(mLock);
+
+    if (gadgetPullup) return true;
+
+    if (mCv.wait_for(lk, timeout_ms * 1ms, [] { return gadgetPullup; })) {
+        ALOGI("monitorFfs signalled true");
+        return true;
+    } else {
+        ALOGI("monitorFfs signalled error");
+        // continue monitoring as the descriptors might be written at a later
+        // point.
+        return false;
+    }
+}
+
+bool MonitorFfs::addInotifyFd(string fd) {
+    lock_guard<mutex> lock(mLockFd);
+    int wfd;
+
+    wfd = inotify_add_watch(mInotifyFd, fd.c_str(), IN_ALL_EVENTS);
+    if (wfd == -1)
+        return false;
+    else
+        mWatchFd.push_back(wfd);
+
+    return true;
+}
+
+void MonitorFfs::addEndPoint(string ep) {
+    lock_guard<mutex> lock(mLockFd);
+
+    mEndpointList.push_back(ep);
+}
+
+void MonitorFfs::registerFunctionsAppliedCallback(void (*callback)(bool functionsApplied,
+                                                                   void* payload),
+                                                  void* payload) {
+    mCallback = callback;
+    mPayload = payload;
+}
+
+}  // namespace gadget
+}  // namespace usb
+}  // namespace hardware
+}  // namespace android
diff --git a/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp b/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp
new file mode 100644
index 0000000..8986556
--- /dev/null
+++ b/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libusbconfigfs"
+
+#include "include/UsbGadgetCommon.h"
+
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+
+int unlinkFunctions(const char* path) {
+    DIR* config = opendir(path);
+    struct dirent* function;
+    char filepath[kMaxFilePathLength];
+    int ret = 0;
+
+    if (config == NULL) return -1;
+
+    // d_type does not seems to be supported in /config
+    // so filtering by name.
+    while (((function = readdir(config)) != NULL)) {
+        if ((strstr(function->d_name, FUNCTION_NAME) == NULL)) continue;
+        // build the path for each file in the folder.
+        sprintf(filepath, "%s/%s", path, function->d_name);
+        ret = remove(filepath);
+        if (ret) {
+            ALOGE("Unable  remove file %s errno:%d", filepath, errno);
+            break;
+        }
+    }
+
+    closedir(config);
+    return ret;
+}
+
+int addEpollFd(const unique_fd& epfd, const unique_fd& fd) {
+    struct epoll_event event;
+    int ret;
+
+    event.data.fd = fd;
+    event.events = EPOLLIN;
+
+    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
+    if (ret) ALOGE("epoll_ctl error %d", errno);
+
+    return ret;
+}
+
+int linkFunction(const char* function, int index) {
+    char functionPath[kMaxFilePathLength];
+    char link[kMaxFilePathLength];
+
+    sprintf(functionPath, "%s%s", FUNCTIONS_PATH, function);
+    sprintf(link, "%s%d", FUNCTION_PATH, index);
+    if (symlink(functionPath, link)) {
+        ALOGE("Cannot create symlink %s -> %s errno:%d", link, functionPath, errno);
+        return -1;
+    }
+    return 0;
+}
+
+Status setVidPid(const char* vid, const char* pid) {
+    if (!WriteStringToFile(vid, VENDOR_ID_PATH)) return Status::ERROR;
+
+    if (!WriteStringToFile(pid, PRODUCT_ID_PATH)) return Status::ERROR;
+
+    return Status::SUCCESS;
+}
+
+std::string getVendorFunctions() {
+    if (GetProperty(kBuildType, "") == "user") return "user";
+
+    std::string bootMode = GetProperty(PERSISTENT_BOOT_MODE, "");
+    std::string persistVendorFunctions = GetProperty(kPersistentVendorConfig, "");
+    std::string vendorFunctions = GetProperty(kVendorConfig, "");
+    std::string ret = "";
+
+    if (vendorFunctions != "") {
+        ret = vendorFunctions;
+    } else if (bootMode == "usbradio" || bootMode == "factory" || bootMode == "ffbm-00" ||
+               bootMode == "ffbm-01") {
+        if (persistVendorFunctions != "")
+            ret = persistVendorFunctions;
+        else
+            ret = "diag";
+        // vendor.usb.config will reflect the current configured functions
+        SetProperty(kVendorConfig, ret);
+    }
+
+    return ret;
+}
+
+Status resetGadget() {
+    ALOGI("setCurrentUsbFunctions None");
+
+    if (!WriteStringToFile("none", PULLUP_PATH)) ALOGI("Gadget cannot be pulled down");
+
+    if (!WriteStringToFile("0", DEVICE_CLASS_PATH)) return Status::ERROR;
+
+    if (!WriteStringToFile("0", DEVICE_SUB_CLASS_PATH)) return Status::ERROR;
+
+    if (!WriteStringToFile("0", DEVICE_PROTOCOL_PATH)) return Status::ERROR;
+
+    if (!WriteStringToFile("0", DESC_USE_PATH)) return Status::ERROR;
+
+    if (unlinkFunctions(CONFIG_PATH)) return Status::ERROR;
+
+    return Status::SUCCESS;
+}
+
+Status addGenericAndroidFunctions(MonitorFfs* monitorFfs, uint64_t functions, bool* ffsEnabled,
+                                  int* functionCount) {
+    if (((functions & GadgetFunction::MTP) != 0)) {
+        *ffsEnabled = true;
+        ALOGI("setCurrentUsbFunctions mtp");
+        if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;
+
+        if (!monitorFfs->addInotifyFd("/dev/usb-ffs/mtp/")) return Status::ERROR;
+
+        if (linkFunction("ffs.mtp", (*functionCount)++)) return Status::ERROR;
+
+        // Add endpoints to be monitored.
+        monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep1");
+        monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep2");
+        monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep3");
+    } else if (((functions & GadgetFunction::PTP) != 0)) {
+        *ffsEnabled = true;
+        ALOGI("setCurrentUsbFunctions ptp");
+        if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;
+
+        if (!monitorFfs->addInotifyFd("/dev/usb-ffs/ptp/")) return Status::ERROR;
+
+        if (linkFunction("ffs.ptp", (*functionCount)++)) return Status::ERROR;
+
+        // Add endpoints to be monitored.
+        monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep1");
+        monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep2");
+        monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep3");
+    }
+
+    if ((functions & GadgetFunction::MIDI) != 0) {
+        ALOGI("setCurrentUsbFunctions MIDI");
+        if (linkFunction("midi.gs5", (*functionCount)++)) return Status::ERROR;
+    }
+
+    if ((functions & GadgetFunction::ACCESSORY) != 0) {
+        ALOGI("setCurrentUsbFunctions Accessory");
+        if (linkFunction("accessory.gs2", (*functionCount)++)) return Status::ERROR;
+    }
+
+    if ((functions & GadgetFunction::AUDIO_SOURCE) != 0) {
+        ALOGI("setCurrentUsbFunctions Audio Source");
+        if (linkFunction("audio_source.gs3", (*functionCount)++)) return Status::ERROR;
+    }
+
+    if ((functions & GadgetFunction::RNDIS) != 0) {
+        ALOGI("setCurrentUsbFunctions rndis");
+        if (linkFunction("gsi.rndis", (*functionCount)++)) return Status::ERROR;
+        std::string rndisFunction = GetProperty(kVendorRndisConfig, "");
+        if (rndisFunction != "") {
+            if (linkFunction(rndisFunction.c_str(), (*functionCount)++)) return Status::ERROR;
+        } else {
+            // link gsi.rndis for older pixel projects
+            if (linkFunction("gsi.rndis", (*functionCount)++)) return Status::ERROR;
+        }
+    }
+
+    if ((functions & GadgetFunction::NCM) != 0) {
+        ALOGI("setCurrentUsbFunctions ncm");
+        if (linkFunction("ncm.gs6", (*functionCount)++)) return Status::ERROR;
+    }
+
+    return Status::SUCCESS;
+}
+
+Status addAdb(MonitorFfs* monitorFfs, int* functionCount) {
+    ALOGI("setCurrentUsbFunctions Adb");
+    if (!monitorFfs->addInotifyFd("/dev/usb-ffs/adb/")) return Status::ERROR;
+
+    if (linkFunction("ffs.adb", (*functionCount)++)) return Status::ERROR;
+    monitorFfs->addEndPoint("/dev/usb-ffs/adb/ep1");
+    monitorFfs->addEndPoint("/dev/usb-ffs/adb/ep2");
+    ALOGI("Service started");
+    return Status::SUCCESS;
+}
+
+}  // namespace gadget
+}  // namespace usb
+}  // namespace hardware
+}  // namespace android
diff --git a/usb/gadget/1.2/default/lib/include/UsbGadgetCommon.h b/usb/gadget/1.2/default/lib/include/UsbGadgetCommon.h
new file mode 100644
index 0000000..18b8101
--- /dev/null
+++ b/usb/gadget/1.2/default/lib/include/UsbGadgetCommon.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HARDWARE_USB_USBGADGETCOMMON_H
+#define HARDWARE_USB_USBGADGETCOMMON_H
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+#include <android/hardware/usb/gadget/1.2/IUsbGadget.h>
+#include <android/hardware/usb/gadget/1.2/types.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+
+constexpr int kBufferSize = 512;
+constexpr int kMaxFilePathLength = 256;
+constexpr int kEpollEvents = 10;
+constexpr bool kDebug = false;
+constexpr int kDisconnectWaitUs = 100000;
+constexpr int kPullUpDelay = 500000;
+constexpr int kShutdownMonitor = 100;
+
+constexpr char kBuildType[] = "ro.build.type";
+constexpr char kPersistentVendorConfig[] = "persist.vendor.usb.usbradio.config";
+constexpr char kVendorConfig[] = "vendor.usb.config";
+constexpr char kVendorRndisConfig[] = "vendor.usb.rndis.config";
+
+#define GADGET_PATH "/config/usb_gadget/g1/"
+#define PULLUP_PATH GADGET_PATH "UDC"
+#define PERSISTENT_BOOT_MODE "ro.bootmode"
+#define VENDOR_ID_PATH GADGET_PATH "idVendor"
+#define PRODUCT_ID_PATH GADGET_PATH "idProduct"
+#define DEVICE_CLASS_PATH GADGET_PATH "bDeviceClass"
+#define DEVICE_SUB_CLASS_PATH GADGET_PATH "bDeviceSubClass"
+#define DEVICE_PROTOCOL_PATH GADGET_PATH "bDeviceProtocol"
+#define DESC_USE_PATH GADGET_PATH "os_desc/use"
+#define OS_DESC_PATH GADGET_PATH "os_desc/b.1"
+#define CONFIG_PATH GADGET_PATH "configs/b.1/"
+#define FUNCTIONS_PATH GADGET_PATH "functions/"
+#define FUNCTION_NAME "function"
+#define FUNCTION_PATH CONFIG_PATH FUNCTION_NAME
+#define RNDIS_PATH FUNCTIONS_PATH "gsi.rndis"
+
+using ::android::base::GetProperty;
+using ::android::base::SetProperty;
+using ::android::base::unique_fd;
+using ::android::base::WriteStringToFile;
+using ::android::hardware::usb::gadget::V1_0::Status;
+using ::android::hardware::usb::gadget::V1_2::GadgetFunction;
+
+using ::std::lock_guard;
+using ::std::move;
+using ::std::mutex;
+using ::std::string;
+using ::std::thread;
+using ::std::unique_ptr;
+using ::std::vector;
+using ::std::chrono::microseconds;
+using ::std::chrono::steady_clock;
+using ::std::literals::chrono_literals::operator""ms;
+
+// MonitorFfs automously manages gadget pullup by monitoring
+// the ep file status. Restarts the usb gadget when the ep
+// owner restarts.
+class MonitorFfs {
+  private:
+    // Monitors the endpoints Inotify events.
+    unique_fd mInotifyFd;
+    // Control pipe for shutting down the mMonitor thread.
+    // mMonitor exits when SHUTDOWN_MONITOR is written into
+    // mEventFd/
+    unique_fd mEventFd;
+    // Pools on mInotifyFd and mEventFd.
+    unique_fd mEpollFd;
+    vector<int> mWatchFd;
+
+    // Maintains the list of Endpoints.
+    vector<string> mEndpointList;
+    // protects the CV.
+    std::mutex mLock;
+    std::condition_variable mCv;
+    // protects mInotifyFd, mEpollFd.
+    std::mutex mLockFd;
+
+    // Flag to maintain the current status of gadget pullup.
+    bool mCurrentUsbFunctionsApplied;
+
+    // Thread object that executes the ep monitoring logic.
+    unique_ptr<thread> mMonitor;
+    // Callback to be invoked when gadget is pulled up.
+    void (*mCallback)(bool functionsApplied, void* payload);
+    void* mPayload;
+    // Name of the USB gadget. Used for pullup.
+    const char* const mGadgetName;
+    // Monitor State
+    bool mMonitorRunning;
+
+  public:
+    MonitorFfs(const char* const gadget);
+    // Inits all the UniqueFds.
+    void reset();
+    // Starts monitoring endpoints and pullup the gadget when
+    // the descriptors are written.
+    bool startMonitor();
+    // Waits for timeout_ms for gadget pull up to happen.
+    // Returns immediately if the gadget is already pulled up.
+    bool waitForPullUp(int timeout_ms);
+    // Adds the given fd to the watch list.
+    bool addInotifyFd(string fd);
+    // Adds the given endpoint to the watch list.
+    void addEndPoint(string ep);
+    // Registers the async callback from the caller to notify the caller
+    // when the gadget pull up happens.
+    void registerFunctionsAppliedCallback(void (*callback)(bool functionsApplied, void*(payload)),
+                                          void* payload);
+    bool isMonitorRunning();
+    // Ep monitoring and the gadget pull up logic.
+    static void* startMonitorFd(void* param);
+};
+
+//**************** Helper functions ************************//
+
+// Adds the given fd to the epollfd(epfd).
+int addEpollFd(const unique_fd& epfd, const unique_fd& fd);
+// Removes all the usb functions link in the specified path.
+int unlinkFunctions(const char* path);
+// Craetes a configfs link for the function.
+int linkFunction(const char* function, int index);
+// Sets the USB VID and PID.
+Status setVidPid(const char* vid, const char* pid);
+// Extracts vendor functions from the vendor init properties.
+std::string getVendorFunctions();
+// Adds Adb to the usb configuration.
+Status addAdb(MonitorFfs* monitorFfs, int* functionCount);
+// Adds all applicable generic android usb functions other than ADB.
+Status addGenericAndroidFunctions(MonitorFfs* monitorFfs, uint64_t functions, bool* ffsEnabled,
+                                  int* functionCount);
+// Pulls down USB gadget.
+Status resetGadget();
+
+}  // namespace gadget
+}  // namespace usb
+}  // namespace hardware
+}  // namespace android
+#endif
diff --git a/usb/gadget/1.2/default/service.cpp b/usb/gadget/1.2/default/service.cpp
new file mode 100644
index 0000000..80c56a6
--- /dev/null
+++ b/usb/gadget/1.2/default/service.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.usb.gadget@1.2-service"
+
+#include <hidl/HidlTransportSupport.h>
+#include "UsbGadget.h"
+
+using android::sp;
+
+// libhwbinder:
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+// Generated HIDL files
+using android::hardware::usb::gadget::V1_2::IUsbGadget;
+using android::hardware::usb::gadget::V1_2::implementation::UsbGadget;
+
+using android::OK;
+using android::status_t;
+
+int main() {
+    configureRpcThreadpool(1, true /*callerWillJoin*/);
+
+    android::sp<IUsbGadget> service = new UsbGadget();
+
+    status_t status = service->registerAsService();
+
+    if (status != OK) {
+        ALOGE("Cannot register USB Gadget HAL service");
+        return 1;
+    }
+
+    ALOGI("USB Gadget HAL Ready.");
+    joinRpcThreadpool();
+    // Under noraml cases, execution will not reach this line.
+    ALOGI("USB Gadget HAL failed to join thread pool.");
+    return 1;
+}
diff --git a/usb/gadget/1.2/types.hal b/usb/gadget/1.2/types.hal
new file mode 100644
index 0000000..a5c079d
--- /dev/null
+++ b/usb/gadget/1.2/types.hal
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb.gadget@1.2;
+
+import android.hardware.usb.gadget@1.0::GadgetFunction;
+
+enum GadgetFunction : @1.0::GadgetFunction {
+    /**
+     * NCM - NCM function.
+     */
+    NCM = 1 << 10,
+};
+
+enum UsbSpeed : uint32_t {
+    /**
+     * UNKNOWN - Not Connected or Unsupported Speed
+     */
+    UNKNOWN = -1,
+
+    /**
+     * USB Low Speed
+     */
+    LOWSPEED = 0,
+
+    /**
+     * USB Full Speed
+     */
+    FULLSPEED = 1,
+
+    /**
+     * USB High Speed
+     */
+    HIGHSPEED = 2,
+
+    /**
+     * USB Super Speed
+     */
+    SUPERSPEED = 3,
+
+    /**
+     * USB Super Speed 10Gbps
+     */
+    SUPERSPEED_10Gb = 4,
+
+    /**
+     * USB Super Speed 20Gbps
+     */
+    SUPERSPEED_20Gb = 5,
+
+    /**
+     * USB4 Gen2 x 1 (10Gbps)
+     */
+    USB4_GEN2_10Gb = 6,
+
+    /**
+     * USB4 Gen2 x 2 (20Gbps)
+     */
+    USB4_GEN2_20Gb = 7,
+
+    /**
+     * USB4 Gen3 x 1 (20Gbps)
+     */
+    USB4_GEN3_20Gb = 8,
+
+    /**
+     * USB4 Gen3 x 2 (40Gbps)
+     */
+    USB4_GEN3_40Gb = 9,
+
+    /**
+     * This is a suggestion if needed.
+     *
+     * Reserved Speed -- It is a newer speed after USB4 v1.0 announcement.
+     * If this speed is detected, the HAL implementation should convert current
+     * speed to above speeds which is lower and the closest.
+     */
+    RESERVED_SPEED = 64,
+};
diff --git a/vibrator/aidl/TEST_MAPPING b/vibrator/aidl/TEST_MAPPING
index 5ae32e7..2414b84 100644
--- a/vibrator/aidl/TEST_MAPPING
+++ b/vibrator/aidl/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "VtsHalVibratorTargetTest"
+    },
+    {
+      "name": "VtsHalVibratorManagerTargetTest"
     }
   ]
 }
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl
new file mode 100644
index 0000000..99cd448
--- /dev/null
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/IVibratorManager.aidl
@@ -0,0 +1,35 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.vibrator;
+@VintfStability
+interface IVibratorManager {
+  int getCapabilities();
+  int[] getVibratorIds();
+  android.hardware.vibrator.IVibrator getVibrator(in int vibratorId);
+  void prepareSynced(in int[] vibratorIds);
+  void triggerSynced(in android.hardware.vibrator.IVibratorCallback callback);
+  void cancelSynced();
+  const int CAP_SYNC = 1;
+  const int CAP_PREPARE_ON = 2;
+  const int CAP_PREPARE_PERFORM = 4;
+  const int CAP_PREPARE_COMPOSE = 8;
+  const int CAP_MIXED_TRIGGER_ON = 16;
+  const int CAP_MIXED_TRIGGER_PERFORM = 32;
+  const int CAP_MIXED_TRIGGER_COMPOSE = 64;
+  const int CAP_TRIGGER_CALLBACK = 128;
+}
diff --git a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
index 0b21248..cd7b603 100644
--- a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
@@ -206,7 +206,10 @@
      * device/board configuration files ensuring that no ID is assigned to
      * multiple clients. No client should use this API unless explicitly
      * assigned an always-on source ID. Clients must develop their own way to
-     * get IDs from vendor in a stable way.
+     * get IDs from vendor in a stable way. For instance, a client may expose
+     * a stable API (via HAL, sysprops, or xml overlays) to allow vendor to
+     * associate a hardware ID with a specific usecase. When that usecase is
+     * triggered, a client would use that hardware ID here.
      *
      * @param id The device-specific always-on source ID to enable.
      * @param effect The type of haptic event to trigger.
diff --git a/vibrator/aidl/android/hardware/vibrator/IVibratorManager.aidl b/vibrator/aidl/android/hardware/vibrator/IVibratorManager.aidl
new file mode 100644
index 0000000..eb5e9cc
--- /dev/null
+++ b/vibrator/aidl/android/hardware/vibrator/IVibratorManager.aidl
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.vibrator;
+
+import android.hardware.vibrator.IVibrator;
+import android.hardware.vibrator.IVibratorCallback;
+
+@VintfStability
+interface IVibratorManager {
+    /**
+     * Whether prepare/trigger synced are supported.
+     */
+    const int CAP_SYNC = 1 << 0;
+    /**
+     * Whether IVibrator 'on' can be used with 'prepareSynced' function.
+     */
+    const int CAP_PREPARE_ON = 1 << 1;
+    /**
+     * Whether IVibrator 'perform' can be used with 'prepareSynced' function.
+     */
+    const int CAP_PREPARE_PERFORM = 1 << 2;
+    /**
+     * Whether IVibrator 'compose' can be used with 'prepareSynced' function.
+     */
+    const int CAP_PREPARE_COMPOSE = 1 << 3;
+    /**
+     * Whether IVibrator 'on' can be triggered with other functions in sync with 'triggerSynced'.
+     */
+    const int CAP_MIXED_TRIGGER_ON = 1 << 4;
+    /**
+     * Whether IVibrator 'perform' can be triggered with other functions in sync with 'triggerSynced'.
+     */
+    const int CAP_MIXED_TRIGGER_PERFORM = 1 << 5;
+    /**
+     * Whether IVibrator 'compose' can be triggered with other functions in sync with 'triggerSynced'.
+     */
+    const int CAP_MIXED_TRIGGER_COMPOSE = 1 << 6;
+    /**
+     * Whether on w/ IVibratorCallback can be used w/ 'trigerSynced' function.
+     */
+    const int CAP_TRIGGER_CALLBACK = 1 << 7;
+
+    /**
+     * Determine capabilities of the vibrator manager HAL (CAP_* mask)
+     */
+    int getCapabilities();
+
+    /**
+     * List the id of available vibrators. This result should be static and not change.
+     */
+    int[] getVibratorIds();
+
+    /**
+     * Return an available vibrator identified with given id.
+     */
+    IVibrator getVibrator(in int vibratorId);
+
+    /**
+     * Start preparation for a synced vibration
+     *
+     * This function must only be called after the previous synced vibration was triggered or
+     * canceled (through cancelSynced()).
+     *
+     * Doing this operation while any of the specified vibrators is already on is undefined behavior.
+     * Clients should explicitly call off in each vibrator.
+     *
+     * @param vibratorIds ids of the vibrators to play vibrations in sync.
+     */
+    void prepareSynced(in int[] vibratorIds);
+
+    /**
+     * Trigger a prepared synced vibration
+     *
+     * Trigger a previously-started preparation for synced vibration, if any.
+     * A callback is only expected to be supported when getCapabilities CAP_TRIGGER_CALLBACK
+     * is specified.
+     *
+     * @param callback A callback used to inform Frameworks of state change, if supported.
+     */
+    void triggerSynced(in IVibratorCallback callback);
+
+    /**
+     * Cancel preparation of synced vibration
+     *
+     * Cancel a previously-started preparation for synced vibration, if any.
+     */
+    void cancelSynced();
+}
diff --git a/vibrator/aidl/default/Android.bp b/vibrator/aidl/default/Android.bp
index 9e6d9cf..f9d45bb 100644
--- a/vibrator/aidl/default/Android.bp
+++ b/vibrator/aidl/default/Android.bp
@@ -4,10 +4,13 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.vibrator-ndk_platform",
+        "android.hardware.vibrator-unstable-ndk_platform",
     ],
     export_include_dirs: ["include"],
-    srcs: ["Vibrator.cpp"],
+    srcs: [
+        "Vibrator.cpp",
+        "VibratorManager.cpp",
+    ],
     visibility: [
         ":__subpackages__",
         "//hardware/interfaces/tests/extension/vibrator:__subpackages__",
@@ -23,7 +26,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.vibrator-ndk_platform",
+        "android.hardware.vibrator-unstable-ndk_platform",
     ],
     static_libs: [
         "libvibratorexampleimpl",
diff --git a/vibrator/aidl/default/VibratorManager.cpp b/vibrator/aidl/default/VibratorManager.cpp
new file mode 100644
index 0000000..7cf9e6a
--- /dev/null
+++ b/vibrator/aidl/default/VibratorManager.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vibrator-impl/VibratorManager.h"
+
+#include <android-base/logging.h>
+#include <thread>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+static constexpr int32_t kDefaultVibratorId = 1;
+
+ndk::ScopedAStatus VibratorManager::getCapabilities(int32_t* _aidl_return) {
+    LOG(INFO) << "Vibrator manager reporting capabilities";
+    *_aidl_return =
+            IVibratorManager::CAP_SYNC | IVibratorManager::CAP_PREPARE_ON |
+            IVibratorManager::CAP_PREPARE_PERFORM | IVibratorManager::CAP_PREPARE_COMPOSE |
+            IVibratorManager::CAP_MIXED_TRIGGER_ON | IVibratorManager::CAP_MIXED_TRIGGER_PERFORM |
+            IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE | IVibratorManager::CAP_TRIGGER_CALLBACK;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VibratorManager::getVibratorIds(std::vector<int32_t>* _aidl_return) {
+    LOG(INFO) << "Vibrator manager getting vibrator ids";
+    *_aidl_return = {kDefaultVibratorId};
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VibratorManager::getVibrator(int32_t vibratorId,
+                                                std::shared_ptr<IVibrator>* _aidl_return) {
+    LOG(INFO) << "Vibrator manager getting vibrator " << vibratorId;
+    if (vibratorId == kDefaultVibratorId) {
+        *_aidl_return = mDefaultVibrator;
+        return ndk::ScopedAStatus::ok();
+    } else {
+        *_aidl_return = nullptr;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+}
+
+ndk::ScopedAStatus VibratorManager::prepareSynced(const std::vector<int32_t>& vibratorIds) {
+    LOG(INFO) << "Vibrator Manager prepare synced";
+    if (vibratorIds.size() == 1 && vibratorIds[0] == kDefaultVibratorId) {
+        return ndk::ScopedAStatus::ok();
+    } else {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+}
+
+ndk::ScopedAStatus VibratorManager::triggerSynced(
+        const std::shared_ptr<IVibratorCallback>& callback) {
+    LOG(INFO) << "Vibrator Manager trigger synced";
+    std::thread([=] {
+        if (callback != nullptr) {
+            LOG(INFO) << "Notifying perform complete";
+            callback->onComplete();
+        }
+    }).detach();
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus VibratorManager::cancelSynced() {
+    LOG(INFO) << "Vibrator Manager cancel synced";
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/vibrator/aidl/default/include/vibrator-impl/VibratorManager.h b/vibrator/aidl/default/include/vibrator-impl/VibratorManager.h
new file mode 100644
index 0000000..319eb05
--- /dev/null
+++ b/vibrator/aidl/default/include/vibrator-impl/VibratorManager.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/vibrator/BnVibratorManager.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+class VibratorManager : public BnVibratorManager {
+  public:
+    VibratorManager(std::shared_ptr<IVibrator> vibrator) : mDefaultVibrator(std::move(vibrator)){};
+    ndk::ScopedAStatus getCapabilities(int32_t* _aidl_return) override;
+    ndk::ScopedAStatus getVibratorIds(std::vector<int32_t>* _aidl_return) override;
+    ndk::ScopedAStatus getVibrator(int32_t vibratorId,
+                                   std::shared_ptr<IVibrator>* _aidl_return) override;
+    ndk::ScopedAStatus prepareSynced(const std::vector<int32_t>& vibratorIds) override;
+    ndk::ScopedAStatus triggerSynced(const std::shared_ptr<IVibratorCallback>& callback) override;
+    ndk::ScopedAStatus cancelSynced() override;
+
+  private:
+    std::shared_ptr<IVibrator> mDefaultVibrator;
+};
+
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/vibrator/aidl/default/main.cpp b/vibrator/aidl/default/main.cpp
index ebb0905..bd834d2 100644
--- a/vibrator/aidl/default/main.cpp
+++ b/vibrator/aidl/default/main.cpp
@@ -15,19 +15,29 @@
  */
 
 #include "vibrator-impl/Vibrator.h"
+#include "vibrator-impl/VibratorManager.h"
 
 #include <android-base/logging.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 
 using aidl::android::hardware::vibrator::Vibrator;
+using aidl::android::hardware::vibrator::VibratorManager;
 
 int main() {
     ABinderProcess_setThreadPoolMaxThreadCount(0);
-    std::shared_ptr<Vibrator> vib = ndk::SharedRefBase::make<Vibrator>();
 
-    const std::string instance = std::string() + Vibrator::descriptor + "/default";
-    binder_status_t status = AServiceManager_addService(vib->asBinder().get(), instance.c_str());
+    // make a default vibrator service
+    auto vib = ndk::SharedRefBase::make<Vibrator>();
+    const std::string vibName = std::string() + Vibrator::descriptor + "/default";
+    binder_status_t status = AServiceManager_addService(vib->asBinder().get(), vibName.c_str());
+    CHECK(status == STATUS_OK);
+
+    // make the vibrator manager service with a different vibrator
+    auto managedVib = ndk::SharedRefBase::make<Vibrator>();
+    auto vibManager = ndk::SharedRefBase::make<VibratorManager>(std::move(managedVib));
+    const std::string vibManagerName = std::string() + VibratorManager::descriptor + "/default";
+    status = AServiceManager_addService(vibManager->asBinder().get(), vibManagerName.c_str());
     CHECK(status == STATUS_OK);
 
     ABinderProcess_joinThreadPool();
diff --git a/vibrator/aidl/default/vibrator-default.xml b/vibrator/aidl/default/vibrator-default.xml
index 49b11ec..9f9cd40 100644
--- a/vibrator/aidl/default/vibrator-default.xml
+++ b/vibrator/aidl/default/vibrator-default.xml
@@ -3,4 +3,8 @@
         <name>android.hardware.vibrator</name>
         <fqname>IVibrator/default</fqname>
     </hal>
+    <hal format="aidl">
+        <name>android.hardware.vibrator</name>
+        <fqname>IVibratorManager/default</fqname>
+    </hal>
 </manifest>
diff --git a/vibrator/aidl/vts/Android.bp b/vibrator/aidl/vts/Android.bp
index 28cb4d9..d06b50e 100644
--- a/vibrator/aidl/vts/Android.bp
+++ b/vibrator/aidl/vts/Android.bp
@@ -9,7 +9,26 @@
         "libbinder",
     ],
     static_libs: [
-        "android.hardware.vibrator-cpp",
+        "android.hardware.vibrator-unstable-cpp",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
+
+cc_test {
+    name: "VtsHalVibratorManagerTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["VtsHalVibratorManagerTargetTest.cpp"],
+    shared_libs: [
+        "libbinder",
+    ],
+    static_libs: [
+        "android.hardware.vibrator-unstable-cpp",
     ],
     test_suites: [
         "general-tests",
diff --git a/vibrator/aidl/vts/VtsHalVibratorManagerTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorManagerTargetTest.cpp
new file mode 100644
index 0000000..9789188
--- /dev/null
+++ b/vibrator/aidl/vts/VtsHalVibratorManagerTargetTest.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <android/hardware/vibrator/BnVibratorCallback.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <android/hardware/vibrator/IVibratorManager.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+#include <cmath>
+#include <future>
+
+using android::ProcessState;
+using android::sp;
+using android::String16;
+using android::binder::Status;
+using android::hardware::vibrator::BnVibratorCallback;
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+using android::hardware::vibrator::IVibrator;
+using android::hardware::vibrator::IVibratorManager;
+using std::chrono::high_resolution_clock;
+
+const std::vector<Effect> kEffects{android::enum_range<Effect>().begin(),
+                                   android::enum_range<Effect>().end()};
+const std::vector<EffectStrength> kEffectStrengths{android::enum_range<EffectStrength>().begin(),
+                                                   android::enum_range<EffectStrength>().end()};
+const std::vector<CompositePrimitive> kPrimitives{android::enum_range<CompositePrimitive>().begin(),
+                                                  android::enum_range<CompositePrimitive>().end()};
+
+class CompletionCallback : public BnVibratorCallback {
+  public:
+    CompletionCallback(const std::function<void()>& callback) : mCallback(callback) {}
+    Status onComplete() override {
+        mCallback();
+        return Status::ok();
+    }
+
+  private:
+    std::function<void()> mCallback;
+};
+
+class VibratorAidl : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        manager = android::waitForDeclaredService<IVibratorManager>(String16(GetParam().c_str()));
+        ASSERT_NE(manager, nullptr);
+        ASSERT_TRUE(manager->getCapabilities(&capabilities).isOk());
+        EXPECT_TRUE(manager->getVibratorIds(&vibratorIds).isOk());
+    }
+
+    sp<IVibratorManager> manager;
+    int32_t capabilities;
+    std::vector<int32_t> vibratorIds;
+};
+
+TEST_P(VibratorAidl, ValidateExistingVibrators) {
+    sp<IVibrator> vibrator;
+    for (auto& id : vibratorIds) {
+        EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+        ASSERT_NE(vibrator, nullptr);
+    }
+}
+
+TEST_P(VibratorAidl, GetVibratorWithInvalidId) {
+    int32_t invalidId = *max_element(vibratorIds.begin(), vibratorIds.end()) + 1;
+    sp<IVibrator> vibrator;
+    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
+              manager->getVibrator(invalidId, &vibrator).exceptionCode());
+    ASSERT_EQ(vibrator, nullptr);
+}
+
+TEST_P(VibratorAidl, ValidatePrepareSyncedExistingVibrators) {
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    if (vibratorIds.empty()) return;
+    EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
+}
+
+TEST_P(VibratorAidl, PrepareSyncedEmptySetIsInvalid) {
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    std::vector<int32_t> emptyIds;
+    EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, manager->prepareSynced(emptyIds).exceptionCode());
+}
+
+TEST_P(VibratorAidl, PrepareSyncedNotSupported) {
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) {
+        EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION,
+                  manager->prepareSynced(vibratorIds).exceptionCode());
+    }
+}
+
+TEST_P(VibratorAidl, PrepareOnNotSupported) {
+    if (vibratorIds.empty()) return;
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    if (!(capabilities & IVibratorManager::CAP_PREPARE_ON)) {
+        uint32_t durationMs = 250;
+        EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
+        sp<IVibrator> vibrator;
+        for (auto& id : vibratorIds) {
+            EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+            ASSERT_NE(vibrator, nullptr);
+            EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION,
+                      vibrator->on(durationMs, nullptr).exceptionCode());
+        }
+        EXPECT_TRUE(manager->cancelSynced().isOk());
+    }
+}
+
+TEST_P(VibratorAidl, PreparePerformNotSupported) {
+    if (vibratorIds.empty()) return;
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    if (!(capabilities & IVibratorManager::CAP_PREPARE_ON)) {
+        EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
+        sp<IVibrator> vibrator;
+        for (auto& id : vibratorIds) {
+            EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+            ASSERT_NE(vibrator, nullptr);
+            int32_t lengthMs = 0;
+            Status status = vibrator->perform(kEffects[0], kEffectStrengths[0], nullptr, &lengthMs);
+            EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION, status.exceptionCode());
+        }
+        EXPECT_TRUE(manager->cancelSynced().isOk());
+    }
+}
+
+TEST_P(VibratorAidl, PrepareComposeNotSupported) {
+    if (vibratorIds.empty()) return;
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    if (!(capabilities & IVibratorManager::CAP_PREPARE_ON)) {
+        std::vector<CompositeEffect> composite;
+        CompositeEffect effect;
+        effect.delayMs = 10;
+        effect.primitive = kPrimitives[0];
+        effect.scale = 1.0f;
+        composite.emplace_back(effect);
+
+        EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
+        sp<IVibrator> vibrator;
+        for (auto& id : vibratorIds) {
+            EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+            ASSERT_NE(vibrator, nullptr);
+            Status status = vibrator->compose(composite, nullptr);
+            EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION, status.exceptionCode());
+        }
+        EXPECT_TRUE(manager->cancelSynced().isOk());
+    }
+}
+
+TEST_P(VibratorAidl, TriggerWithCallback) {
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    if (!(capabilities & IVibratorManager::CAP_PREPARE_ON)) return;
+    if (!(capabilities & IVibratorManager::CAP_TRIGGER_CALLBACK)) return;
+    if (vibratorIds.empty()) return;
+
+    std::promise<void> completionPromise;
+    std::future<void> completionFuture{completionPromise.get_future()};
+    sp<CompletionCallback> callback =
+            new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+    uint32_t durationMs = 250;
+    std::chrono::milliseconds timeout{durationMs * 2};
+
+    EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
+    sp<IVibrator> vibrator;
+    for (auto& id : vibratorIds) {
+        EXPECT_TRUE(manager->getVibrator(id, &vibrator).isOk());
+        ASSERT_NE(vibrator, nullptr);
+        EXPECT_TRUE(vibrator->on(durationMs, nullptr).isOk());
+    }
+
+    EXPECT_TRUE(manager->triggerSynced(callback).isOk());
+    EXPECT_EQ(completionFuture.wait_for(timeout), std::future_status::ready);
+    EXPECT_TRUE(manager->cancelSynced().isOk());
+}
+
+TEST_P(VibratorAidl, TriggerSyncNotSupported) {
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) {
+        EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION,
+                  manager->triggerSynced(nullptr).exceptionCode());
+    }
+}
+
+TEST_P(VibratorAidl, TriggerCallbackNotSupported) {
+    if (!(capabilities & IVibratorManager::CAP_SYNC)) return;
+    if (!(capabilities & IVibratorManager::CAP_TRIGGER_CALLBACK)) {
+        sp<CompletionCallback> callback = new CompletionCallback([] {});
+        EXPECT_TRUE(manager->prepareSynced(vibratorIds).isOk());
+        EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION,
+                  manager->triggerSynced(callback).exceptionCode());
+    }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VibratorAidl);
+INSTANTIATE_TEST_SUITE_P(
+        Vibrator, VibratorAidl,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IVibratorManager::descriptor)),
+        android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
index 888a403..adbb0cf 100644
--- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
+++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
@@ -18,6 +18,7 @@
 
 #include <android/hardware/vibrator/BnVibratorCallback.h>
 #include <android/hardware/vibrator/IVibrator.h>
+#include <android/hardware/vibrator/IVibratorManager.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 
@@ -34,6 +35,7 @@
 using android::hardware::vibrator::Effect;
 using android::hardware::vibrator::EffectStrength;
 using android::hardware::vibrator::IVibrator;
+using android::hardware::vibrator::IVibratorManager;
 using std::chrono::high_resolution_clock;
 
 const std::vector<Effect> kEffects{android::enum_range<Effect>().begin(),
@@ -77,10 +79,28 @@
     std::function<void()> mCallback;
 };
 
-class VibratorAidl : public testing::TestWithParam<std::string> {
+class VibratorAidl : public testing::TestWithParam<std::tuple<int32_t, int32_t>> {
   public:
     virtual void SetUp() override {
-        vibrator = android::waitForDeclaredService<IVibrator>(String16(GetParam().c_str()));
+        int32_t managerIdx = std::get<0>(GetParam());
+        int32_t vibratorId = std::get<1>(GetParam());
+        auto managerAidlNames = android::getAidlHalInstanceNames(IVibratorManager::descriptor);
+
+        if (managerIdx < 0) {
+            // Testing a unmanaged vibrator, using vibratorId as index from registered HALs
+            auto vibratorAidlNames = android::getAidlHalInstanceNames(IVibrator::descriptor);
+            ASSERT_LT(vibratorId, vibratorAidlNames.size());
+            auto vibratorName = String16(vibratorAidlNames[vibratorId].c_str());
+            vibrator = android::waitForDeclaredService<IVibrator>(vibratorName);
+        } else {
+            // Testing a managed vibrator, using vibratorId to retrieve it from the manager
+            ASSERT_LT(managerIdx, managerAidlNames.size());
+            auto managerName = String16(managerAidlNames[managerIdx].c_str());
+            auto vibratorManager = android::waitForDeclaredService<IVibratorManager>(managerName);
+            auto vibratorResult = vibratorManager->getVibrator(vibratorId, &vibrator);
+            ASSERT_TRUE(vibratorResult.isOk());
+        }
+
         ASSERT_NE(vibrator, nullptr);
         ASSERT_TRUE(vibrator->getCapabilities(&capabilities).isOk());
     }
@@ -96,7 +116,7 @@
 }
 
 TEST_P(VibratorAidl, OnWithCallback) {
-    if (!(capabilities & IVibrator::CAP_PERFORM_CALLBACK)) return;
+    if (!(capabilities & IVibrator::CAP_ON_CALLBACK)) return;
 
     std::promise<void> completionPromise;
     std::future<void> completionFuture{completionPromise.get_future()};
@@ -110,7 +130,7 @@
 }
 
 TEST_P(VibratorAidl, OnCallbackNotSupported) {
-    if (!(capabilities & IVibrator::CAP_PERFORM_CALLBACK)) {
+    if (!(capabilities & IVibrator::CAP_ON_CALLBACK)) {
         sp<CompletionCallback> callback = new CompletionCallback([] {});
         EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION, vibrator->on(250, callback).exceptionCode());
     }
@@ -518,10 +538,41 @@
     }
 }
 
+std::vector<std::tuple<int32_t, int32_t>> GenerateVibratorMapping() {
+    std::vector<std::tuple<int32_t, int32_t>> tuples;
+    auto managerAidlNames = android::getAidlHalInstanceNames(IVibratorManager::descriptor);
+    std::vector<int32_t> vibratorIds;
+
+    for (int i = 0; i < managerAidlNames.size(); i++) {
+        auto managerName = String16(managerAidlNames[i].c_str());
+        auto vibratorManager = android::waitForDeclaredService<IVibratorManager>(managerName);
+        if (vibratorManager->getVibratorIds(&vibratorIds).isOk()) {
+            for (auto& vibratorId : vibratorIds) {
+                tuples.push_back(std::make_tuple(i, vibratorId));
+            }
+        }
+    }
+
+    auto vibratorAidlNames = android::getAidlHalInstanceNames(IVibrator::descriptor);
+    for (int i = 0; i < vibratorAidlNames.size(); i++) {
+        tuples.push_back(std::make_tuple(-1, i));
+    }
+
+    return tuples;
+}
+
+std::string PrintGeneratedTest(const testing::TestParamInfo<VibratorAidl::ParamType>& info) {
+    const auto& [managerIdx, vibratorId] = info.param;
+    if (managerIdx < 0) {
+        return std::string("TOP_LEVEL_VIBRATOR_") + std::to_string(vibratorId);
+    }
+    return std::string("MANAGER_") + std::to_string(managerIdx) + "_VIBRATOR_ID_" +
+           std::to_string(vibratorId);
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VibratorAidl);
-INSTANTIATE_TEST_SUITE_P(Vibrator, VibratorAidl,
-                         testing::ValuesIn(android::getAidlHalInstanceNames(IVibrator::descriptor)),
-                         android::PrintInstanceNameToString);
+INSTANTIATE_TEST_SUITE_P(Vibrator, VibratorAidl, testing::ValuesIn(GenerateVibratorMapping()),
+                         PrintGeneratedTest);
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
diff --git a/wifi/1.5/Android.bp b/wifi/1.5/Android.bp
index 5a62a3a..e2c38ce 100644
--- a/wifi/1.5/Android.bp
+++ b/wifi/1.5/Android.bp
@@ -7,6 +7,7 @@
         "types.hal",
         "IWifi.hal",
         "IWifiChip.hal",
+        "IWifiApIface.hal",
         "IWifiNanIface.hal",
         "IWifiNanIfaceEventCallback.hal",
         "IWifiStaIface.hal",
diff --git a/wifi/1.5/IWifiApIface.hal b/wifi/1.5/IWifiApIface.hal
new file mode 100644
index 0000000..9625a6b
--- /dev/null
+++ b/wifi/1.5/IWifiApIface.hal
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi@1.5;
+
+import @1.4::IWifiApIface;
+import @1.0::MacAddress;
+import @1.0::WifiStatus;
+
+/**
+ * Represents a network interface in AP mode.
+ *
+ * This can be obtained through @1.0::IWifiChip.getApIface() and casting
+ * IWifiApIface up to 1.5.
+ */
+interface IWifiApIface extends @1.4::IWifiApIface {
+    /**
+     * Reset all of the AP interfaces MAC address to the factory MAC address.
+     *
+     * @return status WifiStatus of the operation
+     *         Possible status codes:
+     *         |WifiStatusCode.SUCCESS|,
+     *         |WifiStatusCode.ERROR_WIFI_IFACE_INVALID|,
+     *         |WifiStatusCode.ERROR_UNKNOWN|
+     */
+    resetToFactoryMacAddress() generates (WifiStatus status);
+};
diff --git a/wifi/1.5/IWifiChip.hal b/wifi/1.5/IWifiChip.hal
index dcc9279..e9caa3d 100644
--- a/wifi/1.5/IWifiChip.hal
+++ b/wifi/1.5/IWifiChip.hal
@@ -17,6 +17,8 @@
 package android.hardware.wifi@1.5;
 
 import @1.0::WifiStatus;
+import @1.0::IfaceType;
+import @1.5::IWifiApIface;
 import @1.0::IWifiIface;
 import @1.3::IWifiChip;
 import @1.4::IWifiChip;
@@ -127,4 +129,85 @@
      *         |WifiStatusCode.ERROR_INVALID_ARGS|
      */
     setMultiStaUseCase(MultiStaUseCase useCase) generates (WifiStatus status);
+
+    /**
+     * Create bridged IWifiApIface.
+     *
+     * Depending on the mode the chip is configured in, the interface creation
+     * may fail (code: |ERROR_NOT_AVAILABLE|) if we've already reached the maximum
+     * allowed (specified in |ChipIfaceCombination|) number of ifaces of the AP
+     * type.
+     *
+     * @return status WifiStatus of the operation.
+     *         Possible status codes:
+     *         |WifiStatusCode.SUCCESS|,
+     *         |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|,
+     *         |WifiStatusCode.ERROR_NOT_SUPPORTED|,
+     *         |WifiStatusCode.ERROR_NOT_AVAILABLE|
+     * @return iface HIDL interface object representing the iface if
+     *         successful, null otherwise.
+     */
+    createBridgedApIface() generates (WifiStatus status, IWifiApIface iface);
+
+    /**
+     * Removes one of the instance on the AP Iface with the provided ifaceName and
+     * ifaceInstanceName.
+     *
+     * Use the API: removeApIface with brIfaceName in the V1_0::WifiChip.hal to remove bridge Iface.
+     *
+     * @param brIfaceName Name of the bridged AP iface.
+     * @param ifaceInstanceName Name of the instance. The empty instance is
+     * invalid.
+     * @return status WifiStatus of the operation.
+     *         Possible status codes:
+     *         |WifiStatusCode.SUCCESS|,
+     *         |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|,
+     *         |WifiStatusCode.ERROR_INVALID_ARGS|,
+     *         |WifiStatusCode.ERROR_NOT_AVAILABLE|
+     */
+    removeIfaceInstanceFromBridgedApIface(string brIfaceName, string ifaceInstanceName)
+        generates (WifiStatus status);
+
+    /**
+     * Representation of a Wi-Fi channel for Wi-Fi coex channel avoidance.
+     */
+    struct CoexUnsafeChannel {
+        /* The band of the channel */
+        WifiBand band;
+        /* The channel number */
+        uint32_t channel;
+        /** The power cap will be a maximum power value in dbm that is allowed to be transmitted by
+            the chip on this channel. A value of PowerCapConstant.NO_POWER_CAP means no limitation
+            on transmitted power is needed by the chip for this channel.
+        */
+        int32_t powerCapDbm;
+    };
+
+    enum PowerCapConstant : int32_t {
+        NO_POWER_CAP = 0x7FFFFFFF,
+    };
+
+    /**
+     * Invoked to indicate that the provided |CoexUnsafeChannels| should be avoided with the
+     * specified restrictions.
+     *
+     * Channel avoidance is a suggestion and should be done on a best-effort approach. If a provided
+     * channel is used, then the specified power cap should be applied.
+     *
+     * In addition, hard restrictions on the Wifi modes may be indicated by |IfaceType| bits
+     * (STA, AP, P2P, NAN, etc) in the |restrictions| bitfield. If a hard restriction is provided,
+     * then the channels should be completely avoided for the provided Wifi modes instead of by
+     * best-effort.
+     *
+     * @param unsafeChannels List of |CoexUnsafeChannels| to avoid.
+     * @param restrictions Bitset of |IfaceType| values indicating Wifi modes to completely avoid.
+     * @return status WifiStatus of the operation.
+     *         Possible status codes:
+     *         |WifiStatusCode.SUCCESS|,
+     *         |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|,
+     *         |WifiStatusCode.ERROR_INVALID_ARGS|,
+     */
+    setCoexUnsafeChannels(
+        vec<CoexUnsafeChannel> unsafeChannels, bitfield<IfaceType> restrictions)
+            generates (WifiStatus status);
 };
diff --git a/wifi/1.5/default/hidl_struct_util.cpp b/wifi/1.5/default/hidl_struct_util.cpp
index 83d06fe..8e2e647 100644
--- a/wifi/1.5/default/hidl_struct_util.cpp
+++ b/wifi/1.5/default/hidl_struct_util.cpp
@@ -2800,6 +2800,47 @@
     }
     CHECK(false);
 }
+
+bool convertHidlCoexUnsafeChannelToLegacy(
+    const IWifiChip::CoexUnsafeChannel& hidl_unsafe_channel,
+    legacy_hal::wifi_coex_unsafe_channel* legacy_unsafe_channel) {
+    if (!legacy_unsafe_channel) {
+        return false;
+    }
+    *legacy_unsafe_channel = {};
+    switch (hidl_unsafe_channel.band) {
+        case WifiBand::BAND_24GHZ:
+            legacy_unsafe_channel->band = legacy_hal::WLAN_MAC_2_4_BAND;
+            break;
+        case WifiBand::BAND_5GHZ:
+            legacy_unsafe_channel->band = legacy_hal::WLAN_MAC_5_0_BAND;
+            break;
+        default:
+            return false;
+    };
+    legacy_unsafe_channel->channel = hidl_unsafe_channel.channel;
+    legacy_unsafe_channel->power_cap_dbm = hidl_unsafe_channel.powerCapDbm;
+    return true;
+}
+
+bool convertHidlVectorOfCoexUnsafeChannelToLegacy(
+    const std::vector<IWifiChip::CoexUnsafeChannel>& hidl_unsafe_channels,
+    std::vector<legacy_hal::wifi_coex_unsafe_channel>* legacy_unsafe_channels) {
+    if (!legacy_unsafe_channels) {
+        return false;
+    }
+    *legacy_unsafe_channels = {};
+    for (const auto& hidl_unsafe_channel : hidl_unsafe_channels) {
+        legacy_hal::wifi_coex_unsafe_channel legacy_unsafe_channel;
+        if (!hidl_struct_util::convertHidlCoexUnsafeChannelToLegacy(
+                hidl_unsafe_channel, &legacy_unsafe_channel)) {
+            return false;
+        }
+        legacy_unsafe_channels->push_back(legacy_unsafe_channel);
+    }
+    return true;
+}
+
 }  // namespace hidl_struct_util
 }  // namespace implementation
 }  // namespace V1_5
diff --git a/wifi/1.5/default/hidl_struct_util.h b/wifi/1.5/default/hidl_struct_util.h
index 49d8a12..feb47ef 100644
--- a/wifi/1.5/default/hidl_struct_util.h
+++ b/wifi/1.5/default/hidl_struct_util.h
@@ -71,6 +71,12 @@
     IfaceType hidl_interface_type);
 legacy_hal::wifi_multi_sta_use_case convertHidlMultiStaUseCaseToLegacy(
     IWifiChip::MultiStaUseCase use_case);
+bool convertHidlCoexUnsafeChannelToLegacy(
+    const IWifiChip::CoexUnsafeChannel& hidl_unsafe_channel,
+    legacy_hal::wifi_coex_unsafe_channel* legacy_unsafe_channel);
+bool convertHidlVectorOfCoexUnsafeChannelToLegacy(
+    const std::vector<IWifiChip::CoexUnsafeChannel>& hidl_unsafe_channels,
+    std::vector<legacy_hal::wifi_coex_unsafe_channel>* legacy_unsafe_channels);
 
 // STA iface conversion methods.
 bool convertLegacyFeaturesToHidlStaCapabilities(
diff --git a/wifi/1.5/default/service.cpp b/wifi/1.5/default/service.cpp
index 8539a37..23e2b47 100644
--- a/wifi/1.5/default/service.cpp
+++ b/wifi/1.5/default/service.cpp
@@ -17,6 +17,7 @@
 #include <android-base/logging.h>
 #include <hidl/HidlLazyUtils.h>
 #include <hidl/HidlTransportSupport.h>
+#include <signal.h>
 #include <utils/Looper.h>
 #include <utils/StrongPointer.h>
 
@@ -45,6 +46,7 @@
 #endif
 
 int main(int /*argc*/, char** argv) {
+    signal(SIGPIPE, SIG_IGN);
     android::base::InitLogging(
         argv, android::base::LogdLogger(android::base::SYSTEM));
     LOG(INFO) << "Wifi Hal is booting up...";
diff --git a/wifi/1.5/default/wifi_ap_iface.cpp b/wifi/1.5/default/wifi_ap_iface.cpp
index 04e382a..d98aa45 100644
--- a/wifi/1.5/default/wifi_ap_iface.cpp
+++ b/wifi/1.5/default/wifi_ap_iface.cpp
@@ -29,10 +29,11 @@
 using hidl_return_util::validateAndCall;
 
 WifiApIface::WifiApIface(
-    const std::string& ifname,
+    const std::string& ifname, const std::vector<std::string>& instances,
     const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
     const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util)
     : ifname_(ifname),
+      instances_(instances),
       legacy_hal_(legacy_hal),
       iface_util_(iface_util),
       is_valid_(true) {}
@@ -81,6 +82,14 @@
     getFactoryMacAddress_cb hidl_status_cb) {
     return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
                            &WifiApIface::getFactoryMacAddressInternal,
+                           hidl_status_cb,
+                           instances_.size() > 0 ? instances_[0] : ifname_);
+}
+
+Return<void> WifiApIface::resetToFactoryMacAddress(
+    resetToFactoryMacAddress_cb hidl_status_cb) {
+    return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+                           &WifiApIface::resetToFactoryMacAddressInternal,
                            hidl_status_cb);
 }
 
@@ -94,8 +103,8 @@
 
 WifiStatus WifiApIface::setCountryCodeInternal(
     const std::array<int8_t, 2>& code) {
-    legacy_hal::wifi_error legacy_status =
-        legacy_hal_.lock()->setCountryCode(ifname_, code);
+    legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->setCountryCode(
+        instances_.size() > 0 ? instances_[0] : ifname_, code);
     return createWifiStatusFromLegacyError(legacy_status);
 }
 
@@ -107,13 +116,30 @@
     std::vector<uint32_t> valid_frequencies;
     std::tie(legacy_status, valid_frequencies) =
         legacy_hal_.lock()->getValidFrequenciesForBand(
-            ifname_, hidl_struct_util::convertHidlWifiBandToLegacy(band));
+            instances_.size() > 0 ? instances_[0] : ifname_,
+            hidl_struct_util::convertHidlWifiBandToLegacy(band));
     return {createWifiStatusFromLegacyError(legacy_status), valid_frequencies};
 }
 
 WifiStatus WifiApIface::setMacAddressInternal(
     const std::array<uint8_t, 6>& mac) {
-    bool status = iface_util_.lock()->setMacAddress(ifname_, mac);
+    bool status;
+    // Support random MAC up to 2 interfaces
+    if (instances_.size() == 2) {
+        int rbyte = 1;
+        for (auto const& intf : instances_) {
+            std::array<uint8_t, 6> rmac = mac;
+            // reverse the bits to avoid clision
+            rmac[rbyte] = 0xff - rmac[rbyte];
+            status = iface_util_.lock()->setMacAddress(intf, rmac);
+            if (!status) {
+                LOG(INFO) << "Failed to set random mac address on " << intf;
+            }
+            rbyte++;
+        }
+    } else {
+        status = iface_util_.lock()->setMacAddress(ifname_, mac);
+    }
     if (!status) {
         return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
     }
@@ -121,15 +147,37 @@
 }
 
 std::pair<WifiStatus, std::array<uint8_t, 6>>
-WifiApIface::getFactoryMacAddressInternal() {
+WifiApIface::getFactoryMacAddressInternal(const std::string& ifaceName) {
     std::array<uint8_t, 6> mac =
-        iface_util_.lock()->getFactoryMacAddress(ifname_);
+        iface_util_.lock()->getFactoryMacAddress(ifaceName);
     if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 &&
         mac[4] == 0 && mac[5] == 0) {
         return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), mac};
     }
     return {createWifiStatus(WifiStatusCode::SUCCESS), mac};
 }
+
+WifiStatus WifiApIface::resetToFactoryMacAddressInternal() {
+    std::pair<WifiStatus, std::array<uint8_t, 6>> getMacResult;
+    if (instances_.size() == 2) {
+        for (auto const& intf : instances_) {
+            getMacResult = getFactoryMacAddressInternal(intf);
+            LOG(DEBUG) << "Reset MAC to factory MAC on " << intf;
+            if (getMacResult.first.code != WifiStatusCode::SUCCESS ||
+                !iface_util_.lock()->setMacAddress(intf, getMacResult.second)) {
+                return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+            }
+        }
+    } else {
+        getMacResult = getFactoryMacAddressInternal(ifname_);
+        LOG(DEBUG) << "Reset MAC to factory MAC on " << ifname_;
+        if (getMacResult.first.code != WifiStatusCode::SUCCESS ||
+            !iface_util_.lock()->setMacAddress(ifname_, getMacResult.second)) {
+            return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+        }
+    }
+    return createWifiStatus(WifiStatusCode::SUCCESS);
+}
 }  // namespace implementation
 }  // namespace V1_5
 }  // namespace wifi
diff --git a/wifi/1.5/default/wifi_ap_iface.h b/wifi/1.5/default/wifi_ap_iface.h
index 48b444a..02fb2d8 100644
--- a/wifi/1.5/default/wifi_ap_iface.h
+++ b/wifi/1.5/default/wifi_ap_iface.h
@@ -18,7 +18,7 @@
 #define WIFI_AP_IFACE_H_
 
 #include <android-base/macros.h>
-#include <android/hardware/wifi/1.4/IWifiApIface.h>
+#include <android/hardware/wifi/1.5/IWifiApIface.h>
 
 #include "wifi_iface_util.h"
 #include "wifi_legacy_hal.h"
@@ -33,9 +33,10 @@
 /**
  * HIDL interface object used to control a AP Iface instance.
  */
-class WifiApIface : public V1_4::IWifiApIface {
+class WifiApIface : public V1_5::IWifiApIface {
    public:
     WifiApIface(const std::string& ifname,
+                const std::vector<std::string>& instances,
                 const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
                 const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util);
     // Refer to |WifiChip::invalidate()|.
@@ -55,6 +56,8 @@
                                setMacAddress_cb hidl_status_cb) override;
     Return<void> getFactoryMacAddress(
         getFactoryMacAddress_cb hidl_status_cb) override;
+    Return<void> resetToFactoryMacAddress(
+        resetToFactoryMacAddress_cb hidl_status_cb) override;
 
    private:
     // Corresponding worker functions for the HIDL methods.
@@ -64,10 +67,12 @@
     std::pair<WifiStatus, std::vector<WifiChannelInMhz>>
     getValidFrequenciesForBandInternal(V1_0::WifiBand band);
     WifiStatus setMacAddressInternal(const std::array<uint8_t, 6>& mac);
-    std::pair<WifiStatus, std::array<uint8_t, 6>>
-    getFactoryMacAddressInternal();
+    std::pair<WifiStatus, std::array<uint8_t, 6>> getFactoryMacAddressInternal(
+        const std::string& ifaceName);
+    WifiStatus resetToFactoryMacAddressInternal();
 
     std::string ifname_;
+    std::vector<std::string> instances_;
     std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
     std::weak_ptr<iface_util::WifiIfaceUtil> iface_util_;
     bool is_valid_;
diff --git a/wifi/1.5/default/wifi_chip.cpp b/wifi/1.5/default/wifi_chip.cpp
index 1c238c8..1b0353b 100644
--- a/wifi/1.5/default/wifi_chip.cpp
+++ b/wifi/1.5/default/wifi_chip.cpp
@@ -19,6 +19,7 @@
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <cutils/properties.h>
+#include <net/if.h>
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
 
@@ -44,6 +45,7 @@
 constexpr char kActiveWlanIfaceNameProperty[] = "wifi.active.interface";
 constexpr char kNoActiveWlanIfaceNamePropertyValue[] = "";
 constexpr unsigned kMaxWlanIfaces = 5;
+constexpr char kApBridgeIfacePrefix[] = "ap_br_";
 
 template <typename Iface>
 void invalidateAndClear(std::vector<sp<Iface>>& ifaces, sp<Iface> iface) {
@@ -101,24 +103,36 @@
     return "wlan" + std::to_string(idx);
 }
 
-// Returns the dedicated iface name if one is defined.
-std::string getApIfaceName() {
+// Returns the dedicated iface name if defined.
+// Returns two ifaces in bridged mode.
+std::vector<std::string> getPredefinedApIfaceNames(bool is_bridged) {
+    std::vector<std::string> ifnames;
     std::array<char, PROPERTY_VALUE_MAX> buffer;
+    buffer.fill(0);
     if (property_get("ro.vendor.wifi.sap.interface", buffer.data(), nullptr) ==
         0) {
-        return {};
+        return ifnames;
     }
-    return buffer.data();
+    ifnames.push_back(buffer.data());
+    if (is_bridged) {
+        buffer.fill(0);
+        if (property_get("ro.vendor.wifi.sap.concurrent.interface",
+                         buffer.data(), nullptr) == 0) {
+            return ifnames;
+        }
+        ifnames.push_back(buffer.data());
+    }
+    return ifnames;
 }
 
-std::string getP2pIfaceName() {
+std::string getPredefinedP2pIfaceName() {
     std::array<char, PROPERTY_VALUE_MAX> buffer;
     property_get("wifi.direct.interface", buffer.data(), "p2p0");
     return buffer.data();
 }
 
 // Returns the dedicated iface name if one is defined.
-std::string getNanIfaceName() {
+std::string getPredefinedNanIfaceName() {
     std::array<char, PROPERTY_VALUE_MAX> buffer;
     if (property_get("wifi.aware.interface", buffer.data(), nullptr) == 0) {
         return {};
@@ -434,6 +448,13 @@
                            &WifiChip::createApIfaceInternal, hidl_status_cb);
 }
 
+Return<void> WifiChip::createBridgedApIface(
+    createBridgedApIface_cb hidl_status_cb) {
+    return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+                           &WifiChip::createBridgedApIfaceInternal,
+                           hidl_status_cb);
+}
+
 Return<void> WifiChip::getApIfaceNames(getApIfaceNames_cb hidl_status_cb) {
     return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
                            &WifiChip::getApIfaceNamesInternal, hidl_status_cb);
@@ -453,6 +474,15 @@
                            ifname);
 }
 
+Return<void> WifiChip::removeIfaceInstanceFromBridgedApIface(
+    const hidl_string& ifname, const hidl_string& ifInstanceName,
+    removeIfaceInstanceFromBridgedApIface_cb hidl_status_cb) {
+    return validateAndCall(
+        this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+        &WifiChip::removeIfaceInstanceFromBridgedApIfaceInternal,
+        hidl_status_cb, ifname, ifInstanceName);
+}
+
 Return<void> WifiChip::createNanIface(createNanIface_cb hidl_status_cb) {
     return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
                            &WifiChip::createNanIfaceInternal, hidl_status_cb);
@@ -692,7 +722,17 @@
                            hidl_status_cb, use_case);
 }
 
+Return<void> WifiChip::setCoexUnsafeChannels(
+    const hidl_vec<CoexUnsafeChannel>& unsafeChannels,
+    hidl_bitfield<IfaceType> restrictions,
+    setCoexUnsafeChannels_cb hidl_status_cb) {
+    return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+                           &WifiChip::setCoexUnsafeChannelsInternal,
+                           hidl_status_cb, unsafeChannels, restrictions);
+}
+
 void WifiChip::invalidateAndRemoveAllIfaces() {
+    invalidateAndClearBridgedApAll();
     invalidateAndClearAll(ap_ifaces_);
     invalidateAndClearAll(nan_ifaces_);
     invalidateAndClearAll(p2p_ifaces_);
@@ -861,21 +901,28 @@
     return {createWifiStatus(WifiStatusCode::SUCCESS), firmware_dump};
 }
 
-std::pair<WifiStatus, sp<IWifiApIface>> WifiChip::createApIfaceInternal() {
-    if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::AP)) {
-        return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
-    }
-    std::string ifname = allocateApIfaceName();
-    legacy_hal::wifi_error legacy_status =
-        legacy_hal_.lock()->createVirtualInterface(
-            ifname,
-            hidl_struct_util::convertHidlIfaceTypeToLegacy(IfaceType::AP));
+WifiStatus WifiChip::createVirtualApInterface(const std::string& apVirtIf) {
+    legacy_hal::wifi_error legacy_status;
+    legacy_status = legacy_hal_.lock()->createVirtualInterface(
+        apVirtIf,
+        hidl_struct_util::convertHidlIfaceTypeToLegacy(IfaceType::AP));
     if (legacy_status != legacy_hal::WIFI_SUCCESS) {
-        LOG(ERROR) << "Failed to add interface: " << ifname << " "
+        LOG(ERROR) << "Failed to add interface: " << apVirtIf << " "
                    << legacyErrorToString(legacy_status);
-        return {createWifiStatusFromLegacyError(legacy_status), {}};
+        return createWifiStatusFromLegacyError(legacy_status);
     }
-    sp<WifiApIface> iface = new WifiApIface(ifname, legacy_hal_, iface_util_);
+    return createWifiStatus(WifiStatusCode::SUCCESS);
+}
+
+sp<WifiApIface> WifiChip::newWifiApIface(std::string& ifname) {
+    std::vector<std::string> ap_instances;
+    for (auto const& it : br_ifaces_ap_instances_) {
+        if (it.first == ifname) {
+            ap_instances = it.second;
+        }
+    }
+    sp<WifiApIface> iface =
+        new WifiApIface(ifname, ap_instances, legacy_hal_, iface_util_);
     ap_ifaces_.push_back(iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceAdded(IfaceType::AP, ifname).isOk()) {
@@ -883,6 +930,61 @@
         }
     }
     setActiveWlanIfaceNameProperty(getFirstActiveWlanIfaceName());
+    return iface;
+}
+
+std::pair<WifiStatus, sp<V1_5::IWifiApIface>>
+WifiChip::createApIfaceInternal() {
+    if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::AP)) {
+        return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
+    }
+    std::string ifname = allocateApIfaceName();
+    WifiStatus status = createVirtualApInterface(ifname);
+    if (status.code != WifiStatusCode::SUCCESS) {
+        return {status, {}};
+    }
+    sp<WifiApIface> iface = newWifiApIface(ifname);
+    return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
+}
+
+std::pair<WifiStatus, sp<V1_5::IWifiApIface>>
+WifiChip::createBridgedApIfaceInternal() {
+    if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::AP)) {
+        return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
+    }
+    std::vector<std::string> ap_instances = allocateBridgedApInstanceNames();
+    if (ap_instances.size() < 2) {
+        LOG(ERROR) << "Fail to allocate two instances";
+        return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
+    }
+    std::string br_ifname = kApBridgeIfacePrefix + ap_instances[0];
+    for (int i = 0; i < 2; i++) {
+        WifiStatus status = createVirtualApInterface(ap_instances[i]);
+        if (status.code != WifiStatusCode::SUCCESS) {
+            if (i != 0) {  // The failure happened when creating second virtual
+                           // iface.
+                legacy_hal_.lock()->deleteVirtualInterface(
+                    ap_instances.front());  // Remove the first virtual iface.
+            }
+            return {status, {}};
+        }
+    }
+    br_ifaces_ap_instances_[br_ifname] = ap_instances;
+    if (!iface_util_.lock()->createBridge(br_ifname)) {
+        LOG(ERROR) << "Failed createBridge - br_name=" << br_ifname.c_str();
+        invalidateAndClearBridgedAp(br_ifname);
+        return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
+    }
+    for (auto const& instance : ap_instances) {
+        // Bind ap instance interface to AP bridge
+        if (!iface_util_.lock()->addIfaceToBridge(br_ifname, instance)) {
+            LOG(ERROR) << "Failed add if to Bridge - if_name="
+                       << instance.c_str();
+            invalidateAndClearBridgedAp(br_ifname);
+            return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
+        }
+    }
+    sp<WifiApIface> iface = newWifiApIface(br_ifname);
     return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
 }
 
@@ -894,7 +996,7 @@
     return {createWifiStatus(WifiStatusCode::SUCCESS), getNames(ap_ifaces_)};
 }
 
-std::pair<WifiStatus, sp<IWifiApIface>> WifiChip::getApIfaceInternal(
+std::pair<WifiStatus, sp<V1_5::IWifiApIface>> WifiChip::getApIfaceInternal(
     const std::string& ifname) {
     const auto iface = findUsingName(ap_ifaces_, ifname);
     if (!iface.get()) {
@@ -913,12 +1015,8 @@
     // nan/rtt objects over AP iface. But, there is no harm to do it
     // here and not make that assumption all over the place.
     invalidateAndRemoveDependencies(ifname);
-    legacy_hal::wifi_error legacy_status =
-        legacy_hal_.lock()->deleteVirtualInterface(ifname);
-    if (legacy_status != legacy_hal::WIFI_SUCCESS) {
-        LOG(ERROR) << "Failed to remove interface: " << ifname << " "
-                   << legacyErrorToString(legacy_status);
-    }
+    // Clear the bridge interface and the iface instance.
+    invalidateAndClearBridgedAp(ifname);
     invalidateAndClear(ap_ifaces_, iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceRemoved(IfaceType::AP, ifname).isOk()) {
@@ -929,13 +1027,49 @@
     return createWifiStatus(WifiStatusCode::SUCCESS);
 }
 
+WifiStatus WifiChip::removeIfaceInstanceFromBridgedApIfaceInternal(
+    const std::string& ifname, const std::string& ifInstanceName) {
+    legacy_hal::wifi_error legacy_status;
+    const auto iface = findUsingName(ap_ifaces_, ifname);
+    if (!iface.get() || !ifInstanceName.empty()) {
+        return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+    }
+    // Requires to remove one of the instance in bridge mode
+    for (auto const& it : br_ifaces_ap_instances_) {
+        if (it.first == ifname) {
+            for (auto const& iface : it.second) {
+                if (iface == ifInstanceName) {
+                    if (!iface_util_.lock()->removeIfaceFromBridge(it.first,
+                                                                   iface)) {
+                        LOG(ERROR) << "Failed to remove interface: " << iface
+                                   << " from " << ifname << ", error: "
+                                   << legacyErrorToString(legacy_status);
+                        return createWifiStatus(
+                            WifiStatusCode::ERROR_NOT_AVAILABLE);
+                    }
+                    legacy_status =
+                        legacy_hal_.lock()->deleteVirtualInterface(iface);
+                    if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+                        LOG(ERROR) << "Failed to del interface: " << iface
+                                   << " " << legacyErrorToString(legacy_status);
+                        return createWifiStatusFromLegacyError(legacy_status);
+                    }
+                }
+            }
+            break;
+        }
+    }
+    br_ifaces_ap_instances_.erase(ifInstanceName);
+    return createWifiStatus(WifiStatusCode::SUCCESS);
+}
+
 std::pair<WifiStatus, sp<V1_4::IWifiNanIface>>
 WifiChip::createNanIfaceInternal() {
     if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::NAN)) {
         return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
     }
     bool is_dedicated_iface = true;
-    std::string ifname = getNanIfaceName();
+    std::string ifname = getPredefinedNanIfaceName();
     if (ifname.empty() || !iface_util_.lock()->ifNameToIndex(ifname)) {
         // Use the first shared STA iface (wlan0) if a dedicated aware iface is
         // not defined.
@@ -988,7 +1122,7 @@
     if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::P2P)) {
         return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
     }
-    std::string ifname = getP2pIfaceName();
+    std::string ifname = getPredefinedP2pIfaceName();
     sp<WifiP2pIface> iface = new WifiP2pIface(ifname, legacy_hal_);
     p2p_ifaces_.push_back(iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
@@ -1322,6 +1456,18 @@
     return createWifiStatusFromLegacyError(legacy_status);
 }
 
+WifiStatus WifiChip::setCoexUnsafeChannelsInternal(
+    std::vector<CoexUnsafeChannel> unsafe_channels, uint32_t restrictions) {
+    std::vector<legacy_hal::wifi_coex_unsafe_channel> legacy_unsafe_channels;
+    if (!hidl_struct_util::convertHidlVectorOfCoexUnsafeChannelToLegacy(
+            unsafe_channels, &legacy_unsafe_channels)) {
+        return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+    }
+    auto legacy_status = legacy_hal_.lock()->setCoexUnsafeChannels(
+        legacy_unsafe_channels, restrictions);
+    return createWifiStatusFromLegacyError(legacy_status);
+}
+
 WifiStatus WifiChip::handleChipConfiguration(
     /* NONNULL */ std::unique_lock<std::recursive_mutex>* lock,
     ChipModeId mode_id) {
@@ -1607,7 +1753,7 @@
 //    ChipIfaceCombination.
 // b) Check if the requested iface type can be added to the current mode.
 bool WifiChip::canCurrentModeSupportIfaceOfType(IfaceType requested_type) {
-    // Check if we can support atleast 1 iface of type.
+    // Check if we can support at least 1 iface of type.
     std::map<IfaceType, size_t> req_iface_combo;
     req_iface_combo[requested_type] = 1;
     return canCurrentModeSupportIfaceCombo(req_iface_combo);
@@ -1623,17 +1769,17 @@
 }
 
 bool WifiChip::isStaApConcurrencyAllowedInCurrentMode() {
-    // Check if we can support atleast 1 STA & 1 AP concurrently.
+    // Check if we can support at least 1 STA & 1 AP concurrently.
     std::map<IfaceType, size_t> req_iface_combo;
     req_iface_combo[IfaceType::AP] = 1;
     req_iface_combo[IfaceType::STA] = 1;
     return canCurrentModeSupportIfaceCombo(req_iface_combo);
 }
 
-bool WifiChip::isDualApAllowedInCurrentMode() {
-    // Check if we can support atleast 1 STA & 1 AP concurrently.
+bool WifiChip::isDualStaConcurrencyAllowedInCurrentMode() {
+    // Check if we can support at least 2 STA concurrently.
     std::map<IfaceType, size_t> req_iface_combo;
-    req_iface_combo[IfaceType::AP] = 2;
+    req_iface_combo[IfaceType::STA] = 2;
     return canCurrentModeSupportIfaceCombo(req_iface_combo);
 }
 
@@ -1653,6 +1799,7 @@
                                                uint32_t start_idx) {
     for (unsigned idx = start_idx; idx < kMaxWlanIfaces; idx++) {
         const auto ifname = getWlanIfaceNameWithType(type, idx);
+        if (findUsingNameFromBridgedApInstances(ifname)) continue;
         if (findUsingName(ap_ifaces_, ifname)) continue;
         if (findUsingName(sta_ifaces_, ifname)) continue;
         return ifname;
@@ -1662,19 +1809,46 @@
     return {};
 }
 
+uint32_t WifiChip::startIdxOfApIface() {
+    if (isDualStaConcurrencyAllowedInCurrentMode()) {
+        // When the HAL support dual STAs, AP should start with idx 2.
+        return 2;
+    } else if (isStaApConcurrencyAllowedInCurrentMode()) {
+        //  When the HAL support STA + AP but it doesn't support dual STAs.
+        //  AP should start with idx 1.
+        return 1;
+    }
+    // No concurrency support.
+    return 0;
+}
+
 // AP iface names start with idx 1 for modes supporting
 // concurrent STA and not dual AP, else start with idx 0.
 std::string WifiChip::allocateApIfaceName() {
     // Check if we have a dedicated iface for AP.
-    std::string ifname = getApIfaceName();
-    if (!ifname.empty()) {
-        return ifname;
+    std::vector<std::string> ifnames = getPredefinedApIfaceNames(false);
+    if (!ifnames.empty()) {
+        return ifnames[0];
     }
-    return allocateApOrStaIfaceName(IfaceType::AP,
-                                    (isStaApConcurrencyAllowedInCurrentMode() &&
-                                     !isDualApAllowedInCurrentMode())
-                                        ? 1
-                                        : 0);
+    return allocateApOrStaIfaceName(IfaceType::AP, startIdxOfApIface());
+}
+
+std::vector<std::string> WifiChip::allocateBridgedApInstanceNames() {
+    // Check if we have a dedicated iface for AP.
+    std::vector<std::string> instances = getPredefinedApIfaceNames(true);
+    if (instances.size() == 2) {
+        return instances;
+    } else {
+        int num_ifaces_need_to_allocate = 2 - instances.size();
+        for (int i = 0; i < num_ifaces_need_to_allocate; i++) {
+            std::string instance_name =
+                allocateApOrStaIfaceName(IfaceType::AP, startIdxOfApIface());
+            if (!instance_name.empty()) {
+                instances.push_back(instance_name);
+            }
+        }
+    }
+    return instances;
 }
 
 // STA iface names start with idx 0.
@@ -1727,6 +1901,48 @@
     return getWlanIfaceName(idx);
 }
 
+void WifiChip::invalidateAndClearBridgedApAll() {
+    for (auto const& it : br_ifaces_ap_instances_) {
+        for (auto const& iface : it.second) {
+            iface_util_.lock()->removeIfaceFromBridge(it.first, iface);
+            legacy_hal_.lock()->deleteVirtualInterface(iface);
+        }
+        iface_util_.lock()->deleteBridge(it.first);
+    }
+    br_ifaces_ap_instances_.clear();
+}
+
+void WifiChip::invalidateAndClearBridgedAp(const std::string& br_name) {
+    if (br_name.empty()) return;
+    // delete managed interfaces
+    for (auto const& it : br_ifaces_ap_instances_) {
+        if (it.first == br_name) {
+            for (auto const& iface : it.second) {
+                iface_util_.lock()->removeIfaceFromBridge(br_name, iface);
+                legacy_hal_.lock()->deleteVirtualInterface(iface);
+            }
+            iface_util_.lock()->deleteBridge(br_name);
+            br_ifaces_ap_instances_.erase(br_name);
+            break;
+        }
+    }
+    return;
+}
+
+bool WifiChip::findUsingNameFromBridgedApInstances(const std::string& name) {
+    for (auto const& it : br_ifaces_ap_instances_) {
+        if (it.first == name) {
+            return true;
+        }
+        for (auto const& iface : it.second) {
+            if (iface == name) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 }  // namespace implementation
 }  // namespace V1_5
 }  // namespace wifi
diff --git a/wifi/1.5/default/wifi_chip.h b/wifi/1.5/default/wifi_chip.h
index 693d480..95c122d 100644
--- a/wifi/1.5/default/wifi_chip.h
+++ b/wifi/1.5/default/wifi_chip.h
@@ -94,11 +94,16 @@
     Return<void> requestFirmwareDebugDump(
         requestFirmwareDebugDump_cb hidl_status_cb) override;
     Return<void> createApIface(createApIface_cb hidl_status_cb) override;
+    Return<void> createBridgedApIface(
+        createBridgedApIface_cb hidl_status_cb) override;
     Return<void> getApIfaceNames(getApIfaceNames_cb hidl_status_cb) override;
     Return<void> getApIface(const hidl_string& ifname,
                             getApIface_cb hidl_status_cb) override;
     Return<void> removeApIface(const hidl_string& ifname,
                                removeApIface_cb hidl_status_cb) override;
+    Return<void> removeIfaceInstanceFromBridgedApIface(
+        const hidl_string& brIfaceName, const hidl_string& ifaceInstanceName,
+        removeIfaceInstanceFromBridgedApIface_cb hidl_status_cb) override;
     Return<void> createNanIface(createNanIface_cb hidl_status_cb) override;
     Return<void> getNanIfaceNames(getNanIfaceNames_cb hidl_status_cb) override;
     Return<void> getNanIface(const hidl_string& ifname,
@@ -169,6 +174,10 @@
     Return<void> setMultiStaUseCase(
         MultiStaUseCase use_case,
         setMultiStaUseCase_cb hidl_status_cb) override;
+    Return<void> setCoexUnsafeChannels(
+        const hidl_vec<CoexUnsafeChannel>& unsafe_channels,
+        hidl_bitfield<IfaceType> restrictions,
+        setCoexUnsafeChannels_cb hidl_status_cb) override;
 
    private:
     void invalidateAndRemoveAllIfaces();
@@ -192,11 +201,17 @@
     requestDriverDebugDumpInternal();
     std::pair<WifiStatus, std::vector<uint8_t>>
     requestFirmwareDebugDumpInternal();
-    std::pair<WifiStatus, sp<IWifiApIface>> createApIfaceInternal();
+    sp<WifiApIface> newWifiApIface(std::string& ifname);
+    WifiStatus createVirtualApInterface(const std::string& apVirtIf);
+    std::pair<WifiStatus, sp<V1_5::IWifiApIface>> createApIfaceInternal();
+    std::pair<WifiStatus, sp<V1_5::IWifiApIface>>
+    createBridgedApIfaceInternal();
     std::pair<WifiStatus, std::vector<hidl_string>> getApIfaceNamesInternal();
-    std::pair<WifiStatus, sp<IWifiApIface>> getApIfaceInternal(
+    std::pair<WifiStatus, sp<V1_5::IWifiApIface>> getApIfaceInternal(
         const std::string& ifname);
     WifiStatus removeApIfaceInternal(const std::string& ifname);
+    WifiStatus removeIfaceInstanceFromBridgedApIfaceInternal(
+        const std::string& brIfaceName, const std::string& ifInstanceName);
     std::pair<WifiStatus, sp<V1_4::IWifiNanIface>> createNanIfaceInternal();
     std::pair<WifiStatus, std::vector<hidl_string>> getNanIfaceNamesInternal();
     std::pair<WifiStatus, sp<V1_4::IWifiNanIface>> getNanIfaceInternal(
@@ -241,6 +256,8 @@
         const sp<V1_4::IWifiChipEventCallback>& event_callback);
     WifiStatus setMultiStaPrimaryConnectionInternal(const std::string& ifname);
     WifiStatus setMultiStaUseCaseInternal(MultiStaUseCase use_case);
+    WifiStatus setCoexUnsafeChannelsInternal(
+        std::vector<CoexUnsafeChannel> unsafe_channels, uint32_t restrictions);
 
     WifiStatus handleChipConfiguration(
         std::unique_lock<std::recursive_mutex>* lock, ChipModeId mode_id);
@@ -265,13 +282,18 @@
     bool canCurrentModeSupportIfaceOfType(IfaceType requested_type);
     bool isValidModeId(ChipModeId mode_id);
     bool isStaApConcurrencyAllowedInCurrentMode();
-    bool isDualApAllowedInCurrentMode();
+    bool isDualStaConcurrencyAllowedInCurrentMode();
+    uint32_t startIdxOfApIface();
     std::string getFirstActiveWlanIfaceName();
     std::string allocateApOrStaIfaceName(IfaceType type, uint32_t start_idx);
     std::string allocateApIfaceName();
+    std::vector<std::string> allocateBridgedApInstanceNames();
     std::string allocateStaIfaceName();
     bool writeRingbufferFilesInternal();
     std::string getWlanIfaceNameWithType(IfaceType type, unsigned idx);
+    void invalidateAndClearBridgedApAll();
+    void invalidateAndClearBridgedAp(const std::string& br_name);
+    bool findUsingNameFromBridgedApInstances(const std::string& name);
 
     ChipId chip_id_;
     std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
@@ -296,7 +318,7 @@
         event_cb_handler_;
 
     const std::function<void(const std::string&)> subsystemCallbackHandler_;
-
+    std::map<std::string, std::vector<std::string>> br_ifaces_ap_instances_;
     DISALLOW_COPY_AND_ASSIGN(WifiChip);
 };
 
diff --git a/wifi/1.5/default/wifi_feature_flags.cpp b/wifi/1.5/default/wifi_feature_flags.cpp
index 9f91bd7..124ba32 100644
--- a/wifi/1.5/default/wifi_feature_flags.cpp
+++ b/wifi/1.5/default/wifi_feature_flags.cpp
@@ -14,6 +14,11 @@
  * limitations under the License.
  */
 
+#include <string>
+
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+
 #include "wifi_feature_flags.h"
 
 namespace android {
@@ -131,7 +136,7 @@
 #define AP IfaceType::AP
 #define P2P IfaceType::P2P
 #define NAN IfaceType::NAN
-static const std::vector<IWifiChip::ChipMode> kChipModes{
+static const std::vector<IWifiChip::ChipMode> kChipModesPrimary{
     {kMainModeId,
      ChipIfaceCombination::make_vec({WIFI_HAL_INTERFACE_COMBINATIONS})},
 #ifdef WIFI_HAL_INTERFACE_COMBINATIONS_AP
@@ -146,6 +151,50 @@
                              {WIFI_HAL_INTERFACE_COMBINATIONS_SECONDARY_CHIP})},
 #endif
 };
+
+constexpr char kDebugPresetInterfaceCombinationIdxProperty[] =
+    "persist.vendor.debug.wifi.hal.preset_interface_combination_idx";
+// List of pre-defined interface combinations that can be enabled at runtime via
+// setting the property: "kDebugPresetInterfaceCombinationIdxProperty" to the
+// corresponding index value.
+static const std::vector<
+    std::pair<std::string, std::vector<IWifiChip::ChipMode>>>
+    kDebugChipModes{
+        // Legacy combination - No STA/AP concurrencies.
+        // 0 - (1 AP) or (1 STA + 1 of (P2P or NAN))
+        {"No STA/AP Concurrency",
+         {{kMainModeId,
+           ChipIfaceCombination::make_vec(
+               {{{{AP}, 1}}, {{{STA}, 1}, {{P2P, NAN}, 1}}})}}},
+
+        // STA + AP concurrency
+        // 1 - (1 STA + 1 AP) or (1 STA + 1 of (P2P or NAN))
+        {"STA + AP Concurrency",
+         {{kMainModeId,
+           ChipIfaceCombination::make_vec(
+               {{{{STA}, 1}, {{AP}, 1}}, {{{STA}, 1}, {{P2P, NAN}, 1}}})}}},
+
+        // STA + STA concurrency
+        // 2 - (1 STA + 1 AP) or (2 STA + 1 of (P2P or NAN))
+        {"Dual STA Concurrency",
+         {{kMainModeId,
+           ChipIfaceCombination::make_vec(
+               {{{{STA}, 1}, {{AP}, 1}}, {{{STA}, 2}, {{P2P, NAN}, 1}}})}}},
+
+        // AP + AP + STA concurrency
+        // 3 - (1 STA + 2 AP) or (1 STA + 1 of (P2P or NAN))
+        {"Dual AP Concurrency",
+         {{kMainModeId,
+           ChipIfaceCombination::make_vec(
+               {{{{STA}, 1}, {{AP}, 2}}, {{{STA}, 1}, {{P2P, NAN}, 1}}})}}},
+
+        // STA + STA concurrency and AP + AP + STA concurrency
+        // 4 - (1 STA + 2 AP) or (2 STA + 1 of (P2P or NAN))
+        {"Dual STA & Dual AP Concurrency",
+         {{kMainModeId,
+           ChipIfaceCombination::make_vec(
+               {{{{STA}, 1}, {{AP}, 2}}, {{{STA}, 2}, {{P2P, NAN}, 1}}})}}}};
+
 #undef STA
 #undef AP
 #undef P2P
@@ -161,9 +210,31 @@
 
 WifiFeatureFlags::WifiFeatureFlags() {}
 
+std::vector<IWifiChip::ChipMode> WifiFeatureFlags::getChipModesForPrimary() {
+    std::array<char, PROPERTY_VALUE_MAX> buffer;
+    auto res = property_get(kDebugPresetInterfaceCombinationIdxProperty,
+                            buffer.data(), nullptr);
+    // Debug propety not set, use the device preset interface combination.
+    if (res <= 0) return kChipModesPrimary;
+
+    // Debug propety set, use one of the debug preset interface combination.
+    unsigned long idx = std::stoul(buffer.data());
+    if (idx >= kDebugChipModes.size()) {
+        LOG(ERROR) << "Invalid index set in property: "
+                   << kDebugPresetInterfaceCombinationIdxProperty;
+        return kChipModesPrimary;
+    }
+    std::string name;
+    std::vector<IWifiChip::ChipMode> chip_modes;
+    std::tie(name, chip_modes) = kDebugChipModes[idx];
+    LOG(INFO) << "Using debug chip mode: <" << name << "> set via property: "
+              << kDebugPresetInterfaceCombinationIdxProperty;
+    return chip_modes;
+}
+
 std::vector<IWifiChip::ChipMode> WifiFeatureFlags::getChipModes(
     bool is_primary) {
-    return (is_primary) ? kChipModes : kChipModesSecondary;
+    return (is_primary) ? getChipModesForPrimary() : kChipModesSecondary;
 }
 
 }  // namespace feature_flags
diff --git a/wifi/1.5/default/wifi_feature_flags.h b/wifi/1.5/default/wifi_feature_flags.h
index cb68b8c..7d561fc 100644
--- a/wifi/1.5/default/wifi_feature_flags.h
+++ b/wifi/1.5/default/wifi_feature_flags.h
@@ -44,6 +44,9 @@
 
     virtual std::vector<V1_0::IWifiChip::ChipMode> getChipModes(
         bool is_primary);
+
+   private:
+    std::vector<V1_0::IWifiChip::ChipMode> getChipModesForPrimary();
 };
 
 }  // namespace feature_flags
diff --git a/wifi/1.5/default/wifi_iface_util.cpp b/wifi/1.5/default/wifi_iface_util.cpp
index 07175e4..2a0aef8 100644
--- a/wifi/1.5/default/wifi_iface_util.cpp
+++ b/wifi/1.5/default/wifi_iface_util.cpp
@@ -127,6 +127,36 @@
 unsigned WifiIfaceUtil::ifNameToIndex(const std::string& iface_name) {
     return if_nametoindex(iface_name.c_str());
 }
+
+bool WifiIfaceUtil::createBridge(const std::string& br_name) {
+    if (!iface_tool_.lock()->createBridge(br_name)) {
+        return false;
+    }
+
+    if (!iface_tool_.lock()->SetUpState(br_name.c_str(), true)) {
+        LOG(ERROR) << "bridge SetUpState(true) failed.";
+    }
+    return true;
+}
+
+bool WifiIfaceUtil::deleteBridge(const std::string& br_name) {
+    if (!iface_tool_.lock()->SetUpState(br_name.c_str(), false)) {
+        LOG(INFO) << "SetUpState(false) failed for bridge=" << br_name.c_str();
+    }
+
+    return iface_tool_.lock()->deleteBridge(br_name);
+}
+
+bool WifiIfaceUtil::addIfaceToBridge(const std::string& br_name,
+                                     const std::string& if_name) {
+    return iface_tool_.lock()->addIfaceToBridge(br_name, if_name);
+}
+
+bool WifiIfaceUtil::removeIfaceFromBridge(const std::string& br_name,
+                                          const std::string& if_name) {
+    return iface_tool_.lock()->removeIfaceFromBridge(br_name, if_name);
+}
+
 }  // namespace iface_util
 }  // namespace implementation
 }  // namespace V1_5
diff --git a/wifi/1.5/default/wifi_iface_util.h b/wifi/1.5/default/wifi_iface_util.h
index 7b812fa..296d182 100644
--- a/wifi/1.5/default/wifi_iface_util.h
+++ b/wifi/1.5/default/wifi_iface_util.h
@@ -59,6 +59,16 @@
     virtual bool setUpState(const std::string& iface_name, bool request_up);
     virtual unsigned ifNameToIndex(const std::string& iface_name);
 
+    virtual bool createBridge(const std::string& br_name);
+
+    virtual bool deleteBridge(const std::string& br_name);
+
+    virtual bool addIfaceToBridge(const std::string& br_name,
+                                  const std::string& if_name);
+
+    virtual bool removeIfaceFromBridge(const std::string& br_name,
+                                       const std::string& if_name);
+
    private:
     std::array<uint8_t, 6> createRandomMacAddress();
 
diff --git a/wifi/1.5/default/wifi_legacy_hal.cpp b/wifi/1.5/default/wifi_legacy_hal.cpp
index 76e718b..773dd9b 100644
--- a/wifi/1.5/default/wifi_legacy_hal.cpp
+++ b/wifi/1.5/default/wifi_legacy_hal.cpp
@@ -1521,6 +1521,14 @@
                                                           use_case);
 }
 
+wifi_error WifiLegacyHal::setCoexUnsafeChannels(
+    std::vector<wifi_coex_unsafe_channel> unsafe_channels,
+    uint32_t restrictions) {
+    return global_func_table_.wifi_set_coex_unsafe_channels(
+        global_handle_, unsafe_channels.size(), unsafe_channels.data(),
+        restrictions);
+}
+
 void WifiLegacyHal::invalidate() {
     global_handle_ = nullptr;
     iface_name_to_handle_.clear();
diff --git a/wifi/1.5/default/wifi_legacy_hal.h b/wifi/1.5/default/wifi_legacy_hal.h
index 555c540..6266cf6 100644
--- a/wifi/1.5/default/wifi_legacy_hal.h
+++ b/wifi/1.5/default/wifi_legacy_hal.h
@@ -386,6 +386,11 @@
     virtual wifi_error multiStaSetPrimaryConnection(const std::string& ifname);
     virtual wifi_error multiStaSetUseCase(wifi_multi_sta_use_case use_case);
 
+    // Coex functions.
+    virtual wifi_error setCoexUnsafeChannels(
+        std::vector<wifi_coex_unsafe_channel> unsafe_channels,
+        uint32_t restrictions);
+
    private:
     // Retrieve interface handles for all the available interfaces.
     wifi_error retrieveIfaceHandles();
diff --git a/wifi/1.5/default/wifi_legacy_hal_stubs.cpp b/wifi/1.5/default/wifi_legacy_hal_stubs.cpp
index 71d2ddf..b6c908b 100644
--- a/wifi/1.5/default/wifi_legacy_hal_stubs.cpp
+++ b/wifi/1.5/default/wifi_legacy_hal_stubs.cpp
@@ -149,6 +149,7 @@
     populateStubFor(&hal_fn->wifi_get_chip_feature_set);
     populateStubFor(&hal_fn->wifi_multi_sta_set_primary_connection);
     populateStubFor(&hal_fn->wifi_multi_sta_set_use_case);
+    populateStubFor(&hal_fn->wifi_set_coex_unsafe_channels);
 
     return true;
 }
diff --git a/wifi/1.5/default/wifi_sta_iface.cpp b/wifi/1.5/default/wifi_sta_iface.cpp
index f3dcfc5..82bfcf1 100644
--- a/wifi/1.5/default/wifi_sta_iface.cpp
+++ b/wifi/1.5/default/wifi_sta_iface.cpp
@@ -648,6 +648,10 @@
 WifiStaIface::getFactoryMacAddressInternal() {
     std::array<uint8_t, 6> mac =
         iface_util_.lock()->getFactoryMacAddress(ifname_);
+    if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 &&
+        mac[4] == 0 && mac[5] == 0) {
+        return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), mac};
+    }
     return {createWifiStatus(WifiStatusCode::SUCCESS), mac};
 }
 
diff --git a/wifi/1.5/vts/functional/Android.bp b/wifi/1.5/vts/functional/Android.bp
index eb595c9..2d8b412 100644
--- a/wifi/1.5/vts/functional/Android.bp
+++ b/wifi/1.5/vts/functional/Android.bp
@@ -60,3 +60,26 @@
         "vts",
     ],
 }
+
+// SoftAP-specific tests, similar to VtsHalWifiApV1_0TargetTest.
+cc_test {
+    name: "VtsHalWifiApV1_5TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "wifi_chip_hidl_ap_test.cpp",
+    ],
+    static_libs: [
+        "VtsHalWifiV1_0TargetTestUtil",
+        "android.hardware.wifi@1.0",
+        "android.hardware.wifi@1.1",
+        "android.hardware.wifi@1.2",
+        "android.hardware.wifi@1.3",
+        "android.hardware.wifi@1.4",
+        "android.hardware.wifi@1.5",
+        "libwifi-system-iface",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/wifi/1.5/vts/functional/wifi_chip_hidl_ap_test.cpp b/wifi/1.5/vts/functional/wifi_chip_hidl_ap_test.cpp
new file mode 100644
index 0000000..395d317
--- /dev/null
+++ b/wifi/1.5/vts/functional/wifi_chip_hidl_ap_test.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <android-base/logging.h>
+
+#undef NAN  // NAN is defined in bionic/libc/include/math.h:38
+
+#include <android/hardware/wifi/1.4/IWifiChipEventCallback.h>
+#include <android/hardware/wifi/1.5/IWifi.h>
+#include <android/hardware/wifi/1.5/IWifiApIface.h>
+#include <android/hardware/wifi/1.5/IWifiChip.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include "wifi_hidl_call_util.h"
+#include "wifi_hidl_test_utils.h"
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::wifi::V1_0::ChipModeId;
+using ::android::hardware::wifi::V1_0::IfaceType;
+using ::android::hardware::wifi::V1_0::IWifiIface;
+using ::android::hardware::wifi::V1_0::WifiDebugRingBufferStatus;
+using ::android::hardware::wifi::V1_0::WifiStatus;
+using ::android::hardware::wifi::V1_0::WifiStatusCode;
+using ::android::hardware::wifi::V1_4::IWifiChipEventCallback;
+using ::android::hardware::wifi::V1_5::IWifiApIface;
+using ::android::hardware::wifi::V1_5::IWifiChip;
+
+/**
+ * Fixture for IWifiChip tests that are conditioned on SoftAP support.
+ */
+class WifiChipHidlTest : public ::testing::TestWithParam<std::string> {
+   public:
+    virtual void SetUp() override {
+        // Make sure to start with a clean state
+        stopWifi(GetInstanceName());
+
+        wifi_chip_ = IWifiChip::castFrom(getWifiChip(GetInstanceName()));
+        ASSERT_NE(nullptr, wifi_chip_.get());
+    }
+
+    virtual void TearDown() override { stopWifi(GetInstanceName()); }
+
+   protected:
+    // Helper function to configure the Chip in one of the supported modes.
+    // Most of the non-mode-configuration-related methods require chip
+    // to be first configured.
+    ChipModeId configureChipForIfaceType(IfaceType type, bool expectSuccess) {
+        ChipModeId mode_id;
+        EXPECT_EQ(expectSuccess,
+                  configureChipToSupportIfaceType(wifi_chip_, type, &mode_id));
+        return mode_id;
+    }
+
+    WifiStatusCode createApIface(sp<IWifiApIface>* ap_iface) {
+        configureChipForIfaceType(IfaceType::AP, true);
+        const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createApIface);
+        *ap_iface = IWifiApIface::castFrom(status_and_iface.second);
+        return status_and_iface.first.code;
+    }
+
+    WifiStatusCode createBridgedApIface(sp<IWifiApIface>* ap_iface) {
+        configureChipForIfaceType(IfaceType::AP, true);
+        const auto& status_and_iface =
+            HIDL_INVOKE(wifi_chip_, createBridgedApIface);
+        *ap_iface = status_and_iface.second;
+        return status_and_iface.first.code;
+    }
+
+    sp<IWifiChip> wifi_chip_;
+
+   private:
+    std::string GetInstanceName() { return GetParam(); }
+};
+
+// TODO: b/173999527. Add test for bridged API.
+
+/*
+ * resetToFactoryMacAddress
+ */
+TEST_P(WifiChipHidlTest, resetToFactoryMacAddressTest) {
+    sp<IWifiApIface> wifi_ap_iface;
+    const auto& status_code = createApIface(&wifi_ap_iface);
+    if (status_code != WifiStatusCode::SUCCESS) {
+        EXPECT_EQ(WifiStatusCode::ERROR_NOT_SUPPORTED, status_code);
+    }
+    const auto& status = HIDL_INVOKE(wifi_ap_iface, resetToFactoryMacAddress);
+    EXPECT_EQ(WifiStatusCode::SUCCESS, status.code);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiChipHidlTest);
+INSTANTIATE_TEST_SUITE_P(
+    PerInstance, WifiChipHidlTest,
+    testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+        ::android::hardware::wifi::V1_5::IWifi::descriptor)),
+    android::hardware::PrintInstanceNameToString);
diff --git a/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp
index 1c3141e..b9c7b30 100644
--- a/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -22,7 +22,7 @@
 #include <VtsCoreUtil.h>
 #include <android/hardware/wifi/1.0/IWifi.h>
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantStaIface.h>
-#include <android/hardware/wifi/supplicant/1.1/ISupplicantStaIface.h>
+#include <android/hardware/wifi/supplicant/1.4/ISupplicantStaIface.h>
 
 #include "supplicant_hidl_call_util.h"
 #include "supplicant_hidl_test_utils.h"
@@ -74,7 +74,7 @@
         sta_iface_ = getSupplicantStaIface(supplicant_);
         ASSERT_NE(sta_iface_.get(), nullptr);
 
-        v1_1 = ::android::hardware::wifi::supplicant::V1_1::
+        v1_4 = ::android::hardware::wifi::supplicant::V1_4::
             ISupplicantStaIface::castFrom(sta_iface_);
 
         memcpy(mac_addr_.data(), kTestMacAddr, mac_addr_.size());
@@ -82,7 +82,7 @@
 
    protected:
     bool isP2pOn_ = false;
-    sp<::android::hardware::wifi::supplicant::V1_1::ISupplicantStaIface> v1_1 =
+    sp<::android::hardware::wifi::supplicant::V1_4::ISupplicantStaIface> v1_4 =
         nullptr;
     // ISupplicantStaIface object used for all tests in this fixture.
     sp<ISupplicantStaIface> sta_iface_;
@@ -181,8 +181,9 @@
  * RegisterCallback
  */
 TEST_P(SupplicantStaIfaceHidlTest, RegisterCallback) {
+    // This API is deprecated from v1.4 HAL.
     SupplicantStatusCode expectedCode =
-        (nullptr != v1_1) ? SupplicantStatusCode::FAILURE_UNKNOWN
+        (nullptr != v1_4) ? SupplicantStatusCode::FAILURE_UNKNOWN
                           : SupplicantStatusCode::SUCCESS;
     sta_iface_->registerCallback(new IfaceCallback(),
                                  [&](const SupplicantStatus& status) {
diff --git a/wifi/supplicant/1.1/vts/functional/Android.bp b/wifi/supplicant/1.1/vts/functional/Android.bp
index ee80628..d9ae3e0 100644
--- a/wifi/supplicant/1.1/vts/functional/Android.bp
+++ b/wifi/supplicant/1.1/vts/functional/Android.bp
@@ -48,6 +48,8 @@
         "android.hardware.wifi.supplicant@1.0",
         "android.hardware.wifi.supplicant@1.1",
         "android.hardware.wifi.supplicant@1.2",
+        "android.hardware.wifi.supplicant@1.3",
+        "android.hardware.wifi.supplicant@1.4",
         "android.hardware.wifi@1.0",
         "android.hardware.wifi@1.1",
         "libgmock",
diff --git a/wifi/supplicant/1.1/vts/functional/supplicant_hidl_test.cpp b/wifi/supplicant/1.1/vts/functional/supplicant_hidl_test.cpp
index 8d7ea54..a8e72b8 100644
--- a/wifi/supplicant/1.1/vts/functional/supplicant_hidl_test.cpp
+++ b/wifi/supplicant/1.1/vts/functional/supplicant_hidl_test.cpp
@@ -75,7 +75,7 @@
  * AddP2pInterface
  */
 TEST_P(SupplicantHidlTest, AddP2pInterface) {
-    if (isP2pOn_) return;
+    if (!isP2pOn_) return;
     ISupplicant::IfaceInfo iface_info;
     iface_info.name = getP2pIfaceName();
     iface_info.type = IfaceType::P2P;
@@ -115,7 +115,7 @@
  * RemoveP2pInterface
  */
 TEST_P(SupplicantHidlTest, RemoveP2pInterface) {
-    if (isP2pOn_) return;
+    if (!isP2pOn_) return;
     ISupplicant::IfaceInfo iface_info;
     iface_info.name = getP2pIfaceName();
     iface_info.type = IfaceType::P2P;
diff --git a/wifi/supplicant/1.1/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.1/vts/functional/supplicant_sta_iface_hidl_test.cpp
index 418def2..7e6e3e4 100644
--- a/wifi/supplicant/1.1/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.1/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -20,7 +20,7 @@
 #include <android/hardware/wifi/1.0/IWifi.h>
 #include <android/hardware/wifi/1.1/IWifi.h>
 #include <android/hardware/wifi/supplicant/1.1/ISupplicantStaIface.h>
-#include <android/hardware/wifi/supplicant/1.2/ISupplicantStaIface.h>
+#include <android/hardware/wifi/supplicant/1.4/ISupplicantStaIface.h>
 #include <gtest/gtest.h>
 #include <hidl/GtestPrinter.h>
 #include <hidl/ServiceManagement.h>
@@ -47,14 +47,14 @@
         sta_iface_ = getSupplicantStaIface_1_1(supplicant_);
         ASSERT_NE(sta_iface_.get(), nullptr);
 
-        v1_2 = ::android::hardware::wifi::supplicant::V1_2::
+        v1_4 = ::android::hardware::wifi::supplicant::V1_4::
             ISupplicantStaIface::castFrom(sta_iface_);
     }
 
    protected:
     // ISupplicantStaIface object used for all tests in this fixture.
     sp<ISupplicantStaIface> sta_iface_;
-    sp<::android::hardware::wifi::supplicant::V1_2::ISupplicantStaIface> v1_2 =
+    sp<::android::hardware::wifi::supplicant::V1_4::ISupplicantStaIface> v1_4 =
         nullptr;
 };
 
@@ -139,8 +139,9 @@
  * RegisterCallback_1_1
  */
 TEST_P(SupplicantStaIfaceHidlTest, RegisterCallback_1_1) {
+    // This API is deprecated from v1.4 HAL.
     SupplicantStatusCode expectedCode =
-        (nullptr != v1_2) ? SupplicantStatusCode::FAILURE_UNKNOWN
+        (nullptr != v1_4) ? SupplicantStatusCode::FAILURE_UNKNOWN
                           : SupplicantStatusCode::SUCCESS;
     sta_iface_->registerCallback_1_1(new IfaceCallback(),
                                      [&](const SupplicantStatus& status) {
diff --git a/wifi/supplicant/1.2/vts/functional/Android.bp b/wifi/supplicant/1.2/vts/functional/Android.bp
index c23585a..2592a2b 100644
--- a/wifi/supplicant/1.2/vts/functional/Android.bp
+++ b/wifi/supplicant/1.2/vts/functional/Android.bp
@@ -51,6 +51,7 @@
         "android.hardware.wifi.supplicant@1.1",
         "android.hardware.wifi.supplicant@1.2",
         "android.hardware.wifi.supplicant@1.3",
+        "android.hardware.wifi.supplicant@1.4",
         "android.hardware.wifi@1.0",
         "android.hardware.wifi@1.1",
         "libgmock",
diff --git a/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp
index 7799390..7f81206 100644
--- a/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -25,6 +25,7 @@
 #include <android/hardware/wifi/supplicant/1.2/types.h>
 #include <android/hardware/wifi/supplicant/1.3/ISupplicantStaIface.h>
 #include <android/hardware/wifi/supplicant/1.3/types.h>
+#include <android/hardware/wifi/supplicant/1.4/ISupplicantStaIface.h>
 #include <hidl/GtestPrinter.h>
 #include <hidl/HidlSupport.h>
 #include <hidl/ServiceManagement.h>
@@ -63,6 +64,8 @@
 
         v1_3 = ::android::hardware::wifi::supplicant::V1_3::
             ISupplicantStaIface::castFrom(sta_iface_);
+        v1_4 = ::android::hardware::wifi::supplicant::V1_4::
+            ISupplicantStaIface::castFrom(sta_iface_);
     }
 
     enum DppCallbackType {
@@ -106,6 +109,7 @@
     // ISupplicantStaIface object used for all tests in this fixture.
     sp<ISupplicantStaIface> sta_iface_;
     sp<::android::hardware::wifi::supplicant::V1_3::ISupplicantStaIface> v1_3;
+    sp<::android::hardware::wifi::supplicant::V1_4::ISupplicantStaIface> v1_4;
 
     bool isDppSupported() {
         uint32_t keyMgmtMask = 0;
@@ -266,8 +270,9 @@
  * RegisterCallback_1_2
  */
 TEST_P(SupplicantStaIfaceHidlTest, RegisterCallback_1_2) {
+    // This API is deprecated from v1.4 HAL.
     SupplicantStatusCode expectedCode =
-        (nullptr != v1_3) ? SupplicantStatusCode::FAILURE_UNKNOWN
+        (nullptr != v1_4) ? SupplicantStatusCode::FAILURE_UNKNOWN
                           : SupplicantStatusCode::SUCCESS;
     sta_iface_->registerCallback_1_2(new IfaceCallback(),
                                      [&](const SupplicantStatus& status) {
diff --git a/wifi/supplicant/1.4/ISupplicantStaIface.hal b/wifi/supplicant/1.4/ISupplicantStaIface.hal
index f9e4c9b..7441ba5 100644
--- a/wifi/supplicant/1.4/ISupplicantStaIface.hal
+++ b/wifi/supplicant/1.4/ISupplicantStaIface.hal
@@ -71,8 +71,7 @@
      *         |SupplicantStatusCode.FAILURE_UNKNOWN|,
      *         |SupplicantStatusCode.FAILURE_IFACE_INVALID|
      */
-    initiateVenueUrlAnqpQuery(MacAddress macAddress)
-        generates (SupplicantStatus status);
+    initiateVenueUrlAnqpQuery(MacAddress macAddress) generates (SupplicantStatus status);
 
     /**
      * Get wpa driver capabilities.
@@ -84,4 +83,55 @@
      */
     getWpaDriverCapabilities_1_4() generates (SupplicantStatus status,
         bitfield<WpaDriverCapabilitiesMask> driverCapabilitiesMask);
+
+    /**
+     * Generates DPP bootstrap information: Bootstrap ID, DPP URI and listen
+     * channel for responder mode.
+     *
+     * @param MacAddress MAC address of the interface for the DPP operation.
+     * @param deviceInfo Device specific information.
+     *        As per DPP Specification V1.0 section 5.2,
+     *        allowed Range of ASCII characters in deviceInfo - %x20-7E
+     *        semicolon is not allowed.
+     * @param DppCurve Elliptic curve cryptography type used to generate DPP
+     * public/private key pair.
+     * @return status of operation and bootstrap info.
+     *         Possible status codes:
+     *         |SupplicantStatusCode.SUCCESS|,
+     *         |SupplicantStatusCode.FAILURE_IFACE_INVALID|,
+     *         |SupplicantStatusCode.FAILURE_UNKNOWN|
+     *         |SupplicantStatusCode.FAILURE_UNSUPPORTED|
+     */
+    generateDppBootstrapInfoForResponder(MacAddress macAddress, string deviceInfo, DppCurve curve)
+        generates (SupplicantStatus status, DppResponderBootstrapInfo bootstrapInfo);
+
+    /**
+     * Start DPP in Enrollee-Responder mode.
+     * Framework must first call |generateDppBootstrapInfoForResponder| to generate
+     * the bootstrapping information: Bootstrap ID, DPP URI and the listen channel.
+     * Then call this API with derived listen channel to start listening for
+     * authentication request from Peer initiator.
+     *
+     * @param listenChannel DPP listen channel.
+     * @return status Status of the operation.
+     *         Possible status codes:
+     *         |SupplicantStatusCode.SUCCESS|,
+     *         |SupplicantStatusCode.FAILURE_UNKNOWN|,
+     *         |SupplicantStatusCode.FAILURE_IFACE_INVALID|
+     *         |SupplicantStatusCode.FAILURE_UNSUPPORTED|
+     */
+    startDppEnrolleeResponder(uint32_t listenChannel) generates (SupplicantStatus status);
+
+    /**
+     * Stop DPP Responder operation - Remove the bootstrap code and stop listening.
+     *
+     * @param ownBootstrapId Local device's URI ID obtained through
+     *        |generateDppBootstrapInfoForResponder| call.
+     * @return status Status of the operation.
+     *         Possible status codes:
+     *         |SupplicantStatusCode.SUCCESS|,
+     *         |SupplicantStatusCode.FAILURE_IFACE_INVALID|
+     *         |SupplicantStatusCode.FAILURE_UNSUPPORTED|
+     */
+    stopDppResponder(uint32_t ownBootstrapId) generates (SupplicantStatus status);
 };
diff --git a/wifi/supplicant/1.4/ISupplicantStaIfaceCallback.hal b/wifi/supplicant/1.4/ISupplicantStaIfaceCallback.hal
index 852696d..efcebda 100644
--- a/wifi/supplicant/1.4/ISupplicantStaIfaceCallback.hal
+++ b/wifi/supplicant/1.4/ISupplicantStaIfaceCallback.hal
@@ -40,8 +40,12 @@
         /**
          * Baseline information as defined in HAL 1.0.
          */
-        @1.0::ISupplicantStaIfaceCallback.AnqpData V1_0; /* Container for v1.0 of this struct */
-        vec<uint8_t> venueUrl; /* Venue URL ANQP-element */
+        @1.0::ISupplicantStaIfaceCallback.AnqpData V1_0;
+
+        /*
+         * Container for v1.0 of this struct
+         */
+        vec<uint8_t> venueUrl;
     };
 
     /**
diff --git a/wifi/supplicant/1.4/ISupplicantStaNetwork.hal b/wifi/supplicant/1.4/ISupplicantStaNetwork.hal
index 80beedf..6bed5ab 100644
--- a/wifi/supplicant/1.4/ISupplicantStaNetwork.hal
+++ b/wifi/supplicant/1.4/ISupplicantStaNetwork.hal
@@ -17,7 +17,7 @@
 package android.hardware.wifi.supplicant@1.4;
 
 import @1.3::ISupplicantStaNetwork;
-import @1.4::ISupplicantStaNetworkCallback;
+import ISupplicantStaNetworkCallback;
 
 /**
  * Interface exposed by the supplicant for each station mode network
diff --git a/wifi/supplicant/1.4/types.hal b/wifi/supplicant/1.4/types.hal
index 18af8fe..aec61c4 100644
--- a/wifi/supplicant/1.4/types.hal
+++ b/wifi/supplicant/1.4/types.hal
@@ -18,6 +18,7 @@
 
 import @1.0::SupplicantStatusCode;
 import @1.3::ConnectionCapabilities;
+import @1.3::DppFailureCode;
 import @1.3::WpaDriverCapabilitiesMask;
 
 /**
@@ -40,6 +41,29 @@
 };
 
 /**
+ * DppFailureCode: Error codes for DPP (Easy Connect)
+ */
+enum DppFailureCode : @1.3::DppFailureCode {
+    /**
+     * Failure to generate a DPP URI.
+     */
+    URI_GENERATION,
+};
+
+/**
+ * DppCurve: Elliptic curve cryptography type used to generate DPP
+ * public/private key pair.
+ */
+enum DppCurve : uint32_t {
+    PRIME256V1,
+    SECP384R1,
+    SECP521R1,
+    BRAINPOOLP256R1,
+    BRAINPOOLP384R1,
+    BRAINPOOLP512R1,
+};
+
+/**
  * Connection Capabilities supported by current network and device
  */
 struct ConnectionCapabilities {
@@ -47,6 +71,7 @@
      * Baseline information as defined in HAL 1.3.
      */
     @1.3::ConnectionCapabilities V1_3;
+
     /**
      * detailed network mode for legacy network
      */
@@ -64,13 +89,14 @@
  * Generic structure to return the status of any supplicant operation.
  */
 struct SupplicantStatus {
-  SupplicantStatusCode code;
-  /**
-   * A vendor specific error message to provide more information beyond the
-   * status code.
-   * This will be used for debbuging purposes only.
-   */
-  string debugMessage;
+    SupplicantStatusCode code;
+
+    /**
+     * A vendor specific error message to provide more information beyond the
+     * status code.
+     * This will be used for debbuging purposes only.
+     */
+    string debugMessage;
 };
 
 /**
@@ -78,7 +104,27 @@
  */
 enum WpaDriverCapabilitiesMask : @1.3::WpaDriverCapabilitiesMask {
     /**
-     *
      */
     SAE_PK = 1 << 2,
 };
+
+/**
+ * DPP bootstrap info generated for responder mode operation
+ */
+struct DppResponderBootstrapInfo {
+    /**
+     * Generated bootstrap identifier
+     */
+    uint32_t bootstrapId;
+
+    /**
+     * The Wi-Fi channel that the DPP responder is listening on.
+     */
+    uint32_t listenChannel;
+
+    /**
+     * Bootstrapping URI per DPP specification, "section 5.2 Bootstrapping
+     * information", may contain listen channel, MAC address, public key, or other information.
+     */
+    string uri;
+};
diff --git a/wifi/supplicant/1.4/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.4/vts/functional/supplicant_sta_iface_hidl_test.cpp
index c0b5a70..ccd469d 100644
--- a/wifi/supplicant/1.4/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.4/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -19,6 +19,7 @@
 #include <android/hardware/wifi/supplicant/1.1/ISupplicant.h>
 #include <android/hardware/wifi/supplicant/1.2/types.h>
 #include <android/hardware/wifi/supplicant/1.3/ISupplicant.h>
+#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaNetwork.h>
 #include <android/hardware/wifi/supplicant/1.3/types.h>
 #include <android/hardware/wifi/supplicant/1.4/ISupplicant.h>
 #include <android/hardware/wifi/supplicant/1.4/ISupplicantStaIface.h>
@@ -45,7 +46,10 @@
 using ::android::hardware::wifi::supplicant::V1_2::DppNetRole;
 using ::android::hardware::wifi::supplicant::V1_2::DppProgressCode;
 using ::android::hardware::wifi::supplicant::V1_3::DppSuccessCode;
+using ::android::hardware::wifi::supplicant::V1_3::ISupplicantStaNetwork;
 using ::android::hardware::wifi::supplicant::V1_4::ConnectionCapabilities;
+using ::android::hardware::wifi::supplicant::V1_4::DppCurve;
+using ::android::hardware::wifi::supplicant::V1_4::DppResponderBootstrapInfo;
 using ::android::hardware::wifi::supplicant::V1_4::ISupplicant;
 using ::android::hardware::wifi::supplicant::V1_4::ISupplicantStaIface;
 using ::android::hardware::wifi::supplicant::V1_4::ISupplicantStaIfaceCallback;
@@ -74,6 +78,24 @@
     sp<ISupplicantStaIface> sta_iface_;
     // MAC address to use for various tests.
     std::array<uint8_t, 6> mac_addr_;
+
+    bool isDppSupported() {
+        uint32_t keyMgmtMask = 0;
+
+        // We need to first get the key management capabilities from the device.
+        // If DPP is not supported, we just pass the test.
+        sta_iface_->getKeyMgmtCapabilities_1_3(
+            [&](const SupplicantStatus& status, uint32_t keyMgmtMaskInternal) {
+                EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+                keyMgmtMask = keyMgmtMaskInternal;
+            });
+
+        if (!(keyMgmtMask & ISupplicantStaNetwork::KeyMgmtMask::DPP)) {
+            // DPP not supported
+            return false;
+        }
+        return true;
+    }
 };
 
 class IfaceCallback : public ISupplicantStaIfaceCallback {
@@ -255,6 +277,48 @@
         });
 }
 
+/*
+ * StartDppEnrolleeResponder
+ */
+TEST_P(SupplicantStaIfaceHidlTest, StartDppEnrolleeResponder) {
+    // We need to first get the key management capabilities from the device.
+    // If DPP is not supported, we just pass the test.
+    if (!isDppSupported()) {
+        // DPP not supported
+        return;
+    }
+
+    hidl_string deviceInfo = "DPP_Responder_Mode_VTS_Test";
+    uint32_t bootstrap_id = 0;
+    uint32_t listen_channel = 0;
+    uint8_t mac_address[6] = {0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+
+    // Generate DPP bootstrap information
+    sta_iface_->generateDppBootstrapInfoForResponder(
+        mac_address, deviceInfo, DppCurve::PRIME256V1,
+        [&](const SupplicantStatusV1_4& status,
+            DppResponderBootstrapInfo bootstrapInfo) {
+            EXPECT_EQ(SupplicantStatusCodeV1_4::SUCCESS, status.code);
+            EXPECT_NE(-1, bootstrapInfo.bootstrapId);
+            EXPECT_NE(0, bootstrapInfo.bootstrapId);
+            bootstrap_id = bootstrapInfo.bootstrapId;
+            listen_channel = bootstrapInfo.listenChannel;
+            EXPECT_NE(0, bootstrapInfo.listenChannel);
+        });
+
+    // Start DPP as Enrollee-Responder.
+    sta_iface_->startDppEnrolleeResponder(
+        listen_channel, [&](const SupplicantStatusV1_4& status) {
+            EXPECT_EQ(SupplicantStatusCodeV1_4::SUCCESS, status.code);
+        });
+
+    // Stop DPP Enrollee-Responder mode, ie remove the URI and stop listen.
+    sta_iface_->stopDppResponder(
+        bootstrap_id, [&](const SupplicantStatusV1_4& status) {
+            EXPECT_EQ(SupplicantStatusCodeV1_4::SUCCESS, status.code);
+        });
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaIfaceHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantStaIfaceHidlTest,