Merge "gralloc4-vts: setting USAGE is always BAD_VALUE"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 0e18f48..543acf6 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -7,7 +7,7 @@
"name": "vts_treble_vintf_vendor_test"
},
{
- "name": "hidl_implementation_test"
+ "name": "hal_implementation_test"
}
]
}
diff --git a/audio/6.0/IDevice.hal b/audio/6.0/IDevice.hal
index c2e310c..2026d8f 100644
--- a/audio/6.0/IDevice.hal
+++ b/audio/6.0/IDevice.hal
@@ -149,7 +149,10 @@
AudioConfig suggestedConfig);
/**
- * Returns whether HAL supports audio patches.
+ * Returns whether HAL supports audio patches. Patch represents a connection
+ * between signal source(s) and signal sink(s). If HAL doesn't support
+ * patches natively (in hardware) then audio system will need to establish
+ * them in software.
*
* @return supports true if audio patches are supported.
*/
@@ -316,7 +319,9 @@
close() generates (Result retval);
/**
- * Applies an audio effect to an audio device.
+ * Applies an audio effect to an audio device. The effect is inserted
+ * according to its insertion preference specified by INSERT_... EffectFlags
+ * in the EffectDescriptor.
*
* @param device identifies the sink or source device this effect must be applied to.
* "device" is the AudioPortHandle indicated for the device when the audio
diff --git a/audio/common/6.0/types.hal b/audio/common/6.0/types.hal
index 26e8acb..b2806c7 100644
--- a/audio/common/6.0/types.hal
+++ b/audio/common/6.0/types.hal
@@ -91,37 +91,76 @@
enum AudioStreamType : int32_t {
// These values must kept in sync with
// frameworks/base/media/java/android/media/AudioSystem.java
+ /** Used to identify the default audio stream volume. */
DEFAULT = -1,
+ /** Specifies the minimum value for use in checks and loops. */
MIN = 0,
+ /** Used to identify the volume of audio streams for phone calls. */
VOICE_CALL = 0,
+ /** Used to identify the volume of audio streams for system sounds. */
SYSTEM = 1,
+ /**
+ * Used to identify the volume of audio streams for the phone ring
+ * and message alerts.
+ */
RING = 2,
+ /** Used to identify the volume of audio streams for music playback. */
MUSIC = 3,
+ /** Used to identify the volume of audio streams for alarms. */
ALARM = 4,
+ /** Used to identify the volume of audio streams for notifications. */
NOTIFICATION = 5,
+ /**
+ * Used to identify the volume of audio streams for phone calls
+ * when connected on bluetooth.
+ */
BLUETOOTH_SCO = 6,
- ENFORCED_AUDIBLE = 7, // Sounds that cannot be muted by user and must be
- // routed to speaker
+ /**
+ * Used to identify the volume of audio streams for enforced system
+ * sounds in certain countries (e.g camera in Japan). */
+ ENFORCED_AUDIBLE = 7,
+ /** Used to identify the volume of audio streams for DTMF tones. */
DTMF = 8,
- TTS = 9, // Transmitted Through Speaker. Plays over speaker
- // only, silent on other devices
- ACCESSIBILITY = 10, // For accessibility talk back prompts
- ASSISTANT = 11, // For virtual assistant service
+ /**
+ * Used to identify the volume of audio streams exclusively transmitted
+ * through the speaker (TTS) of the device.
+ */
+ TTS = 9,
+ /**
+ * Used to identify the volume of audio streams for accessibility prompts.
+ */
+ ACCESSIBILITY = 10,
+ /** Used to identify the volume of audio streams for virtual assistant. */
+ ASSISTANT = 11,
};
@export(name="audio_source_t", value_prefix="AUDIO_SOURCE_")
enum AudioSource : int32_t {
// These values must kept in sync with
// frameworks/base/media/java/android/media/MediaRecorder.java,
- // frameworks/av/services/audiopolicy/AudioPolicyService.cpp,
// system/media/audio_effects/include/audio_effects/audio_effects_conf.h
+ /** Default audio source. */
DEFAULT = 0,
+ /** Microphone audio source. */
MIC = 1,
+ /** Voice call uplink (Tx) audio source. */
VOICE_UPLINK = 2,
+ /** Voice call downlink (Rx) audio source. */
VOICE_DOWNLINK = 3,
+ /** Voice call uplink + downlink audio source. */
VOICE_CALL = 4,
+ /**
+ * Microphone audio source tuned for video recording, with the same
+ * orientation as the camera if available.
+ */
CAMCORDER = 5,
+ /** Microphone audio source tuned for voice recognition. */
VOICE_RECOGNITION = 6,
+ /**
+ * Microphone audio source tuned for voice communications such as VoIP. It
+ * will for instance take advantage of echo cancellation or automatic gain
+ * control if available.
+ */
VOICE_COMMUNICATION = 7,
/**
* Source for the mix to be presented remotely. An example of remote
@@ -146,7 +185,9 @@
* to include all post processing applied to the playback path.
*/
ECHO_REFERENCE = 1997,
+ /** Virtual source for the built-in FM tuner. */
FM_TUNER = 1998,
+ /** Virtual source for the last captured hotword. */
HOTWORD = 1999,
};
@@ -562,7 +603,7 @@
IN_CALL = 2,
/** Calls handled by apps (Eg: Hangout). */
IN_COMMUNICATION = 3,
- /** Call screening in progress */
+ /** Call screening in progress. */
CALL_SCREEN = 4,
};
@@ -748,23 +789,85 @@
// These values must kept in sync with
// frameworks/base/media/java/android/media/AudioAttributes.java
// Note that not all framework values are exposed
+ /**
+ * Usage value to use when the usage is unknown.
+ */
UNKNOWN = 0,
+ /**
+ * Usage value to use when the usage is media, such as music, or movie
+ * soundtracks.
+ */
MEDIA = 1,
+ /**
+ * Usage value to use when the usage is voice communications, such as
+ * telephony or VoIP.
+ */
VOICE_COMMUNICATION = 2,
+ /**
+ * Usage value to use when the usage is in-call signalling, such as with
+ * a "busy" beep, or DTMF tones.
+ */
VOICE_COMMUNICATION_SIGNALLING = 3,
+ /**
+ * Usage value to use when the usage is an alarm (e.g. wake-up alarm).
+ */
ALARM = 4,
+ /**
+ * Usage value to use when the usage is a generic notification.
+ */
NOTIFICATION = 5,
+ /**
+ * Usage value to use when the usage is telephony ringtone.
+ */
NOTIFICATION_TELEPHONY_RINGTONE = 6,
+ /**
+ * Usage value to use when the usage is for accessibility, such as with
+ * a screen reader.
+ */
ASSISTANCE_ACCESSIBILITY = 11,
+ /**
+ * Usage value to use when the usage is driving or navigation directions.
+ */
ASSISTANCE_NAVIGATION_GUIDANCE = 12,
+ /**
+ * Usage value to use when the usage is sonification, such as with user
+ * interface sounds.
+ */
ASSISTANCE_SONIFICATION = 13,
+ /**
+ * Usage value to use when the usage is for game audio.
+ */
GAME = 14,
+ /**
+ * Usage value to use when feeding audio to the platform and replacing
+ * "traditional" audio source, such as audio capture devices.
+ */
VIRTUAL_SOURCE = 15,
+ /**
+ * Usage value to use for audio responses to user queries, audio
+ * instructions or help utterances.
+ */
ASSISTANT = 16,
+ /**
+ * Usage value to use for assistant voice interaction with remote caller
+ * on Cell and VoIP calls.
+ */
CALL_ASSISTANT = 17,
+ /**
+ * Usage value to use when the usage is an emergency.
+ */
EMERGENCY = 1000,
+ /**
+ * Usage value to use when the usage is a safety sound.
+ */
SAFETY = 1001,
+ /**
+ * Usage value to use when the usage is a vehicle status.
+ */
VEHICLE_STATUS = 1002,
+ /**
+ * Usage value to use when the usage is an announcement.
+ */
ANNOUNCEMENT = 1003,
};
@@ -773,10 +876,30 @@
enum AudioContentType : uint32_t {
// Do not change these values without updating their counterparts
// in frameworks/base/media/java/android/media/AudioAttributes.java
+ /**
+ * Content type value to use when the content type is unknown, or other than
+ * the ones defined.
+ */
UNKNOWN = 0,
+ /**
+ * Content type value to use when the content type is speech.
+ */
SPEECH = 1,
+ /**
+ * Content type value to use when the content type is music.
+ */
MUSIC = 2,
+ /**
+ * Content type value to use when the content type is a soundtrack,
+ * typically accompanying a movie or TV program.
+ */
MOVIE = 3,
+ /**
+ * Content type value to use when the content type is a sound used to
+ * accompany a user action, such as a beep or sound effect expressing a key
+ * click, or event, such as the type of a sound for a bonus being received
+ * in a game. These sounds are mostly synthesized or short Foley sounds.
+ */
SONIFICATION = 4,
};
diff --git a/audio/common/all-versions/copyHAL.sh b/audio/common/all-versions/copyHAL.sh
index d07012f..0a32a51 100755
--- a/audio/common/all-versions/copyHAL.sh
+++ b/audio/common/all-versions/copyHAL.sh
@@ -21,11 +21,12 @@
readonly FWK_DIRECTORY=frameworks/av/media/libaudiohal
readonly IMPL_DIRECTORY=impl
-readonly IMPL_FACTORYHAL=$IMPL_DIRECTORY/include/libaudiohal/FactoryHalHidl.h
+readonly IMPL_FACTORYHAL=FactoryHalHidl.cpp
readonly VTS_DIRECTORY=test/vts-testcase/hal/audio
readonly VTS_LIST=test/vts/tools/build/tasks/list/vts_test_lib_hidl_package_list.mk
readonly WATCHDOG=frameworks/base/services/core/java/com/android/server/Watchdog.cpp
+readonly DUMP_UTILS=frameworks/native/libs/dumputils/dump_utils.cpp
readonly GSI_CURRENT=build/make/target/product/gsi/current.txt
readonly BASE_VERSION=${1:-$(ls $ANDROID_BUILD_TOP/$HAL_DIRECTORY | grep -E '[0-9]+\.[0-9]+' |
@@ -149,7 +150,7 @@
createFrameworkAdapter() {
updateVersion -v original_before=1 Android.bp
- updateVersion -v original_before=1 -v RS= -v ORS='\n\n' $IMPL_FACTORYHAL/Android.bp
+ updateVersion -v original_before=1 -v RS= -v ORS='\n\n' $IMPL_DIRECTORY/Android.bp
updateVersion -v original_after=1 $IMPL_FACTORYHAL
}
echo "Now creating the framework adapter version"
@@ -171,6 +172,9 @@
echo "Now update watchdog"
runIfNeeded $(dirname $WATCHDOG) updateAudioVersion -v original_before=1 $(basename $WATCHDOG)
+echo "Now update dumputils"
+runIfNeeded $(dirname $DUMP_UTILS) updateAudioVersion -v original_before=1 $(basename $DUMP_UTILS)
+
echo "Now update GSI current.txt"
runIfNeeded $(dirname $GSI_CURRENT) update-vndk-list.sh
diff --git a/audio/common/all-versions/default/service/Android.bp b/audio/common/all-versions/default/service/Android.bp
index f58a599..3e8b715 100644
--- a/audio/common/all-versions/default/service/Android.bp
+++ b/audio/common/all-versions/default/service/Android.bp
@@ -24,24 +24,6 @@
"liblog",
"libutils",
"libhardware",
- "android.hardware.audio@2.0",
- "android.hardware.audio@4.0",
- "android.hardware.audio@5.0",
- "android.hardware.audio@6.0",
- "android.hardware.audio.common@2.0",
- "android.hardware.audio.common@4.0",
- "android.hardware.audio.common@5.0",
- "android.hardware.audio.common@6.0",
- "android.hardware.audio.effect@2.0",
- "android.hardware.audio.effect@4.0",
- "android.hardware.audio.effect@5.0",
- "android.hardware.audio.effect@6.0",
- "android.hardware.bluetooth.a2dp@1.0",
- "android.hardware.bluetooth.audio@2.0",
- "android.hardware.soundtrigger@2.0",
- "android.hardware.soundtrigger@2.1",
- "android.hardware.soundtrigger@2.2",
- "android.hardware.soundtrigger@2.3",
],
}
diff --git a/audio/common/all-versions/default/service/service.cpp b/audio/common/all-versions/default/service/service.cpp
index 00bcb19..147d062 100644
--- a/audio/common/all-versions/default/service/service.cpp
+++ b/audio/common/all-versions/default/service/service.cpp
@@ -16,20 +16,9 @@
#define LOG_TAG "audiohalservice"
-#include <android/hardware/audio/2.0/IDevicesFactory.h>
-#include <android/hardware/audio/4.0/IDevicesFactory.h>
-#include <android/hardware/audio/5.0/IDevicesFactory.h>
-#include <android/hardware/audio/6.0/IDevicesFactory.h>
-#include <android/hardware/audio/effect/2.0/IEffectsFactory.h>
-#include <android/hardware/audio/effect/4.0/IEffectsFactory.h>
-#include <android/hardware/audio/effect/5.0/IEffectsFactory.h>
-#include <android/hardware/audio/effect/6.0/IEffectsFactory.h>
-#include <android/hardware/bluetooth/a2dp/1.0/IBluetoothAudioOffload.h>
-#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvidersFactory.h>
-#include <android/hardware/soundtrigger/2.0/ISoundTriggerHw.h>
-#include <android/hardware/soundtrigger/2.1/ISoundTriggerHw.h>
-#include <android/hardware/soundtrigger/2.2/ISoundTriggerHw.h>
-#include <android/hardware/soundtrigger/2.3/ISoundTriggerHw.h>
+#include <string>
+#include <vector>
+
#include <binder/ProcessState.h>
#include <cutils/properties.h>
#include <hidl/HidlTransportSupport.h>
@@ -39,13 +28,20 @@
using namespace android::hardware;
using android::OK;
+using InterfacesList = std::vector<std::string>;
+
/** Try to register the provided factories in the provided order.
* If any registers successfully, do not register any other and return true.
* If all fail, return false.
*/
-template <class... Factories>
-bool registerPassthroughServiceImplementations() {
- return ((registerPassthroughServiceImplementation<Factories>() != OK) && ...);
+template <class Iter>
+static bool registerPassthroughServiceImplementations(Iter first, Iter last) {
+ for (; first != last; ++first) {
+ if (registerPassthroughServiceImplementation(*first) == OK) {
+ return true;
+ }
+ }
+ return false;
}
int main(int /* argc */, char* /* argv */ []) {
@@ -62,36 +58,58 @@
}
configureRpcThreadpool(16, true /*callerWillJoin*/);
- // Keep versions on a separate line for easier parsing
+ // Automatic formatting tries to compact the lines, making them less readable
// clang-format off
- LOG_ALWAYS_FATAL_IF((registerPassthroughServiceImplementations<
- audio::V6_0::IDevicesFactory,
- audio::V5_0::IDevicesFactory,
- audio::V4_0::IDevicesFactory,
- audio::V2_0::IDevicesFactory>()),
- "Could not register audio core API");
+ const std::vector<InterfacesList> mandatoryInterfaces = {
+ {
+ "Audio Core API",
+ "android.hardware.audio@6.0::IDevicesFactory",
+ "android.hardware.audio@5.0::IDevicesFactory",
+ "android.hardware.audio@4.0::IDevicesFactory",
+ "android.hardware.audio@2.0::IDevicesFactory"
+ },
+ {
+ "Audio Effect API",
+ "android.hardware.audio.effect@6.0::IEffectsFactory",
+ "android.hardware.audio.effect@5.0::IEffectsFactory",
+ "android.hardware.audio.effect@4.0::IEffectsFactory",
+ "android.hardware.audio.effect@2.0::IEffectsFactory",
+ }
+ };
- LOG_ALWAYS_FATAL_IF((registerPassthroughServiceImplementations<
- audio::effect::V6_0::IEffectsFactory,
- audio::effect::V5_0::IEffectsFactory,
- audio::effect::V4_0::IEffectsFactory,
- audio::effect::V2_0::IEffectsFactory>()),
- "Could not register audio effect API");
+ const std::vector<InterfacesList> optionalInterfaces = {
+ {
+ "Soundtrigger API",
+ "android.hardware.soundtrigger@2.3::ISoundTriggerHw",
+ "android.hardware.soundtrigger@2.2::ISoundTriggerHw",
+ "android.hardware.soundtrigger@2.1::ISoundTriggerHw",
+ "android.hardware.soundtrigger@2.0::ISoundTriggerHw",
+ },
+ {
+ "Bluetooth Audio API",
+ "android.hardware.bluetooth.audio@2.0::IBluetoothAudioProvidersFactory"
+ },
+ // remove the old HIDL when Bluetooth Audio Hal V2 has offloading supported
+ {
+ "Bluetooth Audio Offload API",
+ "android.hardware.bluetooth.a2dp@1.0::IBluetoothAudioOffload"
+ }
+ };
// clang-format on
- ALOGW_IF((registerPassthroughServiceImplementations<
- soundtrigger::V2_3::ISoundTriggerHw, soundtrigger::V2_2::ISoundTriggerHw,
- soundtrigger::V2_1::ISoundTriggerHw, soundtrigger::V2_0::ISoundTriggerHw>()),
- "Could not register soundtrigger API");
+ for (const auto& listIter : mandatoryInterfaces) {
+ auto iter = listIter.begin();
+ const std::string& interfaceFamilyName = *iter++;
+ LOG_ALWAYS_FATAL_IF(!registerPassthroughServiceImplementations(iter, listIter.end()),
+ "Could not register %s", interfaceFamilyName.c_str());
+ }
- ALOGW_IF(registerPassthroughServiceImplementations<
- bluetooth::audio::V2_0::IBluetoothAudioProvidersFactory>(),
- "Could not register Bluetooth audio API");
-
- // remove the old HIDL when Bluetooth Audio Hal V2 has offloading supported
- ALOGW_IF(registerPassthroughServiceImplementations<
- bluetooth::a2dp::V1_0::IBluetoothAudioOffload>(),
- "Could not register Bluetooth audio offload API");
+ for (const auto& listIter : optionalInterfaces) {
+ auto iter = listIter.begin();
+ const std::string& interfaceFamilyName = *iter++;
+ ALOGW_IF(!registerPassthroughServiceImplementations(iter, listIter.end()),
+ "Could not register %s", interfaceFamilyName.c_str());
+ }
joinRpcThreadpool();
}
diff --git a/audio/effect/6.0/xml/api/current.txt b/audio/effect/6.0/xml/api/current.txt
index 2dfcb9b..cd5ca2a 100644
--- a/audio/effect/6.0/xml/api/current.txt
+++ b/audio/effect/6.0/xml/api/current.txt
@@ -3,11 +3,13 @@
public class AudioEffectsConf {
ctor public AudioEffectsConf();
+ method public audio.effects.V6_0.AudioEffectsConf.DeviceEffects getDeviceEffects();
method public audio.effects.V6_0.EffectsType getEffects();
method public audio.effects.V6_0.LibrariesType getLibraries();
method public audio.effects.V6_0.AudioEffectsConf.Postprocess getPostprocess();
method public audio.effects.V6_0.AudioEffectsConf.Preprocess getPreprocess();
method public audio.effects.V6_0.VersionType getVersion();
+ method public void setDeviceEffects(audio.effects.V6_0.AudioEffectsConf.DeviceEffects);
method public void setEffects(audio.effects.V6_0.EffectsType);
method public void setLibraries(audio.effects.V6_0.LibrariesType);
method public void setPostprocess(audio.effects.V6_0.AudioEffectsConf.Postprocess);
@@ -15,6 +17,11 @@
method public void setVersion(audio.effects.V6_0.VersionType);
}
+ public static class AudioEffectsConf.DeviceEffects {
+ ctor public AudioEffectsConf.DeviceEffects();
+ method public java.util.List<audio.effects.V6_0.DeviceProcessType> getDevicePort();
+ }
+
public static class AudioEffectsConf.Postprocess {
ctor public AudioEffectsConf.Postprocess();
method public java.util.List<audio.effects.V6_0.StreamPostprocessType> getStream();
@@ -25,6 +32,72 @@
method public java.util.List<audio.effects.V6_0.StreamPreprocessType> getStream();
}
+ public class DeviceProcessType extends audio.effects.V6_0.StreamProcessingType {
+ ctor public DeviceProcessType();
+ method public String getAddress();
+ method public audio.effects.V6_0.DeviceType getType();
+ method public void setAddress(String);
+ method public void setType(audio.effects.V6_0.DeviceType);
+ }
+
+ public enum DeviceType {
+ method public String getRawName();
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_AUX_DIGITAL;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_BACK_MIC;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_BLUETOOTH_BLE;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_BUILTIN_MIC;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_BUS;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_COMMUNICATION;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_ECHO_REFERENCE;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_FM_TUNER;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_HDMI;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_HDMI_ARC;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_IP;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_LINE;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_LOOPBACK;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_PROXY;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_REMOTE_SUBMIX;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_SPDIF;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_TELEPHONY_RX;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_TV_TUNER;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_USB_ACCESSORY;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_USB_DEVICE;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_USB_HEADSET;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_VOICE_CALL;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_IN_WIRED_HEADSET;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_AUX_DIGITAL;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_AUX_LINE;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_BUS;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_EARPIECE;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_ECHO_CANCELLER;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_FM;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_HDMI;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_HDMI_ARC;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_HEARING_AID;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_IP;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_LINE;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_PROXY;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_SPDIF;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_SPEAKER;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_TELEPHONY_TX;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_USB_ACCESSORY;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_USB_DEVICE;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_USB_HEADSET;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
+ enum_constant public static final audio.effects.V6_0.DeviceType AUDIO_DEVICE_OUT_WIRED_HEADSET;
+ }
+
public class EffectImplType {
ctor public EffectImplType();
method public String getLibrary();
@@ -69,6 +142,8 @@
public enum StreamInputType {
method public String getRawName();
enum_constant public static final audio.effects.V6_0.StreamInputType camcorder;
+ enum_constant public static final audio.effects.V6_0.StreamInputType echo_reference;
+ enum_constant public static final audio.effects.V6_0.StreamInputType fm_tuner;
enum_constant public static final audio.effects.V6_0.StreamInputType mic;
enum_constant public static final audio.effects.V6_0.StreamInputType unprocessed;
enum_constant public static final audio.effects.V6_0.StreamInputType voice_call;
diff --git a/audio/effect/6.0/xml/audio_effects_conf.xsd b/audio/effect/6.0/xml/audio_effects_conf.xsd
index a7ff20b..eeaaf63 100644
--- a/audio/effect/6.0/xml/audio_effects_conf.xsd
+++ b/audio/effect/6.0/xml/audio_effects_conf.xsd
@@ -36,6 +36,8 @@
<xs:enumeration value="voice_communication"/>
<xs:enumeration value="unprocessed"/>
<xs:enumeration value="voice_performance"/>
+ <xs:enumeration value="echo_reference"/>
+ <xs:enumeration value="fm_tuner"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="streamOutputType">
@@ -58,6 +60,65 @@
<xs:pattern value="[^/].*"/>
</xs:restriction>
</xs:simpleType>
+ <xs:simpleType name="deviceType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="AUDIO_DEVICE_OUT_EARPIECE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADPHONE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_DIGITAL"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_USB_ACCESSORY"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_USB_DEVICE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_REMOTE_SUBMIX"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_TELEPHONY_TX"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_LINE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI_ARC"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_SPDIF"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_FM"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_LINE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER_SAFE"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_IP"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_BUS"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_PROXY"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_USB_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_HEARING_AID"/>
+ <xs:enumeration value="AUDIO_DEVICE_OUT_ECHO_CANCELLER"/>
+ <!-- Due to the xml format, IN types can not be a separated from OUT types -->
+ <xs:enumeration value="AUDIO_DEVICE_IN_COMMUNICATION"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BUILTIN_MIC"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_WIRED_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_AUX_DIGITAL"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_HDMI"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_VOICE_CALL"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_TELEPHONY_RX"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BACK_MIC"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_REMOTE_SUBMIX"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_USB_ACCESSORY"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_USB_DEVICE"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_FM_TUNER"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_TV_TUNER"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_LINE"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_SPDIF"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_A2DP"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_LOOPBACK"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_IP"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BUS"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_PROXY"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_USB_HEADSET"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_BLE"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_HDMI_ARC"/>
+ <xs:enumeration value="AUDIO_DEVICE_IN_ECHO_REFERENCE"/>
+ </xs:restriction>
+ </xs:simpleType>
<!-- Complex types -->
<xs:complexType name="librariesType">
<xs:annotation>
@@ -174,6 +235,31 @@
</xs:extension>
</xs:complexContent>
</xs:complexType>
+ <xs:complexType name="deviceProcessType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Audio Device Effects configuration. The processing configuration consists
+ of a list of effects to be automatically added on a device Port when involved in an audio
+ patch.
+ Valid device type are listed in "deviceType" and shall be aligned.
+ Each stream element contains a list of "apply" elements. The value of the
+ "effect" attr must correspond to the name of an "effect" element.
+ Note that if the device is involved in a hardware patch, the effect must be hardware
+ accelerated.
+ Example:
+ <devicePort address="BUS00_USAGE_MAIN" type="AUDIO_DEVICE_OUT_BUS">
+ <apply effect="equalizer"/>
+ <apply effect="effect2"/>
+ </devicePort>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexContent>
+ <xs:extension base="aec:streamProcessingType">
+ <xs:attribute name="address" type="xs:string" use="required"/>
+ <xs:attribute name="type" type="aec:deviceType" use="required"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
<!-- Root element -->
<xs:element name="audio_effects_conf">
<xs:complexType>
@@ -194,6 +280,13 @@
</xs:sequence>
</xs:complexType>
</xs:element>
+ <xs:element name="deviceEffects" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="devicePort" type="aec:deviceProcessType" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
</xs:sequence>
<xs:attribute name="version" type="aec:versionType" use="required"/>
</xs:complexType>
@@ -227,4 +320,4 @@
<xs:field xpath="@effect"/>
</xs:keyref>
</xs:element>
-</xs:schema>
\ No newline at end of file
+</xs:schema>
diff --git a/automotive/audiocontrol/1.0/default/Android.bp b/automotive/audiocontrol/1.0/default/Android.bp
index 3d04c89..ae4b805 100644
--- a/automotive/audiocontrol/1.0/default/Android.bp
+++ b/automotive/audiocontrol/1.0/default/Android.bp
@@ -30,9 +30,4 @@
"libutils",
],
vintf_fragments: ["audiocontrol_manifest.xml"],
- cflags: [
- "-DLOG_TAG=\"AudCntrlDrv\"",
- "-O0",
- "-g",
- ],
}
diff --git a/automotive/can/1.0/ICanController.hal b/automotive/can/1.0/ICanController.hal
index 0c6f53e..aaf85e9 100644
--- a/automotive/can/1.0/ICanController.hal
+++ b/automotive/can/1.0/ICanController.hal
@@ -27,51 +27,22 @@
*/
interface ICanController {
/**
- * Type of an interface, a mean to express the domain of device address.
+ * Type of an interface, an equivalent to BusConfig::InterfaceId
+ * union discriminator. Defines a number of specific standard hardware
+ * families and a generic catch-all type of {@see INDEXED}.
*/
enum InterfaceType : uint8_t {
- /**
- * Virtual SocketCAN interface.
- *
- * The address is an interface name, such as vcan0. If the interface
- * doesn't exist, HAL server must create it.
- *
- * Valid InterfaceIdentifier types:
- * - address.
- */
+ /** Virtual SocketCAN interface. */
VIRTUAL,
- /**
- * Native SocketCAN interface.
- *
- * The address is an interface name, such as can0.
- *
- * Valid InterfaceIdentifier types:
- * - address;
- * - serialno.
- */
+ /** Native SocketCAN interface. */
SOCKETCAN,
- /**
- * Serial-based interface.
- *
- * The address is a patch to a device, such as /dev/ttyUSB0.
- *
- * Valid InterfaceIdentifier types:
- * - address;
- * - serialno.
- */
+ /** Serial line CAN interface. */
SLCAN,
- /**
- * Proprietary interface, specific to the hardware system Android
- * is running on. Instead of using address field, the interface is
- * addressed with 0-based index.
- *
- * Valid InterfaceIdentifier types:
- * - index
- */
- INDEXED
+ /** Proprietary, device-specific interface. */
+ INDEXED,
};
enum Result : uint8_t {
@@ -92,13 +63,19 @@
NOT_SUPPORTED,
/**
- * Provided address (interface name, device path) doesn't exist or there
- * is no device with a given serial no.
+ * Provided interface ID (index, name, device path) doesn't exist or
+ * there is no device with a given serial number.
*/
- BAD_ADDRESS,
+ BAD_INTERFACE_ID,
/** Provided bit rate is not supported by the hardware. */
BAD_BITRATE,
+
+ /**
+ * Provided service name ({@see BusConfig#name}) either has invalid
+ * format or is not listed in device manifest file.
+ */
+ BAD_SERVICE_NAME,
};
/**
@@ -106,49 +83,76 @@
*
* ISO TP and CAN FD are currently not supported.
*/
- struct BusConfiguration {
+ struct BusConfig {
/**
* Name under which ICanBus HIDL service should be published.
*
* It must consist of only alphanumeric characters and underscore
* (a-z, A-Z, 0-9, '_'), at least 1 and at most 32 characters long.
+ *
+ * This field is *not* meant to distinguish between hardware interfaces
+ * nor preselect parameters like bitrate. The only intended side-effect
+ * of changing it should be a different ICanBus HIDL service name and
+ * the HIDL service should make no assumptions on its contents.
*/
string name;
/**
- * Type of the hardware (or virtual) CAN interface.
+ * Hardware interface configuration.
+ *
+ * This union's discriminator has an equivalent enum
+ * {@see InterfaceType} to express compatibility via
+ * getSupportedInterfaceTypes().
*/
- InterfaceType iftype;
+ safe_union InterfaceId {
+ /** Virtual SocketCAN interface. */
+ struct Virtual {
+ /** Interface name, such as vcan0. If the interface doesn't
+ * exist, HAL server must create it.
+ */
+ string ifname;
+ } virtualif;
- /**
- * Identification of hardware interface to configure.
- */
- safe_union InterfaceIdentifier {
- /**
- * Interface name or other mean of identification of the specific
- * interface port. Syntax depends on {@see iftype}, for details
- * {@see InterfaceType}.
- */
- string address;
+ /** Native SocketCAN interface. */
+ safe_union Socketcan {
+ /** Interface name, such as can0. */
+ string ifname;
+ /**
+ * Alternatively to providing {@see ifname}, one may provide a
+ * list of interface serial number suffixes. If there happens to
+ * be a device (like USB2CAN) with a matching serial number
+ * suffix, the HAL service will have to select it.
+ *
+ * Client may utilize this in two ways: by matching against the
+ * entire serial number, or the last few characters (usually
+ * one). The former is better for small-scale test deployments
+ * (with just a handful of vehicles), the latter is good for
+ * larger scale (where a small suffix list may support large
+ * test fleet).
+ */
+ vec<string> serialno;
+ } socketcan;
+
+ /** Serial line CAN interface. */
+ safe_union Slcan {
+ /** Path to a device, such as /dev/ttyUSB0. */
+ string ttyname;
+ /**
+ * List of interface serial number suffixes.
+ * {@see Socketcan::serialno}
+ */
+ vec<string> serialno;
+ } slcan;
/**
- * Numerical identifier of interface, used for InterfaceType#INDEXED.
- */
- uint8_t index;
-
- /**
- * Alternatively to providing {@see address}, one may provide a list
- * of interface serial number suffixes. If there happens to be
- * a device (like USB2CAN) with a matching serial number suffix,
- * it gets selected.
+ * Proprietary, device-specific interface.
*
- * Client may utilize this in two ways: by matching against the
- * entire serial number, or the last few characters (usually one).
- * The former is better for small-scale test deployments (with just
- * a handful of vehicles), the latter is good for larger scale
- * (where a small suffix list may support large test fleet).
+ * Non-SocketCAN interfaces should use this variant.
*/
- vec<string> serialno;
+ struct Indexed {
+ /** Interface number, 0-based. */
+ uint8_t index;
+ } indexed;
} interfaceId;
/**
@@ -156,7 +160,8 @@
*
* Typical bit rates are: 100000, 125000, 250000, 500000.
*
- * For virtual interfaces this value is ignored.
+ * For {@see interfaceId#virtual} and pre-configured
+ * {@see interfaceId#indexed} interfaces this value is ignored.
*/
uint32_t bitrate;
};
@@ -164,17 +169,17 @@
/**
* Fetches the list of interface types supported by this HAL server.
*
- * @return iftypes The list of supported interface types
+ * @return iftypes The list of supported interface types.
*/
getSupportedInterfaceTypes() generates (vec<InterfaceType> iftypes);
/**
* Bring up the CAN interface and publish ICanBus server instance.
*
- * @param config Configuration of the CAN interface
+ * @param config Configuration of the CAN interface.
* @return result OK if the operation succeeded; error code otherwise.
*/
- upInterface(BusConfiguration config) generates (Result result);
+ upInterface(BusConfig config) generates (Result result);
/**
* Unpublish ICanBus server instance and bring down the CAN interface.
@@ -182,9 +187,9 @@
* In case of failure, at least the ICanBus server instance must be
* unpublished and resources freed on best-effort basis.
*
- * @param name Name of the interface (@see BusConfiguration#name} to
- * bring down
- * @return success true in case of success, false otherwise
+ * @param name Name of the interface (@see BusConfig#name} to
+ * bring down.
+ * @return success true in case of success, false otherwise.
*/
downInterface(string name) generates (bool success);
};
diff --git a/automotive/can/1.0/default/CanBus.cpp b/automotive/can/1.0/default/CanBus.cpp
index 8fb09eb..9f704c1 100644
--- a/automotive/can/1.0/default/CanBus.cpp
+++ b/automotive/can/1.0/default/CanBus.cpp
@@ -124,7 +124,7 @@
if (!isUp.has_value()) {
// preUp() should prepare the interface (either create or make sure it's there)
LOG(ERROR) << "Interface " << mIfname << " didn't get prepared";
- return ICanController::Result::BAD_ADDRESS;
+ return ICanController::Result::BAD_INTERFACE_ID;
}
if (!*isUp && !netdevice::up(mIfname)) {
diff --git a/automotive/can/1.0/default/CanBusNative.cpp b/automotive/can/1.0/default/CanBusNative.cpp
index ef04d01..aafbecc 100644
--- a/automotive/can/1.0/default/CanBusNative.cpp
+++ b/automotive/can/1.0/default/CanBusNative.cpp
@@ -28,7 +28,7 @@
ICanController::Result CanBusNative::preUp() {
if (!netdevice::exists(mIfname)) {
LOG(ERROR) << "Interface " << mIfname << " doesn't exist";
- return ICanController::Result::BAD_ADDRESS;
+ return ICanController::Result::BAD_INTERFACE_ID;
}
if (mBitrate == 0) {
diff --git a/automotive/can/1.0/default/CanBusSlcan.cpp b/automotive/can/1.0/default/CanBusSlcan.cpp
index 0feee8f..d15905d 100644
--- a/automotive/can/1.0/default/CanBusSlcan.cpp
+++ b/automotive/can/1.0/default/CanBusSlcan.cpp
@@ -81,7 +81,7 @@
mFd = base::unique_fd(open(mUartName.c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY));
if (!mFd.ok()) {
LOG(ERROR) << "SLCAN Failed to open " << mUartName << ": " << strerror(errno);
- return ICanController::Result::BAD_ADDRESS;
+ return ICanController::Result::BAD_INTERFACE_ID;
}
// If the device is already up, update the iface name in our CanBusSlcan object
diff --git a/automotive/can/1.0/default/CanController.cpp b/automotive/can/1.0/default/CanController.cpp
index fb648c1..0700c77 100644
--- a/automotive/can/1.0/default/CanController.cpp
+++ b/automotive/can/1.0/default/CanController.cpp
@@ -27,7 +27,8 @@
namespace android::hardware::automotive::can::V1_0::implementation {
-using IfaceIdDisc = ICanController::BusConfiguration::InterfaceIdentifier::hidl_discriminator;
+using IfId = ICanController::BusConfig::InterfaceId;
+using IfIdDisc = ICanController::BusConfig::InterfaceId::hidl_discriminator;
Return<void> CanController::getSupportedInterfaceTypes(getSupportedInterfaceTypes_cb _hidl_cb) {
_hidl_cb({ICanController::InterfaceType::VIRTUAL, ICanController::InterfaceType::SOCKETCAN,
@@ -40,15 +41,14 @@
return std::regex_match(name, nameRE);
}
-Return<ICanController::Result> CanController::upInterface(
- const ICanController::BusConfiguration& config) {
+Return<ICanController::Result> CanController::upInterface(const ICanController::BusConfig& config) {
LOG(VERBOSE) << "Attempting to bring interface up: " << toString(config);
std::lock_guard<std::mutex> lck(mCanBusesGuard);
if (!isValidName(config.name)) {
LOG(ERROR) << "Bus name " << config.name << " is invalid";
- return ICanController::Result::UNKNOWN_ERROR;
+ return ICanController::Result::BAD_SERVICE_NAME;
}
if (mCanBuses.find(config.name) != mCanBuses.end()) {
@@ -58,24 +58,23 @@
sp<CanBus> busService;
- if (config.iftype == ICanController::InterfaceType::SOCKETCAN) {
- // TODO(b/135918744): support serialno
- if (config.interfaceId.getDiscriminator() == IfaceIdDisc::address) {
- busService = new CanBusNative(config.interfaceId.address(), config.bitrate);
+ if (config.interfaceId.getDiscriminator() == IfIdDisc::socketcan) {
+ // TODO(b/142654031): support serialno
+ auto& socketcan = config.interfaceId.socketcan();
+ if (socketcan.getDiscriminator() == IfId::Socketcan::hidl_discriminator::ifname) {
+ busService = new CanBusNative(socketcan.ifname(), config.bitrate);
} else {
- return ICanController::Result::BAD_ADDRESS;
+ return ICanController::Result::BAD_INTERFACE_ID;
}
- } else if (config.iftype == ICanController::InterfaceType::VIRTUAL) {
- if (config.interfaceId.getDiscriminator() == IfaceIdDisc::address) {
- busService = new CanBusVirtual(config.interfaceId.address());
+ } else if (config.interfaceId.getDiscriminator() == IfIdDisc::virtualif) {
+ busService = new CanBusVirtual(config.interfaceId.virtualif().ifname);
+ } else if (config.interfaceId.getDiscriminator() == IfIdDisc::slcan) {
+ // TODO(b/142654031): support serialno
+ auto& slcan = config.interfaceId.slcan();
+ if (slcan.getDiscriminator() == IfId::Slcan::hidl_discriminator::ttyname) {
+ busService = new CanBusSlcan(slcan.ttyname(), config.bitrate);
} else {
- return ICanController::Result::BAD_ADDRESS;
- }
- } else if (config.iftype == ICanController::InterfaceType::SLCAN) {
- if (config.interfaceId.getDiscriminator() == IfaceIdDisc::address) {
- busService = new CanBusSlcan(config.interfaceId.address(), config.bitrate);
- } else {
- return ICanController::Result::BAD_ADDRESS;
+ return ICanController::Result::BAD_INTERFACE_ID;
}
} else {
return ICanController::Result::NOT_SUPPORTED;
@@ -91,7 +90,7 @@
if (!busService->down()) {
LOG(WARNING) << "Failed to bring down CAN bus that failed to register";
}
- return ICanController::Result::UNKNOWN_ERROR;
+ return ICanController::Result::BAD_SERVICE_NAME;
}
mCanBuses[config.name] = busService;
diff --git a/automotive/can/1.0/default/CanController.h b/automotive/can/1.0/default/CanController.h
index 99a551a..27e82f3 100644
--- a/automotive/can/1.0/default/CanController.h
+++ b/automotive/can/1.0/default/CanController.h
@@ -25,8 +25,7 @@
struct CanController : public ICanController {
Return<void> getSupportedInterfaceTypes(getSupportedInterfaceTypes_cb _hidl_cb) override;
- Return<ICanController::Result> upInterface(
- const ICanController::BusConfiguration& config) override;
+ Return<ICanController::Result> upInterface(const ICanController::BusConfig& config) override;
Return<bool> downInterface(const hidl_string& name) override;
private:
diff --git a/automotive/can/1.0/tools/canhalctrl.cpp b/automotive/can/1.0/tools/canhalctrl.cpp
index 5494ba3..33755bf 100644
--- a/automotive/can/1.0/tools/canhalctrl.cpp
+++ b/automotive/can/1.0/tools/canhalctrl.cpp
@@ -71,15 +71,31 @@
if (!isSupported(ctrl, type)) continue;
anySupported = true;
- ICanController::BusConfiguration config = {};
+ ICanController::BusConfig config = {};
config.name = busName;
- config.iftype = type;
config.bitrate = bitrate;
- if (type == ICanController::InterfaceType::INDEXED) {
- config.interfaceId.index(std::stol(interface));
+ // TODO(b/146214370): move interfaceId constructors to a library
+ using IfCfg = ICanController::BusConfig::InterfaceId;
+ if (type == ICanController::InterfaceType::VIRTUAL) {
+ config.interfaceId.virtualif({interface});
+ } else if (type == ICanController::InterfaceType::SOCKETCAN) {
+ IfCfg::Socketcan socketcan = {};
+ socketcan.ifname(interface);
+ config.interfaceId.socketcan(socketcan);
+ } else if (type == ICanController::InterfaceType::SLCAN) {
+ IfCfg::Slcan slcan = {};
+ slcan.ttyname(interface);
+ config.interfaceId.slcan(slcan);
+ } else if (type == ICanController::InterfaceType::INDEXED) {
+ auto idx = std::stol(interface);
+ if (idx < 0 || idx > UINT8_MAX) {
+ std::cerr << "Interface index out of range: " << idx;
+ return -1;
+ }
+ config.interfaceId.indexed({uint8_t(idx)});
} else {
- config.interfaceId.address(interface);
+ CHECK(false) << "Unexpected interface type: " << toString(type);
}
const auto upresult = ctrl->upInterface(config);
diff --git a/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp b/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
index efaad53..68d555d 100644
--- a/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
+++ b/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
@@ -81,7 +81,7 @@
struct Bus {
DISALLOW_COPY_AND_ASSIGN(Bus);
- Bus(sp<ICanController> controller, const ICanController::BusConfiguration& config)
+ Bus(sp<ICanController> controller, const ICanController::BusConfig& config)
: mIfname(config.name), mController(controller) {
const auto result = controller->upInterface(config);
EXPECT_EQ(ICanController::Result::OK, result);
@@ -122,6 +122,7 @@
void send(const CanMessage& msg) {
EXPECT_NE(mBus, nullptr);
+ if (!mBus) return;
const auto result = mBus->send(msg);
EXPECT_EQ(Result::OK, result);
}
@@ -196,10 +197,9 @@
const auto idx = mLastIface++;
EXPECT_LT(idx, mBusNames.size());
- ICanController::BusConfiguration config = {};
+ ICanController::BusConfig config = {};
config.name = mBusNames[idx];
- config.iftype = InterfaceType::VIRTUAL;
- config.interfaceId.address("vcan50");
+ config.interfaceId.virtualif({"vcan50"});
return Bus(mCanController, config);
}
diff --git a/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp b/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
index b2edd78..8397215 100644
--- a/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
+++ b/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
@@ -31,6 +31,7 @@
using hardware::hidl_vec;
using InterfaceType = ICanController::InterfaceType;
+using IfId = ICanController::BusConfig::InterfaceId;
static utils::SimpleHidlEnvironment<ICanController>* gEnv = nullptr;
@@ -89,10 +90,23 @@
bool CanControllerHalTest::up(InterfaceType iftype, std::string srvname, std::string ifname,
ICanController::Result expected) {
- ICanController::BusConfiguration config = {};
+ ICanController::BusConfig config = {};
config.name = srvname;
- config.iftype = iftype;
- config.interfaceId.address(ifname);
+
+ // TODO(b/146214370): move interfaceId constructors to a library
+ if (iftype == InterfaceType::SOCKETCAN) {
+ IfId::Socketcan socketcan = {};
+ socketcan.ifname(ifname);
+ config.interfaceId.socketcan(socketcan);
+ } else if (iftype == InterfaceType::SLCAN) {
+ IfId::Slcan slcan = {};
+ slcan.ttyname(ifname);
+ config.interfaceId.slcan(slcan);
+ } else if (iftype == InterfaceType::VIRTUAL) {
+ config.interfaceId.virtualif({ifname});
+ } else {
+ EXPECT_TRUE(false) << "Unexpected iftype: " << toString(iftype);
+ }
const auto upresult = mCanController->upInterface(config);
@@ -155,54 +169,64 @@
assertRegistered(name, false);
}
-TEST_F(CanControllerHalTest, IdentifierCompatibility) {
- using IdDisc = ICanController::BusConfiguration::InterfaceIdentifier::hidl_discriminator;
- static const std::map<InterfaceType, std::vector<IdDisc>> compatMatrix = {
- {InterfaceType::VIRTUAL, {IdDisc::address}},
- {InterfaceType::SOCKETCAN, {IdDisc::address, IdDisc::serialno}},
- {InterfaceType::SLCAN, {IdDisc::address, IdDisc::serialno}},
- {InterfaceType::INDEXED, {IdDisc::index}},
+TEST_F(CanControllerHalTest, ConfigCompatibility) {
+ // using random-ish addresses, which may not be valid - we can't test the success case
+ // TODO(b/146214370): move interfaceId constructors to a library
+ IfId virtualCfg = {};
+ virtualCfg.virtualif({"vcan70"});
+
+ IfId::Socketcan socketcanIfname = {};
+ socketcanIfname.ifname("can0");
+ IfId socketcanIfnameCfg = {};
+ socketcanIfnameCfg.socketcan(socketcanIfname);
+
+ IfId::Socketcan socketcanSerial = {};
+ socketcanSerial.serialno({"1234", "2345"});
+ IfId socketcanSerialCfg = {};
+ socketcanSerialCfg.socketcan(socketcanSerial);
+
+ IfId::Slcan slcanTtyname = {};
+ slcanTtyname.ttyname("/dev/ttyUSB0");
+ IfId slcanTtynameCfg = {};
+ slcanTtynameCfg.slcan(slcanTtyname);
+
+ IfId::Slcan slcanSerial = {};
+ slcanSerial.serialno({"dead", "beef"});
+ IfId slcanSerialCfg = {};
+ slcanSerialCfg.slcan(slcanSerial);
+
+ IfId indexedCfg = {};
+ indexedCfg.indexed({0});
+
+ static const std::vector<std::pair<InterfaceType, IfId>> compatMatrix = {
+ {InterfaceType::VIRTUAL, virtualCfg},
+ {InterfaceType::SOCKETCAN, socketcanIfnameCfg},
+ {InterfaceType::SOCKETCAN, socketcanSerialCfg},
+ {InterfaceType::SLCAN, slcanTtynameCfg},
+ {InterfaceType::SLCAN, slcanSerialCfg},
+ {InterfaceType::INDEXED, indexedCfg},
};
- static const std::vector<IdDisc> allDisc = {IdDisc::address, IdDisc::index, IdDisc::serialno};
- for (const auto [iftype, supported] : compatMatrix) {
- for (const auto iddisc : allDisc) {
- LOG(INFO) << "Compatibility testing: " << iftype << " / " << iddisc;
+ for (const auto [iftype, cfg] : compatMatrix) {
+ LOG(INFO) << "Compatibility testing: " << iftype << " / " << cfg;
- ICanController::BusConfiguration config = {};
- config.name = "compattestsrv";
- config.iftype = iftype;
- config.bitrate = 125000;
+ ICanController::BusConfig config = {};
+ config.name = "compattestsrv";
+ config.bitrate = 125000;
+ config.interfaceId = cfg;
- // using random-ish addresses, which may not be valid - we can't test the success case
- if (iddisc == IdDisc::address) {
- config.interfaceId.address("can0");
- } else if (iddisc == IdDisc::index) {
- config.interfaceId.index(0);
- } else if (iddisc == IdDisc::serialno) {
- config.interfaceId.serialno({"dummy", "dummier"});
- }
+ const auto upresult = mCanController->upInterface(config);
- const auto upresult = mCanController->upInterface(config);
+ if (!isSupported(iftype)) {
+ ASSERT_EQ(ICanController::Result::NOT_SUPPORTED, upresult);
+ continue;
+ }
+ ASSERT_NE(ICanController::Result::NOT_SUPPORTED, upresult);
- if (!isSupported(iftype)) {
- ASSERT_EQ(ICanController::Result::NOT_SUPPORTED, upresult);
- continue;
- }
- ASSERT_NE(ICanController::Result::NOT_SUPPORTED, upresult);
-
- bool isSupportedDisc =
- std::find(supported.begin(), supported.end(), iddisc) != supported.end();
- if (!isSupportedDisc) {
- ASSERT_EQ(ICanController::Result::BAD_ADDRESS, upresult);
- continue;
- }
-
- if (upresult == ICanController::Result::OK) {
- const auto dnresult = mCanController->downInterface(config.name);
- ASSERT_TRUE(dnresult);
- continue;
- }
+ if (upresult == ICanController::Result::OK) {
+ const auto dnresult = mCanController->downInterface(config.name);
+ ASSERT_TRUE(dnresult);
+ continue;
}
}
}
@@ -211,7 +235,7 @@
const std::string name = "";
assertRegistered(name, false);
- if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::UNKNOWN_ERROR)) {
+ if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::BAD_SERVICE_NAME)) {
GTEST_SKIP();
}
assertRegistered(name, false);
@@ -222,7 +246,7 @@
const std::string name = "ab012345678901234567890123456789c";
assertRegistered(name, false);
- if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::UNKNOWN_ERROR)) {
+ if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::BAD_SERVICE_NAME)) {
GTEST_SKIP();
}
assertRegistered(name, false);
@@ -232,7 +256,9 @@
const std::string name = mBusNames[0];
assertRegistered(name, false);
- if (!up(InterfaceType::VIRTUAL, name, "", ICanController::Result::BAD_ADDRESS)) GTEST_SKIP();
+ if (!up(InterfaceType::VIRTUAL, name, "", ICanController::Result::BAD_INTERFACE_ID)) {
+ GTEST_SKIP();
+ }
assertRegistered(name, false);
}
@@ -240,10 +266,30 @@
const std::string name = mBusNames[0];
assertRegistered(name, false);
- if (!up(InterfaceType::SOCKETCAN, name, "can87", ICanController::Result::BAD_ADDRESS)) {
+ if (!up(InterfaceType::SOCKETCAN, name, "can87", ICanController::Result::BAD_INTERFACE_ID)) {
GTEST_SKIP();
}
assertRegistered(name, false);
+
+ auto supported =
+ up(InterfaceType::SOCKETCAN, name, "", ICanController::Result::BAD_INTERFACE_ID);
+ ASSERT_TRUE(supported);
+ assertRegistered(name, false);
+}
+
+TEST_F(CanControllerHalTest, FailBadSlcanAddress) {
+ const std::string name = mBusNames[0];
+
+ assertRegistered(name, false);
+ if (!up(InterfaceType::SLCAN, name, "/dev/shouldnotexist123",
+ ICanController::Result::BAD_INTERFACE_ID)) {
+ GTEST_SKIP();
+ }
+ assertRegistered(name, false);
+
+ auto supported = up(InterfaceType::SLCAN, name, "", ICanController::Result::BAD_INTERFACE_ID);
+ ASSERT_TRUE(supported);
+ assertRegistered(name, false);
}
} // namespace android::hardware::automotive::can::V1_0::vts
diff --git a/automotive/can/1.0/vts/utils/include/can-vts-utils/can-hal-printers.h b/automotive/can/1.0/vts/utils/include/can-vts-utils/can-hal-printers.h
index 3c30744..383b54c 100644
--- a/automotive/can/1.0/vts/utils/include/can-vts-utils/can-hal-printers.h
+++ b/automotive/can/1.0/vts/utils/include/can-vts-utils/can-hal-printers.h
@@ -35,8 +35,7 @@
DEFINE_CAN_HAL_PRINTER(CanMessage, toString)
DEFINE_CAN_HAL_PRINTER(ErrorEvent, toString)
-DEFINE_CAN_HAL_PRINTER_SIMPLE(
- ICanController::BusConfiguration::InterfaceIdentifier::hidl_discriminator, int)
+DEFINE_CAN_HAL_PRINTER(ICanController::BusConfig::InterfaceId, toString);
DEFINE_CAN_HAL_PRINTER(ICanController::InterfaceType, toString)
DEFINE_CAN_HAL_PRINTER(ICanController::Result, toString)
DEFINE_CAN_HAL_PRINTER(Result, toString)
diff --git a/automotive/evs/1.1/Android.bp b/automotive/evs/1.1/Android.bp
index c850c91..17f31e4 100644
--- a/automotive/evs/1.1/Android.bp
+++ b/automotive/evs/1.1/Android.bp
@@ -10,9 +10,11 @@
"types.hal",
"IEvsCamera.hal",
"IEvsCameraStream.hal",
+ "IEvsDisplay.hal",
"IEvsEnumerator.hal",
],
interfaces: [
+ "android.frameworks.automotive.display@1.0",
"android.hardware.automotive.evs@1.0",
"android.hardware.camera.device@3.2",
"android.hardware.graphics.common@1.0",
diff --git a/automotive/evs/1.1/IEvsCamera.hal b/automotive/evs/1.1/IEvsCamera.hal
index fc68e60..38e6c42 100644
--- a/automotive/evs/1.1/IEvsCamera.hal
+++ b/automotive/evs/1.1/IEvsCamera.hal
@@ -178,4 +178,41 @@
* values as backing camera devices.
*/
getIntParameter(CameraParam id) generates(EvsResult result, vec<int32_t> value);
+
+ /**
+ * Request driver specific information from the HAL implementation.
+ *
+ * The values allowed for opaqueIdentifier are driver specific,
+ * but no value passed in may crash the driver. The driver should
+ * return EvsResult::INVALID_ARG for any unrecognized opaqueIdentifier.
+ *
+ * @param opaqueIdentifier An unique identifier of the information to
+ * request.
+ * @return result EvsResult::OK if the driver recognizes a given
+ * identifier.
+ * EvsResult::INVALID_ARG, otherwise.
+ * @return value Requested information. Zero-size vector is
+ * returned if the driver does not recognize a
+ * given identifier.
+ */
+ getExtendedInfo_1_1(uint32_t opaqueIdentifier)
+ generates (EvsResult result, vec<uint8_t> value);
+
+ /**
+ * Send a driver specific value to the HAL implementation.
+ *
+ * This extension is provided to facilitate car specific
+ * extensions, but no HAL implementation may require this call
+ * in order to function in a default state.
+ * INVALID_ARG is returned if the opaqueValue is not meaningful to
+ * the driver implementation.
+ *
+ * @param opaqueIdentifier An unique identifier of the information to
+ * program.
+ * opaqueValue A value to program.
+ * @return result EvsResult::OK is returned if this call is successful.
+ * EvsResult::INVALID_ARG, otherwise.
+ */
+ setExtendedInfo_1_1(uint32_t opaqueIdentifier, vec<uint8_t> opaqueValue)
+ generates (EvsResult result);
};
diff --git a/automotive/evs/1.1/IEvsDisplay.hal b/automotive/evs/1.1/IEvsDisplay.hal
new file mode 100644
index 0000000..38da536
--- /dev/null
+++ b/automotive/evs/1.1/IEvsDisplay.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.automotive.evs@1.1;
+
+import @1.0::IEvsDisplay;
+import @1.0::EvsResult;
+import android.frameworks.automotive.display@1.0::HwDisplayConfig;
+import android.frameworks.automotive.display@1.0::HwDisplayState;
+
+/**
+ * Represents a single display.
+ */
+interface IEvsDisplay extends @1.0::IEvsDisplay {
+ /**
+ * Returns the description of this display.
+ *
+ * @return cfg Current configuration of this display.
+ * @return state Current state of this display.
+ */
+ getDisplayInfo_1_1() generates (HwDisplayConfig cfg, HwDisplayState state);
+};
diff --git a/automotive/evs/1.1/IEvsEnumerator.hal b/automotive/evs/1.1/IEvsEnumerator.hal
index 1695821..84dd21f 100644
--- a/automotive/evs/1.1/IEvsEnumerator.hal
+++ b/automotive/evs/1.1/IEvsEnumerator.hal
@@ -17,6 +17,7 @@
package android.hardware.automotive.evs@1.1;
import IEvsCamera;
+import IEvsDisplay;
import @1.0::IEvsEnumerator;
import @1.0::EvsResult;
import android.hardware.camera.device@3.2::Stream;
@@ -47,4 +48,32 @@
* configured differently by another client.
*/
openCamera_1_1(string cameraId, Stream streamCfg) generates (IEvsCamera evsCamera);
+
+ /**
+ * Tells whether this is EVS manager or HAL implementation.
+ *
+ * @return result False for EVS manager implementations and true for all others.
+ */
+ isHardware() generates (bool result);
+
+ /**
+ * Returns a list of all EVS displays available to the system
+ *
+ * @return displayIds Identifiers of available displays.
+ */
+ getDisplayIdList() generates (vec<uint8_t> displayIds);
+
+ /**
+ * Get exclusive access to IEvsDisplay for the system
+ *
+ * There can be more than one EVS display objects for the system and this function
+ * requests access to the display identified by a given ID. If the target EVS display
+ * is not available or is already in use the old instance shall be closed and give
+ * the new caller exclusive access.
+ * When done using the display, the caller may release it by calling closeDisplay().
+ *
+ * @param id Target display identifier.
+ * @return display EvsDisplay object to be used.
+ */
+ openDisplay_1_1(uint8_t id) generates (IEvsDisplay display);
};
diff --git a/automotive/evs/1.1/default/Android.bp b/automotive/evs/1.1/default/Android.bp
index 88fd657..a35c9db 100644
--- a/automotive/evs/1.1/default/Android.bp
+++ b/automotive/evs/1.1/default/Android.bp
@@ -27,6 +27,10 @@
"libutils",
"libcamera_metadata",
"libtinyxml2",
+ "android.hidl.token@1.0-utils",
+ "android.frameworks.automotive.display@1.0",
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.graphics.bufferqueue@2.0",
],
cflags: [
@@ -41,7 +45,7 @@
prebuilt_etc {
name: "evs_default_configuration.xml",
-
+ soc_specific: true,
src: "resources/evs_default_configuration.xml",
sub_dir: "automotive/evs",
}
diff --git a/automotive/evs/1.1/default/EvsCamera.cpp b/automotive/evs/1.1/default/EvsCamera.cpp
index b7e4efa..5196c95 100644
--- a/automotive/evs/1.1/default/EvsCamera.cpp
+++ b/automotive/evs/1.1/default/EvsCamera.cpp
@@ -280,7 +280,7 @@
return EvsResult::OK;
}
-Return<EvsResult> EvsCamera::forceMaster(const sp<IEvsDisplay>& ) {
+Return<EvsResult> EvsCamera::forceMaster(const sp<IEvsDisplay_1_0>& ) {
// Default implementation does not expect multiple subscribers and therefore
// return a success code always.
return EvsResult::OK;
@@ -334,6 +334,27 @@
}
+Return<EvsResult> EvsCamera::setExtendedInfo_1_1(uint32_t opaqueIdentifier,
+ const hidl_vec<uint8_t>& opaqueValue) {
+ // Default implementation does not use an extended info.
+ (void)opaqueIdentifier;
+ (void)opaqueValue;
+ return EvsResult::INVALID_ARG;
+}
+
+
+Return<void> EvsCamera::getExtendedInfo_1_1(uint32_t opaqueIdentifier,
+ getExtendedInfo_1_1_cb _hidl_cb) {
+ // Default implementation does not use an extended info.
+ (void)opaqueIdentifier;
+
+ hidl_vec<uint8_t> value;
+ _hidl_cb(EvsResult::INVALID_ARG, value);
+ return Void();
+}
+
+
+
bool EvsCamera::setAvailableFrames_Locked(unsigned bufferCount) {
if (bufferCount < 1) {
ALOGE("Ignoring request to set buffer count to zero");
diff --git a/automotive/evs/1.1/default/EvsCamera.h b/automotive/evs/1.1/default/EvsCamera.h
index 72a1b57..0fa83b4 100644
--- a/automotive/evs/1.1/default/EvsCamera.h
+++ b/automotive/evs/1.1/default/EvsCamera.h
@@ -20,7 +20,7 @@
#include <android/hardware/automotive/evs/1.1/types.h>
#include <android/hardware/automotive/evs/1.1/IEvsCamera.h>
#include <android/hardware/automotive/evs/1.1/IEvsCameraStream.h>
-#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>
+#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
#include <ui/GraphicBuffer.h>
#include <thread>
@@ -33,7 +33,8 @@
using IEvsCameraStream_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCameraStream;
using ::android::hardware::automotive::evs::V1_0::EvsResult;
using ::android::hardware::automotive::evs::V1_0::CameraDesc;
-using ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
+using IEvsDisplay_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
+using IEvsDisplay_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsDisplay;
namespace android {
@@ -68,7 +69,7 @@
Return<EvsResult> resumeVideoStream() override;
Return<EvsResult> doneWithFrame_1_1(const hidl_vec<BufferDesc_1_1>& buffer) override;
Return<EvsResult> setMaster() override;
- Return<EvsResult> forceMaster(const sp<IEvsDisplay>& display) override;
+ Return<EvsResult> forceMaster(const sp<IEvsDisplay_1_0>& display) override;
Return<EvsResult> unsetMaster() override;
Return<void> getParameterList(getParameterList_cb _hidl_cb) override;
Return<void> getIntParameterRange(CameraParam id,
@@ -77,6 +78,10 @@
setIntParameter_cb _hidl_cb) override;
Return<void> getIntParameter(CameraParam id,
getIntParameter_cb _hidl_cb) override;
+ Return<EvsResult> setExtendedInfo_1_1(uint32_t opaqueIdentifier,
+ const hidl_vec<uint8_t>& opaqueValue) override;
+ Return<void> getExtendedInfo_1_1(uint32_t opaqueIdentifier,
+ getExtendedInfo_1_1_cb _hidl_cb) override;
static sp<EvsCamera> Create(const char *deviceName);
static sp<EvsCamera> Create(const char *deviceName,
diff --git a/automotive/evs/1.1/default/EvsDisplay.cpp b/automotive/evs/1.1/default/EvsDisplay.cpp
index 74c099a..2b5a4a9 100644
--- a/automotive/evs/1.1/default/EvsDisplay.cpp
+++ b/automotive/evs/1.1/default/EvsDisplay.cpp
@@ -21,6 +21,8 @@
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
+using ::android::frameworks::automotive::display::V1_0::HwDisplayConfig;
+using ::android::frameworks::automotive::display::V1_0::HwDisplayState;
namespace android {
namespace hardware {
@@ -31,6 +33,13 @@
EvsDisplay::EvsDisplay() {
+ EvsDisplay(nullptr, 0);
+}
+
+
+EvsDisplay::EvsDisplay(sp<IAutomotiveDisplayProxyService> pDisplayProxy, uint64_t displayId)
+ : mDisplayProxy(pDisplayProxy),
+ mDisplayId(displayId) {
ALOGD("EvsDisplay instantiated");
// Set up our self description
@@ -327,6 +336,18 @@
}
+Return<void> EvsDisplay::getDisplayInfo_1_1(getDisplayInfo_1_1_cb _info_cb) {
+ if (mDisplayProxy != nullptr) {
+ return mDisplayProxy->getDisplayInfo(mDisplayId, _info_cb);
+ } else {
+ HwDisplayConfig nullConfig;
+ HwDisplayState nullState;
+ _info_cb(nullConfig, nullState);
+ return Void();
+ }
+}
+
+
} // namespace implementation
} // namespace V1_1
} // namespace evs
diff --git a/automotive/evs/1.1/default/EvsDisplay.h b/automotive/evs/1.1/default/EvsDisplay.h
index 2a56535..9b2ed90 100644
--- a/automotive/evs/1.1/default/EvsDisplay.h
+++ b/automotive/evs/1.1/default/EvsDisplay.h
@@ -17,14 +17,16 @@
#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSDISPLAY_H
#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSDISPLAY_H
-#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>
+#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
+#include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
#include <ui/GraphicBuffer.h>
-using ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
+using ::android::hardware::automotive::evs::V1_1::IEvsDisplay;
using ::android::hardware::automotive::evs::V1_0::DisplayDesc;
using ::android::hardware::automotive::evs::V1_0::DisplayState;
using ::android::hardware::automotive::evs::V1_0::EvsResult;
using BufferDesc_1_0 = ::android::hardware::automotive::evs::V1_0::BufferDesc;
+using android::frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService;
namespace android {
namespace hardware {
@@ -43,9 +45,12 @@
Return<void> getTargetBuffer(getTargetBuffer_cb _hidl_cb) override;
Return<EvsResult> returnTargetBufferForDisplay(const BufferDesc_1_0& buffer) override;
+ // Methods from ::android::hardware::automotive::evs::V1_1::IEvsDisplay follow.
+ Return<void> getDisplayInfo_1_1(getDisplayInfo_1_1_cb _info_cb) override;
// Implementation details
EvsDisplay();
+ EvsDisplay(sp<IAutomotiveDisplayProxyService> pDisplayProxy, uint64_t displayId);
virtual ~EvsDisplay() override;
void forceShutdown(); // This gets called if another caller "steals" ownership of the display
@@ -60,6 +65,9 @@
DisplayState mRequestedState = DisplayState::NOT_VISIBLE;
std::mutex mAccessLock;
+
+ sp<IAutomotiveDisplayProxyService> mDisplayProxy;
+ uint64_t mDisplayId;
};
} // namespace implementation
diff --git a/automotive/evs/1.1/default/EvsEnumerator.cpp b/automotive/evs/1.1/default/EvsEnumerator.cpp
index a010729..0319560 100644
--- a/automotive/evs/1.1/default/EvsEnumerator.cpp
+++ b/automotive/evs/1.1/default/EvsEnumerator.cpp
@@ -34,18 +34,38 @@
std::list<EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
wp<EvsDisplay> EvsEnumerator::sActiveDisplay;
unique_ptr<ConfigManager> EvsEnumerator::sConfigManager;
+sp<IAutomotiveDisplayProxyService> EvsEnumerator::sDisplayProxyService;
+std::unordered_map<uint8_t, uint64_t> EvsEnumerator::sDisplayPortList;
-EvsEnumerator::EvsEnumerator() {
+EvsEnumerator::EvsEnumerator(sp<IAutomotiveDisplayProxyService> windowService) {
ALOGD("EvsEnumerator created");
// Add sample camera data to our list of cameras
// In a real driver, this would be expected to can the available hardware
sConfigManager =
- ConfigManager::Create("/etc/automotive/evs/evs_sample_configuration.xml");
+ ConfigManager::Create("/vendor/etc/automotive/evs/evs_default_configuration.xml");
+
+ // Add available cameras
for (auto v : sConfigManager->getCameraList()) {
sCameraList.emplace_back(v.c_str());
}
+
+ if (sDisplayProxyService == nullptr) {
+ /* sets a car-window service handle */
+ sDisplayProxyService = windowService;
+ }
+
+ // Add available displays
+ if (sDisplayProxyService != nullptr) {
+ // Get a display ID list.
+ sDisplayProxyService->getDisplayIdList([](const auto& displayIds) {
+ for (const auto& id : displayIds) {
+ const auto port = id & 0xF;
+ sDisplayPortList.insert_or_assign(port, id);
+ }
+ });
+ }
}
@@ -165,7 +185,7 @@
}
-Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay() {
+Return<sp<IEvsDisplay_1_0>> EvsEnumerator::openDisplay() {
ALOGD("openDisplay");
// If we already have a display active, then we need to shut it down so we can
@@ -185,7 +205,42 @@
}
-Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay>& pDisplay) {
+Return<void> EvsEnumerator::getDisplayIdList(getDisplayIdList_cb _list_cb) {
+ hidl_vec<uint8_t> ids;
+
+ ids.resize(sDisplayPortList.size());
+ unsigned i = 0;
+ for (const auto& [port, id] : sDisplayPortList) {
+ ids[i++] = port;
+ }
+
+ _list_cb(ids);
+ return Void();
+}
+
+
+Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay_1_1(uint8_t port) {
+ ALOGD("%s", __FUNCTION__);
+
+ // If we already have a display active, then we need to shut it down so we can
+ // give exclusive access to the new caller.
+ sp<EvsDisplay> pActiveDisplay = sActiveDisplay.promote();
+ if (pActiveDisplay != nullptr) {
+ ALOGW("Killing previous display because of new caller");
+ closeDisplay(pActiveDisplay);
+ }
+
+ // Create a new display interface and return it
+ pActiveDisplay = new EvsDisplay(sDisplayProxyService, sDisplayPortList[port]);
+ sActiveDisplay = pActiveDisplay;
+
+ ALOGD("Returning new EvsDisplay object %p", pActiveDisplay.get());
+ return pActiveDisplay;
+}
+
+
+
+Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay_1_0>& pDisplay) {
ALOGD("closeDisplay");
// Do we still have a display object we think should be active?
diff --git a/automotive/evs/1.1/default/EvsEnumerator.h b/automotive/evs/1.1/default/EvsEnumerator.h
index 475ec76..9415953 100644
--- a/automotive/evs/1.1/default/EvsEnumerator.h
+++ b/automotive/evs/1.1/default/EvsEnumerator.h
@@ -19,19 +19,22 @@
#include <android/hardware/automotive/evs/1.1/IEvsEnumerator.h>
#include <android/hardware/automotive/evs/1.1/IEvsCamera.h>
+#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
+#include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
#include <list>
#include "ConfigManager.h"
using ::android::hardware::automotive::evs::V1_0::EvsResult;
-using ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
using ::android::hardware::automotive::evs::V1_0::DisplayState;
using IEvsCamera_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsCamera;
using IEvsCamera_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCamera;
using CameraDesc_1_0 = ::android::hardware::automotive::evs::V1_0::CameraDesc;
using CameraDesc_1_1 = ::android::hardware::automotive::evs::V1_1::CameraDesc;
-
+using IEvsDisplay_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
+using IEvsDisplay_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsDisplay;
+using android::frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService;
namespace android {
namespace hardware {
@@ -51,17 +54,20 @@
Return<void> getCameraList(getCameraList_cb _hidl_cb) override;
Return<sp<IEvsCamera_1_0>> openCamera(const hidl_string& cameraId) override;
Return<void> closeCamera(const ::android::sp<IEvsCamera_1_0>& carCamera) override;
- Return<sp<IEvsDisplay>> openDisplay() override;
- Return<void> closeDisplay(const ::android::sp<IEvsDisplay>& display) override;
+ Return<sp<IEvsDisplay_1_0>> openDisplay() override;
+ Return<void> closeDisplay(const ::android::sp<IEvsDisplay_1_0>& display) override;
Return<DisplayState> getDisplayState() override;
// Methods from ::android::hardware::automotive::evs::V1_1::IEvsEnumerator follow.
Return<void> getCameraList_1_1(getCameraList_1_1_cb _hidl_cb) override;
Return<sp<IEvsCamera_1_1>> openCamera_1_1(const hidl_string& cameraId,
const Stream& streamCfg) override;
+ Return<bool> isHardware() override { return true; }
+ Return<void> getDisplayIdList(getDisplayIdList_cb _list_cb) override;
+ Return<sp<IEvsDisplay_1_1>> openDisplay_1_1(uint8_t port) override;
// Implementation details
- EvsEnumerator();
+ EvsEnumerator(sp<IAutomotiveDisplayProxyService> windowService = nullptr);
private:
// NOTE: All members values are static so that all clients operate on the same state
@@ -82,6 +88,10 @@
static wp<EvsDisplay> sActiveDisplay;
static unique_ptr<ConfigManager> sConfigManager;
+
+ static sp<IAutomotiveDisplayProxyService> sDisplayProxyService;
+ static std::unordered_map<uint8_t,
+ uint64_t> sDisplayPortList;
};
} // namespace implementation
diff --git a/automotive/evs/1.1/default/ServiceNames.h b/automotive/evs/1.1/default/ServiceNames.h
index 1178da5..84b1697 100644
--- a/automotive/evs/1.1/default/ServiceNames.h
+++ b/automotive/evs/1.1/default/ServiceNames.h
@@ -14,4 +14,4 @@
* limitations under the License.
*/
-const static char kEnumeratorServiceName[] = "EvsEnumeratorHw";
+const static char kEnumeratorServiceName[] = "hw/0";
diff --git a/automotive/evs/1.1/default/service.cpp b/automotive/evs/1.1/default/service.cpp
index 5135864..374b646 100644
--- a/automotive/evs/1.1/default/service.cpp
+++ b/automotive/evs/1.1/default/service.cpp
@@ -34,7 +34,6 @@
// Generated HIDL files
using android::hardware::automotive::evs::V1_1::IEvsEnumerator;
-using android::hardware::automotive::evs::V1_0::IEvsDisplay;
// The namespace in which all our implementation code lives
using namespace android::hardware::automotive::evs::V1_1::implementation;
diff --git a/automotive/evs/1.1/types.hal b/automotive/evs/1.1/types.hal
index f88d223..aafdb70 100644
--- a/automotive/evs/1.1/types.hal
+++ b/automotive/evs/1.1/types.hal
@@ -69,6 +69,8 @@
* Time that this buffer is being filled.
*/
int64_t timestamp;
+
+ vec<uint8_t> metadata;
};
/**
diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index 4fc4e4c..2c8c1e1 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -54,9 +54,11 @@
#include <android/hardware/automotive/evs/1.1/IEvsCamera.h>
#include <android/hardware/automotive/evs/1.1/IEvsCameraStream.h>
#include <android/hardware/automotive/evs/1.1/IEvsEnumerator.h>
-#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>
+#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
#include <system/camera_metadata.h>
+#include <ui/DisplayConfig.h>
+#include <ui/DisplayState.h>
#include <VtsHalHidlTargetTestBase.h>
#include <VtsHalHidlTargetTestEnvBase.h>
@@ -76,6 +78,8 @@
using ::android::hardware::graphics::common::V1_0::PixelFormat;
using IEvsCamera_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsCamera;
using IEvsCamera_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCamera;
+using IEvsDisplay_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsDisplay;
+using IEvsDisplay_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsDisplay;
/*
* Plese note that this is different from what is defined in
@@ -117,7 +121,7 @@
pEnumerator = getService<IEvsEnumerator>(service_name);
ASSERT_NE(pEnumerator.get(), nullptr);
- mIsHwModule = !service_name.compare(kEnumeratorName);
+ mIsHwModule = pEnumerator->isHardware();
}
virtual void TearDown() override {
@@ -292,6 +296,17 @@
}
);
+ // Verify methods for extended info
+ const auto id = 0xFFFFFFFF; // meaningless id
+ hidl_vec<uint8_t> values;
+ auto err = pCam->setExtendedInfo_1_1(id, values);
+ ASSERT_EQ(EvsResult::INVALID_ARG, err);
+
+ pCam->getExtendedInfo_1_1(id, [](const auto& result, const auto& data) {
+ ASSERT_EQ(EvsResult::INVALID_ARG, result);
+ ASSERT_EQ(0, data.size());
+ });
+
// Explicitly close the camera so resources are released right away
pEnumerator->closeCamera(pCam);
}
@@ -567,9 +582,30 @@
// output format.
Stream nullCfg = {};
- // Request exclusive access to the EVS display
- sp<IEvsDisplay> pDisplay = pEnumerator->openDisplay();
+ // Request available display IDs
+ uint8_t targetDisplayId = 0;
+ pEnumerator->getDisplayIdList([&targetDisplayId](auto ids) {
+ ASSERT_GT(ids.size(), 0);
+ targetDisplayId = ids[0];
+ });
+
+ // Request exclusive access to the first EVS display
+ sp<IEvsDisplay_1_1> pDisplay = pEnumerator->openDisplay_1_1(targetDisplayId);
ASSERT_NE(pDisplay, nullptr);
+ ALOGI("Display %d is in use.", targetDisplayId);
+
+ // Get the display descriptor
+ pDisplay->getDisplayInfo_1_1([](const auto& config, const auto& state) {
+ android::DisplayConfig* pConfig = (android::DisplayConfig*)config.data();
+ const auto width = pConfig->resolution.getWidth();
+ const auto height = pConfig->resolution.getHeight();
+ ALOGI(" Resolution: %dx%d", width, height);
+ ASSERT_GT(width, 0);
+ ASSERT_GT(height, 0);
+
+ android::ui::DisplayState* pState = (android::ui::DisplayState*)state.data();
+ ASSERT_NE(pState->layerStack, -1);
+ });
// Test each reported camera
for (auto&& cam: cameraInfo) {
@@ -1556,7 +1592,7 @@
Stream nullCfg = {};
// Request exclusive access to the EVS display
- sp<IEvsDisplay> pDisplay = pEnumerator->openDisplay();
+ sp<IEvsDisplay_1_0> pDisplay = pEnumerator->openDisplay();
ASSERT_NE(pDisplay, nullptr);
// Test each reported camera
@@ -1920,7 +1956,7 @@
loadCameraList();
// Request exclusive access to the EVS display
- sp<IEvsDisplay> pDisplay = pEnumerator->openDisplay();
+ sp<IEvsDisplay_1_0> pDisplay = pEnumerator->openDisplay();
ASSERT_NE(pDisplay, nullptr);
// Test each reported camera
diff --git a/automotive/evs/1.1/vts/fuzzing/Android.bp b/automotive/evs/1.1/vts/fuzzing/Android.bp
new file mode 100644
index 0000000..48427ee
--- /dev/null
+++ b/automotive/evs/1.1/vts/fuzzing/Android.bp
@@ -0,0 +1,50 @@
+//
+// 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.
+//
+
+cc_defaults {
+ name: "android.hardware.automotive.evs@fuzz-defaults",
+ defaults: ["VtsHalTargetTestDefaults"],
+ shared_libs: [
+ "libui",
+ "libcamera_metadata",
+ ],
+ static_libs: [
+ "android.hardware.automotive.evs@1.0",
+ "android.hardware.automotive.evs@1.1",
+ "android.hardware.automotive.evs@common-default-lib",
+ "android.hardware.graphics.common@1.0",
+ "android.hardware.graphics.common@1.1",
+ "android.hardware.graphics.common@1.2",
+ "android.hardware.camera.device@3.2",
+ ],
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+}
+
+cc_fuzz {
+ name: "VtsHalEvsV1_1CameraOpenFuzz",
+ defaults: ["android.hardware.automotive.evs@fuzz-defaults"],
+ srcs: [
+ "VtsHalEvsV1_1CameraOpenFuzz.cpp",
+ ],
+ fuzz_config: {
+ // wait for Haiku device ready
+ fuzz_on_haiku_device: false,
+ fuzz_on_haiku_host: false,
+ },
+}
diff --git a/automotive/evs/1.1/vts/fuzzing/VtsHalEvsV1_1CameraOpenFuzz.cpp b/automotive/evs/1.1/vts/fuzzing/VtsHalEvsV1_1CameraOpenFuzz.cpp
new file mode 100644
index 0000000..4f05d21
--- /dev/null
+++ b/automotive/evs/1.1/vts/fuzzing/VtsHalEvsV1_1CameraOpenFuzz.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "common.h"
+
+using ::android::hardware::automotive::evs::V1_0::DisplayDesc;
+using ::android::hardware::camera::device::V3_2::Stream;
+using IEvsCamera_1_1 = ::android::hardware::automotive::evs::V1_1::IEvsCamera;
+
+extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
+ UNUSED(argc);
+ UNUSED(argv);
+ pEnumerator = IEvsEnumerator::getService(kEnumeratorName);
+ sp<EvsDeathRecipient> dr = new EvsDeathRecipient();
+
+ pEnumerator->linkToDeath(dr, 0);
+
+ loadCameraList();
+ return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ std::vector<sp<IEvsCamera_1_1>> camList;
+ Stream nullCfg = {};
+
+ while (fdp.remaining_bytes() > 4) {
+ switch (fdp.ConsumeIntegralInRange<uint32_t>(0, 3)) {
+ case 0: // open camera
+ {
+ uint32_t whichCam = fdp.ConsumeIntegralInRange<uint32_t>(0, cameraInfo.size() - 1);
+ sp<IEvsCamera_1_1> pCam =
+ IEvsCamera_1_1::castFrom(pEnumerator->openCamera_1_1(
+ cameraInfo[whichCam].v1.cameraId, nullCfg))
+ .withDefault(nullptr);
+ camList.emplace_back(pCam);
+ break;
+ }
+ case 1: // close camera
+ {
+ if (!camList.empty()) {
+ uint32_t whichCam = fdp.ConsumeIntegralInRange<uint32_t>(0, camList.size() - 1);
+ pEnumerator->closeCamera(camList[whichCam]);
+ }
+ break;
+ }
+ case 2: // get camera info
+ {
+ if (!camList.empty()) {
+ uint32_t whichCam = fdp.ConsumeIntegralInRange<uint32_t>(0, camList.size() - 1);
+ camList[whichCam]->getCameraInfo_1_1([](CameraDesc desc) { UNUSED(desc); });
+ }
+ break;
+ }
+ case 3: // setMaxFramesInFlight
+ {
+ if (!camList.empty()) {
+ uint32_t whichCam = fdp.ConsumeIntegralInRange<uint32_t>(0, camList.size() - 1);
+ int32_t numFrames = fdp.ConsumeIntegral<int32_t>();
+ camList[whichCam]->setMaxFramesInFlight(numFrames);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/automotive/evs/1.1/vts/fuzzing/common.h b/automotive/evs/1.1/vts/fuzzing/common.h
new file mode 100644
index 0000000..af6fd54
--- /dev/null
+++ b/automotive/evs/1.1/vts/fuzzing/common.h
@@ -0,0 +1,57 @@
+/*
+ * 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 "VtsHalEvsTest"
+#define UNUSED(x) (void)(x)
+
+const static char kEnumeratorName[] = "EvsEnumeratorHw";
+
+#include <android/hardware/automotive/evs/1.1/IEvsEnumerator.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <hidl/HidlTransportSupport.h>
+#include <utils/StrongPointer.h>
+
+using namespace ::android::hardware::automotive::evs::V1_1;
+
+using ::android::sp;
+using ::android::hardware::hidl_death_recipient;
+using ::android::hardware::hidl_vec;
+
+static sp<IEvsEnumerator> pEnumerator;
+static std::vector<CameraDesc> cameraInfo;
+
+class EvsDeathRecipient : public hidl_death_recipient {
+ public:
+ void serviceDied(uint64_t /*cookie*/,
+ const android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) override {
+ abort();
+ }
+};
+
+void loadCameraList() {
+ // SetUp() must run first!
+ assert(pEnumerator != nullptr);
+
+ // Get the camera list
+ pEnumerator->getCameraList_1_1([](hidl_vec<CameraDesc> cameraList) {
+ ALOGI("Camera list callback received %zu cameras", cameraList.size());
+ cameraInfo.reserve(cameraList.size());
+ for (auto&& cam : cameraList) {
+ ALOGI("Found camera %s", cam.v1.cameraId.c_str());
+ cameraInfo.push_back(cam);
+ }
+ });
+}
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index a94a37e..8e57901 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -47,6 +47,9 @@
"common/src/VehicleUtils.cpp",
"common/src/VmsUtils.cpp",
],
+ shared_libs: [
+ "libbase",
+ ],
local_include_dirs: ["common/include/vhal_v2_0"],
export_include_dirs: ["common/include"],
}
@@ -82,20 +85,6 @@
],
}
-// VHal virtualization utils
-cc_library_static {
- name: "android.hardware.automotive.vehicle@2.0-virtualization-utils",
- vendor: true,
- defaults: ["vhal_v2_0_defaults"],
- srcs: [
- "impl/vhal_v2_0/virtualization/Utils.cpp",
- ],
- export_include_dirs: ["impl"],
- shared_libs: [
- "libbase",
- ],
-}
-
cc_test {
name: "android.hardware.automotive.vehicle@2.0-manager-unit-tests",
vendor: true,
@@ -109,6 +98,9 @@
"tests/VehiclePropConfigIndex_test.cpp",
"tests/VmsUtils_test.cpp",
],
+ shared_libs: [
+ "libbase",
+ ],
header_libs: ["libbase_headers"],
test_suites: ["general-tests"],
}
@@ -147,59 +139,3 @@
"libqemu_pipe",
],
}
-
-cc_binary {
- name: "android.hardware.automotive.vehicle@2.0-virtualization-service",
- defaults: ["vhal_v2_0_defaults"],
- init_rc: ["android.hardware.automotive.vehicle@2.0-virtualization-service.rc"],
- vendor: true,
- relative_install_path: "hw",
- srcs: [
- "impl/vhal_v2_0/virtualization/GrpcVehicleClient.cpp",
- "VirtualizedVehicleService.cpp",
- ],
- shared_libs: [
- "libbase",
- "libcutils",
- "libjsoncpp",
- "libprotobuf-cpp-full",
- "libgrpc++",
- ],
- static_libs: [
- "android.hardware.automotive.vehicle@2.0-manager-lib",
- "android.hardware.automotive.vehicle@2.0-default-impl-lib",
- "android.hardware.automotive.vehicle@2.0-grpc",
- "android.hardware.automotive.vehicle@2.0-virtualization-utils",
- "libqemu_pipe",
- ],
- cflags: [
- "-Wno-unused-parameter"
- ],
-}
-
-cc_binary {
- name: "android.hardware.automotive.vehicle@2.0-virtualization-grpc-server",
- init_rc: ["android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc"],
- defaults: ["vhal_v2_0_defaults"],
- vendor: true,
- relative_install_path: "hw",
- srcs: [
- "impl/vhal_v2_0/virtualization/GrpcVehicleServer.cpp",
- "VirtualizationGrpcServer.cpp",
- ],
- shared_libs: [
- "libbase",
- "libjsoncpp",
- "libprotobuf-cpp-full",
- "libgrpc++",
- ],
- static_libs: [
- "android.hardware.automotive.vehicle@2.0-manager-lib",
- "android.hardware.automotive.vehicle@2.0-default-impl-lib",
- "android.hardware.automotive.vehicle@2.0-grpc",
- "android.hardware.automotive.vehicle@2.0-virtualization-utils",
- ],
- cflags: [
- "-Wno-unused-parameter"
- ],
-}
diff --git a/automotive/vehicle/2.0/default/VirtualizationGrpcServer.cpp b/automotive/vehicle/2.0/default/VirtualizationGrpcServer.cpp
deleted file mode 100644
index cca65d9..0000000
--- a/automotive/vehicle/2.0/default/VirtualizationGrpcServer.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-#include <android-base/logging.h>
-#include <getopt.h>
-#include <unistd.h>
-
-#include "vhal_v2_0/virtualization/GrpcVehicleServer.h"
-#include "vhal_v2_0/virtualization/Utils.h"
-
-int main(int argc, char* argv[]) {
- namespace vhal_impl = android::hardware::automotive::vehicle::V2_0::impl;
-
- vhal_impl::VsockServerInfo serverInfo;
-
- // unique values to identify the options
- constexpr int OPT_VHAL_SERVER_CID = 1001;
- constexpr int OPT_VHAL_SERVER_PORT_NUMBER = 1002;
-
- struct option longOptions[] = {
- {"server_cid", 1, 0, OPT_VHAL_SERVER_CID},
- {"server_port", 1, 0, OPT_VHAL_SERVER_PORT_NUMBER},
- {nullptr, 0, nullptr, 0},
- };
-
- int optValue;
- while ((optValue = getopt_long_only(argc, argv, ":", longOptions, 0)) != -1) {
- switch (optValue) {
- case OPT_VHAL_SERVER_CID:
- serverInfo.serverCid = std::atoi(optarg);
- LOG(DEBUG) << "Vehicle HAL server CID: " << serverInfo.serverCid;
- break;
- case OPT_VHAL_SERVER_PORT_NUMBER:
- serverInfo.serverPort = std::atoi(optarg);
- LOG(DEBUG) << "Vehicle HAL server port: " << serverInfo.serverPort;
- break;
- default:
- // ignore other options
- break;
- }
- }
-
- if (serverInfo.serverCid == 0 || serverInfo.serverPort == 0) {
- LOG(FATAL) << "Invalid server information, CID: " << serverInfo.serverCid
- << "; port: " << serverInfo.serverPort;
- // Will abort after logging
- }
-
- auto server = vhal_impl::makeGrpcVehicleServer(vhal_impl::getVsockUri(serverInfo));
- server->Start();
- return 0;
-}
diff --git a/automotive/vehicle/2.0/default/VirtualizedVehicleService.cpp b/automotive/vehicle/2.0/default/VirtualizedVehicleService.cpp
deleted file mode 100644
index 1de81ae..0000000
--- a/automotive/vehicle/2.0/default/VirtualizedVehicleService.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.
- */
-
-#include <android-base/logging.h>
-#include <cutils/properties.h>
-#include <hidl/HidlTransportSupport.h>
-
-#include <vhal_v2_0/EmulatedVehicleConnector.h>
-#include <vhal_v2_0/EmulatedVehicleHal.h>
-#include <vhal_v2_0/VehicleHalManager.h>
-#include <vhal_v2_0/virtualization/GrpcVehicleClient.h>
-#include <vhal_v2_0/virtualization/Utils.h>
-
-using namespace android;
-using namespace android::hardware;
-using namespace android::hardware::automotive::vehicle::V2_0;
-
-int main(int argc, char* argv[]) {
- constexpr const char* VHAL_SERVER_CID_PROPERTY_KEY = "ro.vendor.vehiclehal.server.cid";
- constexpr const char* VHAL_SERVER_PORT_PROPERTY_KEY = "ro.vendor.vehiclehal.server.port";
-
- auto property_get_uint = [](const char* key, unsigned int default_value) {
- auto value = property_get_int64(key, default_value);
- if (value < 0 || value > UINT_MAX) {
- LOG(DEBUG) << key << ": " << value << " is out of bound, using default value '"
- << default_value << "' instead";
- return default_value;
- }
- return static_cast<unsigned int>(value);
- };
-
- impl::VsockServerInfo serverInfo{property_get_uint(VHAL_SERVER_CID_PROPERTY_KEY, 0),
- property_get_uint(VHAL_SERVER_PORT_PROPERTY_KEY, 0)};
-
- if (serverInfo.serverCid == 0 || serverInfo.serverPort == 0) {
- LOG(FATAL) << "Invalid server information, CID: " << serverInfo.serverCid
- << "; port: " << serverInfo.serverPort;
- // Will abort after logging
- }
-
- auto store = std::make_unique<VehiclePropertyStore>();
- auto connector = impl::makeGrpcVehicleClient(impl::getVsockUri(serverInfo));
- auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get());
- auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get());
- auto service = std::make_unique<VehicleHalManager>(hal.get());
-
- configureRpcThreadpool(4, true /* callerWillJoin */);
-
- LOG(INFO) << "Registering as service...";
- status_t status = service->registerAsService();
-
- if (status != OK) {
- LOG(ERROR) << "Unable to register vehicle service (" << status << ")";
- return 1;
- }
-
- LOG(INFO) << "Ready";
- joinRpcThreadpool();
-
- return 1;
-}
diff --git a/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc
deleted file mode 100644
index 29147ad..0000000
--- a/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server.rc
+++ /dev/null
@@ -1,10 +0,0 @@
-# It is an interim state to run GRPC server as an Android service.
-# Eventually it will run outside of Android (e.g., AGL),
-# so the command line arguments are expected, though not conventionally used in Android
-service vendor.vehicle-hal-2.0-server \
- /vendor/bin/hw/android.hardware.automotive.vehicle@2.0-virtualization-grpc-server \
- -server_cid ${ro.vendor.vehiclehal.server.cid:-0} \
- -server_port ${ro.vendor.vehiclehal.server.port:-0}
- class hal
- user vehicle_network
- group system inet
diff --git a/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-service.rc b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-service.rc
deleted file mode 100644
index 234de59..0000000
--- a/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-virtualization-service.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service vendor.vehicle-hal-2.0 /vendor/bin/hw/android.hardware.automotive.vehicle@2.0-virtualization-service
- class hal
- user vehicle_network
- group system inet
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleConnector.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleConnector.h
index d40f122..00b5afe 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleConnector.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleConnector.h
@@ -65,6 +65,12 @@
// updateStatus is true if and only if the value is
// generated by car (ECU/fake generator/injected)
virtual void onPropertyValue(const VehiclePropValue& value, bool updateStatus) = 0;
+
+ // Dump method forwarded from HIDL's debug()
+ // If implemented, it must return whether the caller should dump its state.
+ virtual bool dump(const hidl_handle& /* handle */, const hidl_vec<hidl_string>& /* options */) {
+ return true;
+ }
};
/**
@@ -97,6 +103,13 @@
// updateStatus is true if and only if the value is
// generated by car (ECU/fake generator/injected)
virtual void onPropertyValueFromCar(const VehiclePropValue& value, bool updateStatus) = 0;
+
+ // Dump method forwarded from HIDL's debug()
+ // If implemented, it must return whether the caller should dump its state.
+ virtual bool onDump(const hidl_handle& /* handle */,
+ const hidl_vec<hidl_string>& /* options */) {
+ return true;
+ }
};
/**
@@ -134,6 +147,10 @@
return this->onPropertyValue(value, updateStatus);
}
+ bool dump(const hidl_handle& handle, const hidl_vec<hidl_string>& options) override {
+ return this->onDump(handle, options);
+ }
+
// To be implemented:
// virtual std::vector<VehiclePropConfig> onGetAllPropertyConfig() = 0;
// virtual void onPropertyValue(const VehiclePropValue& value) = 0;
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHal.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHal.h
index fd28483..fe01867 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHal.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHal.h
@@ -70,6 +70,26 @@
*/
virtual void onCreate() {}
+ /**
+ * Dump method forwarded from HIDL's debug().
+ *
+ * By default it doesn't dump anything and let caller dump its properties, but it could be
+ * override to change the behavior. For example:
+ *
+ * - To augment caller's dump, it should dump its state and return true.
+ * - To not dump anything at all, it should just return false.
+ * - To provide custom dump (like dumping just specific state or executing a custom command),
+ * it should check if options is not empty, handle the options accordingly, then return false.
+ *
+ * @param handle handle used to dump the contents.
+ * @param options options passed to dump.
+ *
+ * @return whether the caller should dump its state.
+ */
+ virtual bool dump(const hidl_handle& /* handle */, const hidl_vec<hidl_string>& /* options */) {
+ return true;
+ }
+
void init(
VehiclePropValuePool* valueObjectPool,
const HalEventFunction& onHalEvent,
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHalManager.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHalManager.h
index c1e9e88..fcfe761 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHalManager.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHalManager.h
@@ -73,7 +73,9 @@
int32_t propId) override;
Return<void> debugDump(debugDump_cb _hidl_cb = nullptr) override;
-private:
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
+
+ private:
using VehiclePropValuePtr = VehicleHal::VehiclePropValuePtr;
// Returns true if needs to call again shortly.
using RetriableAction = std::function<bool()>;
@@ -96,6 +98,22 @@
bool checkReadPermission(const VehiclePropConfig &config) const;
void onAllClientsUnsubscribed(int32_t propertyId);
+ // Dump and commands
+ // TODO: most functions below (exception dump() and cmdSetOne()) should be const, but they rely
+ // on IVehicle.get(), which isn't...
+ void cmdDump(int fd, const hidl_vec<hidl_string>& options);
+ void cmdDumpOneProperty(int fd, int32_t prop, int32_t areaId);
+ void cmdDumpOneProperty(int fd, int rowNumber, const VehiclePropConfig& config);
+
+ static bool checkArgumentsSize(int fd, const hidl_vec<hidl_string>& options, size_t minSize);
+ static bool checkCallerHasWritePermissions(int fd);
+ static bool safelyParseInt(int fd, int index, std::string s, int* out);
+ void cmdHelp(int fd) const;
+ void cmdListAllProperties(int fd) const;
+ void cmdDumpAllProperties(int fd);
+ void cmdDumpSpecificProperties(int fd, const hidl_vec<hidl_string>& options);
+ void cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options);
+
static bool isSubscribable(const VehiclePropConfig& config,
SubscribeFlags flags);
static bool isSampleRateFixed(VehiclePropertyChangeMode mode);
diff --git a/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp b/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp
index 393d3ec..5bebd1e 100644
--- a/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp
@@ -21,11 +21,20 @@
#include <cmath>
#include <fstream>
-#include <android/log.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
#include <android/hardware/automotive/vehicle/2.0/BpHwVehicleCallback.h>
+#include <android/log.h>
+
+#include <hwbinder/IPCThreadState.h>
+
+#include <utils/SystemClock.h>
#include "VehicleUtils.h"
+// TODO: figure out how to include private/android_filesystem_config.h instead...
+#define AID_ROOT 0 /* traditional unix root user */
+
namespace android {
namespace hardware {
namespace automotive {
@@ -34,6 +43,10 @@
using namespace std::placeholders;
+using ::android::base::EqualsIgnoreCase;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+
constexpr std::chrono::milliseconds kHalEventBatchingTimeWindow(10);
const VehiclePropValue kEmptyValue{};
@@ -172,6 +185,251 @@
return Void();
}
+Return<void> VehicleHalManager::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
+ if (fd.getNativeHandle() == nullptr || fd->numFds == 0) {
+ ALOGE("Invalid parameters passed to debug()");
+ return Void();
+ }
+
+ bool shouldContinue = mHal->dump(fd, options);
+ if (!shouldContinue) {
+ ALOGI("Dumped HAL only");
+ return Void();
+ }
+
+ // Do our dump
+ cmdDump(fd->data[0], options);
+ return Void();
+}
+
+void VehicleHalManager::cmdDump(int fd, const hidl_vec<hidl_string>& options) {
+ if (options.size() == 0) {
+ cmdDumpAllProperties(fd);
+ return;
+ }
+ std::string option = options[0];
+ if (EqualsIgnoreCase(option, "--help")) {
+ cmdHelp(fd);
+ } else if (EqualsIgnoreCase(option, "--list")) {
+ cmdListAllProperties(fd);
+ } else if (EqualsIgnoreCase(option, "--get")) {
+ cmdDumpSpecificProperties(fd, options);
+ } else if (EqualsIgnoreCase(option, "--set")) {
+ cmdSetOneProperty(fd, options);
+ } else {
+ dprintf(fd, "Invalid option: %s\n", option.c_str());
+ }
+}
+
+bool VehicleHalManager::checkCallerHasWritePermissions(int fd) {
+ // Double check that's only called by root - it should be be blocked at the HIDL debug() level,
+ // but it doesn't hurt to make sure...
+ if (hardware::IPCThreadState::self()->getCallingUid() != AID_ROOT) {
+ dprintf(fd, "Must be root\n");
+ return false;
+ }
+ return true;
+}
+
+bool VehicleHalManager::checkArgumentsSize(int fd, const hidl_vec<hidl_string>& options,
+ size_t minSize) {
+ size_t size = options.size();
+ if (size >= minSize) {
+ return true;
+ }
+ dprintf(fd, "Invalid number of arguments: required at least %zu, got %zu\n", minSize, size);
+ return false;
+}
+
+bool VehicleHalManager::safelyParseInt(int fd, int index, std::string s, int* out) {
+ if (!android::base::ParseInt(s, out)) {
+ dprintf(fd, "non-integer argument at index %d: %s\n", index, s.c_str());
+ return false;
+ }
+ return true;
+}
+
+void VehicleHalManager::cmdHelp(int fd) const {
+ dprintf(fd, "Usage: \n\n");
+ dprintf(fd, "[no args]: dumps (id and value) all supported properties \n");
+ dprintf(fd, "--help: shows this help\n");
+ dprintf(fd, "--list: lists the ids of all supported properties\n");
+ dprintf(fd, "--get <PROP1> [PROP2] [PROPN]: dumps the value of specific properties \n");
+ // TODO: support other formats (int64, float, bytes)
+ dprintf(fd,
+ "--set <PROP> <i|s> <VALUE_1> [<i|s> <VALUE_N>] [a AREA_ID] : sets the value of "
+ "property PROP, using arbitrary number of key/value parameters (i for int32, "
+ "s for string) and an optional area.\n"
+ "Notice that the string value can be set just once, while the other can have multiple "
+ "values (so they're used in the respective array)\n");
+}
+
+void VehicleHalManager::cmdListAllProperties(int fd) const {
+ auto& halConfig = mConfigIndex->getAllConfigs();
+ size_t size = halConfig.size();
+ if (size == 0) {
+ dprintf(fd, "no properties to list\n");
+ return;
+ }
+ int i = 0;
+ dprintf(fd, "listing %zu properties\n", size);
+ for (const auto& config : halConfig) {
+ dprintf(fd, "%d: %d\n", ++i, config.prop);
+ }
+}
+
+void VehicleHalManager::cmdDumpAllProperties(int fd) {
+ auto& halConfig = mConfigIndex->getAllConfigs();
+ size_t size = halConfig.size();
+ if (size == 0) {
+ dprintf(fd, "no properties to dump\n");
+ return;
+ }
+ int rowNumber = 0;
+ dprintf(fd, "dumping %zu properties\n", size);
+ for (auto& config : halConfig) {
+ cmdDumpOneProperty(fd, ++rowNumber, config);
+ }
+}
+
+void VehicleHalManager::cmdDumpOneProperty(int fd, int rowNumber, const VehiclePropConfig& config) {
+ size_t numberAreas = config.areaConfigs.size();
+ if (numberAreas == 0) {
+ if (rowNumber > 0) {
+ dprintf(fd, "%d: ", rowNumber);
+ }
+ cmdDumpOneProperty(fd, config.prop, /* areaId= */ 0);
+ return;
+ }
+ for (size_t j = 0; j < numberAreas; ++j) {
+ if (rowNumber > 0) {
+ if (numberAreas > 1) {
+ dprintf(fd, "%d/%zu: ", rowNumber, j);
+ } else {
+ dprintf(fd, "%d: ", rowNumber);
+ }
+ }
+ cmdDumpOneProperty(fd, config.prop, config.areaConfigs[j].areaId);
+ }
+}
+
+void VehicleHalManager::cmdDumpSpecificProperties(int fd, const hidl_vec<hidl_string>& options) {
+ if (!checkArgumentsSize(fd, options, 2)) return;
+
+ // options[0] is the command itself...
+ int rowNumber = 0;
+ size_t size = options.size();
+ for (size_t i = 1; i < size; ++i) {
+ int prop;
+ if (!safelyParseInt(fd, i, options[i], &prop)) return;
+ const auto* config = getPropConfigOrNull(prop);
+ if (config == nullptr) {
+ dprintf(fd, "No property %d\n", prop);
+ continue;
+ }
+ if (size > 2) {
+ // Only show row number if there's more than 1
+ rowNumber++;
+ }
+ cmdDumpOneProperty(fd, rowNumber, *config);
+ }
+}
+
+void VehicleHalManager::cmdDumpOneProperty(int fd, int32_t prop, int32_t areaId) {
+ VehiclePropValue input;
+ input.prop = prop;
+ input.areaId = areaId;
+ auto callback = [&](StatusCode status, const VehiclePropValue& output) {
+ if (status == StatusCode::OK) {
+ dprintf(fd, "%s\n", toString(output).c_str());
+ } else {
+ dprintf(fd, "Could not get property %d. Error: %s\n", prop, toString(status).c_str());
+ }
+ };
+ get(input, callback);
+}
+
+void VehicleHalManager::cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options) {
+ if (!checkCallerHasWritePermissions(fd) || !checkArgumentsSize(fd, options, 3)) return;
+
+ size_t size = options.size();
+
+ // Syntax is --set PROP Type1 Value1 TypeN ValueN, so number of arguments must be even
+ if (size % 2 != 0) {
+ dprintf(fd, "must pass even number of arguments (passed %zu)\n", size);
+ return;
+ }
+ int numberValues = (size - 2) / 2;
+
+ VehiclePropValue prop;
+ if (!safelyParseInt(fd, 1, options[1], &prop.prop)) return;
+ prop.timestamp = elapsedRealtimeNano();
+ prop.status = VehiclePropertyStatus::AVAILABLE;
+
+ // First pass: calculate sizes
+ int sizeInt32 = 0;
+ int stringIndex = 0;
+ int areaIndex = 0;
+ for (int i = 2, kv = 1; kv <= numberValues; kv++) {
+ // iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step
+ std::string type = options[i];
+ std::string value = options[i + 1];
+ if (EqualsIgnoreCase(type, "i")) {
+ sizeInt32++;
+ } else if (EqualsIgnoreCase(type, "s")) {
+ if (stringIndex != 0) {
+ dprintf(fd,
+ "defining string value (%s) again at index %d (already defined at %d=%s"
+ ")\n",
+ value.c_str(), i, stringIndex, options[stringIndex + 1].c_str());
+ return;
+ }
+ stringIndex = i;
+ } else if (EqualsIgnoreCase(type, "a")) {
+ if (areaIndex != 0) {
+ dprintf(fd,
+ "defining area value (%s) again at index %d (already defined at %d=%s"
+ ")\n",
+ value.c_str(), i, areaIndex, options[areaIndex + 1].c_str());
+ return;
+ }
+ areaIndex = i;
+ } else {
+ dprintf(fd, "invalid (%s) type at index %d\n", type.c_str(), i);
+ return;
+ }
+ i += 2;
+ }
+ prop.value.int32Values.resize(sizeInt32);
+
+ // Second pass: populate it
+ int indexInt32 = 0;
+ for (int i = 2, kv = 1; kv <= numberValues; kv++) {
+ // iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step
+ int valueIndex = i + 1;
+ std::string type = options[i];
+ std::string value = options[valueIndex];
+ if (EqualsIgnoreCase(type, "i")) {
+ int safeInt;
+ if (!safelyParseInt(fd, valueIndex, value, &safeInt)) return;
+ prop.value.int32Values[indexInt32++] = safeInt;
+ } else if (EqualsIgnoreCase(type, "s")) {
+ prop.value.stringValue = value;
+ } else if (EqualsIgnoreCase(type, "a")) {
+ if (!safelyParseInt(fd, valueIndex, value, &prop.areaId)) return;
+ }
+ i += 2;
+ }
+ ALOGD("Setting prop %s", toString(prop).c_str());
+ auto status = set(prop);
+ if (status == StatusCode::OK) {
+ dprintf(fd, "Set property %s\n", toString(prop).c_str());
+ } else {
+ dprintf(fd, "Failed to set property %s: %s\n", toString(prop).c_str(),
+ toString(status).c_str());
+ }
+}
+
void VehicleHalManager::init() {
ALOGI("VehicleHalManager::init");
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 bf85da2..785f0e0 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
@@ -76,6 +76,7 @@
constexpr int WHEEL_FRONT_RIGHT = (int)VehicleAreaWheel::RIGHT_FRONT;
constexpr int WHEEL_REAR_LEFT = (int)VehicleAreaWheel::LEFT_REAR;
constexpr int WHEEL_REAR_RIGHT = (int)VehicleAreaWheel::RIGHT_REAR;
+constexpr int INITIAL_USER_INFO = (int)VehicleProperty::INITIAL_USER_INFO;
/**
* This property is used for test purpose to generate fake events. Here is the test package that
@@ -87,23 +88,23 @@
/**
* This property is used for test purpose to set properties' value from vehicle.
* For example: Mocking hard button press triggering a HVAC fan speed change.
- * Android set kSetPropertyFromVehcileForTest with an array of integer {HVAC_FAN_SPEED, value of
+ * Android set kSetPropertyFromVehicleForTest with an array of integer {HVAC_FAN_SPEED, value of
* fan speed} and a long value indicates the timestamp of the events .
* It only works with integer type properties.
*/
-const int32_t kSetIntPropertyFromVehcileForTest =
+const int32_t kSetIntPropertyFromVehicleForTest =
0x1112 | VehiclePropertyGroup::VENDOR | VehicleArea::GLOBAL | VehiclePropertyType::MIXED;
/**
* This property is used for test purpose to set properties' value from vehicle.
* It only works with float type properties.
*/
-const int32_t kSetFloatPropertyFromVehcileForTest =
+const int32_t kSetFloatPropertyFromVehicleForTest =
0x1113 | VehiclePropertyGroup::VENDOR | VehicleArea::GLOBAL | VehiclePropertyType::MIXED;
/**
* This property is used for test purpose to set properties' value from vehicle.
* It only works with boolean type properties.
*/
-const int32_t kSetBooleanPropertyFromVehcileForTest =
+const int32_t kSetBooleanPropertyFromVehicleForTest =
0x1114 | VehiclePropertyGroup::VENDOR | VehicleArea::GLOBAL | VehiclePropertyType::MIXED;
/**
@@ -695,7 +696,7 @@
{
.config =
{
- .prop = kSetIntPropertyFromVehcileForTest,
+ .prop = kSetIntPropertyFromVehicleForTest,
.access = VehiclePropertyAccess::WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
.configArray = {0, 0, 0, 2, 1, 0, 0, 0, 0},
@@ -705,7 +706,7 @@
{
.config =
{
- .prop = kSetFloatPropertyFromVehcileForTest,
+ .prop = kSetFloatPropertyFromVehicleForTest,
.access = VehiclePropertyAccess::WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
.configArray = {0, 0, 1, 0, 1, 0, 1, 0, 0},
@@ -715,7 +716,7 @@
{
.config =
{
- .prop = kSetBooleanPropertyFromVehcileForTest,
+ .prop = kSetBooleanPropertyFromVehicleForTest,
.access = VehiclePropertyAccess::WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
.configArray = {0, 1, 1, 0, 1, 0, 0, 0, 0},
@@ -990,6 +991,16 @@
(int)VehicleVendorPermission::PERMISSION_DEFAULT},
},
.initialValue = {.int32Values = {1}}},
+
+ {
+ .config =
+ {
+ .prop = toInt(VehicleProperty::INITIAL_USER_INFO),
+ .access = VehiclePropertyAccess::READ_WRITE,
+ .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+ },
+ },
+
};
} // impl
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp
index 222fe5e..7f90914 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp
@@ -13,6 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+#define LOG_TAG "automotive.vehicle@2.0-connector"
+
+#include <fstream>
+
#include <android-base/logging.h>
#include <utils/SystemClock.h>
@@ -202,8 +207,8 @@
case kGenerateFakeDataControllingProperty:
return handleGenerateFakeDataRequest(value);
- // set the value from vehcile side, used in end to end test.
- case kSetIntPropertyFromVehcileForTest: {
+ // set the value from vehicle side, used in end to end test.
+ case kSetIntPropertyFromVehicleForTest: {
auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::INT32, 1);
updatedPropValue->prop = value.value.int32Values[0];
updatedPropValue->value.int32Values[0] = value.value.int32Values[1];
@@ -212,7 +217,7 @@
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
- case kSetFloatPropertyFromVehcileForTest: {
+ case kSetFloatPropertyFromVehicleForTest: {
auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::FLOAT, 1);
updatedPropValue->prop = value.value.int32Values[0];
updatedPropValue->value.floatValues[0] = value.value.floatValues[0];
@@ -221,7 +226,7 @@
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
- case kSetBooleanPropertyFromVehcileForTest: {
+ case kSetBooleanPropertyFromVehicleForTest: {
auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::BOOLEAN, 1);
updatedPropValue->prop = value.value.int32Values[1];
updatedPropValue->value.int32Values[0] = value.value.int32Values[0];
@@ -261,6 +266,8 @@
break;
}
break;
+ case INITIAL_USER_INFO:
+ return onSetInitialUserInfo(value, updateStatus);
default:
break;
}
@@ -274,6 +281,135 @@
return StatusCode::OK;
}
+/**
+ * INITIAL_USER_INFO is called by Android when it starts, and it's expecting a property change
+ * indicating what the initial user should be.
+ *
+ * During normal circumstances, the emulator will reply right away, passing a response if
+ * InitialUserInfoResponseAction::DEFAULT (so Android could use its own logic to decide which user
+ * to boot).
+ *
+ * But during development / testing, the behavior can be changed using lshal dump, which must use
+ * the areaId to indicate what should happen next.
+ *
+ * So, the behavior of set(INITIAL_USER_INFO) is:
+ *
+ * - if it has an areaId, store the property into mInitialUserResponseFromCmd (as it was called by
+ * lshal).
+ * - else if mInitialUserResponseFromCmd is not set, return a response with the same request id and
+ * InitialUserInfoResponseAction::DEFAULT
+ * - else the behavior is defined by the areaId on mInitialUserResponseFromCmd:
+ * - if it's 1, reply with mInitialUserResponseFromCmd and the right request id
+ * - if it's 2, reply with mInitialUserResponseFromCmd but a wrong request id (so Android can test
+ * this error scenario)
+ * - if it's 3, then don't send a property change (so Android can emulate a timeout)
+ *
+ */
+StatusCode EmulatedVehicleServer::onSetInitialUserInfo(const VehiclePropValue& value,
+ bool updateStatus) {
+ // TODO: LOG calls below might be more suited to be DEBUG, but those are not being logged
+ // (even when explicitly calling setprop log.tag. As this class should be using ALOG instead of
+ // LOG, it's not worth investigating why...
+
+ if (value.value.int32Values.size() == 0) {
+ LOG(ERROR) << "set(INITIAL_USER_INFO): no int32values, ignoring it: " << toString(value);
+ return StatusCode::INVALID_ARG;
+ }
+
+ if (value.areaId != 0) {
+ LOG(INFO) << "set(INITIAL_USER_INFO) called from lshal; storing it: " << toString(value);
+ mInitialUserResponseFromCmd.reset(new VehiclePropValue(value));
+ return StatusCode::OK;
+ }
+ LOG(INFO) << "set(INITIAL_USER_INFO) called from Android: " << toString(value);
+
+ int32_t requestId = value.value.int32Values[0];
+
+ // Create the update property and set common values
+ auto updatedValue = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
+ updatedValue->prop = INITIAL_USER_INFO;
+ updatedValue->timestamp = elapsedRealtimeNano();
+
+ if (mInitialUserResponseFromCmd == nullptr) {
+ updatedValue->value.int32Values.resize(2);
+ updatedValue->value.int32Values[0] = requestId;
+ updatedValue->value.int32Values[1] = (int32_t)InitialUserInfoResponseAction::DEFAULT;
+ LOG(INFO) << "no lshal response; returning InitialUserInfoResponseAction::DEFAULT: "
+ << toString(*updatedValue);
+ onPropertyValueFromCar(*updatedValue, updateStatus);
+ return StatusCode::OK;
+ }
+
+ // mInitialUserResponseFromCmd is used for just one request
+ std::unique_ptr<VehiclePropValue> response = std::move(mInitialUserResponseFromCmd);
+
+ // TODO(b/138709788): rather than populate the raw values directly, it should use the
+ // libraries that convert a InitialUserInfoResponse into a VehiclePropValue)
+
+ switch (response->areaId) {
+ case 1:
+ LOG(INFO) << "returning response with right request id";
+ *updatedValue = *response;
+ updatedValue->areaId = 0;
+ updatedValue->value.int32Values[0] = requestId;
+ break;
+ case 2:
+ LOG(INFO) << "returning response with wrong request id";
+ *updatedValue = *response;
+ updatedValue->areaId = 0;
+ updatedValue->value.int32Values[0] = -requestId;
+ break;
+ case 3:
+ LOG(INFO) << "not generating a property change event because of lshal prop: "
+ << toString(*response);
+ return StatusCode::OK;
+ default:
+ LOG(ERROR) << "invalid action on lshal response: " << toString(*response);
+ return StatusCode::INTERNAL_ERROR;
+ }
+
+ LOG(INFO) << "updating property to: " << toString(*updatedValue);
+ onPropertyValueFromCar(*updatedValue, updateStatus);
+ return StatusCode::OK;
+}
+
+bool EmulatedVehicleServer::onDump(const hidl_handle& handle,
+ const hidl_vec<hidl_string>& options) {
+ int fd = handle->data[0];
+
+ if (options.size() > 0) {
+ if (options[0] == "--help") {
+ dprintf(fd, "Emulator-specific usage:\n");
+ dprintf(fd, "--user-hal: dumps state used for user management \n");
+ dprintf(fd, "\n");
+ // Include caller's help options
+ return true;
+ } else if (options[0] == "--user-hal") {
+ dumpUserHal(fd, "");
+ return false;
+
+ } else {
+ // Let caller handle the options...
+ return true;
+ }
+ }
+
+ dprintf(fd, "Emulator-specific state:\n");
+ dumpUserHal(fd, " ");
+ dprintf(fd, "\n");
+
+ return true;
+}
+
+void EmulatedVehicleServer::dumpUserHal(int fd, std::string indent) {
+ if (mInitialUserResponseFromCmd != nullptr) {
+ dprintf(fd, "%sInitial User Info: %s\n", indent.c_str(),
+ toString(*mInitialUserResponseFromCmd).c_str());
+ } else {
+ dprintf(fd, "%sNo Initial User Info\n", indent.c_str());
+ }
+}
+
EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector() {
return std::make_unique<EmulatedPassthroughConnector>();
}
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h
index 5fc6493..4850d32 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h
@@ -54,6 +54,8 @@
StatusCode onSetProperty(const VehiclePropValue& value, bool updateStatus) override;
+ bool onDump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
+
// Set the Property Value Pool used in this server
void setValuePool(VehiclePropValuePool* valuePool);
@@ -77,6 +79,11 @@
std::bind(&EmulatedVehicleServer::onFakeValueGenerated, this, std::placeholders::_1)};
VehiclePropValuePool* mValuePool{nullptr};
+
+ // TODO(b/146207078): it might be clearer to move members below to an EmulatedUserHal class
+ std::unique_ptr<VehiclePropValue> mInitialUserResponseFromCmd;
+ StatusCode onSetInitialUserInfo(const VehiclePropValue& value, bool updateStatus);
+ void dumpUserHal(int fd, std::string indent);
};
// Helper functions
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
index 5c16bf7..692c7f7 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
@@ -131,6 +131,10 @@
return v;
}
+bool EmulatedVehicleHal::dump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
+ return mVehicleClient->dump(fd, options);
+}
+
StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
constexpr bool updateStatus = false;
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
index a8378da..ebc405e 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
@@ -57,6 +57,7 @@
StatusCode set(const VehiclePropValue& propValue) override;
StatusCode subscribe(int32_t property, float sampleRate) override;
StatusCode unsubscribe(int32_t property) override;
+ bool dump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
// Methods from EmulatedVehicleHalIface
bool setPropertyFromVehicle(const VehiclePropValue& propValue) override;
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.cpp
deleted file mode 100644
index e329c5b..0000000
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * 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.
- */
-#include "GrpcVehicleClient.h"
-
-#include <condition_variable>
-#include <mutex>
-
-#include <android-base/logging.h>
-#include <grpc++/grpc++.h>
-
-#include "VehicleServer.grpc.pb.h"
-#include "VehicleServer.pb.h"
-#include "vhal_v2_0/ProtoMessageConverter.h"
-
-namespace android {
-namespace hardware {
-namespace automotive {
-namespace vehicle {
-namespace V2_0 {
-
-namespace impl {
-
-static std::shared_ptr<::grpc::ChannelCredentials> getChannelCredentials() {
- // TODO(chenhaosjtuacm): get secured credentials here
- return ::grpc::InsecureChannelCredentials();
-}
-
-class GrpcVehicleClientImpl : public EmulatedVehicleClient {
- public:
- GrpcVehicleClientImpl(const std::string& addr)
- : mServiceAddr(addr),
- mGrpcChannel(::grpc::CreateChannel(mServiceAddr, getChannelCredentials())),
- mGrpcStub(vhal_proto::VehicleServer::NewStub(mGrpcChannel)) {
- StartValuePollingThread();
- }
-
- ~GrpcVehicleClientImpl() {
- mShuttingDownFlag.store(true);
- mShutdownCV.notify_all();
- if (mPollingThread.joinable()) {
- mPollingThread.join();
- }
- }
-
- // methods from IVehicleClient
-
- std::vector<VehiclePropConfig> getAllPropertyConfig() const override;
-
- StatusCode setProperty(const VehiclePropValue& value, bool updateStatus) override;
-
- private:
- void StartValuePollingThread();
-
- // private data members
-
- std::string mServiceAddr;
- std::shared_ptr<::grpc::Channel> mGrpcChannel;
- std::unique_ptr<vhal_proto::VehicleServer::Stub> mGrpcStub;
- std::thread mPollingThread;
-
- std::mutex mShutdownMutex;
- std::condition_variable mShutdownCV;
- std::atomic<bool> mShuttingDownFlag{false};
-};
-
-std::unique_ptr<EmulatedVehicleClient> makeGrpcVehicleClient(const std::string& addr) {
- return std::make_unique<GrpcVehicleClientImpl>(addr);
-}
-
-std::vector<VehiclePropConfig> GrpcVehicleClientImpl::getAllPropertyConfig() const {
- std::vector<VehiclePropConfig> configs;
- ::grpc::ClientContext context;
- auto config_stream = mGrpcStub->GetAllPropertyConfig(&context, ::google::protobuf::Empty());
- vhal_proto::VehiclePropConfig protoConfig;
- while (config_stream->Read(&protoConfig)) {
- VehiclePropConfig config;
- proto_msg_converter::fromProto(&config, protoConfig);
- configs.emplace_back(std::move(config));
- }
- auto grpc_status = config_stream->Finish();
- if (!grpc_status.ok()) {
- LOG(ERROR) << __func__
- << ": GRPC GetAllPropertyConfig Failed: " << grpc_status.error_message();
- configs.clear();
- }
-
- return configs;
-}
-
-StatusCode GrpcVehicleClientImpl::setProperty(const VehiclePropValue& value, bool updateStatus) {
- ::grpc::ClientContext context;
- vhal_proto::WrappedVehiclePropValue wrappedProtoValue;
- vhal_proto::VehicleHalCallStatus vhal_status;
- proto_msg_converter::toProto(wrappedProtoValue.mutable_value(), value);
- wrappedProtoValue.set_update_status(updateStatus);
-
- auto grpc_status = mGrpcStub->SetProperty(&context, wrappedProtoValue, &vhal_status);
- if (!grpc_status.ok()) {
- LOG(ERROR) << __func__ << ": GRPC SetProperty Failed: " << grpc_status.error_message();
- return StatusCode::INTERNAL_ERROR;
- }
-
- return static_cast<StatusCode>(vhal_status.status_code());
-}
-
-void GrpcVehicleClientImpl::StartValuePollingThread() {
- mPollingThread = std::thread([this]() {
- while (!mShuttingDownFlag.load()) {
- ::grpc::ClientContext context;
-
- std::atomic<bool> rpc_ok{true};
- std::thread shuttingdown_watcher([this, &rpc_ok, &context]() {
- std::unique_lock<std::mutex> shutdownLock(mShutdownMutex);
- mShutdownCV.wait(shutdownLock, [this, &rpc_ok]() {
- return !rpc_ok.load() || mShuttingDownFlag.load();
- });
- context.TryCancel();
- });
-
- auto value_stream =
- mGrpcStub->StartPropertyValuesStream(&context, ::google::protobuf::Empty());
- vhal_proto::WrappedVehiclePropValue wrappedProtoValue;
- while (!mShuttingDownFlag.load() && value_stream->Read(&wrappedProtoValue)) {
- VehiclePropValue value;
- proto_msg_converter::fromProto(&value, wrappedProtoValue.value());
- onPropertyValue(value, wrappedProtoValue.update_status());
- }
-
- rpc_ok.store(false);
- mShutdownCV.notify_all();
- shuttingdown_watcher.join();
-
- auto grpc_status = value_stream->Finish();
- // never reach here until connection lost
- LOG(ERROR) << __func__
- << ": GRPC Value Streaming Failed: " << grpc_status.error_message();
-
- // try to reconnect
- }
- });
-}
-
-} // namespace impl
-
-} // namespace V2_0
-} // namespace vehicle
-} // namespace automotive
-} // namespace hardware
-} // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.h
deleted file mode 100644
index 14eae7f..0000000
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleClient.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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 android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleClient_H_
-#define android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleClient_H_
-
-#include "vhal_v2_0/EmulatedVehicleConnector.h"
-
-namespace android {
-namespace hardware {
-namespace automotive {
-namespace vehicle {
-namespace V2_0 {
-
-namespace impl {
-
-std::unique_ptr<EmulatedVehicleClient> makeGrpcVehicleClient(const std::string& addr);
-
-} // namespace impl
-
-} // namespace V2_0
-} // namespace vehicle
-} // namespace automotive
-} // namespace hardware
-} // namespace android
-
-#endif // android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleClient_H_
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.cpp
deleted file mode 100644
index e30b3be..0000000
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * 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.
- */
-#include "GrpcVehicleServer.h"
-
-#include <condition_variable>
-#include <mutex>
-#include <shared_mutex>
-
-#include <android-base/logging.h>
-#include <grpc++/grpc++.h>
-
-#include "VehicleServer.grpc.pb.h"
-#include "VehicleServer.pb.h"
-#include "vhal_v2_0/ProtoMessageConverter.h"
-
-namespace android {
-namespace hardware {
-namespace automotive {
-namespace vehicle {
-namespace V2_0 {
-
-namespace impl {
-
-class GrpcVehicleServerImpl : public GrpcVehicleServer, public vhal_proto::VehicleServer::Service {
- public:
- GrpcVehicleServerImpl(const std::string& addr) : mServiceAddr(addr) {
- setValuePool(&mValueObjectPool);
- }
-
- // method from GrpcVehicleServer
- void Start() override;
-
- // method from IVehicleServer
- void onPropertyValueFromCar(const VehiclePropValue& value, bool updateStatus) override;
-
- // methods from vhal_proto::VehicleServer::Service
-
- ::grpc::Status GetAllPropertyConfig(
- ::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
- ::grpc::ServerWriter<vhal_proto::VehiclePropConfig>* stream) override;
-
- ::grpc::Status SetProperty(::grpc::ServerContext* context,
- const vhal_proto::WrappedVehiclePropValue* wrappedPropValue,
- vhal_proto::VehicleHalCallStatus* status) override;
-
- ::grpc::Status StartPropertyValuesStream(
- ::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
- ::grpc::ServerWriter<vhal_proto::WrappedVehiclePropValue>* stream) override;
-
- private:
- // We keep long-lasting connection for streaming the prop values.
- // For us, each connection can be represented as a function to send the new value, and
- // an ID to identify this connection
- struct ConnectionDescriptor {
- using ValueWriterType = std::function<bool(const vhal_proto::WrappedVehiclePropValue&)>;
-
- ConnectionDescriptor(ValueWriterType&& value_writer)
- : mValueWriter(std::move(value_writer)),
- mConnectionID(CONNECTION_ID_COUNTER.fetch_add(1)) {}
-
- ConnectionDescriptor(const ConnectionDescriptor&) = delete;
-
- ConnectionDescriptor& operator=(const ConnectionDescriptor&) = delete;
-
- // This move constructor is NOT THREAD-SAFE, which means it cannot be moved
- // while using. Since the connection descriptors are pretected by mConnectionMutex
- // then we are fine here
- ConnectionDescriptor(ConnectionDescriptor&& cd)
- : mValueWriter(std::move(cd.mValueWriter)),
- mConnectionID(cd.mConnectionID),
- mIsAlive(cd.mIsAlive.load()) {
- cd.mIsAlive.store(false);
- }
-
- ValueWriterType mValueWriter;
- uint64_t mConnectionID;
- std::atomic<bool> mIsAlive{true};
-
- static std::atomic<uint64_t> CONNECTION_ID_COUNTER;
- };
-
- std::string mServiceAddr;
- VehiclePropValuePool mValueObjectPool;
- mutable std::shared_mutex mConnectionMutex;
- mutable std::shared_mutex mWriterMutex;
- std::list<ConnectionDescriptor> mValueStreamingConnections;
-};
-
-std::atomic<uint64_t> GrpcVehicleServerImpl::ConnectionDescriptor::CONNECTION_ID_COUNTER = 0;
-
-static std::shared_ptr<::grpc::ServerCredentials> getServerCredentials() {
- // TODO(chenhaosjtuacm): get secured credentials here
- return ::grpc::InsecureServerCredentials();
-}
-
-GrpcVehicleServerPtr makeGrpcVehicleServer(const std::string& addr) {
- return std::make_unique<GrpcVehicleServerImpl>(addr);
-}
-
-void GrpcVehicleServerImpl::Start() {
- ::grpc::ServerBuilder builder;
- builder.RegisterService(this);
- builder.AddListeningPort(mServiceAddr, getServerCredentials());
- std::unique_ptr<::grpc::Server> server(builder.BuildAndStart());
-
- server->Wait();
-}
-
-void GrpcVehicleServerImpl::onPropertyValueFromCar(const VehiclePropValue& value,
- bool updateStatus) {
- vhal_proto::WrappedVehiclePropValue wrappedPropValue;
- proto_msg_converter::toProto(wrappedPropValue.mutable_value(), value);
- wrappedPropValue.set_update_status(updateStatus);
- std::shared_lock read_lock(mConnectionMutex);
-
- bool has_terminated_connections = 0;
-
- for (auto& connection : mValueStreamingConnections) {
- auto writeOK = connection.mValueWriter(wrappedPropValue);
- if (!writeOK) {
- LOG(ERROR) << __func__ << ": Server Write failed, connection lost. ID: "
- << connection.mConnectionID;
- has_terminated_connections = true;
- connection.mIsAlive.store(false);
- }
- }
-
- if (!has_terminated_connections) {
- return;
- }
-
- read_lock.unlock();
-
- std::unique_lock write_lock(mConnectionMutex);
-
- for (auto itr = mValueStreamingConnections.begin(); itr != mValueStreamingConnections.end();) {
- if (!itr->mIsAlive.load()) {
- itr = mValueStreamingConnections.erase(itr);
- } else {
- ++itr;
- }
- }
-}
-
-::grpc::Status GrpcVehicleServerImpl::GetAllPropertyConfig(
- ::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
- ::grpc::ServerWriter<vhal_proto::VehiclePropConfig>* stream) {
- auto configs = onGetAllPropertyConfig();
- for (auto& config : configs) {
- vhal_proto::VehiclePropConfig protoConfig;
- proto_msg_converter::toProto(&protoConfig, config);
- if (!stream->Write(protoConfig)) {
- return ::grpc::Status(::grpc::StatusCode::ABORTED, "Connection lost.");
- }
- }
-
- return ::grpc::Status::OK;
-}
-
-::grpc::Status GrpcVehicleServerImpl::SetProperty(
- ::grpc::ServerContext* context, const vhal_proto::WrappedVehiclePropValue* wrappedPropValue,
- vhal_proto::VehicleHalCallStatus* status) {
- VehiclePropValue value;
- proto_msg_converter::fromProto(&value, wrappedPropValue->value());
-
- auto set_status = static_cast<int32_t>(onSetProperty(value, wrappedPropValue->update_status()));
- if (!vhal_proto::VehicleHalStatusCode_IsValid(set_status)) {
- return ::grpc::Status(::grpc::StatusCode::INTERNAL, "Unknown status code");
- }
-
- status->set_status_code(static_cast<vhal_proto::VehicleHalStatusCode>(set_status));
-
- return ::grpc::Status::OK;
-}
-
-::grpc::Status GrpcVehicleServerImpl::StartPropertyValuesStream(
- ::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
- ::grpc::ServerWriter<vhal_proto::WrappedVehiclePropValue>* stream) {
- std::mutex terminateMutex;
- std::condition_variable terminateCV;
- std::unique_lock<std::mutex> terminateLock(terminateMutex);
- bool terminated{false};
-
- auto callBack = [stream, &terminateMutex, &terminateCV, &terminated,
- this](const vhal_proto::WrappedVehiclePropValue& value) {
- std::unique_lock lock(mWriterMutex);
- if (!stream->Write(value)) {
- std::unique_lock<std::mutex> terminateLock(terminateMutex);
- terminated = true;
- terminateLock.unlock();
- terminateCV.notify_all();
- return false;
- }
- return true;
- };
-
- // Register connection
- std::unique_lock lock(mConnectionMutex);
- auto& conn = mValueStreamingConnections.emplace_back(std::move(callBack));
- lock.unlock();
-
- // Never stop until connection lost
- terminateCV.wait(terminateLock, [&terminated]() { return terminated; });
-
- LOG(ERROR) << __func__ << ": Stream lost, ID : " << conn.mConnectionID;
-
- return ::grpc::Status(::grpc::StatusCode::ABORTED, "Connection lost.");
-}
-
-} // namespace impl
-
-} // namespace V2_0
-} // namespace vehicle
-} // namespace automotive
-} // namespace hardware
-} // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.h
deleted file mode 100644
index 32f4eb2..0000000
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/GrpcVehicleServer.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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 android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleServer_H_
-#define android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleServer_H_
-
-#include "vhal_v2_0/EmulatedVehicleConnector.h"
-
-namespace android {
-namespace hardware {
-namespace automotive {
-namespace vehicle {
-namespace V2_0 {
-
-namespace impl {
-
-// Connect to the Vehicle Client via GRPC
-class GrpcVehicleServer : public EmulatedVehicleServer {
- public:
- // Start listening incoming calls, should never return if working normally
- virtual void Start() = 0;
-};
-
-using GrpcVehicleServerPtr = std::unique_ptr<GrpcVehicleServer>;
-
-GrpcVehicleServerPtr makeGrpcVehicleServer(const std::string& addr);
-
-} // namespace impl
-
-} // namespace V2_0
-} // namespace vehicle
-} // namespace automotive
-} // namespace hardware
-} // namespace android
-
-#endif // android_hardware_automotive_vehicle_V2_0_impl_virtialization_GrpcVehicleServer_H_
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.cpp
deleted file mode 100644
index 41d4827..0000000
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.
- */
-
-#include "Utils.h"
-
-#include <sstream>
-
-namespace android {
-namespace hardware {
-namespace automotive {
-namespace vehicle {
-namespace V2_0 {
-namespace impl {
-
-std::string getVsockUri(const VsockServerInfo& serverInfo) {
- std::stringstream uri_stream;
- uri_stream << "vsock:" << serverInfo.serverCid << ":" << serverInfo.serverPort;
- return uri_stream.str();
-}
-
-} // namespace impl
-} // namespace V2_0
-} // namespace vehicle
-} // namespace automotive
-} // namespace hardware
-} // namespace android
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.h
deleted file mode 100644
index 6b1049c..0000000
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/virtualization/Utils.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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 android_hardware_automotive_vehicle_V2_0_impl_virtualization_Utils_H_
-#define android_hardware_automotive_vehicle_V2_0_impl_virtualization_Utils_H_
-
-#include <string>
-
-namespace android {
-namespace hardware {
-namespace automotive {
-namespace vehicle {
-namespace V2_0 {
-namespace impl {
-
-struct VsockServerInfo {
- unsigned int serverCid{0};
- unsigned int serverPort{0};
-};
-
-std::string getVsockUri(const VsockServerInfo& serverInfo);
-
-} // namespace impl
-} // namespace V2_0
-} // namespace vehicle
-} // namespace automotive
-} // namespace hardware
-} // namespace android
-
-#endif // android_hardware_automotive_vehicle_V2_0_impl_virtualization_Utils_H_
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 6145ea2..cbd9e28 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -65,8 +65,8 @@
* particular door, thus this property must be marked with
* VehicleArea:DOOR flag.
*
- * Other properties may not be associated with particular vehicle area,
- * these kind of properties must have VehicleArea:GLOBAL flag.
+ * Other properties may not be associated with particular vehicle area.
+ * These kinds of properties must have VehicleArea:GLOBAL flag.
*
* [Definition] Area: An area represents a unique element of an AreaType.
* For instance, if AreaType is WINDOW, then an area may be FRONT_WINDSHIELD.
@@ -79,9 +79,9 @@
* Rules for mapping a zoned property to AreaIDs:
* - A property must be mapped to an array of AreaIDs that are impacted when
* the property value changes.
- * - Each element in the array must represent an AreaID, in which, the
+ * - Each element in the array must represent an AreaID, in which the
* property value can only be changed together in all the areas within
- * an AreaID and never independently. That is, when the property value
+ * the AreaID and never independently. That is, when the property value
* changes in one of the areas in an AreaID in the array, then it must
* automatically change in all other areas in the AreaID.
* - The property value must be independently controllable in any two
@@ -140,7 +140,7 @@
* - vehicle area (VehicleArea)
*
* Vendors are allowed to extend this enum with their own properties. In this
- * case they must use VehiclePropertyGroup:VENDOR flag when property is
+ * case they must use VehiclePropertyGroup:VENDOR flag when the property is
* declared.
*
* When a property's status field is not set to AVAILABLE:
@@ -2438,6 +2438,373 @@
| VehiclePropertyType:STRING
| VehicleArea:GLOBAL),
+ /**
+ * Defines the initial Android user to be used during initialization.
+ *
+ * This property is called by the Android system when it initializes and it lets the HAL
+ * define which Android user should be started.
+ *
+ * This request is made by setting a VehiclePropValue (defined by InitialUserInfoRequest),
+ * and the HAL must respond with a property change event (defined by InitialUserInfoResponse).
+ * If the HAL doesn't respond after some time (defined by the Android system), the Android
+ * system will proceed as if HAL returned a response of action
+ * InitialUserInfoResponseAction:DEFAULT.
+ *
+ * For example, on first boot, the request could be:
+ *
+ * int32[0]: 42 // request id (arbitrary number set by Android system)
+ * int32[1]: 1 // InitialUserInfoRequestType::FIRST_BOOT
+ * int32[2]: 0 // id of current user (usersInfo.currentUser.userId)
+ * int32[3]: 1 // flag of current user (usersInfo.currentUser.flags = SYSTEM)
+ * int32[4]: 1 // number of existing users (usersInfo.numberUsers);
+ * int32[5]: 0 // user #0 (usersInfo.existingUsers[0].userId)
+ * int32[6]: 1 // flags of user #0 (usersInfo.existingUsers[0].flags)
+ *
+ * And if the HAL want to respond with the creation of an admin user called "Admin", the
+ * response would be:
+ *
+ * int32[0]: 42 // must match the request id from the request
+ * int32[1]: 2 // action = InitialUserInfoResponseAction::CREATE
+ * int32[2]: -1 // userToSwitchOrCreate.userId (not used as user will be created)
+ * int32[3]: 8 // userToSwitchOrCreate.flags = ADMIN
+ * string: "Admin" // userNameToCreate
+ *
+ * NOTE: if the HAL doesn't support user management, then it should not define this property,
+ * which in turn would disable the other user-related properties (for example, the Android
+ * system would never issue them and user-related requests from the HAL layer would be ignored
+ * by the Android System). But if it supports user management, then it must support all
+ * user-related properties (INITIAL_USER_INFO, SWITCH_USER, CREATE_USER, REMOVE_USER,
+ * and USER_IDENTIFICATION_ASSOCIATION).
+ *
+ * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+ * @access VehiclePropertyAccess:READ_WRITE
+ */
+ INITIAL_USER_INFO = (
+ 0x0F07
+ | VehiclePropertyGroup:SYSTEM
+ | VehiclePropertyType:MIXED
+ | VehicleArea:GLOBAL),
+
+ /**
+ * Defines a request to switch the foreground Android user.
+ *
+ * This property is used primarily by the Android System to inform the HAL that the
+ * current foreground Android user is switching, but it could also be used by the HAL to request
+ * the Android system to switch users - the
+ *
+ * When the request is made by Android, it sets a VehiclePropValue and the HAL must responde
+ * with a property change event; when the HAL is making the request, it must also do it through
+ * a property change event (the main difference is that the request id will be positive in the
+ * former case, and negative in the latter; the SwitchUserMessageType will also be different).
+ *
+ * The format of both request is defined by SwitchUserRequest and the format of the response
+ * (when needed) is defined by SwitchUserResponse. How the HAL (or Android System) should
+ * proceed depends on the message type (which is defined by the SwitchUserMessageType
+ * parameter), as defined below.
+ *
+ * 1.LEGACY_ANDROID_SWITCH
+ * -----------------------
+ *
+ * Called by the Android System to indicate the Android user is about to change, when the change
+ * request was made in a way that is not integrated with the HAL (for example, through
+ * adb shell am switch-user).
+ *
+ * The HAL can switch its internal user once it receives this request, but it doesn't need to
+ * reply back to the Android System. If its internal user cannot be changed for some reason,
+ * then it must wait for the SWITCH_USER(type=ANDROID_POST_SWITCH) call to recover
+ * (for example, it could issue a SWITCH_USER(type=VEHICLE_REQUEST) to switch back to
+ * the previous user), but ideally it should never fail (as switching back could result in a
+ * confusing experience for the end user).
+ *
+ * For example, if the system have users (0, 10, 11) and it's switching from 0 to 11 (where none
+ * of them have any special flag), the request would be:
+ *
+ * int32[0]: 42 // request id
+ * int32[1]: 1 // SwitchUserMessageType::LEGACY_ANDROID_SWITCH
+ * int32[2]: 11 // target user id
+ * int32[3]: 0 // target user flags (none)
+ * int32[4]: 10 // current user
+ * int32[5]: 0 // current user flags (none)
+ * int32[6]: 3 // number of users
+ * int32[7]: 0 // user #0 (Android user id 0)
+ * int32[8]: 0 // flags of user #0 (none)
+ * int32[9]: 10 // user #1 (Android user id 10)
+ * int32[10]: 0 // flags of user #1 (none)
+ * int32[11]: 11 // user #2 (Android user id 11)
+ * int32[12]: 0 // flags of user #2 (none)
+ *
+ * 2.ANDROID_SWITCH
+ * ----------------
+ * Called by the Android System to indicate the Android user is about to change, but Android
+ * will wait for the HAL's response (up to some time) before proceeding.
+ *
+ * The HAL must switch its internal user once it receives this request, then respond back to
+ * Android with a SWITCH_USER(type=VEHICLE_RESPONSE) indicating whether its internal
+ * user was switched or not (through the SwitchUserStatus enum).
+ *
+ * For example, if Android has users (0, 10, 11) and it's switching from 10 to 11 (where
+ * none of them have any special flag), the request would be:
+ *
+ * int32[0]: 42 // request id
+ * int32[1]: 2 // SwitchUserMessageType::ANDROID_SWITCH
+ * int32[2]: 11 // target user id
+ * int32[3]: 0 // target user flags (none)
+ * int32[4]: 10 // current user
+ * int32[5]: 0 // current user flags (none)
+ * int32[6]: 3 // number of users
+ * int32[7]: 0 // 1st user (user 0)
+ * int32[8]: 0 // 1st user flags (none)
+ * int32[9]: 10 // 2nd user (user 10)
+ * int32[10]: 0 // 2nd user flags (none)
+ * int32[11]: 11 // 3rd user (user 11)
+ * int32[12]: 0 // 3rd user flags (none)
+ *
+ * If the request succeeded, the HAL must update the propery with:
+ *
+ * int32[0]: 42 // request id
+ * int32[1]: 3 // messageType = SwitchUserMessageType::VEHICLE_RESPONSE
+ * int32[2]: 1 // status = SwitchUserStatus::SUCCESS
+ *
+ * But if it failed, the response would be something like:
+ *
+ * int32[0]: 42 // request id
+ * int32[1]: 3 // messageType = SwitchUserMessageType::VEHICLE_RESPONSE
+ * int32[2]: 2 // status = SwitchUserStatus::FAILURE
+ * string: "108-D'OH!" // OEM-spefic error message
+ *
+ * 3.VEHICLE_RESPONSE
+ * ------------------
+ * Called by the HAL to indicate whether a request of type ANDROID_SWITCH should proceed or
+ * abort - see the ANDROID_SWITCH section above for more info.
+ *
+ * 4.VEHICLE_REQUEST
+ * ------------------
+ * Called by the HAL to request that the current foreground Android user is switched.
+ *
+ * This is useful in situations where Android started as one user, but the vehicle identified
+ * the driver as another user. For example, user A unlocked the car using the key fob of user B;
+ * the INITIAL_USER_INFO request returned user B, but then a face recognition subsubsystem
+ * identified the user as A.
+ *
+ * The HAL makes this request by a property change event (passing a negative request id), and
+ * the Android system will response by issuye an ANDROID_POST_SWITCH call which the same
+ * request id.
+ *
+ * For example, if the current foreground Android user is 10 and the HAL asked it to switch to
+ * 11, the request would be:
+ *
+ * int32[0]: -108 // request id
+ * int32[1]: 4 // messageType = SwitchUserMessageType::VEHICLE_REQUEST
+ * int32[2]: 11 // Android user id
+ *
+ * If the request succeeded and Android has 3 users (0, 10, 11), the response would be:
+ *
+ * int32[0]: -108 // request id
+ * int32[1]: 5 // messageType = SwitchUserMessageType::ANDROID_SWITCH
+ * int32[2]: 11 // target user id
+ * int32[3]: 11 // target user id flags (none)
+ * int32[4]: 11 // current user
+ * int32[5]: 0 // current user flags (none)
+ * int32[6]: 3 // number of users
+ * int32[7]: 0 // 1st user (user 0)
+ * int32[8]: 0 // 1st user flags (none)
+ * int32[9]: 10 // 2nd user (user 10)
+ * int32[10]: 4 // 2nd user flags (none)
+ * int32[11]: 11 // 3rd user (user 11)
+ * int32[12]: 3 // 3rd user flags (none)
+ *
+ * Notice that both the current and target user ids are the same - if the request failed, then
+ * they would be different (i.e, target user would be 11, but current user would still be 10).
+ *
+ * 5.ANDROID_POST_SWITCH
+ * ---------------------
+ * Called by the Android System after a request to switch a user was made
+ *
+ * This property is called after switch requests of any type (i.e., LEGACY_ANDROID_SWITCH,
+ * ANDROID_SWITCH, or VEHICLE_REQUEST) and can be used to determine if the request succeeded or
+ * failed:
+ *
+ * 1. When it succeeded, it's called when the Android user is in the boot locked state and the
+ * value of the current and target users ids in the response are different. This would be
+ * equivalent to receiving an Intent.ACTION_LOCKED_BOOT_COMPLETED in an Android app.
+ * 2. When it failed it's called right away and the value of the current and target users ids
+ * in the response are the same.
+ *
+ * The HAL can update its internal state once it receives this request, but it doesn't need to
+ * reply back to the Android System.
+ *
+ * Request: the first N values as defined by INITIAL_USER_INFO (where the request-specific
+ * value at index 1 is SwitchUserMessageType::ANDROID_POST_SWITCH), then 2 more values for the
+ * target user id (i.e., the Android user id that was requested to be switched to) and its flags
+ * (as defined by UserFlags).
+ *
+ * Response: none.
+ *
+ * Example: see VEHICLE_REQUEST section above.
+ *
+ * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+ * @access VehiclePropertyAccess:READ_WRITE
+ */
+ SWITCH_USER = (
+ 0x0F08
+ | VehiclePropertyGroup:SYSTEM
+ | VehiclePropertyType:MIXED
+ | VehicleArea:GLOBAL),
+
+ /**
+ * Called by the Android System after an Android user was created.
+ *
+ * The HAL can use this property to create its equivalent user.
+ *
+ * This is an async request: Android makes the request by setting a VehiclePropValue, and HAL
+ * must respond with a property change indicating whether the request succeeded or failed. If
+ * it failed, the Android system will remove the user.
+ *
+ * The format of the request is defined by CreateUserRequest and the format of the response by
+ * CreateUserResponse.
+ *
+ * For example, if system had 2 users (0 and 10) and a 3rd one (which is an ephemeral guest) was
+ * created, the request would be:
+ *
+ * int32[0]: 42 // request id
+ * int32[1]: 11 // Android id of the created user
+ * int32[2]: 3 // Android flags (ephemeral guest) of the created user
+ * int32[3]: 10 // current user
+ * int32[4]: 0 // current user flags (none)
+ * int32[5]: 3 // number of users
+ * int32[6]: 0 // 1st user (user 0)
+ * int32[7]: 0 // 1st user flags (none)
+ * int32[8]: 10 // 2nd user (user 10)
+ * int32[9]: 0 // 2nd user flags (none)
+ * int32[19]: 11 // 3rd user (user 11)
+ * int32[11]: 3 // 3rd user flags (ephemeral guest)
+ * string: "ElGuesto" // name of the new user
+ *
+ * Then if the request succeeded, the HAL would return:
+ *
+ * int32[0]: 42 // request id
+ * int32[1]: 1 // CreateUserStatus::SUCCESS
+ *
+ * But if it failed:
+ *
+ * int32[0]: 42 // request id
+ * int32[1]: 2 // CreateUserStatus::FAILURE
+ * string: "D'OH!" // The meaning is a blackbox - it's passed to the caller (like Settings UI),
+ * // which in turn can take the proper action.
+ *
+ * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+ * @access VehiclePropertyAccess:READ_WRITE
+ */
+ CREATE_USER = (
+ 0x0F09
+ | VehiclePropertyGroup:SYSTEM
+ | VehiclePropertyType:MIXED
+ | VehicleArea:GLOBAL),
+
+ /**
+ * Called by the Android System after an Android user was removed.
+ *
+ * The HAL can use this property to remove its equivalent user.
+ *
+ * This is write-only call - the Android System is not expecting a reply from the HAL. Hence,
+ * this request should not fail - if the equivalent HAL user cannot be removed, then HAL should
+ * mark it as inactive or recover in some other way.
+ *
+ * The request is made by setting the VehiclePropValue with the contents defined by
+ * RemoveUserRequest.
+ *
+ * For example, if system had 3 users (0, 10, and 11) and user 11 was removed, the request
+ * would be:
+ *
+ * int32[0]: 42 // request id
+ * int32[1]: 11 // (Android user id of the removed user)
+ * int32[2]: 0 // (Android user flags of the removed user)
+ * int32[3]: 10 // current user
+ * int32[4]: 0 // current user flags (none)
+ * int32[5]: 2 // number of users
+ * int32[6]: 0 // 1st user (user 0)
+ * int32[7]: 0 // 1st user flags (none)
+ * int32[8]: 10 // 2nd user (user 10)
+ * int32[9]: 0 // 2nd user flags (none)
+ *
+ * @change_mode VehiclePropertyChangeMode:STATIC
+ * @access VehiclePropertyAccess:WRITE
+ */
+ REMOVE_USER = (
+ 0x0F0A
+ | VehiclePropertyGroup:SYSTEM
+ | VehiclePropertyType:MIXED
+ | VehicleArea:GLOBAL),
+
+ /**
+ * Property used to associate (or query the association) the current user with vehicle-specific
+ * identification mechanisms (such as key FOB).
+ *
+ * To query the association, the Android system gets the property, passing a VehiclePropValue
+ * containing the types of associations are being queried, as defined by
+ * UserIdentificationGetRequest. The HAL must return right away, updating the VehiclePropValue
+ * with a UserIdentificationResponse. Notice that user identification should have already
+ * happened while system is booting up and the VHAL implementation should only return the
+ * already identified association (like the key FOB used to unlock the car), instead of starting
+ * a new association from the get call.
+ *
+ * To associate types, the Android system sets the property, passing a VehiclePropValue
+ * containing the types and values of associations being set, as defined by the
+ * UserIdentificationSetRequest. The HAL will then use a property change event (whose
+ * VehiclePropValue is defined by UserIdentificationResponse) indicating the current status of
+ * the types after the request.
+ *
+ * For example, to query if the current user (10) is associated with the FOB that unlocked the
+ * car and a custom mechanism provided by the OEM, the request would be:
+ *
+ * int32[0]: 10 (Android user id)
+ * int32[1]: 0 (Android user flags)
+ * int32[2]: 2 (number of types queried)
+ * int32[3]: 1 (1st type queried, UserIdentificationAssociationType::KEY_FOB)
+ * int32[4]: 101 (2nd type queried, UserIdentificationAssociationType::CUSTOM_1)
+ *
+ * If the user is associated with the FOB but not with the custom mechanism, the response would
+ * be:
+ *
+ * int32[9]: 2 (number of associations in the response)
+ * int32[1]: 1 (1st type: UserIdentificationAssociationType::KEY_FOB)
+ * int32[2]: 2 (1st value: UserIdentificationAssociationValue::ASSOCIATED_CURRENT_USER)
+ * int32[3]: 101 (2st type: UserIdentificationAssociationType::CUSTOM_1)
+ * int32[4]: 4 (2nd value: UserIdentificationAssociationValue::NOT_ASSOCIATED_ANY_USER)
+ *
+ * Then to associate the user with the custom mechanism, a set request would be made:
+ *
+ * int32[0]: 10 (Android user id)
+ * int32[0]: 0 (Android user flags)
+ * int32[1]: 1 (number of associations being set)
+ * int32[2]: 101 (1st type: UserIdentificationAssociationType::CUSTOM_1)
+ * int32[3]: 1 (1st value: UserIdentificationAssociationSETValue::ASSOCIATE_CURRENT_USER)
+ *
+ * If the request succeeded, the response would be simply:
+ *
+ * int32[0]: 2 (number of associations in the response)
+ * int32[1]: 101 (1st type: UserIdentificationAssociationType::CUSTOM_1)
+ * int32[2]: 1 (1st value: UserIdentificationAssociationValue::ASSOCIATED_CURRENT_USER)
+ *
+ * Notice that the set request adds associations, but doesn't remove the existing ones. In the
+ * example above, the end state would be 2 associations (FOB and CUSTOM_1). If we wanted to
+ * associate the user with just CUSTOM_1 but not FOB, then the request should have been:
+ *
+ * int32[0]: 10 (Android user id)
+ * int32[1]: 2 (number of types set)
+ * int32[2]: 1 (1st type: UserIdentificationAssociationType::KEY_FOB)
+ * int32[3]: 2 (1st value: UserIdentificationAssociationValue::DISASSOCIATE_CURRENT_USER)
+ * int32[3]: 101 (2nd type: UserIdentificationAssociationType::CUSTOM_1)
+ * int32[5]: 1 (2nd value: UserIdentificationAssociationValue::ASSOCIATE_CURRENT_USER)
+ *
+ * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+ * @access VehiclePropertyAccess:READ_WRITE
+ */
+ USER_IDENTIFICATION_ASSOCIATION = (
+ 0x0F0B
+ | VehiclePropertyGroup:SYSTEM
+ | VehiclePropertyType:MIXED
+ | VehicleArea:GLOBAL),
};
/**
@@ -2674,26 +3041,52 @@
};
enum VehicleApPowerStateReq : int32_t {
- /** Transition Android from WAIT_FOR_VHAL to ON state */
+ /**
+ * This requests Android to enter its normal operating state.
+ * This may be sent after the AP has reported
+ * VehicleApPowerStateReport#DEEP_SLEEP_EXIT,
+ * VehicleApPowerStateReport#SHUTDOWN_CANCELLED, or
+ * VehicleApPowerStateReport#WAIT_FOR_VHAL.
+ */
ON = 0,
/**
- * The power controller has requested AP to shutdown. AP can either enter
- * sleep state or start full shutdown. AP can also request postponing
- * shutdown by sending VehicleApPowerSetState#SHUTDOWN_POSTPONE message. The
- * power controller must change power state to this state to shutdown
- * system.
+ * The power controller issues this request to shutdown the system.
+ * This may be sent after the AP has reported
+ * VehicleApPowerStateReport#DEEP_SLEEP_EXIT,
+ * VehicleApPowerStateReport#ON,
+ * VehicleApPowerStateReport#SHUTDOWN_CANCELLED,
+ * VehicleApPowerStateReport#SHUTDOWN_POSTPONE,
+ * VehicleApPowerStateReport#SHUTDOWN_PREPARE, or
+ * VehicleApPowerStateReport#WAIT_FOR_VHAL.
*
- * int32Values[1] : one of VehicleApPowerStateShutdownParam
- *
- * SHUTDOWN_PRPARE may be requested from either WAIT_FOR_VHAL or ON states.
+ * int32Values[1] : One of VehicleApPowerStateShutdownParam.
+ * This parameter indicates if the AP should shut
+ * down fully or sleep. This parameter also
+ * indicates if the shutdown should be immediate
+ * or if it can be postponed. If the shutdown can
+ * be postponed, AP requests postponing by sending
+ * VehicleApPowerStateReport#SHUTDOWN_POSTPONE.
*/
SHUTDOWN_PREPARE = 1,
- /** Cancel the shutdown and transition from SHUTDOWN_PREPARE to WAIT_FOR_VHAL state */
+ /**
+ * Cancel the shutdown.
+ * This may be sent after the AP has reported
+ * VehicleApPowerStateReport#SHUTDOWN_POSTPONE or
+ * VehicleApPowerStateReport#SHUTDOWN_PREPARE.
+ * After receiving this request, the AP will report
+ * VehicleApPowerStateReport#WAIT_FOR_VHAL in preparation to going ON.
+ */
CANCEL_SHUTDOWN = 2,
- /** VHAL is finished with shutdown procedures and ready for Android to suspend/shutdown */
+ /**
+ * Completes the shutdown process.
+ * This may be sent after the AP has reported
+ * VehicleApPowerStateReport#DEEP_SLEEP_ENTRY or
+ * VehicleApPowerStateReport#SHUTDOWN_START. The AP will not report new
+ * state information after receiving this request.
+ */
FINISHED = 3,
};
@@ -2725,61 +3118,80 @@
enum VehicleApPowerStateReport : int32_t {
/**
- * Device has booted, CarService has initialized and is ready to accept commands from VHAL.
- * Device starts in WAIT_FOR_VHAL state. The user is not logged in, and vendor apps/services
- * are expected to control the display and audio.
+ * The device has booted. CarService has initialized and is ready to accept commands
+ * from VHAL. The user is not logged in, and vendor apps and services are expected to
+ * control the display and audio.
+ * After reporting this state, AP will accept VehicleApPowerStateReq#ON or
+ * VehicleApPowerStateReq#SHUTDOWN_PREPARE. Other power state requests are ignored.
*/
WAIT_FOR_VHAL = 0x1,
/**
- * AP is ready to suspend and has entered WAIT_FOR_FINISHED state.
+ * AP is ready to suspend.
+ * The AP will not send any more state reports after this.
+ * After reporting this state, AP will accept VehicleApPowerStateReq#FINISHED.
+ * Other power state requests are ignored.
*
- * int32Values[1]: Time to turn on AP in secs. Power controller may turn on
- * AP after specified time so that AP can run tasks like
- * update. If it is set to 0, there is no wake up, and power
- * controller may not necessarily support wake-up.
+ * int32Values[1]: Time to turn AP back on, in seconds. Power controller should turn on
+ * AP after the specified time has elapsed, so AP can run tasks like
+ * update. If this value is 0, no wake up is requested. The power
+ * controller may not necessarily support timed wake-up.
*/
DEEP_SLEEP_ENTRY = 0x2,
/**
- * AP is exiting from deep sleep state, and is in WAIT_FOR_VHAL state.
+ * AP is exiting from deep sleep state.
+ * After reporting this state, AP will accept VehicleApPowerStateReq#ON or
+ * VehicleApPowerStateReq#SHUTDOWN_PREPARE. Other power state requests are ignored.
*/
DEEP_SLEEP_EXIT = 0x3,
/**
- * AP remains in SHUTDOWN_PREPARE state as idle and cleanup tasks execute.
+ * AP sends this message repeatedly while cleanup and idle tasks execute.
+ * After reporting this state, AP will accept VehicleApPowerStateReq#SHUTDOWN_PREPARE
+ * requesting immediate shutdown or VehicleApPowerStateReq#CANCEL_SHUTDOWN. Other
+ * power state requests are ignored.
*
- * int32Values[1]: Time to postpone shutdown in ms. Maximum value can be
+ * int32Values[1]: Time to postpone shutdown in ms. Maximum value is
* 5000 ms.
- * If AP needs more time, it will send another POSTPONE
+ * If AP needs more time, it will send another SHUTDOWN_POSTPONE
* message before the previous one expires.
*/
SHUTDOWN_POSTPONE = 0x4,
/**
- * AP is ready to shutdown and has entered WAIT_FOR_FINISHED state.
+ * AP is ready to shutdown.
+ * The AP will not send any more state reports after this.
+ * After reporting this state, AP will accept VehicleApPowerStateReq#FINISHED.
+ * Other power state requests are ignored.
*
- * int32Values[1]: Time to turn on AP in secs. Power controller may turn on
- * AP after specified time so that AP can run tasks like
- * update. If it is set to 0, there is no wake up, and power
- * controller may not necessarily support wake-up.
+ * int32Values[1]: Time to turn AP back on, in seconds. Power controller should turn on
+ * AP after the specified time has elapsed so AP can run tasks like
+ * update. If this value is 0, no wake up is specified. The power
+ * controller may not necessarily support timed wake-up.
*/
SHUTDOWN_START = 0x5,
/**
- * AP has transitioned from WAIT_FOR_VHAL state to ON.
+ * AP is entering its normal operating state.
+ * After reporting this state, AP will accept VehicleApPowerStateReq#SHUTDOWN_PREPARE.
+ * Other power state requests are ignored.
*/
ON = 0x6,
/**
- * AP has transitions to SHUTDOWN_PREPARE state. In this state, Garage Mode will execute idle
- * tasks, and other services that have registered for this state transition may execute
- * cleanup activities.
+ * AP is preparing to shut down. In this state, Garage Mode is active and idle
+ * tasks are allowed to run.
+ * After reporting this state, AP will accept VehicleApPowerStateReq#SHUTDOWN_PREPARE
+ * requesting immediate shutdown or VehicleApPowerStateReq#CANCEL_SHUTDOWN. Other
+ * power state requests are ignored.
*/
SHUTDOWN_PREPARE = 0x7,
/**
- * AP has transitioned from SHUTDOWN_PREPARE state to WAIT_FOR_VHAL.
+ * AP has stopped preparing to shut down.
+ * After reporting this state, AP will accept VehicleApPowerStateReq#ON or
+ * VehicleApPowerStateReq#SHUTDOWN_PREPARE. Other power state requests are ignored.
*/
SHUTDOWN_CANCELLED = 0x8,
};
@@ -3733,3 +4145,509 @@
enum VmsPublisherInformationIntegerValuesIndex : VmsBaseMessageIntegerValuesIndex {
PUBLISHER_ID = 1,
};
+
+/**
+ * Information about a specific Android user.
+ */
+struct UserInfo {
+
+ UserId userId;
+
+ UserFlags flags;
+};
+
+/**
+ * Id of an Android user.
+ *
+ * Must be > 0 for valid ids, or -10000 (which is the same as Android.UserHandle.USER_NULL) when
+ * it's not used.
+ */
+typedef int32_t UserId;
+
+/**
+ * Flags used to define the characteristics of an Android user.
+ */
+enum UserFlags: int32_t {
+ /**
+ * No flags.
+ */
+ NONE = 0x0,
+
+ /**
+ * System user.
+ * On automotive, that user is always running, although never on foreground (except during
+ * boot or exceptional circumstances).
+ */
+ SYSTEM = 0x01,
+
+ /**
+ * Guest users have restrictions.
+ */
+ GUEST = 0x02,
+
+ /**
+ * Ephemeral users have non-persistent state.
+ */
+ EPHEMERAL = 0x04,
+
+ /**
+ * Admin users have additional privileges such as permission to create other users.
+ */
+ ADMIN = 0x08,
+};
+
+/**
+ * Information about all Android users.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it's part of other structs, which
+ * in turn are converted to a VehiclePropValue.RawValue through libraries provided by the default
+ * Vehicle HAL implementation.
+ */
+struct UsersInfo {
+
+ /** The current foreground user. */
+ UserInfo currentUser;
+
+ /** Number of existing users (includes the current user). */
+ int32_t numberUsers;
+
+ /** List of existing users (includes the current user). */
+ vec<UserInfo> existingUsers;
+ };
+
+/**
+ * Id of a request related to user management.
+ *
+ * This id can be used by the Android system to map responses sent by the HAL, and vice-versa.
+ *
+ * For requests originated by Android, the value is positive (> 0), while for requests originated by
+ * the HAL it must be negative (< 0).
+ */
+typedef int32_t UserRequestId;
+
+/**
+ * Defines the format of a INITIAL_USER_INFO request made by the Android system.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct InitialUserInfoRequest {
+ /**
+ * Arbitrary id used to map the HAL response to the request.
+ */
+ UserRequestId requestId;
+
+ /**
+ * Type of request.
+ */
+ InitialUserInfoRequestType requestType;
+
+ /**
+ * Information about the current state of the Android system.
+ */
+ UsersInfo usersInfo;
+};
+
+/**
+ * Defines when a INITIAL_USER_INFO request was made.
+ */
+enum InitialUserInfoRequestType : int32_t {
+ /** At the first time Android was booted (or after a factory reset). */
+ FIRST_BOOT = 1,
+
+ /** At the first time Android was booted after the system was updated. */
+ FIRST_BOOT_AFTER_OTA = 2,
+
+ /** When Android was booted "from scratch". */
+ COLD_BOOT = 3,
+
+ /** When Android was resumed after the system was suspended to memory. */
+ RESUME = 4,
+};
+
+/**
+ * Defines the format of a HAL response to a INITIAL_USER_INFO request.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct InitialUserInfoResponse {
+ /**
+ * Id of the request being responded.
+ */
+ UserRequestId requestId;
+
+ /**
+ * which action the Android system should take.
+ */
+ InitialUserInfoResponseAction action;
+
+ /**
+ * Information about the user that should be switched to or created.
+ */
+ UserInfo userToSwitchOrCreate;
+
+ /**
+ * Name of the user that should be created.
+ */
+ string userNameToCreate;
+};
+
+/**
+ * Defines which action the Android system should take in an INITIAL_USER_INFO request.
+ */
+enum InitialUserInfoResponseAction : int32_t {
+ /**
+ * Let the Android System decide what to do.
+ *
+ * For example, it might create a new user on first boot, and switch to the last
+ * active user afterwards.
+ */
+ DEFAULT = 0,
+
+ /**
+ * Switch to an existing Android user.
+ */
+ SWITCH = 1,
+
+ /**
+ * Create a new Android user (and switch to it).
+ */
+ CREATE = 2,
+};
+
+/**
+ * Defines the format of a SWITCH_USER property.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct SwitchUserRequest {
+ /**
+ * Arbitrary id used to map the response to the request.
+ */
+ UserRequestId requestId;
+
+ /**
+ * Type of message.
+ */
+ SwitchUserMessageType messageType;
+
+ /**
+ * Information about the Android user being switched to.
+ *
+ * Only the user id (but not the flags) should be set when the request is made by HAL.
+ */
+ UserInfo targetUser;
+
+ /**
+ * Information about the current state of the Android system.
+ *
+ * Should not be set when the request is made by HAL.
+ */
+ UsersInfo usersInfo;
+};
+
+/**
+ * Defines the reason a SWITCH_USER call was made.
+ *
+ * The meaning of each constant is explained in that property.
+ */
+enum SwitchUserMessageType: int32_t {
+ LEGACY_ANDROID_SWITCH = 1,
+ ANDROID_SWITCH = 2,
+ VEHICLE_RESPONSE = 3,
+ VEHICLE_REQUEST = 4,
+ ANDROID_POST_SWITCH = 5,
+};
+
+/**
+ * Defines the result of a SwitchUserRequest.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct SwitchUserResponse {
+ /**
+ * Id of the request being responded.
+ */
+ UserRequestId requestId;
+
+ /**
+ * Type of message.
+ */
+ SwitchUserMessageType messageType;
+
+ /**
+ * Status of the request.
+ */
+ SwitchUserStatus status;
+
+ /**
+ * HAL-specific error message.
+ *
+ * This argument is optional, and when defined, it's passed "as-is" to the caller. It could be
+ * used to show custom error messages to the end user.
+ */
+ string errorMessage;
+};
+
+/**
+ * Status of the response to a SwitchUserRequest.
+ */
+enum SwitchUserStatus : int32_t {
+ /** The request succeeded and the HAL user was switched. */
+ SUCCESS = 1,
+ /** The request failed and the HAL user remained the same. */
+ FAILURE = 2,
+};
+
+/**
+ * Defines the format of a CREATE_USER property.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct CreateUserRequest {
+ /**
+ * Arbitrary id used to map the response to the request.
+ */
+ UserRequestId requestId;
+
+ /**
+ * Basic information about Android user that was created.
+ */
+ UserInfo newUserInfo;
+
+ /**
+ * Name of the new Android user.
+ */
+ string newUserName;
+
+ /**
+ * Information about the current state of the Android system.
+ */
+ UsersInfo usersInfo;
+};
+
+/**
+ * Defines the result of a CreateUserRequest.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct CreateUserResponse {
+ /**
+ * Id of the request being responded.
+ */
+ UserRequestId requestId;
+
+ /**
+ * Status of the request.
+ */
+ CreateUserStatus status;
+
+ /**
+ * HAL-specific error message.
+ *
+ * This argument is optional, and when defined, it's passed "as-is" to the caller. It could be
+ * used to show custom error messages to the end user.
+ */
+ string errorMessage;
+};
+
+/**
+ * Status of the response to a CreateUserRequest.
+ */
+enum CreateUserStatus : int32_t {
+ /**
+ * The request succeeded (for example, HAL created a new internal user, or associated the
+ * Android user to an existing internal user).
+ */
+ SUCCESS = 1,
+
+ /**
+ * The request failed (and Android will remove the Android user).
+ */
+ FAILURE = 2,
+};
+
+/**
+ * Defines the format of a REMOVE_USER property.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct RemoveUserRequest {
+ /**
+ * Arbitrary id used to map the response to the request.
+ */
+ UserRequestId requestId;
+
+ /**
+ * Information about the Android user that was removed.
+ */
+ UserInfo removedUserInfo;
+
+ /**
+ * Information about the current state of the Android system.
+ */
+ UsersInfo usersInfo;
+};
+
+/**
+ * Types of mechanisms used to identify an Android user.
+ *
+ * See USER_IDENTIFICATION_ASSOCIATION for more details and example.
+ */
+enum UserIdentificationAssociationType: int32_t {
+ /** Key used to unlock the car. */
+ KEY_FOB = 1,
+ /** Custom mechanism defined by the OEM. */
+ CUSTOM_1 = 101,
+ /** Custom mechanism defined by the OEM. */
+ CUSTOM_2 = 102,
+ /** Custom mechanism defined by the OEM. */
+ CUSTOM_3 = 103,
+ /** Custom mechanism defined by the OEM. */
+ CUSTOM_4 = 104,
+};
+
+/**
+ * Whether a UserIdentificationAssociationType is associate with an Android user.
+ */
+enum UserIdentificationAssociationValue : int32_t {
+ /**
+ * Used when the status of an association could not be determined.
+ *
+ * For example, in a set() request, it would indicate a failure to set the given type.
+ */
+ UNKNOWN = 1,
+
+ /**
+ * The identification type is associated with the current foreground Android user.
+ */
+ ASSOCIATED_CURRENT_USER = 2,
+
+ /**
+ * The identification type is associated with another Android user.
+ */
+ ASSOCIATED_ANOTHER_USER = 3,
+
+ /**
+ * The identification type is not associated with any Android user.
+ */
+ NOT_ASSOCIATED_ANY_USER = 4,
+};
+
+/**
+ * Used to set a UserIdentificationAssociationType with an Android user.
+ */
+enum UserIdentificationAssociationSetValue : int32_t {
+ /**
+ * Associate the identification type with the current foreground Android user.
+ */
+ ASSOCIATE_CURRENT_USER = 1,
+
+ /**
+ * Disassociate the identification type from the current foreground Android user.
+ */
+ DISASSOCIATE_CURRENT_USER = 2,
+
+ /**
+ * Disassociate the identification type from all Android users.
+ */
+ DISASSOCIATE_ALL_USERS = 3,
+};
+
+/**
+ * Defines the format of a get() call to USER_IDENTIFICATION_ASSOCIATION.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct UserIdentificationGetRequest {
+ /**
+ * Information about the current foreground Android user.
+ */
+ UserInfo userInfo;
+
+ /**
+ * Number of association being queried.
+ */
+ int32_t numberAssociationTypes;
+
+ /**
+ * Types of association being queried.
+ */
+ vec<UserIdentificationAssociationType> associationTypes;
+};
+
+/**
+ * Defines the format of a set() call to USER_IDENTIFICATION_ASSOCIATION.
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct UserIdentificationSetRequest {
+ /**
+ * Information about the current foreground Android user.
+ */
+ UserInfo userInfo;
+
+ /**
+ * Number of association being set.
+ */
+ int32_t numberAssociations;
+
+ /**
+ * Associations being set.
+ */
+ vec<UserIdentificationAssociationSetValue> associations;
+};
+
+/**
+ * Defines the result of a USER_IDENTIFICATION_ASSOCIATION - both for get() and set().
+ *
+ * NOTE: this struct is not used in the HAL properties directly, it must be converted to
+ * VehiclePropValue.RawValue through libraries provided by the default Vehicle HAL implementation.
+ */
+struct UserIdentificationResponse {
+ /**
+ * Number of associations being returned.
+ */
+ int32_t numberAssociation;
+
+ /**
+ * Values associated with the user.
+ */
+ vec<UserIdentificationAssociation> associations;
+
+ /**
+ * HAL-specific error message.
+ *
+ * This argument is optional, and when defined, it's passed "as-is" to the caller. It could be
+ * used to show custom error messages to the end user.
+ */
+ string errorMessage;
+};
+
+/**
+ * Helper struct used when getting a user/identification association type.
+ */
+struct UserIdentificationAssociation {
+
+ UserIdentificationAssociationType type;
+
+ UserIdentificationAssociationValue value;
+};
+
+/**
+ * Helper struct used when setting a user/identification association type.
+ */
+struct UserIdentificationSetAssociation {
+
+ UserIdentificationAssociationType type;
+
+ UserIdentificationAssociationSetValue value;
+};
diff --git a/bluetooth/1.0/vts/functional/Android.bp b/bluetooth/1.0/vts/functional/Android.bp
index 54039e5..cf25cc8 100644
--- a/bluetooth/1.0/vts/functional/Android.bp
+++ b/bluetooth/1.0/vts/functional/Android.bp
@@ -22,5 +22,8 @@
"android.hardware.bluetooth@1.0",
"libbluetooth-types",
],
- test_suites: ["general-tests"],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
}
diff --git a/broadcastradio/common/vts/utils/include/broadcastradio-vts-utils/mock-timeout.h b/broadcastradio/common/vts/utils/include/broadcastradio-vts-utils/mock-timeout.h
index f6cd6ae..8ab2b43 100644
--- a/broadcastradio/common/vts/utils/include/broadcastradio-vts-utils/mock-timeout.h
+++ b/broadcastradio/common/vts/utils/include/broadcastradio-vts-utils/mock-timeout.h
@@ -82,33 +82,44 @@
}; \
return EGMockFlippedComma_<decltype(invokeMock())>(invokeMock, notify);
+// We define this as a variadic macro in case F contains unprotected
+// commas (the same reason that we use variadic macros in other places
+// in this file).
+#define EGMOCK_RESULT_(tn, ...) \
+ tn ::testing::internal::Function<__VA_ARGS__>::Result
+
+// The type of argument N of the given function type.
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define EGMOCK_ARG_(tn, N, ...) \
+ tn ::testing::internal::Function<__VA_ARGS__>::template Arg<N-1>::type
+
/**
* Gmock MOCK_METHOD0 timeout-capable extension.
*/
#define MOCK_TIMEOUT_METHOD0(Method, ...) \
MOCK_METHOD0(egmock_##Method, __VA_ARGS__); \
EGMOCK_TIMEOUT_METHOD_DEF_(Method); \
- virtual GMOCK_RESULT_(, __VA_ARGS__) Method() { EGMOCK_TIMEOUT_METHOD_BODY_(Method); }
+ virtual EGMOCK_RESULT_(, __VA_ARGS__) Method() { EGMOCK_TIMEOUT_METHOD_BODY_(Method); }
/**
* Gmock MOCK_METHOD1 timeout-capable extension.
*/
-#define MOCK_TIMEOUT_METHOD1(Method, ...) \
- MOCK_METHOD1(egmock_##Method, __VA_ARGS__); \
- EGMOCK_TIMEOUT_METHOD_DEF_(Method); \
- virtual GMOCK_RESULT_(, __VA_ARGS__) Method(GMOCK_ARG_(, 1, __VA_ARGS__) egmock_a1) { \
- EGMOCK_TIMEOUT_METHOD_BODY_(Method, egmock_a1); \
+#define MOCK_TIMEOUT_METHOD1(Method, ...) \
+ MOCK_METHOD1(egmock_##Method, __VA_ARGS__); \
+ EGMOCK_TIMEOUT_METHOD_DEF_(Method); \
+ virtual EGMOCK_RESULT_(, __VA_ARGS__) Method(EGMOCK_ARG_(, 1, __VA_ARGS__) egmock_a1) { \
+ EGMOCK_TIMEOUT_METHOD_BODY_(Method, egmock_a1); \
}
/**
* Gmock MOCK_METHOD2 timeout-capable extension.
*/
-#define MOCK_TIMEOUT_METHOD2(Method, ...) \
- MOCK_METHOD2(egmock_##Method, __VA_ARGS__); \
- EGMOCK_TIMEOUT_METHOD_DEF_(Method); \
- virtual GMOCK_RESULT_(, __VA_ARGS__) \
- Method(GMOCK_ARG_(, 1, __VA_ARGS__) egmock_a1, GMOCK_ARG_(, 2, __VA_ARGS__) egmock_a2) { \
- EGMOCK_TIMEOUT_METHOD_BODY_(Method, egmock_a1, egmock_a2); \
+#define MOCK_TIMEOUT_METHOD2(Method, ...) \
+ MOCK_METHOD2(egmock_##Method, __VA_ARGS__); \
+ EGMOCK_TIMEOUT_METHOD_DEF_(Method); \
+ virtual EGMOCK_RESULT_(, __VA_ARGS__) \
+ Method(EGMOCK_ARG_(, 1, __VA_ARGS__) egmock_a1, EGMOCK_ARG_(, 2, __VA_ARGS__) egmock_a2) { \
+ EGMOCK_TIMEOUT_METHOD_BODY_(Method, egmock_a1, egmock_a2); \
}
/**
diff --git a/camera/common/1.0/default/HandleImporter.cpp b/camera/common/1.0/default/HandleImporter.cpp
index ac32c95..7792b31 100644
--- a/camera/common/1.0/default/HandleImporter.cpp
+++ b/camera/common/1.0/default/HandleImporter.cpp
@@ -182,9 +182,8 @@
}
Mutex::Autolock lock(mLock);
- if (mMapperV4 == nullptr && mMapperV3 == nullptr && mMapperV2 == nullptr) {
- ALOGE("%s: mMapperV4, mMapperV3 and mMapperV2 are all null!", __FUNCTION__);
- return;
+ if (!mInitialized) {
+ initializeLocked();
}
if (mMapperV4 != nullptr) {
diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
index 9ff0d74..5f86742 100644
--- a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
@@ -81,8 +81,6 @@
return locked;
}
-buffer_handle_t sEmptyBuffer = nullptr;
-
} // Anonymous namespace
// Static instances
@@ -119,8 +117,8 @@
std::string make, model;
if (ret < 0) {
ALOGW("%s v4l2 QUERYCAP failed", __FUNCTION__);
- make = "Generic UVC webcam";
- model = "Generic UVC webcam";
+ mExifMake = "Generic UVC webcam";
+ mExifModel = "Generic UVC webcam";
} else {
// capability.card is UTF-8 encoded
char card[32];
@@ -134,11 +132,11 @@
}
}
if (j == 0 || card[j - 1] != '\0') {
- make = "Generic UVC webcam";
- model = "Generic UVC webcam";
+ mExifMake = "Generic UVC webcam";
+ mExifModel = "Generic UVC webcam";
} else {
- make = card;
- model = card;
+ mExifMake = card;
+ mExifModel = card;
}
}
@@ -147,7 +145,7 @@
ALOGE("%s: init OutputThread failed!", __FUNCTION__);
return true;
}
- mOutputThread->setExifMakeModel(make, model);
+ mOutputThread->setExifMakeModel(mExifMake, mExifModel);
status_t status = initDefaultRequests();
if (status != OK) {
@@ -161,7 +159,7 @@
ALOGE("%s: invalid request fmq", __FUNCTION__);
return true;
}
- mResultMetadataQueue = std::make_shared<RequestMetadataQueue>(
+ mResultMetadataQueue = std::make_shared<ResultMetadataQueue>(
kMetadataMsgQueueSize, false /* non blocking */);
if (!mResultMetadataQueue->isValid()) {
ALOGE("%s: invalid result fmq", __FUNCTION__);
@@ -183,7 +181,7 @@
}
void ExternalCameraDeviceSession::initOutputThread() {
- mOutputThread = new OutputThread(this, mCroppingType);
+ mOutputThread = new OutputThread(this, mCroppingType, mCameraCharacteristics);
}
void ExternalCameraDeviceSession::closeOutputThread() {
@@ -518,35 +516,9 @@
uint64_t bufId, buffer_handle_t buf,
/*out*/buffer_handle_t** outBufPtr,
bool allowEmptyBuf) {
-
- if (buf == nullptr && bufId == BUFFER_ID_NO_BUFFER) {
- if (allowEmptyBuf) {
- *outBufPtr = &sEmptyBuffer;
- return Status::OK;
- } else {
- ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
- return Status::ILLEGAL_ARGUMENT;
- }
- }
-
- CirculatingBuffers& cbs = mCirculatingBuffers[streamId];
- if (cbs.count(bufId) == 0) {
- if (buf == nullptr) {
- ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
- return Status::ILLEGAL_ARGUMENT;
- }
- // Register a newly seen buffer
- buffer_handle_t importedBuf = buf;
- sHandleImporter.importBuffer(importedBuf);
- if (importedBuf == nullptr) {
- ALOGE("%s: output buffer for stream %d is invalid!", __FUNCTION__, streamId);
- return Status::INTERNAL_ERROR;
- } else {
- cbs[bufId] = importedBuf;
- }
- }
- *outBufPtr = &cbs[bufId];
- return Status::OK;
+ return importBufferImpl(
+ mCirculatingBuffers, sHandleImporter, streamId,
+ bufId, buf, outBufPtr, allowEmptyBuf);
}
Status ExternalCameraDeviceSession::importRequestLockedImpl(
@@ -791,15 +763,32 @@
//TODO: refactor with processCaptureResult
Status ExternalCameraDeviceSession::processCaptureRequestError(
- const std::shared_ptr<HalRequest>& req) {
+ const std::shared_ptr<HalRequest>& req,
+ /*out*/std::vector<NotifyMsg>* outMsgs,
+ /*out*/std::vector<CaptureResult>* outResults) {
ATRACE_CALL();
// Return V4L2 buffer to V4L2 buffer queue
- enqueueV4l2Frame(req->frameIn);
+ sp<V3_4::implementation::V4L2Frame> v4l2Frame =
+ static_cast<V3_4::implementation::V4L2Frame*>(req->frameIn.get());
+ enqueueV4l2Frame(v4l2Frame);
- // NotifyShutter
- notifyShutter(req->frameNumber, req->shutterTs);
+ if (outMsgs == nullptr) {
+ notifyShutter(req->frameNumber, req->shutterTs);
+ notifyError(/*frameNum*/req->frameNumber, /*stream*/-1, ErrorCode::ERROR_REQUEST);
+ } else {
+ NotifyMsg shutter;
+ shutter.type = MsgType::SHUTTER;
+ shutter.msg.shutter.frameNumber = req->frameNumber;
+ shutter.msg.shutter.timestamp = req->shutterTs;
- notifyError(/*frameNum*/req->frameNumber, /*stream*/-1, ErrorCode::ERROR_REQUEST);
+ NotifyMsg error;
+ error.type = MsgType::ERROR;
+ error.msg.error.frameNumber = req->frameNumber;
+ error.msg.error.errorStreamId = -1;
+ error.msg.error.errorCode = ErrorCode::ERROR_REQUEST;
+ outMsgs->push_back(shutter);
+ outMsgs->push_back(error);
+ }
// Fill output buffers
hidl_vec<CaptureResult> results;
@@ -826,16 +815,22 @@
mInflightFrames.erase(req->frameNumber);
}
- // Callback into framework
- invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true);
- freeReleaseFences(results);
+ if (outResults == nullptr) {
+ // Callback into framework
+ invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true);
+ freeReleaseFences(results);
+ } else {
+ outResults->push_back(result);
+ }
return Status::OK;
}
Status ExternalCameraDeviceSession::processCaptureResult(std::shared_ptr<HalRequest>& req) {
ATRACE_CALL();
// Return V4L2 buffer to V4L2 buffer queue
- enqueueV4l2Frame(req->frameIn);
+ sp<V3_4::implementation::V4L2Frame> v4l2Frame =
+ static_cast<V3_4::implementation::V4L2Frame*>(req->frameIn.get());
+ enqueueV4l2Frame(v4l2Frame);
// NotifyShutter
notifyShutter(req->frameNumber, req->shutterTs);
@@ -923,29 +918,10 @@
mProcessCaptureResultLock.unlock();
}
-void ExternalCameraDeviceSession::freeReleaseFences(hidl_vec<CaptureResult>& results) {
- for (auto& result : results) {
- if (result.inputBuffer.releaseFence.getNativeHandle() != nullptr) {
- native_handle_t* handle = const_cast<native_handle_t*>(
- result.inputBuffer.releaseFence.getNativeHandle());
- native_handle_close(handle);
- native_handle_delete(handle);
- }
- for (auto& buf : result.outputBuffers) {
- if (buf.releaseFence.getNativeHandle() != nullptr) {
- native_handle_t* handle = const_cast<native_handle_t*>(
- buf.releaseFence.getNativeHandle());
- native_handle_close(handle);
- native_handle_delete(handle);
- }
- }
- }
- return;
-}
-
ExternalCameraDeviceSession::OutputThread::OutputThread(
- wp<ExternalCameraDeviceSession> parent,
- CroppingType ct) : mParent(parent), mCroppingType(ct) {}
+ wp<OutputThreadInterface> parent, CroppingType ct,
+ const common::V1_0::helper::CameraMetadata& chars) :
+ mParent(parent), mCroppingType(ct), mCameraCharacteristics(chars) {}
ExternalCameraDeviceSession::OutputThread::~OutputThread() {}
@@ -955,88 +931,6 @@
mExifModel = model;
}
-uint32_t ExternalCameraDeviceSession::OutputThread::getFourCcFromLayout(
- const YCbCrLayout& layout) {
- intptr_t cb = reinterpret_cast<intptr_t>(layout.cb);
- intptr_t cr = reinterpret_cast<intptr_t>(layout.cr);
- if (std::abs(cb - cr) == 1 && layout.chromaStep == 2) {
- // Interleaved format
- if (layout.cb > layout.cr) {
- return V4L2_PIX_FMT_NV21;
- } else {
- return V4L2_PIX_FMT_NV12;
- }
- } else if (layout.chromaStep == 1) {
- // Planar format
- if (layout.cb > layout.cr) {
- return V4L2_PIX_FMT_YVU420; // YV12
- } else {
- return V4L2_PIX_FMT_YUV420; // YU12
- }
- } else {
- return FLEX_YUV_GENERIC;
- }
-}
-
-int ExternalCameraDeviceSession::OutputThread::getCropRect(
- CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out) {
- if (out == nullptr) {
- ALOGE("%s: out is null", __FUNCTION__);
- return -1;
- }
-
- uint32_t inW = inSize.width;
- uint32_t inH = inSize.height;
- uint32_t outW = outSize.width;
- uint32_t outH = outSize.height;
-
- // Handle special case where aspect ratio is close to input but scaled
- // dimension is slightly larger than input
- float arIn = ASPECT_RATIO(inSize);
- float arOut = ASPECT_RATIO(outSize);
- if (isAspectRatioClose(arIn, arOut)) {
- out->left = 0;
- out->top = 0;
- out->width = inW;
- out->height = inH;
- return 0;
- }
-
- if (ct == VERTICAL) {
- uint64_t scaledOutH = static_cast<uint64_t>(outH) * inW / outW;
- if (scaledOutH > inH) {
- ALOGE("%s: Output size %dx%d cannot be vertically cropped from input size %dx%d",
- __FUNCTION__, outW, outH, inW, inH);
- return -1;
- }
- scaledOutH = scaledOutH & ~0x1; // make it multiple of 2
-
- out->left = 0;
- out->top = ((inH - scaledOutH) / 2) & ~0x1;
- out->width = inW;
- out->height = static_cast<int32_t>(scaledOutH);
- ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledH %d",
- __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutH));
- } else {
- uint64_t scaledOutW = static_cast<uint64_t>(outW) * inH / outH;
- if (scaledOutW > inW) {
- ALOGE("%s: Output size %dx%d cannot be horizontally cropped from input size %dx%d",
- __FUNCTION__, outW, outH, inW, inH);
- return -1;
- }
- scaledOutW = scaledOutW & ~0x1; // make it multiple of 2
-
- out->left = ((inW - scaledOutW) / 2) & ~0x1;
- out->top = 0;
- out->width = static_cast<int32_t>(scaledOutW);
- out->height = inH;
- ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledW %d",
- __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutW));
- }
-
- return 0;
-}
-
int ExternalCameraDeviceSession::OutputThread::cropAndScaleLocked(
sp<AllocatedFrame>& in, const Size& outSz, YCbCrLayout* out) {
Size inSz = {in->mWidth, in->mHeight};
@@ -1274,265 +1168,6 @@
return 0;
}
-int ExternalCameraDeviceSession::OutputThread::formatConvertLocked(
- const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format) {
- int ret = 0;
- switch (format) {
- case V4L2_PIX_FMT_NV21:
- ret = libyuv::I420ToNV21(
- static_cast<uint8_t*>(in.y),
- in.yStride,
- static_cast<uint8_t*>(in.cb),
- in.cStride,
- static_cast<uint8_t*>(in.cr),
- in.cStride,
- static_cast<uint8_t*>(out.y),
- out.yStride,
- static_cast<uint8_t*>(out.cr),
- out.cStride,
- sz.width,
- sz.height);
- if (ret != 0) {
- ALOGE("%s: convert to NV21 buffer failed! ret %d",
- __FUNCTION__, ret);
- return ret;
- }
- break;
- case V4L2_PIX_FMT_NV12:
- ret = libyuv::I420ToNV12(
- static_cast<uint8_t*>(in.y),
- in.yStride,
- static_cast<uint8_t*>(in.cb),
- in.cStride,
- static_cast<uint8_t*>(in.cr),
- in.cStride,
- static_cast<uint8_t*>(out.y),
- out.yStride,
- static_cast<uint8_t*>(out.cb),
- out.cStride,
- sz.width,
- sz.height);
- if (ret != 0) {
- ALOGE("%s: convert to NV12 buffer failed! ret %d",
- __FUNCTION__, ret);
- return ret;
- }
- break;
- case V4L2_PIX_FMT_YVU420: // YV12
- case V4L2_PIX_FMT_YUV420: // YU12
- // TODO: maybe we can speed up here by somehow save this copy?
- ret = libyuv::I420Copy(
- static_cast<uint8_t*>(in.y),
- in.yStride,
- static_cast<uint8_t*>(in.cb),
- in.cStride,
- static_cast<uint8_t*>(in.cr),
- in.cStride,
- static_cast<uint8_t*>(out.y),
- out.yStride,
- static_cast<uint8_t*>(out.cb),
- out.cStride,
- static_cast<uint8_t*>(out.cr),
- out.cStride,
- sz.width,
- sz.height);
- if (ret != 0) {
- ALOGE("%s: copy to YV12 or YU12 buffer failed! ret %d",
- __FUNCTION__, ret);
- return ret;
- }
- break;
- case FLEX_YUV_GENERIC:
- // TODO: b/72261744 write to arbitrary flexible YUV layout. Slow.
- ALOGE("%s: unsupported flexible yuv layout"
- " y %p cb %p cr %p y_str %d c_str %d c_step %d",
- __FUNCTION__, out.y, out.cb, out.cr,
- out.yStride, out.cStride, out.chromaStep);
- return -1;
- default:
- ALOGE("%s: unknown YUV format 0x%x!", __FUNCTION__, format);
- return -1;
- }
- return 0;
-}
-
-int ExternalCameraDeviceSession::OutputThread::encodeJpegYU12(
- const Size & inSz, const YCbCrLayout& inLayout,
- int jpegQuality, const void *app1Buffer, size_t app1Size,
- void *out, const size_t maxOutSize, size_t &actualCodeSize)
-{
- /* libjpeg is a C library so we use C-style "inheritance" by
- * putting libjpeg's jpeg_destination_mgr first in our custom
- * struct. This allows us to cast jpeg_destination_mgr* to
- * CustomJpegDestMgr* when we get it passed to us in a callback */
- struct CustomJpegDestMgr {
- struct jpeg_destination_mgr mgr;
- JOCTET *mBuffer;
- size_t mBufferSize;
- size_t mEncodedSize;
- bool mSuccess;
- } dmgr;
-
- jpeg_compress_struct cinfo = {};
- jpeg_error_mgr jerr;
-
- /* Initialize error handling with standard callbacks, but
- * then override output_message (to print to ALOG) and
- * error_exit to set a flag and print a message instead
- * of killing the whole process */
- cinfo.err = jpeg_std_error(&jerr);
-
- cinfo.err->output_message = [](j_common_ptr cinfo) {
- char buffer[JMSG_LENGTH_MAX];
-
- /* Create the message */
- (*cinfo->err->format_message)(cinfo, buffer);
- ALOGE("libjpeg error: %s", buffer);
- };
- cinfo.err->error_exit = [](j_common_ptr cinfo) {
- (*cinfo->err->output_message)(cinfo);
- if(cinfo->client_data) {
- auto & dmgr =
- *reinterpret_cast<CustomJpegDestMgr*>(cinfo->client_data);
- dmgr.mSuccess = false;
- }
- };
- /* Now that we initialized some callbacks, let's create our compressor */
- jpeg_create_compress(&cinfo);
-
- /* Initialize our destination manager */
- dmgr.mBuffer = static_cast<JOCTET*>(out);
- dmgr.mBufferSize = maxOutSize;
- dmgr.mEncodedSize = 0;
- dmgr.mSuccess = true;
- cinfo.client_data = static_cast<void*>(&dmgr);
-
- /* These lambdas become C-style function pointers and as per C++11 spec
- * may not capture anything */
- dmgr.mgr.init_destination = [](j_compress_ptr cinfo) {
- auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
- dmgr.mgr.next_output_byte = dmgr.mBuffer;
- dmgr.mgr.free_in_buffer = dmgr.mBufferSize;
- ALOGV("%s:%d jpeg start: %p [%zu]",
- __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize);
- };
-
- dmgr.mgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) {
- ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__);
- return 0;
- };
-
- dmgr.mgr.term_destination = [](j_compress_ptr cinfo) {
- auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
- dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.mgr.free_in_buffer;
- ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize);
- };
- cinfo.dest = reinterpret_cast<struct jpeg_destination_mgr*>(&dmgr);
-
- /* We are going to be using JPEG in raw data mode, so we are passing
- * straight subsampled planar YCbCr and it will not touch our pixel
- * data or do any scaling or anything */
- cinfo.image_width = inSz.width;
- cinfo.image_height = inSz.height;
- cinfo.input_components = 3;
- cinfo.in_color_space = JCS_YCbCr;
-
- /* Initialize defaults and then override what we want */
- jpeg_set_defaults(&cinfo);
-
- jpeg_set_quality(&cinfo, jpegQuality, 1);
- jpeg_set_colorspace(&cinfo, JCS_YCbCr);
- cinfo.raw_data_in = 1;
- cinfo.dct_method = JDCT_IFAST;
-
- /* Configure sampling factors. The sampling factor is JPEG subsampling 420
- * because the source format is YUV420. Note that libjpeg sampling factors
- * are... a little weird. Sampling of Y=2,U=1,V=1 means there is 1 U and
- * 1 V value for each 2 Y values */
- cinfo.comp_info[0].h_samp_factor = 2;
- cinfo.comp_info[0].v_samp_factor = 2;
- cinfo.comp_info[1].h_samp_factor = 1;
- cinfo.comp_info[1].v_samp_factor = 1;
- cinfo.comp_info[2].h_samp_factor = 1;
- cinfo.comp_info[2].v_samp_factor = 1;
-
- /* Let's not hardcode YUV420 in 6 places... 5 was enough */
- int maxVSampFactor = std::max( {
- cinfo.comp_info[0].v_samp_factor,
- cinfo.comp_info[1].v_samp_factor,
- cinfo.comp_info[2].v_samp_factor
- });
- int cVSubSampling = cinfo.comp_info[0].v_samp_factor /
- cinfo.comp_info[1].v_samp_factor;
-
- /* Start the compressor */
- jpeg_start_compress(&cinfo, TRUE);
-
- /* Compute our macroblock height, so we can pad our input to be vertically
- * macroblock aligned.
- * TODO: Does it need to be horizontally MCU aligned too? */
-
- size_t mcuV = DCTSIZE*maxVSampFactor;
- size_t paddedHeight = mcuV * ((inSz.height + mcuV - 1) / mcuV);
-
- /* libjpeg uses arrays of row pointers, which makes it really easy to pad
- * data vertically (unfortunately doesn't help horizontally) */
- std::vector<JSAMPROW> yLines (paddedHeight);
- std::vector<JSAMPROW> cbLines(paddedHeight/cVSubSampling);
- std::vector<JSAMPROW> crLines(paddedHeight/cVSubSampling);
-
- uint8_t *py = static_cast<uint8_t*>(inLayout.y);
- uint8_t *pcr = static_cast<uint8_t*>(inLayout.cr);
- uint8_t *pcb = static_cast<uint8_t*>(inLayout.cb);
-
- for(uint32_t i = 0; i < paddedHeight; i++)
- {
- /* Once we are in the padding territory we still point to the last line
- * effectively replicating it several times ~ CLAMP_TO_EDGE */
- int li = std::min(i, inSz.height - 1);
- yLines[i] = static_cast<JSAMPROW>(py + li * inLayout.yStride);
- if(i < paddedHeight / cVSubSampling)
- {
- crLines[i] = static_cast<JSAMPROW>(pcr + li * inLayout.cStride);
- cbLines[i] = static_cast<JSAMPROW>(pcb + li * inLayout.cStride);
- }
- }
-
- /* If APP1 data was passed in, use it */
- if(app1Buffer && app1Size)
- {
- jpeg_write_marker(&cinfo, JPEG_APP0 + 1,
- static_cast<const JOCTET*>(app1Buffer), app1Size);
- }
-
- /* While we still have padded height left to go, keep giving it one
- * macroblock at a time. */
- while (cinfo.next_scanline < cinfo.image_height) {
- const uint32_t batchSize = DCTSIZE * maxVSampFactor;
- const uint32_t nl = cinfo.next_scanline;
- JSAMPARRAY planes[3]{ &yLines[nl],
- &cbLines[nl/cVSubSampling],
- &crLines[nl/cVSubSampling] };
-
- uint32_t done = jpeg_write_raw_data(&cinfo, planes, batchSize);
-
- if (done != batchSize) {
- ALOGE("%s: compressed %u lines, expected %u (total %u/%u)",
- __FUNCTION__, done, batchSize, cinfo.next_scanline,
- cinfo.image_height);
- return -1;
- }
- }
-
- /* This will flush everything */
- jpeg_finish_compress(&cinfo);
-
- /* Grab the actual code size and set it */
- actualCodeSize = dmgr.mEncodedSize;
-
- return 0;
-}
-
/*
* TODO: There needs to be a mechanism to discover allocated buffer size
* in the HAL.
@@ -1555,25 +1190,9 @@
}
Size ExternalCameraDeviceSession::getMaxThumbResolution() const {
- Size thumbSize { 0, 0 };
- camera_metadata_ro_entry entry =
- mCameraCharacteristics.find(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES);
- for(uint32_t i = 0; i < entry.count; i += 2) {
- Size sz { static_cast<uint32_t>(entry.data.i32[i]),
- static_cast<uint32_t>(entry.data.i32[i+1]) };
- if(sz.width * sz.height > thumbSize.width * thumbSize.height) {
- thumbSize = sz;
- }
- }
-
- if (thumbSize.width * thumbSize.height == 0) {
- ALOGW("%s: non-zero thumbnail size not available", __FUNCTION__);
- }
-
- return thumbSize;
+ return getMaxThumbnailResolution(mCameraCharacteristics);
}
-
ssize_t ExternalCameraDeviceSession::getJpegBufferSize(
uint32_t width, uint32_t height) const {
// Constant from camera3.h
@@ -1616,7 +1235,7 @@
int ExternalCameraDeviceSession::OutputThread::createJpegLocked(
HalStreamBuffer &halBuf,
- const std::shared_ptr<HalRequest>& req)
+ const common::V1_0::helper::CameraMetadata& setting)
{
ATRACE_CALL();
int ret;
@@ -1645,17 +1264,17 @@
Size thumbSize;
bool outputThumbnail = true;
- if (req->setting.exists(ANDROID_JPEG_QUALITY)) {
- camera_metadata_entry entry =
- req->setting.find(ANDROID_JPEG_QUALITY);
+ if (setting.exists(ANDROID_JPEG_QUALITY)) {
+ camera_metadata_ro_entry entry =
+ setting.find(ANDROID_JPEG_QUALITY);
jpegQuality = entry.data.u8[0];
} else {
return lfail("%s: ANDROID_JPEG_QUALITY not set",__FUNCTION__);
}
- if (req->setting.exists(ANDROID_JPEG_THUMBNAIL_QUALITY)) {
- camera_metadata_entry entry =
- req->setting.find(ANDROID_JPEG_THUMBNAIL_QUALITY);
+ if (setting.exists(ANDROID_JPEG_THUMBNAIL_QUALITY)) {
+ camera_metadata_ro_entry entry =
+ setting.find(ANDROID_JPEG_THUMBNAIL_QUALITY);
thumbQuality = entry.data.u8[0];
} else {
return lfail(
@@ -1663,9 +1282,9 @@
__FUNCTION__);
}
- if (req->setting.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) {
- camera_metadata_entry entry =
- req->setting.find(ANDROID_JPEG_THUMBNAIL_SIZE);
+ if (setting.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) {
+ camera_metadata_ro_entry entry =
+ setting.find(ANDROID_JPEG_THUMBNAIL_SIZE);
thumbSize = Size { static_cast<uint32_t>(entry.data.i32[0]),
static_cast<uint32_t>(entry.data.i32[1])
};
@@ -1732,8 +1351,8 @@
/* Combine camera characteristics with request settings to form EXIF
* metadata */
- common::V1_0::helper::CameraMetadata meta(parent->mCameraCharacteristics);
- meta.append(req->setting);
+ common::V1_0::helper::CameraMetadata meta(mCameraCharacteristics);
+ meta.append(setting);
/* Generate EXIF object */
std::unique_ptr<ExifUtils> utils(ExifUtils::create());
@@ -1838,7 +1457,7 @@
// TODO: see if we can save some computation by converting to YV12 here
uint8_t* inData;
size_t inDataSize;
- if (req->frameIn->map(&inData, &inDataSize) != 0) {
+ if (req->frameIn->getData(&inData, &inDataSize) != 0) {
lk.unlock();
return onDeviceError("%s: V4L2 buffer map failed", __FUNCTION__);
}
@@ -1899,7 +1518,7 @@
// Gralloc lockYCbCr the buffer
switch (halBuf.format) {
case PixelFormat::BLOB: {
- int ret = createJpegLocked(halBuf, req);
+ int ret = createJpegLocked(halBuf, req->setting);
if(ret != 0) {
lk.unlock();
@@ -1949,8 +1568,8 @@
}
Size sz {halBuf.width, halBuf.height};
- ATRACE_BEGIN("formatConvertLocked");
- ret = formatConvertLocked(cropAndScaled, outLayout, sz, outputFourcc);
+ ATRACE_BEGIN("formatConvert");
+ ret = formatConvert(cropAndScaled, outLayout, sz, outputFourcc);
ATRACE_END();
if (ret != 0) {
lk.unlock();
@@ -2055,6 +1674,14 @@
return Status::OK;
}
+void ExternalCameraDeviceSession::OutputThread::clearIntermediateBuffers() {
+ std::lock_guard<std::mutex> lk(mBufferLock);
+ mYu12Frame.clear();
+ mYu12ThumbFrame.clear();
+ mIntermediateBuffers.clear();
+ mBlobBufferSize = 0;
+}
+
Status ExternalCameraDeviceSession::OutputThread::submitRequest(
const std::shared_ptr<HalRequest>& req) {
std::unique_lock<std::mutex> lk(mRequestListLock);
@@ -2090,6 +1717,32 @@
}
}
+std::list<std::shared_ptr<HalRequest>>
+ExternalCameraDeviceSession::OutputThread::switchToOffline() {
+ ATRACE_CALL();
+ std::list<std::shared_ptr<HalRequest>> emptyList;
+ auto parent = mParent.promote();
+ if (parent == nullptr) {
+ ALOGE("%s: session has been disconnected!", __FUNCTION__);
+ return emptyList;
+ }
+
+ std::unique_lock<std::mutex> lk(mRequestListLock);
+ std::list<std::shared_ptr<HalRequest>> reqs = std::move(mRequestList);
+ mRequestList.clear();
+ if (mProcessingRequest) {
+ std::chrono::seconds timeout = std::chrono::seconds(kFlushWaitTimeoutSec);
+ auto st = mRequestDoneCond.wait_for(lk, timeout);
+ if (st == std::cv_status::timeout) {
+ ALOGE("%s: wait for inflight request finish timeout!", __FUNCTION__);
+ }
+ }
+ lk.unlock();
+ clearIntermediateBuffers();
+ ALOGV("%s: returning %zu request for offline processing", __FUNCTION__, reqs.size());
+ return reqs;
+}
+
void ExternalCameraDeviceSession::OutputThread::waitForNextRequest(
std::shared_ptr<HalRequest>* out) {
ATRACE_CALL();
@@ -2733,6 +2386,7 @@
return Status::INTERNAL_ERROR;
}
+ mBlobBufferSize = blobBufferSize;
status = mOutputThread->allocateIntermediateBuffers(v4lSize,
mMaxThumbResolution, config.streams, blobBufferSize);
if (status != Status::OK) {
@@ -2916,16 +2570,6 @@
status_t ExternalCameraDeviceSession::fillCaptureResult(
common::V1_0::helper::CameraMetadata &md, nsecs_t timestamp) {
- // android.control
- // For USB camera, we don't know the AE state. Set the state to converged to
- // indicate the frame should be good to use. Then apps don't have to wait the
- // AE state.
- const uint8_t aeState = ANDROID_CONTROL_AE_STATE_CONVERGED;
- UPDATE(md, ANDROID_CONTROL_AE_STATE, &aeState, 1);
-
- const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;
- UPDATE(md, ANDROID_CONTROL_AE_LOCK, &ae_lock, 1);
-
bool afTrigger = false;
{
std::lock_guard<std::mutex> lk(mAfTriggerLock);
@@ -2951,46 +2595,10 @@
}
UPDATE(md, ANDROID_CONTROL_AF_STATE, &afState, 1);
- // Set AWB state to converged to indicate the frame should be good to use.
- const uint8_t awbState = ANDROID_CONTROL_AWB_STATE_CONVERGED;
- UPDATE(md, ANDROID_CONTROL_AWB_STATE, &awbState, 1);
+ camera_metadata_ro_entry activeArraySize =
+ mCameraCharacteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
- UPDATE(md, ANDROID_CONTROL_AWB_LOCK, &awbLock, 1);
-
- camera_metadata_ro_entry active_array_size =
- mCameraCharacteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
-
- if (active_array_size.count == 0) {
- ALOGE("%s: cannot find active array size!", __FUNCTION__);
- return -EINVAL;
- }
-
- const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE;
- UPDATE(md, ANDROID_FLASH_STATE, &flashState, 1);
-
- // This means pipeline latency of X frame intervals. The maximum number is 4.
- const uint8_t requestPipelineMaxDepth = 4;
- UPDATE(md, ANDROID_REQUEST_PIPELINE_DEPTH, &requestPipelineMaxDepth, 1);
-
- // android.scaler
- const int32_t crop_region[] = {
- active_array_size.data.i32[0], active_array_size.data.i32[1],
- active_array_size.data.i32[2], active_array_size.data.i32[3],
- };
- UPDATE(md, ANDROID_SCALER_CROP_REGION, crop_region, ARRAY_SIZE(crop_region));
-
- // android.sensor
- UPDATE(md, ANDROID_SENSOR_TIMESTAMP, ×tamp, 1);
-
- // android.statistics
- const uint8_t lensShadingMapMode = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
- UPDATE(md, ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, &lensShadingMapMode, 1);
-
- const uint8_t sceneFlicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE;
- UPDATE(md, ANDROID_STATISTICS_SCENE_FLICKER, &sceneFlicker, 1);
-
- return OK;
+ return fillCaptureResultCommon(md, timestamp, activeArraySize);
}
#undef ARRAY_SIZE
diff --git a/camera/device/3.4/default/ExternalCameraUtils.cpp b/camera/device/3.4/default/ExternalCameraUtils.cpp
index e25deff..62a4c87 100644
--- a/camera/device/3.4/default/ExternalCameraUtils.cpp
+++ b/camera/device/3.4/default/ExternalCameraUtils.cpp
@@ -18,10 +18,23 @@
#include <log/log.h>
#include <cmath>
+#include <cstring>
#include <sys/mman.h>
#include <linux/videodev2.h>
+
+#define HAVE_JPEG // required for libyuv.h to export MJPEG decode APIs
+#include <libyuv.h>
+
+#include <jpeglib.h>
+
#include "ExternalCameraUtils.h"
+namespace {
+
+buffer_handle_t sEmptyBuffer = nullptr;
+
+} // Anonymous namespace
+
namespace android {
namespace hardware {
namespace camera {
@@ -29,10 +42,13 @@
namespace V3_4 {
namespace implementation {
+Frame::Frame(uint32_t width, uint32_t height, uint32_t fourcc) :
+ mWidth(width), mHeight(height), mFourcc(fourcc) {}
+
V4L2Frame::V4L2Frame(
uint32_t w, uint32_t h, uint32_t fourcc,
int bufIdx, int fd, uint32_t dataSize, uint64_t offset) :
- mWidth(w), mHeight(h), mFourcc(fourcc),
+ Frame(w, h, fourcc),
mBufferIndex(bufIdx), mFd(fd), mDataSize(dataSize), mOffset(offset) {}
int V4L2Frame::map(uint8_t** data, size_t* dataSize) {
@@ -75,9 +91,13 @@
unmap();
}
+int V4L2Frame::getData(uint8_t** outData, size_t* dataSize) {
+ return map(outData, dataSize);
+}
+
AllocatedFrame::AllocatedFrame(
uint32_t w, uint32_t h) :
- mWidth(w), mHeight(h), mFourcc(V4L2_PIX_FMT_YUV420) {};
+ Frame(w, h, V4L2_PIX_FMT_YUV420) {};
AllocatedFrame::~AllocatedFrame() {}
@@ -106,6 +126,17 @@
return 0;
}
+int AllocatedFrame::getData(uint8_t** outData, size_t* dataSize) {
+ YCbCrLayout layout;
+ int ret = allocate(&layout);
+ if (ret != 0) {
+ return ret;
+ }
+ *outData = mData.data();
+ *dataSize = mData.size();
+ return 0;
+}
+
int AllocatedFrame::getLayout(YCbCrLayout* out) {
IMapper::Rect noCrop = {0, 0,
static_cast<int32_t>(mWidth),
@@ -150,8 +181,521 @@
return durationDenominator / static_cast<double>(durationNumerator);
}
+::android::hardware::camera::common::V1_0::Status importBufferImpl(
+ /*inout*/std::map<int, CirculatingBuffers>& circulatingBuffers,
+ /*inout*/HandleImporter& handleImporter,
+ int32_t streamId,
+ uint64_t bufId, buffer_handle_t buf,
+ /*out*/buffer_handle_t** outBufPtr,
+ bool allowEmptyBuf) {
+ using ::android::hardware::camera::common::V1_0::Status;
+ if (buf == nullptr && bufId == BUFFER_ID_NO_BUFFER) {
+ if (allowEmptyBuf) {
+ *outBufPtr = &sEmptyBuffer;
+ return Status::OK;
+ } else {
+ ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ }
+
+ CirculatingBuffers& cbs = circulatingBuffers[streamId];
+ if (cbs.count(bufId) == 0) {
+ if (buf == nullptr) {
+ ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ // Register a newly seen buffer
+ buffer_handle_t importedBuf = buf;
+ handleImporter.importBuffer(importedBuf);
+ if (importedBuf == nullptr) {
+ ALOGE("%s: output buffer for stream %d is invalid!", __FUNCTION__, streamId);
+ return Status::INTERNAL_ERROR;
+ } else {
+ cbs[bufId] = importedBuf;
+ }
+ }
+ *outBufPtr = &cbs[bufId];
+ return Status::OK;
+}
+
+uint32_t getFourCcFromLayout(const YCbCrLayout& layout) {
+ intptr_t cb = reinterpret_cast<intptr_t>(layout.cb);
+ intptr_t cr = reinterpret_cast<intptr_t>(layout.cr);
+ if (std::abs(cb - cr) == 1 && layout.chromaStep == 2) {
+ // Interleaved format
+ if (layout.cb > layout.cr) {
+ return V4L2_PIX_FMT_NV21;
+ } else {
+ return V4L2_PIX_FMT_NV12;
+ }
+ } else if (layout.chromaStep == 1) {
+ // Planar format
+ if (layout.cb > layout.cr) {
+ return V4L2_PIX_FMT_YVU420; // YV12
+ } else {
+ return V4L2_PIX_FMT_YUV420; // YU12
+ }
+ } else {
+ return FLEX_YUV_GENERIC;
+ }
+}
+
+int getCropRect(
+ CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out) {
+ if (out == nullptr) {
+ ALOGE("%s: out is null", __FUNCTION__);
+ return -1;
+ }
+
+ uint32_t inW = inSize.width;
+ uint32_t inH = inSize.height;
+ uint32_t outW = outSize.width;
+ uint32_t outH = outSize.height;
+
+ // Handle special case where aspect ratio is close to input but scaled
+ // dimension is slightly larger than input
+ float arIn = ASPECT_RATIO(inSize);
+ float arOut = ASPECT_RATIO(outSize);
+ if (isAspectRatioClose(arIn, arOut)) {
+ out->left = 0;
+ out->top = 0;
+ out->width = inW;
+ out->height = inH;
+ return 0;
+ }
+
+ if (ct == VERTICAL) {
+ uint64_t scaledOutH = static_cast<uint64_t>(outH) * inW / outW;
+ if (scaledOutH > inH) {
+ ALOGE("%s: Output size %dx%d cannot be vertically cropped from input size %dx%d",
+ __FUNCTION__, outW, outH, inW, inH);
+ return -1;
+ }
+ scaledOutH = scaledOutH & ~0x1; // make it multiple of 2
+
+ out->left = 0;
+ out->top = ((inH - scaledOutH) / 2) & ~0x1;
+ out->width = inW;
+ out->height = static_cast<int32_t>(scaledOutH);
+ ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledH %d",
+ __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutH));
+ } else {
+ uint64_t scaledOutW = static_cast<uint64_t>(outW) * inH / outH;
+ if (scaledOutW > inW) {
+ ALOGE("%s: Output size %dx%d cannot be horizontally cropped from input size %dx%d",
+ __FUNCTION__, outW, outH, inW, inH);
+ return -1;
+ }
+ scaledOutW = scaledOutW & ~0x1; // make it multiple of 2
+
+ out->left = ((inW - scaledOutW) / 2) & ~0x1;
+ out->top = 0;
+ out->width = static_cast<int32_t>(scaledOutW);
+ out->height = inH;
+ ALOGV("%s: crop %dx%d to %dx%d: top %d, scaledW %d",
+ __FUNCTION__, inW, inH, outW, outH, out->top, static_cast<int32_t>(scaledOutW));
+ }
+
+ return 0;
+}
+
+int formatConvert(
+ const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format) {
+ int ret = 0;
+ switch (format) {
+ case V4L2_PIX_FMT_NV21:
+ ret = libyuv::I420ToNV21(
+ static_cast<uint8_t*>(in.y),
+ in.yStride,
+ static_cast<uint8_t*>(in.cb),
+ in.cStride,
+ static_cast<uint8_t*>(in.cr),
+ in.cStride,
+ static_cast<uint8_t*>(out.y),
+ out.yStride,
+ static_cast<uint8_t*>(out.cr),
+ out.cStride,
+ sz.width,
+ sz.height);
+ if (ret != 0) {
+ ALOGE("%s: convert to NV21 buffer failed! ret %d",
+ __FUNCTION__, ret);
+ return ret;
+ }
+ break;
+ case V4L2_PIX_FMT_NV12:
+ ret = libyuv::I420ToNV12(
+ static_cast<uint8_t*>(in.y),
+ in.yStride,
+ static_cast<uint8_t*>(in.cb),
+ in.cStride,
+ static_cast<uint8_t*>(in.cr),
+ in.cStride,
+ static_cast<uint8_t*>(out.y),
+ out.yStride,
+ static_cast<uint8_t*>(out.cb),
+ out.cStride,
+ sz.width,
+ sz.height);
+ if (ret != 0) {
+ ALOGE("%s: convert to NV12 buffer failed! ret %d",
+ __FUNCTION__, ret);
+ return ret;
+ }
+ break;
+ case V4L2_PIX_FMT_YVU420: // YV12
+ case V4L2_PIX_FMT_YUV420: // YU12
+ // TODO: maybe we can speed up here by somehow save this copy?
+ ret = libyuv::I420Copy(
+ static_cast<uint8_t*>(in.y),
+ in.yStride,
+ static_cast<uint8_t*>(in.cb),
+ in.cStride,
+ static_cast<uint8_t*>(in.cr),
+ in.cStride,
+ static_cast<uint8_t*>(out.y),
+ out.yStride,
+ static_cast<uint8_t*>(out.cb),
+ out.cStride,
+ static_cast<uint8_t*>(out.cr),
+ out.cStride,
+ sz.width,
+ sz.height);
+ if (ret != 0) {
+ ALOGE("%s: copy to YV12 or YU12 buffer failed! ret %d",
+ __FUNCTION__, ret);
+ return ret;
+ }
+ break;
+ case FLEX_YUV_GENERIC:
+ // TODO: b/72261744 write to arbitrary flexible YUV layout. Slow.
+ ALOGE("%s: unsupported flexible yuv layout"
+ " y %p cb %p cr %p y_str %d c_str %d c_step %d",
+ __FUNCTION__, out.y, out.cb, out.cr,
+ out.yStride, out.cStride, out.chromaStep);
+ return -1;
+ default:
+ ALOGE("%s: unknown YUV format 0x%x!", __FUNCTION__, format);
+ return -1;
+ }
+ return 0;
+}
+
+int encodeJpegYU12(
+ const Size & inSz, const YCbCrLayout& inLayout,
+ int jpegQuality, const void *app1Buffer, size_t app1Size,
+ void *out, const size_t maxOutSize, size_t &actualCodeSize)
+{
+ /* libjpeg is a C library so we use C-style "inheritance" by
+ * putting libjpeg's jpeg_destination_mgr first in our custom
+ * struct. This allows us to cast jpeg_destination_mgr* to
+ * CustomJpegDestMgr* when we get it passed to us in a callback */
+ struct CustomJpegDestMgr {
+ struct jpeg_destination_mgr mgr;
+ JOCTET *mBuffer;
+ size_t mBufferSize;
+ size_t mEncodedSize;
+ bool mSuccess;
+ } dmgr;
+
+ jpeg_compress_struct cinfo = {};
+ jpeg_error_mgr jerr;
+
+ /* Initialize error handling with standard callbacks, but
+ * then override output_message (to print to ALOG) and
+ * error_exit to set a flag and print a message instead
+ * of killing the whole process */
+ cinfo.err = jpeg_std_error(&jerr);
+
+ cinfo.err->output_message = [](j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* Create the message */
+ (*cinfo->err->format_message)(cinfo, buffer);
+ ALOGE("libjpeg error: %s", buffer);
+ };
+ cinfo.err->error_exit = [](j_common_ptr cinfo) {
+ (*cinfo->err->output_message)(cinfo);
+ if(cinfo->client_data) {
+ auto & dmgr =
+ *reinterpret_cast<CustomJpegDestMgr*>(cinfo->client_data);
+ dmgr.mSuccess = false;
+ }
+ };
+ /* Now that we initialized some callbacks, let's create our compressor */
+ jpeg_create_compress(&cinfo);
+
+ /* Initialize our destination manager */
+ dmgr.mBuffer = static_cast<JOCTET*>(out);
+ dmgr.mBufferSize = maxOutSize;
+ dmgr.mEncodedSize = 0;
+ dmgr.mSuccess = true;
+ cinfo.client_data = static_cast<void*>(&dmgr);
+
+ /* These lambdas become C-style function pointers and as per C++11 spec
+ * may not capture anything */
+ dmgr.mgr.init_destination = [](j_compress_ptr cinfo) {
+ auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
+ dmgr.mgr.next_output_byte = dmgr.mBuffer;
+ dmgr.mgr.free_in_buffer = dmgr.mBufferSize;
+ ALOGV("%s:%d jpeg start: %p [%zu]",
+ __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize);
+ };
+
+ dmgr.mgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) {
+ ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__);
+ return 0;
+ };
+
+ dmgr.mgr.term_destination = [](j_compress_ptr cinfo) {
+ auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
+ dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.mgr.free_in_buffer;
+ ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize);
+ };
+ cinfo.dest = reinterpret_cast<struct jpeg_destination_mgr*>(&dmgr);
+
+ /* We are going to be using JPEG in raw data mode, so we are passing
+ * straight subsampled planar YCbCr and it will not touch our pixel
+ * data or do any scaling or anything */
+ cinfo.image_width = inSz.width;
+ cinfo.image_height = inSz.height;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_YCbCr;
+
+ /* Initialize defaults and then override what we want */
+ jpeg_set_defaults(&cinfo);
+
+ jpeg_set_quality(&cinfo, jpegQuality, 1);
+ jpeg_set_colorspace(&cinfo, JCS_YCbCr);
+ cinfo.raw_data_in = 1;
+ cinfo.dct_method = JDCT_IFAST;
+
+ /* Configure sampling factors. The sampling factor is JPEG subsampling 420
+ * because the source format is YUV420. Note that libjpeg sampling factors
+ * are... a little weird. Sampling of Y=2,U=1,V=1 means there is 1 U and
+ * 1 V value for each 2 Y values */
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 2;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+
+ /* Let's not hardcode YUV420 in 6 places... 5 was enough */
+ int maxVSampFactor = std::max( {
+ cinfo.comp_info[0].v_samp_factor,
+ cinfo.comp_info[1].v_samp_factor,
+ cinfo.comp_info[2].v_samp_factor
+ });
+ int cVSubSampling = cinfo.comp_info[0].v_samp_factor /
+ cinfo.comp_info[1].v_samp_factor;
+
+ /* Start the compressor */
+ jpeg_start_compress(&cinfo, TRUE);
+
+ /* Compute our macroblock height, so we can pad our input to be vertically
+ * macroblock aligned.
+ * TODO: Does it need to be horizontally MCU aligned too? */
+
+ size_t mcuV = DCTSIZE*maxVSampFactor;
+ size_t paddedHeight = mcuV * ((inSz.height + mcuV - 1) / mcuV);
+
+ /* libjpeg uses arrays of row pointers, which makes it really easy to pad
+ * data vertically (unfortunately doesn't help horizontally) */
+ std::vector<JSAMPROW> yLines (paddedHeight);
+ std::vector<JSAMPROW> cbLines(paddedHeight/cVSubSampling);
+ std::vector<JSAMPROW> crLines(paddedHeight/cVSubSampling);
+
+ uint8_t *py = static_cast<uint8_t*>(inLayout.y);
+ uint8_t *pcr = static_cast<uint8_t*>(inLayout.cr);
+ uint8_t *pcb = static_cast<uint8_t*>(inLayout.cb);
+
+ for(uint32_t i = 0; i < paddedHeight; i++)
+ {
+ /* Once we are in the padding territory we still point to the last line
+ * effectively replicating it several times ~ CLAMP_TO_EDGE */
+ int li = std::min(i, inSz.height - 1);
+ yLines[i] = static_cast<JSAMPROW>(py + li * inLayout.yStride);
+ if(i < paddedHeight / cVSubSampling)
+ {
+ li = std::min(i, (inSz.height - 1) / cVSubSampling);
+ crLines[i] = static_cast<JSAMPROW>(pcr + li * inLayout.cStride);
+ cbLines[i] = static_cast<JSAMPROW>(pcb + li * inLayout.cStride);
+ }
+ }
+
+ /* If APP1 data was passed in, use it */
+ if(app1Buffer && app1Size)
+ {
+ jpeg_write_marker(&cinfo, JPEG_APP0 + 1,
+ static_cast<const JOCTET*>(app1Buffer), app1Size);
+ }
+
+ /* While we still have padded height left to go, keep giving it one
+ * macroblock at a time. */
+ while (cinfo.next_scanline < cinfo.image_height) {
+ const uint32_t batchSize = DCTSIZE * maxVSampFactor;
+ const uint32_t nl = cinfo.next_scanline;
+ JSAMPARRAY planes[3]{ &yLines[nl],
+ &cbLines[nl/cVSubSampling],
+ &crLines[nl/cVSubSampling] };
+
+ uint32_t done = jpeg_write_raw_data(&cinfo, planes, batchSize);
+
+ if (done != batchSize) {
+ ALOGE("%s: compressed %u lines, expected %u (total %u/%u)",
+ __FUNCTION__, done, batchSize, cinfo.next_scanline,
+ cinfo.image_height);
+ return -1;
+ }
+ }
+
+ /* This will flush everything */
+ jpeg_finish_compress(&cinfo);
+
+ /* Grab the actual code size and set it */
+ actualCodeSize = dmgr.mEncodedSize;
+
+ return 0;
+}
+
+Size getMaxThumbnailResolution(const common::V1_0::helper::CameraMetadata& chars) {
+ Size thumbSize { 0, 0 };
+ camera_metadata_ro_entry entry =
+ chars.find(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES);
+ for(uint32_t i = 0; i < entry.count; i += 2) {
+ Size sz { static_cast<uint32_t>(entry.data.i32[i]),
+ static_cast<uint32_t>(entry.data.i32[i+1]) };
+ if(sz.width * sz.height > thumbSize.width * thumbSize.height) {
+ thumbSize = sz;
+ }
+ }
+
+ if (thumbSize.width * thumbSize.height == 0) {
+ ALOGW("%s: non-zero thumbnail size not available", __FUNCTION__);
+ }
+
+ return thumbSize;
+}
+
+void freeReleaseFences(hidl_vec<V3_2::CaptureResult>& results) {
+ for (auto& result : results) {
+ if (result.inputBuffer.releaseFence.getNativeHandle() != nullptr) {
+ native_handle_t* handle = const_cast<native_handle_t*>(
+ result.inputBuffer.releaseFence.getNativeHandle());
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ for (auto& buf : result.outputBuffers) {
+ if (buf.releaseFence.getNativeHandle() != nullptr) {
+ native_handle_t* handle = const_cast<native_handle_t*>(
+ buf.releaseFence.getNativeHandle());
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ }
+ }
+ return;
+}
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#define UPDATE(md, tag, data, size) \
+do { \
+ if ((md).update((tag), (data), (size))) { \
+ ALOGE("Update " #tag " failed!"); \
+ return BAD_VALUE; \
+ } \
+} while (0)
+
+status_t fillCaptureResultCommon(
+ common::V1_0::helper::CameraMetadata &md, nsecs_t timestamp,
+ camera_metadata_ro_entry& activeArraySize) {
+ if (activeArraySize.count < 4) {
+ ALOGE("%s: cannot find active array size!", __FUNCTION__);
+ return -EINVAL;
+ }
+ // android.control
+ // For USB camera, we don't know the AE state. Set the state to converged to
+ // indicate the frame should be good to use. Then apps don't have to wait the
+ // AE state.
+ const uint8_t aeState = ANDROID_CONTROL_AE_STATE_CONVERGED;
+ UPDATE(md, ANDROID_CONTROL_AE_STATE, &aeState, 1);
+
+ const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;
+ UPDATE(md, ANDROID_CONTROL_AE_LOCK, &ae_lock, 1);
+
+ // Set AWB state to converged to indicate the frame should be good to use.
+ const uint8_t awbState = ANDROID_CONTROL_AWB_STATE_CONVERGED;
+ UPDATE(md, ANDROID_CONTROL_AWB_STATE, &awbState, 1);
+
+ const uint8_t awbLock = ANDROID_CONTROL_AWB_LOCK_OFF;
+ UPDATE(md, ANDROID_CONTROL_AWB_LOCK, &awbLock, 1);
+
+ const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE;
+ UPDATE(md, ANDROID_FLASH_STATE, &flashState, 1);
+
+ // This means pipeline latency of X frame intervals. The maximum number is 4.
+ const uint8_t requestPipelineMaxDepth = 4;
+ UPDATE(md, ANDROID_REQUEST_PIPELINE_DEPTH, &requestPipelineMaxDepth, 1);
+
+ // android.scaler
+ const int32_t crop_region[] = {
+ activeArraySize.data.i32[0], activeArraySize.data.i32[1],
+ activeArraySize.data.i32[2], activeArraySize.data.i32[3],
+ };
+ UPDATE(md, ANDROID_SCALER_CROP_REGION, crop_region, ARRAY_SIZE(crop_region));
+
+ // android.sensor
+ UPDATE(md, ANDROID_SENSOR_TIMESTAMP, ×tamp, 1);
+
+ // android.statistics
+ const uint8_t lensShadingMapMode = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
+ UPDATE(md, ANDROID_STATISTICS_LENS_SHADING_MAP_MODE, &lensShadingMapMode, 1);
+
+ const uint8_t sceneFlicker = ANDROID_STATISTICS_SCENE_FLICKER_NONE;
+ UPDATE(md, ANDROID_STATISTICS_SCENE_FLICKER, &sceneFlicker, 1);
+
+ return OK;
+}
+
+#undef ARRAY_SIZE
+#undef UPDATE
+
} // namespace implementation
} // namespace V3_4
+
+namespace V3_6 {
+namespace implementation {
+
+AllocatedV4L2Frame::AllocatedV4L2Frame(sp<V3_4::implementation::V4L2Frame> frameIn) :
+ Frame(frameIn->mWidth, frameIn->mHeight, frameIn->mFourcc) {
+ uint8_t* dataIn;
+ size_t dataSize;
+ if (frameIn->getData(&dataIn, &dataSize) != 0) {
+ ALOGE("%s: map input V4L2 frame failed!", __FUNCTION__);
+ return;
+ }
+
+ mData.resize(dataSize);
+ std::memcpy(mData.data(), dataIn, dataSize);
+}
+
+int AllocatedV4L2Frame::getData(uint8_t** outData, size_t* dataSize) {
+ if (outData == nullptr || dataSize == nullptr) {
+ ALOGE("%s: outData(%p)/dataSize(%p) must not be null", __FUNCTION__, outData, dataSize);
+ return -1;
+ }
+
+ *outData = mData.data();
+ *dataSize = mData.size();
+ return 0;
+}
+
+AllocatedV4L2Frame::~AllocatedV4L2Frame() {}
+
+} // namespace implementation
+} // namespace V3_6
} // namespace device
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
index 71b7c17..180f0c1 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H
-#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H
+#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICESESSION_H
+#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICESESSION_H
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
@@ -84,7 +84,8 @@
using ::android::Mutex;
using ::android::base::unique_fd;
-struct ExternalCameraDeviceSession : public virtual RefBase {
+struct ExternalCameraDeviceSession : public virtual RefBase,
+ public virtual OutputThreadInterface {
ExternalCameraDeviceSession(const sp<ICameraDeviceCallback>&,
const ExternalCameraConfig& cfg,
@@ -110,6 +111,82 @@
static const int kMaxStallStream = 1;
static const uint32_t kMaxBytesPerPixel = 2;
+ class OutputThread : public android::Thread {
+ public:
+ OutputThread(wp<OutputThreadInterface> parent, CroppingType,
+ const common::V1_0::helper::CameraMetadata&);
+ virtual ~OutputThread();
+
+ Status allocateIntermediateBuffers(
+ const Size& v4lSize, const Size& thumbSize,
+ const hidl_vec<Stream>& streams,
+ uint32_t blobBufferSize);
+ Status submitRequest(const std::shared_ptr<HalRequest>&);
+ void flush();
+ void dump(int fd);
+ virtual bool threadLoop() override;
+
+ void setExifMakeModel(const std::string& make, const std::string& model);
+
+ // The remaining request list is returned for offline processing
+ std::list<std::shared_ptr<HalRequest>> switchToOffline();
+
+ protected:
+ // Methods to request output buffer in parallel
+ // No-op for device@3.4. Implemented in device@3.5
+ virtual int requestBufferStart(const std::vector<HalStreamBuffer>&) { return 0; }
+ virtual int waitForBufferRequestDone(
+ /*out*/std::vector<HalStreamBuffer>*) { return 0; }
+
+ static const int kFlushWaitTimeoutSec = 3; // 3 sec
+ static const int kReqWaitTimeoutMs = 33; // 33ms
+ static const int kReqWaitTimesMax = 90; // 33ms * 90 ~= 3 sec
+
+ void waitForNextRequest(std::shared_ptr<HalRequest>* out);
+ void signalRequestDone();
+
+ int cropAndScaleLocked(
+ sp<AllocatedFrame>& in, const Size& outSize,
+ YCbCrLayout* out);
+
+ int cropAndScaleThumbLocked(
+ sp<AllocatedFrame>& in, const Size& outSize,
+ YCbCrLayout* out);
+
+ int createJpegLocked(HalStreamBuffer &halBuf,
+ const common::V1_0::helper::CameraMetadata& settings);
+
+ void clearIntermediateBuffers();
+
+ const wp<OutputThreadInterface> mParent;
+ const CroppingType mCroppingType;
+ const common::V1_0::helper::CameraMetadata mCameraCharacteristics;
+
+ mutable std::mutex mRequestListLock; // Protect acccess to mRequestList,
+ // mProcessingRequest and mProcessingFrameNumer
+ std::condition_variable mRequestCond; // signaled when a new request is submitted
+ std::condition_variable mRequestDoneCond; // signaled when a request is done processing
+ std::list<std::shared_ptr<HalRequest>> mRequestList;
+ bool mProcessingRequest = false;
+ uint32_t mProcessingFrameNumer = 0;
+
+ // V4L2 frameIn
+ // (MJPG decode)-> mYu12Frame
+ // (Scale)-> mScaledYu12Frames
+ // (Format convert) -> output gralloc frames
+ mutable std::mutex mBufferLock; // Protect access to intermediate buffers
+ sp<AllocatedFrame> mYu12Frame;
+ sp<AllocatedFrame> mYu12ThumbFrame;
+ std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mIntermediateBuffers;
+ std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mScaledYu12Frames;
+ YCbCrLayout mYu12FrameLayout;
+ YCbCrLayout mYu12ThumbFrameLayout;
+ uint32_t mBlobBufferSize = 0; // 0 -> HAL derive buffer size, else: use given size
+
+ std::string mExifMake;
+ std::string mExifModel;
+ };
+
protected:
// Methods from ::android::hardware::camera::device::V3_2::ICameraDeviceSession follow
@@ -150,27 +227,22 @@
ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb);
protected:
- struct HalStreamBuffer {
- int32_t streamId;
- uint64_t bufferId;
- uint32_t width;
- uint32_t height;
- PixelFormat format;
- V3_2::BufferUsageFlags usage;
- buffer_handle_t* bufPtr;
- int acquireFence;
- bool fenceTimeout;
- };
+ // Methods from OutputThreadInterface
+ virtual Status importBuffer(int32_t streamId,
+ uint64_t bufId, buffer_handle_t buf,
+ /*out*/buffer_handle_t** outBufPtr,
+ bool allowEmptyBuf) override;
- struct HalRequest {
- uint32_t frameNumber;
- common::V1_0::helper::CameraMetadata setting;
- sp<V4L2Frame> frameIn;
- nsecs_t shutterTs;
- std::vector<HalStreamBuffer> buffers;
- };
+ virtual Status processCaptureResult(std::shared_ptr<HalRequest>&) override;
- static const uint64_t BUFFER_ID_NO_BUFFER = 0;
+ virtual Status processCaptureRequestError(const std::shared_ptr<HalRequest>&,
+ /*out*/std::vector<NotifyMsg>* msgs = nullptr,
+ /*out*/std::vector<CaptureResult>* results = nullptr) override;
+
+ virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const override;
+
+ virtual void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) override;
+ // End of OutputThreadInterface methods
Status constructDefaultRequestSettingsRaw(RequestTemplate type,
V3_2::CameraMetadata *outMetadata);
@@ -219,11 +291,6 @@
// Optional argument for ICameraDeviceSession@3.5 impl
bool allowEmptyBuf = false);
- Status importBuffer(int32_t streamId,
- uint64_t bufId, buffer_handle_t buf,
- /*out*/buffer_handle_t** outBufPtr,
- bool allowEmptyBuf);
-
Status importBufferLocked(int32_t streamId,
uint64_t bufId, buffer_handle_t buf,
/*out*/buffer_handle_t** outBufPtr,
@@ -236,106 +303,15 @@
Status processOneCaptureRequest(const CaptureRequest& request);
- Status processCaptureResult(std::shared_ptr<HalRequest>&);
- Status processCaptureRequestError(const std::shared_ptr<HalRequest>&);
void notifyShutter(uint32_t frameNumber, nsecs_t shutterTs);
- void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec);
void invokeProcessCaptureResultCallback(
hidl_vec<CaptureResult> &results, bool tryWriteFmq);
- static void freeReleaseFences(hidl_vec<CaptureResult>&);
Size getMaxJpegResolution() const;
Size getMaxThumbResolution() const;
- ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const;
-
int waitForV4L2BufferReturnLocked(std::unique_lock<std::mutex>& lk);
- class OutputThread : public android::Thread {
- public:
- OutputThread(wp<ExternalCameraDeviceSession> parent, CroppingType);
- virtual ~OutputThread();
-
- Status allocateIntermediateBuffers(
- const Size& v4lSize, const Size& thumbSize,
- const hidl_vec<Stream>& streams,
- uint32_t blobBufferSize);
- Status submitRequest(const std::shared_ptr<HalRequest>&);
- void flush();
- void dump(int fd);
- virtual bool threadLoop() override;
-
- void setExifMakeModel(const std::string& make, const std::string& model);
-
- protected:
- // Methods to request output buffer in parallel
- // No-op for device@3.4. Implemented in device@3.5
- virtual int requestBufferStart(const std::vector<HalStreamBuffer>&) { return 0; }
- virtual int waitForBufferRequestDone(
- /*out*/std::vector<HalStreamBuffer>*) { return 0; }
-
- static const uint32_t FLEX_YUV_GENERIC = static_cast<uint32_t>('F') |
- static_cast<uint32_t>('L') << 8 | static_cast<uint32_t>('E') << 16 |
- static_cast<uint32_t>('X') << 24;
- // returns FLEX_YUV_GENERIC for formats other than YV12/YU12/NV12/NV21
- static uint32_t getFourCcFromLayout(const YCbCrLayout&);
- static int getCropRect(
- CroppingType ct, const Size& inSize, const Size& outSize, IMapper::Rect* out);
-
- static const int kFlushWaitTimeoutSec = 3; // 3 sec
- static const int kReqWaitTimeoutMs = 33; // 33ms
- static const int kReqWaitTimesMax = 90; // 33ms * 90 ~= 3 sec
-
- void waitForNextRequest(std::shared_ptr<HalRequest>* out);
- void signalRequestDone();
-
- int cropAndScaleLocked(
- sp<AllocatedFrame>& in, const Size& outSize,
- YCbCrLayout* out);
-
- int cropAndScaleThumbLocked(
- sp<AllocatedFrame>& in, const Size& outSize,
- YCbCrLayout* out);
-
- int formatConvertLocked(const YCbCrLayout& in, const YCbCrLayout& out,
- Size sz, uint32_t format);
-
- static int encodeJpegYU12(const Size &inSz,
- const YCbCrLayout& inLayout, int jpegQuality,
- const void *app1Buffer, size_t app1Size,
- void *out, size_t maxOutSize,
- size_t &actualCodeSize);
-
- int createJpegLocked(HalStreamBuffer &halBuf, const std::shared_ptr<HalRequest>& req);
-
- const wp<ExternalCameraDeviceSession> mParent;
- const CroppingType mCroppingType;
-
- mutable std::mutex mRequestListLock; // Protect acccess to mRequestList,
- // mProcessingRequest and mProcessingFrameNumer
- std::condition_variable mRequestCond; // signaled when a new request is submitted
- std::condition_variable mRequestDoneCond; // signaled when a request is done processing
- std::list<std::shared_ptr<HalRequest>> mRequestList;
- bool mProcessingRequest = false;
- uint32_t mProcessingFrameNumer = 0;
-
- // V4L2 frameIn
- // (MJPG decode)-> mYu12Frame
- // (Scale)-> mScaledYu12Frames
- // (Format convert) -> output gralloc frames
- mutable std::mutex mBufferLock; // Protect access to intermediate buffers
- sp<AllocatedFrame> mYu12Frame;
- sp<AllocatedFrame> mYu12ThumbFrame;
- std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mIntermediateBuffers;
- std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mScaledYu12Frames;
- YCbCrLayout mYu12FrameLayout;
- YCbCrLayout mYu12ThumbFrameLayout;
- uint32_t mBlobBufferSize = 0; // 0 -> HAL derive buffer size, else: use given size
-
- std::string mExifMake;
- std::string mExifModel;
- };
-
// Protect (most of) HIDL interface methods from synchronized-entering
mutable Mutex mInterfaceLock;
@@ -345,7 +321,7 @@
const common::V1_0::helper::CameraMetadata mCameraCharacteristics;
const std::vector<SupportedV4L2Format> mSupportedFormats;
const CroppingType mCroppingType;
- const std::string& mCameraId;
+ const std::string mCameraId;
// Not protected by mLock, this is almost a const.
// Setup in constructor, reset in close() after OutputThread is joined
@@ -381,12 +357,6 @@
std::mutex mInflightFramesLock; // protect mInflightFrames
std::unordered_set<uint32_t> mInflightFrames;
- // buffers currently circulating between HAL and camera service
- // key: bufferId sent via HIDL interface
- // value: imported buffer_handle_t
- // Buffer will be imported during processCaptureRequest and will be freed
- // when the its stream is deleted or camera device session is closed
- typedef std::unordered_map<uint64_t, buffer_handle_t> CirculatingBuffers;
// Stream ID -> circulating buffers map
std::map<int, CirculatingBuffers> mCirculatingBuffers;
// Protect mCirculatingBuffers, must not lock mLock after acquiring this lock
@@ -395,6 +365,8 @@
std::mutex mAfTriggerLock; // protect mAfTrigger
bool mAfTrigger = false;
+ uint32_t mBlobBufferSize = 0;
+
static HandleImporter sHandleImporter;
/* Beginning of members not changed after initialize() */
@@ -410,6 +382,9 @@
const Size mMaxThumbResolution;
const Size mMaxJpegResolution;
+
+ std::string mExifMake;
+ std::string mExifModel;
/* End of members not changed after initialize() */
private:
@@ -484,4 +459,4 @@
} // namespace hardware
} // namespace android
-#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICE3SESSION_H
+#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMERADEVICESESSION_H
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
index bd79807..1958fcb 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
@@ -105,7 +105,7 @@
// Calls into virtual member function. Do not use it in constructor
status_t initCameraCharacteristics();
// Init available capabilities keys
- status_t initAvailableCapabilities(
+ virtual status_t initAvailableCapabilities(
::android::hardware::camera::common::V1_0::helper::CameraMetadata*);
// Init non-device dependent keys
virtual status_t initDefaultCharsKeys(
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h
index 341c622..74f75eb 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h
@@ -17,16 +17,27 @@
#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMUTIL_H
#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_4_EXTCAMUTIL_H
+#include <android/hardware/camera/common/1.0/types.h>
+#include <android/hardware/camera/device/3.2/types.h>
+#include <android/hardware/graphics/common/1.0/types.h>
#include <android/hardware/graphics/mapper/2.0/IMapper.h>
#include <inttypes.h>
#include <mutex>
+#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "tinyxml2.h" // XML parsing
#include "utils/LightRefBase.h"
+#include "utils/Timers.h"
+#include <CameraMetadata.h>
+#include <HandleImporter.h>
-using android::hardware::graphics::mapper::V2_0::IMapper;
-using android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+
+using ::android::hardware::graphics::mapper::V2_0::IMapper;
+using ::android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::device::V3_2::ErrorCode;
namespace android {
namespace hardware {
@@ -113,16 +124,28 @@
std::vector<FrameRate> frameRates;
};
+// A Base class with basic information about a frame
+struct Frame : public VirtualLightRefBase {
+public:
+ Frame(uint32_t width, uint32_t height, uint32_t fourcc);
+ const uint32_t mWidth;
+ const uint32_t mHeight;
+ const uint32_t mFourcc;
+
+ // getData might involve map/allocation
+ virtual int getData(uint8_t** outData, size_t* dataSize) = 0;
+};
+
// A class provide access to a dequeued V4L2 frame buffer (mostly in MJPG format)
// Also contains necessary information to enqueue the buffer back to V4L2 buffer queue
-class V4L2Frame : public virtual VirtualLightRefBase {
+class V4L2Frame : public Frame {
public:
V4L2Frame(uint32_t w, uint32_t h, uint32_t fourcc, int bufIdx, int fd,
uint32_t dataSize, uint64_t offset);
~V4L2Frame() override;
- const uint32_t mWidth;
- const uint32_t mHeight;
- const uint32_t mFourcc;
+
+ virtual int getData(uint8_t** outData, size_t* dataSize) override;
+
const int mBufferIndex; // for later enqueue
int map(uint8_t** data, size_t* dataSize);
int unmap();
@@ -137,13 +160,13 @@
// A RAII class representing a CPU allocated YUV frame used as intermeidate buffers
// when generating output images.
-class AllocatedFrame : public virtual VirtualLightRefBase {
+class AllocatedFrame : public Frame {
public:
- AllocatedFrame(uint32_t w, uint32_t h); // TODO: use Size?
+ AllocatedFrame(uint32_t w, uint32_t h); // only support V4L2_PIX_FMT_YUV420 for now
~AllocatedFrame() override;
- const uint32_t mWidth;
- const uint32_t mHeight;
- const uint32_t mFourcc; // Only support YU12 format for now
+
+ virtual int getData(uint8_t** outData, size_t* dataSize) override;
+
int allocate(YCbCrLayout* out = nullptr);
int getLayout(YCbCrLayout* out);
int getCroppedLayout(const IMapper::Rect&, YCbCrLayout* out); // return non-zero for bad input
@@ -165,8 +188,110 @@
bool isAspectRatioClose(float ar1, float ar2);
+struct HalStreamBuffer {
+ int32_t streamId;
+ uint64_t bufferId;
+ uint32_t width;
+ uint32_t height;
+ ::android::hardware::graphics::common::V1_0::PixelFormat format;
+ ::android::hardware::camera::device::V3_2::BufferUsageFlags usage;
+ buffer_handle_t* bufPtr;
+ int acquireFence;
+ bool fenceTimeout;
+};
+
+struct HalRequest {
+ uint32_t frameNumber;
+ common::V1_0::helper::CameraMetadata setting;
+ sp<Frame> frameIn;
+ nsecs_t shutterTs;
+ std::vector<HalStreamBuffer> buffers;
+};
+
+static const uint64_t BUFFER_ID_NO_BUFFER = 0;
+
+// buffers currently circulating between HAL and camera service
+// key: bufferId sent via HIDL interface
+// value: imported buffer_handle_t
+// Buffer will be imported during processCaptureRequest (or requestStreamBuffer
+// in the case of HAL buffer manager is enabled) and will be freed
+// when the stream is deleted or camera device session is closed
+typedef std::unordered_map<uint64_t, buffer_handle_t> CirculatingBuffers;
+
+::android::hardware::camera::common::V1_0::Status importBufferImpl(
+ /*inout*/std::map<int, CirculatingBuffers>& circulatingBuffers,
+ /*inout*/HandleImporter& handleImporter,
+ int32_t streamId,
+ uint64_t bufId, buffer_handle_t buf,
+ /*out*/buffer_handle_t** outBufPtr,
+ bool allowEmptyBuf);
+
+static const uint32_t FLEX_YUV_GENERIC = static_cast<uint32_t>('F') |
+ static_cast<uint32_t>('L') << 8 | static_cast<uint32_t>('E') << 16 |
+ static_cast<uint32_t>('X') << 24;
+
+// returns FLEX_YUV_GENERIC for formats other than YV12/YU12/NV12/NV21
+uint32_t getFourCcFromLayout(const YCbCrLayout&);
+
+using ::android::hardware::camera::external::common::Size;
+int getCropRect(CroppingType ct, const Size& inSize,
+ const Size& outSize, IMapper::Rect* out);
+
+int formatConvert(const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format);
+
+int encodeJpegYU12(const Size &inSz,
+ const YCbCrLayout& inLayout, int jpegQuality,
+ const void *app1Buffer, size_t app1Size,
+ void *out, size_t maxOutSize,
+ size_t &actualCodeSize);
+
+Size getMaxThumbnailResolution(const common::V1_0::helper::CameraMetadata&);
+
+void freeReleaseFences(hidl_vec<V3_2::CaptureResult>&);
+
+status_t fillCaptureResultCommon(common::V1_0::helper::CameraMetadata& md, nsecs_t timestamp,
+ camera_metadata_ro_entry& activeArraySize);
+
+// Interface for OutputThread calling back to parent
+struct OutputThreadInterface : public virtual RefBase {
+ virtual ::android::hardware::camera::common::V1_0::Status importBuffer(
+ int32_t streamId, uint64_t bufId, buffer_handle_t buf,
+ /*out*/buffer_handle_t** outBufPtr, bool allowEmptyBuf) = 0;
+
+ virtual void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) = 0;
+
+ // Callbacks are fired within the method if msgs/results are nullptr.
+ // Otherwise the callbacks will be returned and caller is responsible to
+ // fire the callback later
+ virtual ::android::hardware::camera::common::V1_0::Status processCaptureRequestError(
+ const std::shared_ptr<HalRequest>&,
+ /*out*/std::vector<V3_2::NotifyMsg>* msgs = nullptr,
+ /*out*/std::vector<V3_2::CaptureResult>* results = nullptr) = 0;
+
+ virtual ::android::hardware::camera::common::V1_0::Status processCaptureResult(
+ std::shared_ptr<HalRequest>&) = 0;
+
+ virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const = 0;
+};
+
} // namespace implementation
} // namespace V3_4
+
+namespace V3_6 {
+namespace implementation {
+
+// A CPU copy of a mapped V4L2Frame. Will map the input V4L2 frame.
+class AllocatedV4L2Frame : public V3_4::implementation::Frame {
+public:
+ AllocatedV4L2Frame(sp<V3_4::implementation::V4L2Frame> frameIn);
+ ~AllocatedV4L2Frame() override;
+ virtual int getData(uint8_t** outData, size_t* dataSize) override;
+private:
+ std::vector<uint8_t> mData;
+};
+
+} // namespace implementation
+} // namespace V3_6
} // namespace device
} // namespace camera
} // namespace hardware
diff --git a/camera/device/3.5/default/ExternalCameraDeviceSession.cpp b/camera/device/3.5/default/ExternalCameraDeviceSession.cpp
index 00c1d0d..287ac32 100644
--- a/camera/device/3.5/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.5/default/ExternalCameraDeviceSession.cpp
@@ -80,7 +80,7 @@
ExternalCameraDeviceSession::BufferRequestThread::BufferRequestThread(
- wp<ExternalCameraDeviceSession> parent,
+ wp<OutputThreadInterface> parent,
sp<V3_5::ICameraDeviceCallback> callbacks) :
mParent(parent),
mCallbacks(callbacks) {}
@@ -254,7 +254,8 @@
mBufferRequestThread = new BufferRequestThread(this, mCallback_3_5);
mBufferRequestThread->run("ExtCamBufReq", PRIORITY_DISPLAY);
}
- mOutputThread = new OutputThread(this, mCroppingType, mBufferRequestThread);
+ mOutputThread = new OutputThread(
+ this, mCroppingType, mCameraCharacteristics, mBufferRequestThread);
}
void ExternalCameraDeviceSession::closeOutputThreadImpl() {
@@ -271,10 +272,11 @@
}
ExternalCameraDeviceSession::OutputThread::OutputThread(
- wp<ExternalCameraDeviceSession> parent,
+ wp<OutputThreadInterface> parent,
CroppingType ct,
+ const common::V1_0::helper::CameraMetadata& chars,
sp<BufferRequestThread> bufReqThread) :
- V3_4::implementation::ExternalCameraDeviceSession::OutputThread(parent, ct),
+ V3_4::implementation::ExternalCameraDeviceSession::OutputThread(parent, ct, chars),
mBufferRequestThread(bufReqThread) {}
ExternalCameraDeviceSession::OutputThread::~OutputThread() {}
diff --git a/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h b/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h
index 281f93a..e89ef45 100644
--- a/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICE3SESSION_H
-#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICE3SESSION_H
+#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICESESSION_H
+#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICESESSION_H
#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
#include <android/hardware/camera/device/3.5/ICameraDeviceSession.h>
@@ -72,6 +72,7 @@
using ::android::hardware::camera::device::V3_4::implementation::SupportedV4L2Format;
using ::android::hardware::camera::device::V3_4::implementation::CroppingType;
+using ::android::hardware::camera::device::V3_4::implementation::HalStreamBuffer;
struct ExternalCameraDeviceSession : public V3_4::implementation::ExternalCameraDeviceSession {
@@ -97,6 +98,62 @@
config, supportedFormats, devCfg);
}
+ class BufferRequestThread : public android::Thread {
+ public:
+ BufferRequestThread(
+ wp<OutputThreadInterface> parent,
+ sp<V3_5::ICameraDeviceCallback> callbacks);
+
+ int requestBufferStart(const std::vector<HalStreamBuffer>&);
+ int waitForBufferRequestDone(
+ /*out*/std::vector<HalStreamBuffer>*);
+
+ virtual bool threadLoop() override;
+
+ private:
+ void waitForNextRequest();
+
+ const wp<OutputThreadInterface> mParent;
+ const sp<V3_5::ICameraDeviceCallback> mCallbacks;
+
+ std::mutex mLock;
+ bool mRequestingBuffer = false;
+
+ std::vector<HalStreamBuffer> mBufferReqs;
+ std::vector<HalStreamBuffer> mPendingReturnBufferReqs;
+ // mHalBufferReqs is not under mLock protection during the HIDL transaction
+ hidl_vec<BufferRequest> mHalBufferReqs;
+
+ // request buffers takes much less time in steady state, but can take much longer
+ // when requesting 1st buffer from a stream.
+ // TODO: consider a separate timeout for new vs. steady state?
+ // TODO: or make sure framework is warming up the pipeline during configure new stream?
+ static const int kReqProcTimeoutMs = 66;
+
+ static const int kReqWaitTimeoutMs = 33;
+ static const int kReqWaitTimesWarn = 90; // 33ms * 90 ~= 3 sec
+ std::condition_variable mRequestCond; // signaled when a new buffer request incoming
+ std::condition_variable mRequestDoneCond; // signaled when a request is done
+ };
+
+ class OutputThread :
+ public V3_4::implementation::ExternalCameraDeviceSession::OutputThread {
+ public:
+ // TODO: pass buffer request thread to OutputThread ctor
+ OutputThread(wp<OutputThreadInterface> parent, CroppingType,
+ const common::V1_0::helper::CameraMetadata&,
+ sp<BufferRequestThread> bufReqThread);
+ virtual ~OutputThread();
+
+ protected:
+ // Methods to request output buffer in parallel
+ virtual int requestBufferStart(const std::vector<HalStreamBuffer>&) override;
+ virtual int waitForBufferRequestDone(
+ /*out*/std::vector<HalStreamBuffer>*) override;
+
+ const sp<BufferRequestThread> mBufferRequestThread;
+ };
+
protected:
// Methods from v3.4 and earlier will trampoline to inherited implementation
Return<void> configureStreams_3_5(
@@ -120,63 +177,8 @@
hidl_vec<buffer_handle_t*>& allBufPtrs,
hidl_vec<int>& allFences) override;
- class BufferRequestThread : public android::Thread {
- public:
- BufferRequestThread(
- wp<ExternalCameraDeviceSession> parent,
- sp<V3_5::ICameraDeviceCallback> callbacks);
-
- int requestBufferStart(const std::vector<HalStreamBuffer>&);
- int waitForBufferRequestDone(
- /*out*/std::vector<HalStreamBuffer>*);
-
- virtual bool threadLoop() override;
-
- private:
- void waitForNextRequest();
-
- const wp<ExternalCameraDeviceSession> mParent;
- const sp<V3_5::ICameraDeviceCallback> mCallbacks;
-
- std::mutex mLock;
- bool mRequestingBuffer = false;
-
- std::vector<HalStreamBuffer> mBufferReqs;
- std::vector<HalStreamBuffer> mPendingReturnBufferReqs;
- // mHalBufferReqs is not under mLock protection during the HIDL transaction
- hidl_vec<BufferRequest> mHalBufferReqs;
-
- // request buffers takes much less time in steady state, but can take much longer
- // when requesting 1st buffer from a stream.
- // TODO: consider a separate timeout for new vs. steady state?
- // TODO: or make sure framework is warming up the pipeline during configure new stream?
- static const int kReqProcTimeoutMs = 66;
-
- static const int kReqWaitTimeoutMs = 33;
- static const int kReqWaitTimesWarn = 90; // 33ms * 90 ~= 3 sec
- std::condition_variable mRequestCond; // signaled when a new buffer request incoming
- std::condition_variable mRequestDoneCond; // signaled when a request is done
- };
-
sp<BufferRequestThread> mBufferRequestThread;
- class OutputThread :
- public V3_4::implementation::ExternalCameraDeviceSession::OutputThread {
- public:
- // TODO: pass buffer request thread to OutputThread ctor
- OutputThread(wp<ExternalCameraDeviceSession> parent, CroppingType,
- sp<BufferRequestThread> bufReqThread);
- virtual ~OutputThread();
-
- protected:
- // Methods to request output buffer in parallel
- virtual int requestBufferStart(const std::vector<HalStreamBuffer>&) override;
- virtual int waitForBufferRequestDone(
- /*out*/std::vector<HalStreamBuffer>*) override;
-
- const sp<BufferRequestThread> mBufferRequestThread;
- };
-
sp<V3_5::ICameraDeviceCallback> mCallback_3_5;
bool mSupportBufMgr;
@@ -270,4 +272,4 @@
} // namespace hardware
} // namespace android
-#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICE3SESSION_H
+#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_EXTCAMERADEVICESESSION_H
diff --git a/camera/device/3.6/Android.bp b/camera/device/3.6/Android.bp
index 8766b93..19adb34 100644
--- a/camera/device/3.6/Android.bp
+++ b/camera/device/3.6/Android.bp
@@ -8,6 +8,7 @@
},
srcs: [
"types.hal",
+ "ICameraDevice.hal",
"ICameraDeviceSession.hal",
"ICameraOfflineSession.hal",
],
diff --git a/camera/device/3.6/ICameraDevice.hal b/camera/device/3.6/ICameraDevice.hal
new file mode 100644
index 0000000..e859606
--- /dev/null
+++ b/camera/device/3.6/ICameraDevice.hal
@@ -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.camera.device@3.6;
+
+import @3.5::ICameraDevice;
+
+/**
+ * Camera device interface
+ *
+ * Supports the android.hardware.Camera API, and the android.hardware.camera2
+ * API at LIMITED or better hardware level.
+ *
+ * ICameraDevice.open() must return @3.2::ICameraDeviceSession or
+ * @3.5::ICameraDeviceSession or @3.6::ICameraDeviceSession.
+ */
+interface ICameraDevice extends @3.5::ICameraDevice {
+};
diff --git a/camera/device/3.6/default/Android.bp b/camera/device/3.6/default/Android.bp
new file mode 100644
index 0000000..a2ddebd
--- /dev/null
+++ b/camera/device/3.6/default/Android.bp
@@ -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.
+//
+
+cc_library_headers {
+ name: "camera.device@3.6-external-impl_headers",
+ vendor: true,
+ export_include_dirs: ["include/ext_device_v3_6_impl"]
+}
+
+cc_library_shared {
+ name: "camera.device@3.6-external-impl",
+ defaults: ["hidl_defaults"],
+ proprietary: true,
+ vendor: true,
+ srcs: [
+ "ExternalCameraDevice.cpp",
+ "ExternalCameraDeviceSession.cpp",
+ "ExternalCameraOfflineSession.cpp",
+ ],
+ shared_libs: [
+ "libhidlbase",
+ "libutils",
+ "libcutils",
+ "camera.device@3.2-impl",
+ "camera.device@3.3-impl",
+ "camera.device@3.4-external-impl",
+ "camera.device@3.5-external-impl",
+ "android.hardware.camera.device@3.2",
+ "android.hardware.camera.device@3.3",
+ "android.hardware.camera.device@3.4",
+ "android.hardware.camera.device@3.5",
+ "android.hardware.camera.device@3.6",
+ "android.hardware.camera.provider@2.4",
+ "android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
+ "liblog",
+ "libhardware",
+ "libcamera_metadata",
+ "libfmq",
+ "libsync",
+ "libyuv",
+ "libjpeg",
+ "libexif",
+ "libtinyxml2"
+ ],
+ static_libs: [
+ "android.hardware.camera.common@1.0-helper",
+ ],
+ local_include_dirs: ["include/ext_device_v3_6_impl"],
+ export_shared_lib_headers: [
+ "libfmq",
+ ],
+}
diff --git a/camera/device/3.6/default/ExternalCameraDevice.cpp b/camera/device/3.6/default/ExternalCameraDevice.cpp
new file mode 100644
index 0000000..244c7dd
--- /dev/null
+++ b/camera/device/3.6/default/ExternalCameraDevice.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.
+ */
+
+#define LOG_TAG "ExtCamDev@3.6"
+//#define LOG_NDEBUG 0
+#include <log/log.h>
+
+#include "ExternalCameraDevice_3_6.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_6 {
+namespace implementation {
+
+ExternalCameraDevice::ExternalCameraDevice(
+ const std::string& cameraId, const ExternalCameraConfig& cfg) :
+ V3_5::implementation::ExternalCameraDevice(cameraId, cfg) {}
+
+ExternalCameraDevice::~ExternalCameraDevice() {}
+
+sp<V3_4::implementation::ExternalCameraDeviceSession> ExternalCameraDevice::createSession(
+ const sp<V3_2::ICameraDeviceCallback>& cb,
+ const ExternalCameraConfig& cfg,
+ const std::vector<SupportedV4L2Format>& sortedFormats,
+ const CroppingType& croppingType,
+ const common::V1_0::helper::CameraMetadata& chars,
+ const std::string& cameraId,
+ unique_fd v4l2Fd) {
+ return new ExternalCameraDeviceSession(
+ cb, cfg, sortedFormats, croppingType, chars, cameraId, std::move(v4l2Fd));
+}
+
+#define UPDATE(tag, data, size) \
+do { \
+ if (metadata->update((tag), (data), (size))) { \
+ ALOGE("Update " #tag " failed!"); \
+ return -EINVAL; \
+ } \
+} while (0)
+
+status_t ExternalCameraDevice::initAvailableCapabilities(
+ ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) {
+ status_t res =
+ V3_4::implementation::ExternalCameraDevice::initAvailableCapabilities(metadata);
+
+ if (res != OK) {
+ return res;
+ }
+
+ camera_metadata_entry caps = metadata->find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+ std::vector<uint8_t> availableCapabilities;
+
+ for (size_t i = 0; i < caps.count; i++) {
+ uint8_t capability = caps.data.u8[i];
+ availableCapabilities.push_back(capability);
+ }
+
+ // Add OFFLINE_PROCESSING capability to device 3.6
+ availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING);
+
+ UPDATE(ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+ availableCapabilities.data(),
+ availableCapabilities.size());
+
+ return OK;
+}
+
+#undef UPDATE
+
+} // namespace implementation
+} // namespace V3_6
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
diff --git a/camera/device/3.6/default/ExternalCameraDeviceSession.cpp b/camera/device/3.6/default/ExternalCameraDeviceSession.cpp
new file mode 100644
index 0000000..60a1a10
--- /dev/null
+++ b/camera/device/3.6/default/ExternalCameraDeviceSession.cpp
@@ -0,0 +1,359 @@
+/*
+ * 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 "ExtCamDevSsn@3.6"
+#include <android/log.h>
+
+#include <utils/Trace.h>
+#include "ExternalCameraDeviceSession.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_6 {
+namespace implementation {
+
+ExternalCameraDeviceSession::ExternalCameraDeviceSession(
+ const sp<V3_2::ICameraDeviceCallback>& callback,
+ const ExternalCameraConfig& cfg,
+ const std::vector<SupportedV4L2Format>& sortedFormats,
+ const CroppingType& croppingType,
+ const common::V1_0::helper::CameraMetadata& chars,
+ const std::string& cameraId,
+ unique_fd v4l2Fd) :
+ V3_5::implementation::ExternalCameraDeviceSession(
+ callback, cfg, sortedFormats, croppingType, chars, cameraId, std::move(v4l2Fd)) {
+}
+
+ExternalCameraDeviceSession::~ExternalCameraDeviceSession() {}
+
+
+Return<void> ExternalCameraDeviceSession::configureStreams_3_6(
+ const StreamConfiguration& requestedConfiguration,
+ ICameraDeviceSession::configureStreams_3_6_cb _hidl_cb) {
+ V3_2::StreamConfiguration config_v32;
+ V3_3::HalStreamConfiguration outStreams_v33;
+ V3_6::HalStreamConfiguration outStreams;
+ const V3_4::StreamConfiguration& requestedConfiguration_3_4 = requestedConfiguration.v3_4;
+ Mutex::Autolock _il(mInterfaceLock);
+
+ config_v32.operationMode = requestedConfiguration_3_4.operationMode;
+ config_v32.streams.resize(requestedConfiguration_3_4.streams.size());
+ uint32_t blobBufferSize = 0;
+ int numStallStream = 0;
+ for (size_t i = 0; i < config_v32.streams.size(); i++) {
+ config_v32.streams[i] = requestedConfiguration_3_4.streams[i].v3_2;
+ if (config_v32.streams[i].format == PixelFormat::BLOB) {
+ blobBufferSize = requestedConfiguration_3_4.streams[i].bufferSize;
+ numStallStream++;
+ }
+ }
+
+ // Fail early if there are multiple BLOB streams
+ if (numStallStream > kMaxStallStream) {
+ ALOGE("%s: too many stall streams (expect <= %d, got %d)", __FUNCTION__,
+ kMaxStallStream, numStallStream);
+ _hidl_cb(Status::ILLEGAL_ARGUMENT, outStreams);
+ return Void();
+ }
+
+ Status status = configureStreams(config_v32, &outStreams_v33, blobBufferSize);
+
+ fillOutputStream3_6(outStreams_v33, &outStreams);
+
+ _hidl_cb(status, outStreams);
+ return Void();
+}
+
+Return<void> ExternalCameraDeviceSession::switchToOffline(
+ const hidl_vec<int32_t>& streamsToKeep,
+ ICameraDeviceSession::switchToOffline_cb _hidl_cb) {
+ std::vector<NotifyMsg> msgs;
+ std::vector<CaptureResult> results;
+ CameraOfflineSessionInfo info;
+ sp<ICameraOfflineSession> session;
+
+ Status st = switchToOffline(streamsToKeep, &msgs, &results, &info, &session);
+
+ mCallback->notify(msgs);
+ hidl_vec<CaptureResult> hidlResults(std::move(results));
+ invokeProcessCaptureResultCallback(hidlResults, /* tryWriteFmq */true);
+ V3_4::implementation::freeReleaseFences(hidlResults);
+
+ _hidl_cb(st, info, session);
+ return Void();
+}
+
+void ExternalCameraDeviceSession::fillOutputStream3_6(
+ const V3_3::HalStreamConfiguration& outStreams_v33,
+ /*out*/V3_6::HalStreamConfiguration* outStreams_v36) {
+ if (outStreams_v36 == nullptr) {
+ ALOGE("%s: outStreams_v36 must not be null!", __FUNCTION__);
+ return;
+ }
+ Mutex::Autolock _l(mLock);
+ outStreams_v36->streams.resize(outStreams_v33.streams.size());
+ for (size_t i = 0; i < outStreams_v36->streams.size(); i++) {
+ outStreams_v36->streams[i].v3_4.v3_3 = outStreams_v33.streams[i];
+ outStreams_v36->streams[i].supportOffline =
+ supportOfflineLocked(outStreams_v33.streams[i].v3_2.id);
+ }
+}
+
+bool ExternalCameraDeviceSession::supportOfflineLocked(int32_t streamId) {
+ const Stream& stream = mStreamMap[streamId];
+ if (stream.format == PixelFormat::BLOB &&
+ stream.dataSpace == static_cast<int32_t>(Dataspace::V0_JFIF)) {
+ return true;
+ }
+ // TODO: support YUV output stream?
+ return false;
+}
+
+bool ExternalCameraDeviceSession::canDropRequest(const hidl_vec<int32_t>& offlineStreams,
+ std::shared_ptr<V3_4::implementation::HalRequest> halReq) {
+ for (const auto& buffer : halReq->buffers) {
+ for (auto offlineStreamId : offlineStreams) {
+ if (buffer.streamId == offlineStreamId) {
+ return false;
+ }
+ }
+ }
+ // Only drop a request completely if it has no offline output
+ return true;
+}
+
+void ExternalCameraDeviceSession::fillOfflineSessionInfo(const hidl_vec<int32_t>& offlineStreams,
+ std::deque<std::shared_ptr<HalRequest>>& offlineReqs,
+ const std::map<int, CirculatingBuffers>& circulatingBuffers,
+ /*out*/CameraOfflineSessionInfo* info) {
+ if (info == nullptr) {
+ ALOGE("%s: output info must not be null!", __FUNCTION__);
+ return;
+ }
+
+ info->offlineStreams.resize(offlineStreams.size());
+ info->offlineRequests.resize(offlineReqs.size());
+
+ // Fill in offline reqs and count outstanding buffers
+ for (size_t i = 0; i < offlineReqs.size(); i++) {
+ info->offlineRequests[i].frameNumber = offlineReqs[i]->frameNumber;
+ info->offlineRequests[i].pendingStreams.resize(offlineReqs[i]->buffers.size());
+ for (size_t bIdx = 0; bIdx < offlineReqs[i]->buffers.size(); bIdx++) {
+ int32_t streamId = offlineReqs[i]->buffers[bIdx].streamId;
+ info->offlineRequests[i].pendingStreams[bIdx] = streamId;
+ }
+ }
+
+ for (size_t i = 0; i < offlineStreams.size(); i++) {
+ int32_t streamId = offlineStreams[i];
+ info->offlineStreams[i].id = streamId;
+ // outstanding buffers are 0 since we are doing hal buffer management and
+ // offline session will ask for those buffers later
+ info->offlineStreams[i].numOutstandingBuffers = 0;
+ const CirculatingBuffers& bufIdMap = circulatingBuffers.at(streamId);
+ info->offlineStreams[i].circulatingBufferIds.resize(bufIdMap.size());
+ size_t bIdx = 0;
+ for (const auto& pair : bufIdMap) {
+ // Fill in bufferId
+ info->offlineStreams[i].circulatingBufferIds[bIdx++] = pair.first;
+ }
+
+ }
+}
+
+Status ExternalCameraDeviceSession::switchToOffline(const hidl_vec<int32_t>& offlineStreams,
+ /*out*/std::vector<NotifyMsg>* msgs,
+ /*out*/std::vector<CaptureResult>* results,
+ /*out*/CameraOfflineSessionInfo* info,
+ /*out*/sp<ICameraOfflineSession>* session) {
+ ATRACE_CALL();
+ if (offlineStreams.size() > 1) {
+ ALOGE("%s: more than one offline stream is not supported", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ if (msgs == nullptr || results == nullptr || info == nullptr || session == nullptr) {
+ ALOGE("%s: output arguments (%p, %p, %p, %p) must not be null", __FUNCTION__,
+ msgs, results, info, session);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ msgs->clear();
+ results->clear();
+
+ Mutex::Autolock _il(mInterfaceLock);
+ Status status = initStatus();
+ if (status != Status::OK) {
+ return status;
+ }
+
+ Mutex::Autolock _l(mLock);
+ for (auto streamId : offlineStreams) {
+ if (!supportOfflineLocked(streamId)) {
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ }
+
+ // pause output thread and get all remaining inflight requests
+ auto remainingReqs = mOutputThread->switchToOffline();
+ std::vector<std::shared_ptr<V3_4::implementation::HalRequest>> halReqs;
+
+ // Send out buffer/request error for remaining requests and filter requests
+ // to be handled in offline mode
+ for (auto& halReq : remainingReqs) {
+ bool dropReq = canDropRequest(offlineStreams, halReq);
+ if (dropReq) {
+ // Request is dropped completely. Just send request error and
+ // there is no need to send the request to offline session
+ processCaptureRequestError(halReq, msgs, results);
+ continue;
+ }
+
+ // All requests reach here must have at least one offline stream output
+ NotifyMsg shutter;
+ shutter.type = MsgType::SHUTTER;
+ shutter.msg.shutter.frameNumber = halReq->frameNumber;
+ shutter.msg.shutter.timestamp = halReq->shutterTs;
+ msgs->push_back(shutter);
+
+ std::vector<V3_4::implementation::HalStreamBuffer> offlineBuffers;
+ for (const auto& buffer : halReq->buffers) {
+ bool dropBuffer = true;
+ for (auto offlineStreamId : offlineStreams) {
+ if (buffer.streamId == offlineStreamId) {
+ dropBuffer = false;
+ break;
+ }
+ }
+ if (dropBuffer) {
+ NotifyMsg error;
+ error.type = MsgType::ERROR;
+ error.msg.error.frameNumber = halReq->frameNumber;
+ error.msg.error.errorStreamId = buffer.streamId;
+ error.msg.error.errorCode = ErrorCode::ERROR_BUFFER;
+ msgs->push_back(error);
+
+ CaptureResult result;
+ result.frameNumber = halReq->frameNumber;
+ result.partialResult = 0; // buffer only result
+ result.inputBuffer.streamId = -1;
+ result.outputBuffers.resize(1);
+ result.outputBuffers[0].streamId = buffer.streamId;
+ result.outputBuffers[0].bufferId = buffer.bufferId;
+ result.outputBuffers[0].status = BufferStatus::ERROR;
+ if (buffer.acquireFence >= 0) {
+ native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
+ handle->data[0] = buffer.acquireFence;
+ result.outputBuffers[0].releaseFence.setTo(handle, /*shouldOwn*/false);
+ }
+ results->push_back(result);
+ } else {
+ offlineBuffers.push_back(buffer);
+ }
+ }
+ halReq->buffers = offlineBuffers;
+ halReqs.push_back(halReq);
+ }
+
+ // convert hal requests to offline request
+ std::deque<std::shared_ptr<HalRequest>> offlineReqs(halReqs.size());
+ for (auto& v4lReq : halReqs) {
+ std::shared_ptr<HalRequest> halReq = std::make_shared<HalRequest>();
+ halReq->frameNumber = v4lReq->frameNumber;
+ halReq->setting = v4lReq->setting;
+ halReq->shutterTs = v4lReq->shutterTs;
+ halReq->buffers = v4lReq->buffers;
+ sp<V3_4::implementation::V4L2Frame> v4l2Frame =
+ static_cast<V3_4::implementation::V4L2Frame*>(v4lReq->frameIn.get());
+ halReq->frameIn = new AllocatedV4L2Frame(v4l2Frame);
+ offlineReqs.push_back(halReq);
+ // enqueue V4L2 frame
+ enqueueV4l2Frame(v4l2Frame);
+ }
+
+ // Collect buffer caches/streams
+ hidl_vec<Stream> streamInfos;
+ streamInfos.resize(offlineStreams.size());
+ std::map<int, CirculatingBuffers> circulatingBuffers;
+ {
+ Mutex::Autolock _l(mCbsLock);
+ size_t idx = 0;
+ for(auto streamId : offlineStreams) {
+ circulatingBuffers[streamId] = mCirculatingBuffers.at(streamId);
+ mCirculatingBuffers.erase(streamId);
+ streamInfos[idx++] = mStreamMap.at(streamId);
+ mStreamMap.erase(streamId);
+ }
+ }
+
+ fillOfflineSessionInfo(offlineStreams, offlineReqs, circulatingBuffers, info);
+
+ // create the offline session object
+ bool afTrigger;
+ {
+ std::lock_guard<std::mutex> lk(mAfTriggerLock);
+ afTrigger = mAfTrigger;
+ }
+ sp<ExternalCameraOfflineSession> sessionImpl = new ExternalCameraOfflineSession(
+ mCroppingType, mCameraCharacteristics, mCameraId,
+ mExifMake, mExifModel, mBlobBufferSize, afTrigger,
+ streamInfos, offlineReqs, circulatingBuffers);
+
+ bool initFailed = sessionImpl->initialize();
+ if (initFailed) {
+ ALOGE("%s: offline session initialize failed!", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+
+ // cleanup stream and buffer caches
+ {
+ Mutex::Autolock _l(mCbsLock);
+ for(auto pair : mStreamMap) {
+ cleanupBuffersLocked(/*Stream ID*/pair.first);
+ }
+ mCirculatingBuffers.clear();
+ }
+ mStreamMap.clear();
+
+ // update inflight records
+ {
+ std::lock_guard<std::mutex> lk(mInflightFramesLock);
+ mInflightFrames.clear();
+ }
+
+ // stop v4l2 streaming
+ if (v4l2StreamOffLocked() !=0) {
+ ALOGE("%s: stop V4L2 streaming failed!", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+
+ // No need to return session if there is no offline requests left
+ if (offlineReqs.size() != 0) {
+ *session = sessionImpl->getInterface();
+ } else {
+ *session = nullptr;
+ }
+ return Status::OK;
+}
+
+} // namespace implementation
+} // namespace V3_6
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
diff --git a/camera/device/3.6/default/ExternalCameraOfflineSession.cpp b/camera/device/3.6/default/ExternalCameraOfflineSession.cpp
new file mode 100644
index 0000000..e606fda
--- /dev/null
+++ b/camera/device/3.6/default/ExternalCameraOfflineSession.cpp
@@ -0,0 +1,554 @@
+/*
+ * 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 "ExtCamOfflnSsn@3.6"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+#include <android/log.h>
+
+#include <linux/videodev2.h>
+#include <sync/sync.h>
+
+#define HAVE_JPEG // required for libyuv.h to export MJPEG decode APIs
+#include <libyuv.h>
+
+#include <utils/Trace.h>
+#include "ExternalCameraOfflineSession.h"
+
+namespace {
+
+// Size of request/result metadata fast message queue. Change to 0 to always use hwbinder buffer.
+static constexpr size_t kMetadataMsgQueueSize = 1 << 18 /* 256kB */;
+
+} // anonymous namespace
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_6 {
+namespace implementation {
+
+// static instance
+HandleImporter ExternalCameraOfflineSession::sHandleImporter;
+
+using V3_5::implementation::ExternalCameraDeviceSession;
+
+ExternalCameraOfflineSession::ExternalCameraOfflineSession(
+ const CroppingType& croppingType,
+ const common::V1_0::helper::CameraMetadata& chars,
+ const std::string& cameraId,
+ const std::string& exifMake,
+ const std::string& exifModel,
+ const uint32_t blobBufferSize,
+ const bool afTrigger,
+ const hidl_vec<Stream>& offlineStreams,
+ std::deque<std::shared_ptr<HalRequest>>& offlineReqs,
+ const std::map<int, CirculatingBuffers>& circulatingBuffers) :
+ mCroppingType(croppingType), mChars(chars), mCameraId(cameraId),
+ mExifMake(exifMake), mExifModel(exifModel), mBlobBufferSize(blobBufferSize),
+ mAfTrigger(afTrigger), mOfflineStreams(offlineStreams), mOfflineReqs(offlineReqs),
+ mCirculatingBuffers(circulatingBuffers) {}
+
+ExternalCameraOfflineSession::~ExternalCameraOfflineSession() {
+ close();
+}
+
+bool ExternalCameraOfflineSession::initialize() {
+ mResultMetadataQueue = std::make_shared<ResultMetadataQueue>(
+ kMetadataMsgQueueSize, false /* non blocking */);
+ if (!mResultMetadataQueue->isValid()) {
+ ALOGE("%s: invalid result fmq", __FUNCTION__);
+ return true;
+ }
+ return false;
+}
+
+void ExternalCameraOfflineSession::initOutputThread() {
+ if (mOutputThread != nullptr) {
+ ALOGE("%s: OutputThread already exist!", __FUNCTION__);
+ return;
+ }
+
+ mBufferRequestThread = new ExternalCameraDeviceSession::BufferRequestThread(
+ this, mCallback);
+ mBufferRequestThread->run("ExtCamBufReq", PRIORITY_DISPLAY);
+
+ mOutputThread = new OutputThread(this, mCroppingType, mChars,
+ mBufferRequestThread, mOfflineReqs);
+
+ mOutputThread->setExifMakeModel(mExifMake, mExifModel);
+
+ Size inputSize = { mOfflineReqs[0]->frameIn->mWidth, mOfflineReqs[0]->frameIn->mHeight};
+ Size maxThumbSize = V3_4::implementation::getMaxThumbnailResolution(mChars);
+ mOutputThread->allocateIntermediateBuffers(
+ inputSize, maxThumbSize, mOfflineStreams, mBlobBufferSize);
+
+ mOutputThread->run("ExtCamOfflnOut", PRIORITY_DISPLAY);
+}
+
+bool ExternalCameraOfflineSession::OutputThread::threadLoop() {
+ auto parent = mParent.promote();
+ if (parent == nullptr) {
+ ALOGE("%s: session has been disconnected!", __FUNCTION__);
+ return false;
+ }
+
+ if (mOfflineReqs.empty()) {
+ ALOGI("%s: all offline requests are processed. Stopping.", __FUNCTION__);
+ return false;
+ }
+
+ std::shared_ptr<HalRequest> req = mOfflineReqs.front();
+ mOfflineReqs.pop_front();
+
+ auto onDeviceError = [&](auto... args) {
+ ALOGE(args...);
+ parent->notifyError(
+ req->frameNumber, /*stream*/-1, ErrorCode::ERROR_DEVICE);
+ signalRequestDone();
+ return false;
+ };
+
+ if (req->frameIn->mFourcc != V4L2_PIX_FMT_MJPEG && req->frameIn->mFourcc != V4L2_PIX_FMT_Z16) {
+ return onDeviceError("%s: do not support V4L2 format %c%c%c%c", __FUNCTION__,
+ req->frameIn->mFourcc & 0xFF,
+ (req->frameIn->mFourcc >> 8) & 0xFF,
+ (req->frameIn->mFourcc >> 16) & 0xFF,
+ (req->frameIn->mFourcc >> 24) & 0xFF);
+ }
+
+ int res = requestBufferStart(req->buffers);
+ if (res != 0) {
+ ALOGE("%s: send BufferRequest failed! res %d", __FUNCTION__, res);
+ return onDeviceError("%s: failed to send buffer request!", __FUNCTION__);
+ }
+
+ std::unique_lock<std::mutex> lk(mBufferLock);
+ // Convert input V4L2 frame to YU12 of the same size
+ // TODO: see if we can save some computation by converting to YV12 here
+ uint8_t* inData;
+ size_t inDataSize;
+ if (req->frameIn->getData(&inData, &inDataSize) != 0) {
+ lk.unlock();
+ return onDeviceError("%s: V4L2 buffer map failed", __FUNCTION__);
+ }
+
+ // TODO: in some special case maybe we can decode jpg directly to gralloc output?
+ if (req->frameIn->mFourcc == V4L2_PIX_FMT_MJPEG) {
+ ATRACE_BEGIN("MJPGtoI420");
+ int res = libyuv::MJPGToI420(
+ inData, inDataSize, static_cast<uint8_t*>(mYu12FrameLayout.y), mYu12FrameLayout.yStride,
+ static_cast<uint8_t*>(mYu12FrameLayout.cb), mYu12FrameLayout.cStride,
+ static_cast<uint8_t*>(mYu12FrameLayout.cr), mYu12FrameLayout.cStride,
+ mYu12Frame->mWidth, mYu12Frame->mHeight, mYu12Frame->mWidth, mYu12Frame->mHeight);
+ ATRACE_END();
+
+ if (res != 0) {
+ // For some webcam, the first few V4L2 frames might be malformed...
+ ALOGE("%s: Convert V4L2 frame to YU12 failed! res %d", __FUNCTION__, res);
+ lk.unlock();
+ Status st = parent->processCaptureRequestError(req);
+ if (st != Status::OK) {
+ return onDeviceError("%s: failed to process capture request error!", __FUNCTION__);
+ }
+ signalRequestDone();
+ return true;
+ }
+ }
+
+ ATRACE_BEGIN("Wait for BufferRequest done");
+ res = waitForBufferRequestDone(&req->buffers);
+ ATRACE_END();
+
+ if (res != 0) {
+ ALOGE("%s: wait for BufferRequest done failed! res %d", __FUNCTION__, res);
+ lk.unlock();
+ return onDeviceError("%s: failed to process buffer request error!", __FUNCTION__);
+ }
+
+ ALOGV("%s processing new request", __FUNCTION__);
+ const int kSyncWaitTimeoutMs = 500;
+ for (auto& halBuf : req->buffers) {
+ if (*(halBuf.bufPtr) == nullptr) {
+ ALOGW("%s: buffer for stream %d missing", __FUNCTION__, halBuf.streamId);
+ halBuf.fenceTimeout = true;
+ } else if (halBuf.acquireFence >= 0) {
+ int ret = sync_wait(halBuf.acquireFence, kSyncWaitTimeoutMs);
+ if (ret) {
+ halBuf.fenceTimeout = true;
+ } else {
+ ::close(halBuf.acquireFence);
+ halBuf.acquireFence = -1;
+ }
+ }
+
+ if (halBuf.fenceTimeout) {
+ continue;
+ }
+
+ // Gralloc lockYCbCr the buffer
+ switch (halBuf.format) {
+ case PixelFormat::BLOB: {
+ int ret = createJpegLocked(halBuf, req->setting);
+
+ if(ret != 0) {
+ lk.unlock();
+ return onDeviceError("%s: createJpegLocked failed with %d",
+ __FUNCTION__, ret);
+ }
+ } break;
+ case PixelFormat::Y16: {
+ void* outLayout = sHandleImporter.lock(*(halBuf.bufPtr), halBuf.usage, inDataSize);
+
+ std::memcpy(outLayout, inData, inDataSize);
+
+ int relFence = sHandleImporter.unlock(*(halBuf.bufPtr));
+ if (relFence >= 0) {
+ halBuf.acquireFence = relFence;
+ }
+ } break;
+ case PixelFormat::YCBCR_420_888:
+ case PixelFormat::YV12: {
+ IMapper::Rect outRect {0, 0,
+ static_cast<int32_t>(halBuf.width),
+ static_cast<int32_t>(halBuf.height)};
+ YCbCrLayout outLayout = sHandleImporter.lockYCbCr(
+ *(halBuf.bufPtr), halBuf.usage, outRect);
+ ALOGV("%s: outLayout y %p cb %p cr %p y_str %d c_str %d c_step %d",
+ __FUNCTION__, outLayout.y, outLayout.cb, outLayout.cr,
+ outLayout.yStride, outLayout.cStride, outLayout.chromaStep);
+
+ // Convert to output buffer size/format
+ uint32_t outputFourcc = V3_4::implementation::getFourCcFromLayout(outLayout);
+ ALOGV("%s: converting to format %c%c%c%c", __FUNCTION__,
+ outputFourcc & 0xFF,
+ (outputFourcc >> 8) & 0xFF,
+ (outputFourcc >> 16) & 0xFF,
+ (outputFourcc >> 24) & 0xFF);
+
+ YCbCrLayout cropAndScaled;
+ ATRACE_BEGIN("cropAndScaleLocked");
+ int ret = cropAndScaleLocked(
+ mYu12Frame,
+ Size { halBuf.width, halBuf.height },
+ &cropAndScaled);
+ ATRACE_END();
+ if (ret != 0) {
+ lk.unlock();
+ return onDeviceError("%s: crop and scale failed!", __FUNCTION__);
+ }
+
+ Size sz {halBuf.width, halBuf.height};
+ ATRACE_BEGIN("formatConvert");
+ ret = V3_4::implementation::formatConvert(cropAndScaled, outLayout, sz, outputFourcc);
+ ATRACE_END();
+ if (ret != 0) {
+ lk.unlock();
+ return onDeviceError("%s: format coversion failed!", __FUNCTION__);
+ }
+ int relFence = sHandleImporter.unlock(*(halBuf.bufPtr));
+ if (relFence >= 0) {
+ halBuf.acquireFence = relFence;
+ }
+ } break;
+ default:
+ lk.unlock();
+ return onDeviceError("%s: unknown output format %x", __FUNCTION__, halBuf.format);
+ }
+ } // for each buffer
+ mScaledYu12Frames.clear();
+
+ // Don't hold the lock while calling back to parent
+ lk.unlock();
+ Status st = parent->processCaptureResult(req);
+ if (st != Status::OK) {
+ return onDeviceError("%s: failed to process capture result!", __FUNCTION__);
+ }
+ signalRequestDone();
+ return true;
+}
+
+Status ExternalCameraOfflineSession::importBuffer(int32_t streamId,
+ uint64_t bufId, buffer_handle_t buf,
+ /*out*/buffer_handle_t** outBufPtr,
+ bool allowEmptyBuf) {
+ Mutex::Autolock _l(mCbsLock);
+ return V3_4::implementation::importBufferImpl(
+ mCirculatingBuffers, sHandleImporter, streamId,
+ bufId, buf, outBufPtr, allowEmptyBuf);
+ return Status::OK;
+};
+
+#define UPDATE(md, tag, data, size) \
+do { \
+ if ((md).update((tag), (data), (size))) { \
+ ALOGE("Update " #tag " failed!"); \
+ return BAD_VALUE; \
+ } \
+} while (0)
+
+status_t ExternalCameraOfflineSession::fillCaptureResult(
+ common::V1_0::helper::CameraMetadata &md, nsecs_t timestamp) {
+ bool afTrigger = false;
+ {
+ std::lock_guard<std::mutex> lk(mAfTriggerLock);
+ afTrigger = mAfTrigger;
+ if (md.exists(ANDROID_CONTROL_AF_TRIGGER)) {
+ camera_metadata_entry entry = md.find(ANDROID_CONTROL_AF_TRIGGER);
+ if (entry.data.u8[0] == ANDROID_CONTROL_AF_TRIGGER_START) {
+ mAfTrigger = afTrigger = true;
+ } else if (entry.data.u8[0] == ANDROID_CONTROL_AF_TRIGGER_CANCEL) {
+ mAfTrigger = afTrigger = false;
+ }
+ }
+ }
+
+ // For USB camera, the USB camera handles everything and we don't have control
+ // over AF. We only simply fake the AF metadata based on the request
+ // received here.
+ uint8_t afState;
+ if (afTrigger) {
+ afState = ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED;
+ } else {
+ afState = ANDROID_CONTROL_AF_STATE_INACTIVE;
+ }
+ UPDATE(md, ANDROID_CONTROL_AF_STATE, &afState, 1);
+
+ camera_metadata_ro_entry activeArraySize =
+ mChars.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+
+ return V3_4::implementation::fillCaptureResultCommon(md, timestamp, activeArraySize);
+}
+
+#undef UPDATE
+
+Status ExternalCameraOfflineSession::processCaptureResult(std::shared_ptr<HalRequest>& req) {
+ ATRACE_CALL();
+ // Fill output buffers
+ hidl_vec<CaptureResult> results;
+ results.resize(1);
+ CaptureResult& result = results[0];
+ result.frameNumber = req->frameNumber;
+ result.partialResult = 1;
+ result.inputBuffer.streamId = -1;
+ result.outputBuffers.resize(req->buffers.size());
+ for (size_t i = 0; i < req->buffers.size(); i++) {
+ result.outputBuffers[i].streamId = req->buffers[i].streamId;
+ result.outputBuffers[i].bufferId = req->buffers[i].bufferId;
+ if (req->buffers[i].fenceTimeout) {
+ result.outputBuffers[i].status = BufferStatus::ERROR;
+ if (req->buffers[i].acquireFence >= 0) {
+ native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
+ handle->data[0] = req->buffers[i].acquireFence;
+ result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/false);
+ }
+ notifyError(req->frameNumber, req->buffers[i].streamId, ErrorCode::ERROR_BUFFER);
+ } else {
+ result.outputBuffers[i].status = BufferStatus::OK;
+ // TODO: refactor
+ if (req->buffers[i].acquireFence >= 0) {
+ native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
+ handle->data[0] = req->buffers[i].acquireFence;
+ result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/false);
+ }
+ }
+ }
+
+ // Fill capture result metadata
+ fillCaptureResult(req->setting, req->shutterTs);
+ const camera_metadata_t *rawResult = req->setting.getAndLock();
+ V3_2::implementation::convertToHidl(rawResult, &result.result);
+ req->setting.unlock(rawResult);
+
+ // Callback into framework
+ invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true);
+ V3_4::implementation::freeReleaseFences(results);
+ return Status::OK;
+};
+
+void ExternalCameraOfflineSession::invokeProcessCaptureResultCallback(
+ hidl_vec<CaptureResult> &results, bool tryWriteFmq) {
+ if (mProcessCaptureResultLock.tryLock() != OK) {
+ const nsecs_t NS_TO_SECOND = 1000000000;
+ ALOGV("%s: previous call is not finished! waiting 1s...", __FUNCTION__);
+ if (mProcessCaptureResultLock.timedLock(/* 1s */NS_TO_SECOND) != OK) {
+ ALOGE("%s: cannot acquire lock in 1s, cannot proceed",
+ __FUNCTION__);
+ return;
+ }
+ }
+ if (tryWriteFmq && mResultMetadataQueue->availableToWrite() > 0) {
+ for (CaptureResult &result : results) {
+ if (result.result.size() > 0) {
+ if (mResultMetadataQueue->write(result.result.data(), result.result.size())) {
+ result.fmqResultSize = result.result.size();
+ result.result.resize(0);
+ } else {
+ ALOGW("%s: couldn't utilize fmq, fall back to hwbinder", __FUNCTION__);
+ result.fmqResultSize = 0;
+ }
+ } else {
+ result.fmqResultSize = 0;
+ }
+ }
+ }
+ auto status = mCallback->processCaptureResult(results);
+ if (!status.isOk()) {
+ ALOGE("%s: processCaptureResult ERROR : %s", __FUNCTION__,
+ status.description().c_str());
+ }
+
+ mProcessCaptureResultLock.unlock();
+}
+
+Status ExternalCameraOfflineSession::processCaptureRequestError(
+ const std::shared_ptr<HalRequest>& req,
+ /*out*/std::vector<NotifyMsg>* outMsgs,
+ /*out*/std::vector<CaptureResult>* outResults) {
+ ATRACE_CALL();
+
+ if (outMsgs == nullptr) {
+ notifyError(/*frameNum*/req->frameNumber, /*stream*/-1, ErrorCode::ERROR_REQUEST);
+ } else {
+ NotifyMsg shutter;
+ shutter.type = MsgType::SHUTTER;
+ shutter.msg.shutter.frameNumber = req->frameNumber;
+ shutter.msg.shutter.timestamp = req->shutterTs;
+
+ NotifyMsg error;
+ error.type = MsgType::ERROR;
+ error.msg.error.frameNumber = req->frameNumber;
+ error.msg.error.errorStreamId = -1;
+ error.msg.error.errorCode = ErrorCode::ERROR_REQUEST;
+ outMsgs->push_back(shutter);
+ outMsgs->push_back(error);
+ }
+
+ // Fill output buffers
+ hidl_vec<CaptureResult> results;
+ results.resize(1);
+ CaptureResult& result = results[0];
+ result.frameNumber = req->frameNumber;
+ result.partialResult = 1;
+ result.inputBuffer.streamId = -1;
+ result.outputBuffers.resize(req->buffers.size());
+ for (size_t i = 0; i < req->buffers.size(); i++) {
+ result.outputBuffers[i].streamId = req->buffers[i].streamId;
+ result.outputBuffers[i].bufferId = req->buffers[i].bufferId;
+ result.outputBuffers[i].status = BufferStatus::ERROR;
+ if (req->buffers[i].acquireFence >= 0) {
+ native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
+ handle->data[0] = req->buffers[i].acquireFence;
+ result.outputBuffers[i].releaseFence.setTo(handle, /*shouldOwn*/false);
+ }
+ }
+
+ if (outResults == nullptr) {
+ // Callback into framework
+ invokeProcessCaptureResultCallback(results, /* tryWriteFmq */true);
+ V3_4::implementation::freeReleaseFences(results);
+ } else {
+ outResults->push_back(result);
+ }
+ return Status::OK;
+};
+
+ssize_t ExternalCameraOfflineSession::getJpegBufferSize(
+ uint32_t /*width*/, uint32_t /*height*/) const {
+ // Empty implementation here as the jpeg buffer size is passed in by ctor
+ return 0;
+};
+
+void ExternalCameraOfflineSession::notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) {
+ NotifyMsg msg;
+ msg.type = MsgType::ERROR;
+ msg.msg.error.frameNumber = frameNumber;
+ msg.msg.error.errorStreamId = streamId;
+ msg.msg.error.errorCode = ec;
+ mCallback->notify({msg});
+};
+
+Return<void> ExternalCameraOfflineSession::setCallback(const sp<ICameraDeviceCallback>& cb) {
+ Mutex::Autolock _il(mInterfaceLock);
+ if (mCallback != nullptr && cb != nullptr) {
+ ALOGE("%s: callback must not be set twice!", __FUNCTION__);
+ return Void();
+ }
+ mCallback = cb;
+
+ initOutputThread();
+
+ if (mOutputThread == nullptr) {
+ ALOGE("%s: init OutputThread failed!", __FUNCTION__);
+ }
+ return Void();
+}
+
+Return<void> ExternalCameraOfflineSession::getCaptureResultMetadataQueue(
+ V3_3::ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb) {
+ Mutex::Autolock _il(mInterfaceLock);
+ _hidl_cb(*mResultMetadataQueue->getDesc());
+ return Void();
+}
+
+void ExternalCameraOfflineSession::cleanupBuffersLocked(int id) {
+ for (auto& pair : mCirculatingBuffers.at(id)) {
+ sHandleImporter.freeBuffer(pair.second);
+ }
+ mCirculatingBuffers[id].clear();
+ mCirculatingBuffers.erase(id);
+}
+
+Return<void> ExternalCameraOfflineSession::close() {
+ Mutex::Autolock _il(mInterfaceLock);
+ {
+ Mutex::Autolock _l(mLock);
+ if (mClosed) {
+ ALOGW("%s: offline session already closed!", __FUNCTION__);
+ return Void();
+ }
+ }
+ if (mBufferRequestThread) {
+ mBufferRequestThread->requestExit();
+ mBufferRequestThread->join();
+ mBufferRequestThread.clear();
+ }
+ if (mOutputThread) {
+ mOutputThread->flush();
+ mOutputThread->requestExit();
+ mOutputThread->join();
+ mOutputThread.clear();
+ }
+
+ Mutex::Autolock _l(mLock);
+ // free all buffers
+ {
+ Mutex::Autolock _cbl(mCbsLock);
+ for(auto stream : mOfflineStreams) {
+ cleanupBuffersLocked(stream.id);
+ }
+ }
+ mCallback.clear();
+ mClosed = true;
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V3_6
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
diff --git a/camera/device/3.6/default/OWNERS b/camera/device/3.6/default/OWNERS
new file mode 100644
index 0000000..f48a95c
--- /dev/null
+++ b/camera/device/3.6/default/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/av:/camera/OWNERS
diff --git a/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDeviceSession.h b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDeviceSession.h
new file mode 100644
index 0000000..db0d9a5
--- /dev/null
+++ b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDeviceSession.h
@@ -0,0 +1,208 @@
+/*
+ * 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_CAMERA_DEVICE_V3_6_EXTCAMERADEVICESESSION_H
+#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICESESSION_H
+
+#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
+#include <android/hardware/camera/device/3.6/ICameraDeviceSession.h>
+#include <../../3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h>
+#include "ExternalCameraOfflineSession.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_6 {
+namespace implementation {
+
+using ::android::hardware::camera::device::V3_2::BufferCache;
+using ::android::hardware::camera::device::V3_2::CameraMetadata;
+using ::android::hardware::camera::device::V3_2::CaptureRequest;
+using ::android::hardware::camera::device::V3_2::CaptureResult;
+using ::android::hardware::camera::device::V3_5::ICameraDeviceCallback;
+using ::android::hardware::camera::device::V3_2::RequestTemplate;
+using ::android::hardware::camera::device::V3_2::Stream;
+using ::android::hardware::camera::device::V3_5::StreamConfiguration;
+using ::android::hardware::camera::device::V3_6::ICameraDeviceSession;
+using ::android::hardware::camera::device::V3_6::ICameraOfflineSession;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::external::common::ExternalCameraConfig;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::sp;
+using ::android::Mutex;
+using ::android::base::unique_fd;
+
+using ::android::hardware::camera::device::V3_4::implementation::SupportedV4L2Format;
+using ::android::hardware::camera::device::V3_4::implementation::CroppingType;
+
+struct ExternalCameraDeviceSession : public V3_5::implementation::ExternalCameraDeviceSession {
+
+ ExternalCameraDeviceSession(const sp<V3_2::ICameraDeviceCallback>&,
+ const ExternalCameraConfig& cfg,
+ const std::vector<SupportedV4L2Format>& sortedFormats,
+ const CroppingType& croppingType,
+ const common::V1_0::helper::CameraMetadata& chars,
+ const std::string& cameraId,
+ unique_fd v4l2Fd);
+ virtual ~ExternalCameraDeviceSession();
+
+ // Retrieve the HIDL interface, split into its own class to avoid inheritance issues when
+ // dealing with minor version revs and simultaneous implementation and interface inheritance
+ virtual sp<V3_4::ICameraDeviceSession> getInterface() override {
+ return new TrampolineSessionInterface_3_6(this);
+ }
+
+protected:
+ // Methods from v3.5 and earlier will trampoline to inherited implementation
+ Return<void> configureStreams_3_6(
+ const StreamConfiguration& requestedConfiguration,
+ ICameraDeviceSession::configureStreams_3_6_cb _hidl_cb);
+
+ Return<void> switchToOffline(
+ const hidl_vec<int32_t>& streamsToKeep,
+ ICameraDeviceSession::switchToOffline_cb _hidl_cb);
+
+ void fillOutputStream3_6(const V3_3::HalStreamConfiguration& outStreams_v33,
+ /*out*/V3_6::HalStreamConfiguration* outStreams_v36);
+ bool supportOfflineLocked(int32_t streamId);
+
+ // Main body of switchToOffline. This method does not invoke any callbacks
+ // but instead returns the necessary callbacks in output arguments so callers
+ // can callback later without holding any locks
+ Status switchToOffline(const hidl_vec<int32_t>& offlineStreams,
+ /*out*/std::vector<NotifyMsg>* msgs,
+ /*out*/std::vector<CaptureResult>* results,
+ /*out*/CameraOfflineSessionInfo* info,
+ /*out*/sp<ICameraOfflineSession>* session);
+
+ // Whether a request can be completely dropped when switching to offline
+ bool canDropRequest(const hidl_vec<int32_t>& offlineStreams,
+ std::shared_ptr<V3_4::implementation::HalRequest> halReq);
+
+ void fillOfflineSessionInfo(const hidl_vec<int32_t>& offlineStreams,
+ std::deque<std::shared_ptr<HalRequest>>& offlineReqs,
+ const std::map<int, CirculatingBuffers>& circulatingBuffers,
+ /*out*/CameraOfflineSessionInfo* info);
+
+private:
+
+ struct TrampolineSessionInterface_3_6 : public ICameraDeviceSession {
+ TrampolineSessionInterface_3_6(sp<ExternalCameraDeviceSession> parent) :
+ mParent(parent) {}
+
+ virtual Return<void> constructDefaultRequestSettings(
+ RequestTemplate type,
+ V3_3::ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) override {
+ return mParent->constructDefaultRequestSettings(type, _hidl_cb);
+ }
+
+ virtual Return<void> configureStreams(
+ const V3_2::StreamConfiguration& requestedConfiguration,
+ V3_3::ICameraDeviceSession::configureStreams_cb _hidl_cb) override {
+ return mParent->configureStreams(requestedConfiguration, _hidl_cb);
+ }
+
+ virtual Return<void> processCaptureRequest(const hidl_vec<V3_2::CaptureRequest>& requests,
+ const hidl_vec<V3_2::BufferCache>& cachesToRemove,
+ V3_3::ICameraDeviceSession::processCaptureRequest_cb _hidl_cb) override {
+ return mParent->processCaptureRequest(requests, cachesToRemove, _hidl_cb);
+ }
+
+ virtual Return<void> getCaptureRequestMetadataQueue(
+ V3_3::ICameraDeviceSession::getCaptureRequestMetadataQueue_cb _hidl_cb) override {
+ return mParent->getCaptureRequestMetadataQueue(_hidl_cb);
+ }
+
+ virtual Return<void> getCaptureResultMetadataQueue(
+ V3_3::ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb) override {
+ return mParent->getCaptureResultMetadataQueue(_hidl_cb);
+ }
+
+ virtual Return<Status> flush() override {
+ return mParent->flush();
+ }
+
+ virtual Return<void> close() override {
+ return mParent->close();
+ }
+
+ virtual Return<void> configureStreams_3_3(
+ const V3_2::StreamConfiguration& requestedConfiguration,
+ configureStreams_3_3_cb _hidl_cb) override {
+ return mParent->configureStreams_3_3(requestedConfiguration, _hidl_cb);
+ }
+
+ virtual Return<void> configureStreams_3_4(
+ const V3_4::StreamConfiguration& requestedConfiguration,
+ configureStreams_3_4_cb _hidl_cb) override {
+ return mParent->configureStreams_3_4(requestedConfiguration, _hidl_cb);
+ }
+
+ virtual Return<void> processCaptureRequest_3_4(const hidl_vec<V3_4::CaptureRequest>& requests,
+ const hidl_vec<V3_2::BufferCache>& cachesToRemove,
+ ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb) override {
+ return mParent->processCaptureRequest_3_4(requests, cachesToRemove, _hidl_cb);
+ }
+
+ virtual Return<void> configureStreams_3_5(
+ const StreamConfiguration& requestedConfiguration,
+ configureStreams_3_5_cb _hidl_cb) override {
+ return mParent->configureStreams_3_5(requestedConfiguration, _hidl_cb);
+ }
+
+ virtual Return<void> signalStreamFlush(
+ const hidl_vec<int32_t>& requests,
+ uint32_t streamConfigCounter) override {
+ return mParent->signalStreamFlush(requests, streamConfigCounter);
+ }
+
+ virtual Return<void> isReconfigurationRequired(const V3_2::CameraMetadata& oldSessionParams,
+ const V3_2::CameraMetadata& newSessionParams,
+ ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb) override {
+ return mParent->isReconfigurationRequired(oldSessionParams, newSessionParams, _hidl_cb);
+ }
+
+ virtual Return<void> configureStreams_3_6(
+ const StreamConfiguration& requestedConfiguration,
+ configureStreams_3_6_cb _hidl_cb) override {
+ return mParent->configureStreams_3_6(requestedConfiguration, _hidl_cb);
+ }
+
+ virtual Return<void> switchToOffline(
+ const hidl_vec<int32_t>& streamsToKeep,
+ switchToOffline_cb _hidl_cb) override {
+ return mParent->switchToOffline(streamsToKeep, _hidl_cb);
+ }
+
+ private:
+ sp<ExternalCameraDeviceSession> mParent;
+ };
+};
+
+} // namespace implementation
+} // namespace V3_6
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICESESSION_H
diff --git a/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDevice_3_6.h b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDevice_3_6.h
new file mode 100644
index 0000000..020bec4
--- /dev/null
+++ b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraDevice_3_6.h
@@ -0,0 +1,126 @@
+/*
+ * 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_CAMERA_DEVICE_V3_6_EXTCAMERADEVICE_H
+#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICE_H
+
+#include <android/hardware/camera/device/3.6/ICameraDevice.h>
+
+#include "ExternalCameraDeviceSession.h"
+#include <../../../../3.5/default/include/ext_device_v3_5_impl/ExternalCameraDevice_3_5.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_6 {
+namespace implementation {
+
+using namespace ::android::hardware::camera::device;
+using ::android::hardware::camera::device::V3_6::ICameraDevice;
+using ::android::hardware::camera::common::V1_0::CameraResourceCost;
+using ::android::hardware::camera::common::V1_0::TorchMode;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::external::common::ExternalCameraConfig;
+using ::android::hardware::camera::external::common::Size;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::sp;
+
+/*
+ * The camera device HAL implementation is opened lazily (via the open call)
+ */
+struct ExternalCameraDevice : public V3_5::implementation::ExternalCameraDevice {
+
+ // Called by external camera provider HAL.
+ // Provider HAL must ensure the uniqueness of CameraDevice object per cameraId, or there could
+ // be multiple CameraDevice trying to access the same physical camera. Also, provider will have
+ // to keep track of all CameraDevice objects in order to notify CameraDevice when the underlying
+ // camera is detached.
+ ExternalCameraDevice(const std::string& cameraId, const ExternalCameraConfig& cfg);
+ virtual ~ExternalCameraDevice();
+
+ virtual sp<V3_2::ICameraDevice> getInterface() override {
+ return new TrampolineDeviceInterface_3_6(this);
+ }
+
+protected:
+ virtual sp<V3_4::implementation::ExternalCameraDeviceSession> createSession(
+ const sp<V3_2::ICameraDeviceCallback>&,
+ const ExternalCameraConfig& cfg,
+ const std::vector<SupportedV4L2Format>& sortedFormats,
+ const CroppingType& croppingType,
+ const common::V1_0::helper::CameraMetadata& chars,
+ const std::string& cameraId,
+ unique_fd v4l2Fd) override;
+
+ virtual status_t initAvailableCapabilities(
+ ::android::hardware::camera::common::V1_0::helper::CameraMetadata*) override;
+
+private:
+ struct TrampolineDeviceInterface_3_6 : public ICameraDevice {
+ TrampolineDeviceInterface_3_6(sp<ExternalCameraDevice> parent) :
+ mParent(parent) {}
+
+ virtual Return<void> getResourceCost(V3_2::ICameraDevice::getResourceCost_cb _hidl_cb)
+ override {
+ return mParent->getResourceCost(_hidl_cb);
+ }
+
+ virtual Return<void> getCameraCharacteristics(
+ V3_2::ICameraDevice::getCameraCharacteristics_cb _hidl_cb) override {
+ return mParent->getCameraCharacteristics(_hidl_cb);
+ }
+
+ virtual Return<Status> setTorchMode(TorchMode mode) override {
+ return mParent->setTorchMode(mode);
+ }
+
+ virtual Return<void> open(const sp<V3_2::ICameraDeviceCallback>& callback,
+ V3_2::ICameraDevice::open_cb _hidl_cb) override {
+ return mParent->open(callback, _hidl_cb);
+ }
+
+ virtual Return<void> dumpState(const hidl_handle& fd) override {
+ return mParent->dumpState(fd);
+ }
+
+ virtual Return<void> getPhysicalCameraCharacteristics(const hidl_string& physicalCameraId,
+ V3_5::ICameraDevice::getPhysicalCameraCharacteristics_cb _hidl_cb) override {
+ return mParent->getPhysicalCameraCharacteristics(physicalCameraId, _hidl_cb);
+ }
+
+ virtual Return<void> isStreamCombinationSupported(
+ const V3_4::StreamConfiguration& streams,
+ V3_5::ICameraDevice::isStreamCombinationSupported_cb _hidl_cb) override {
+ return mParent->isStreamCombinationSupported(streams, _hidl_cb);
+ }
+
+ private:
+ sp<ExternalCameraDevice> mParent;
+ };
+};
+
+} // namespace implementation
+} // namespace V3_6
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERADEVICE_H
diff --git a/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraOfflineSession.h b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraOfflineSession.h
new file mode 100644
index 0000000..230b67c
--- /dev/null
+++ b/camera/device/3.6/default/include/ext_device_v3_6_impl/ExternalCameraOfflineSession.h
@@ -0,0 +1,232 @@
+/*
+ * 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_CAMERA_DEVICE_V3_6_EXTCAMERAOFFLINESESSION_H
+#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERAOFFLINESESSION_H
+
+#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
+#include <android/hardware/camera/device/3.6/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.6/ICameraOfflineSession.h>
+#include <android/hardware/camera/common/1.0/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <deque>
+#include <../../3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h>
+#include <../../3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h>
+#include <HandleImporter.h>
+#include <Exif.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace V3_6 {
+namespace implementation {
+
+using ::android::hardware::camera::device::V3_2::BufferCache;
+using ::android::hardware::camera::device::V3_5::BufferRequest;
+using ::android::hardware::camera::device::V3_5::BufferRequestStatus;
+using ::android::hardware::camera::device::V3_2::BufferStatus;
+using ::android::hardware::camera::device::V3_2::CameraMetadata;
+using ::android::hardware::camera::device::V3_2::CaptureRequest;
+using ::android::hardware::camera::device::V3_2::CaptureResult;
+using ::android::hardware::camera::device::V3_2::ErrorCode;
+using ::android::hardware::camera::device::V3_5::ICameraDeviceCallback;
+using ::android::hardware::camera::device::V3_2::MsgType;
+using ::android::hardware::camera::device::V3_2::NotifyMsg;
+using ::android::hardware::camera::device::V3_2::RequestTemplate;
+using ::android::hardware::camera::device::V3_2::Stream;
+using ::android::hardware::camera::device::V3_5::StreamConfiguration;
+using ::android::hardware::camera::device::V3_2::StreamConfigurationMode;
+using ::android::hardware::camera::device::V3_2::StreamRotation;
+using ::android::hardware::camera::device::V3_2::StreamType;
+using ::android::hardware::camera::device::V3_2::DataspaceFlags;
+using ::android::hardware::camera::device::V3_2::CameraBlob;
+using ::android::hardware::camera::device::V3_2::CameraBlobId;
+using ::android::hardware::camera::device::V3_4::HalStreamConfiguration;
+using ::android::hardware::camera::device::V3_6::ICameraOfflineSession;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
+using ::android::hardware::camera::common::V1_0::helper::ExifUtils;
+using ::android::hardware::camera::external::common::ExternalCameraConfig;
+using ::android::hardware::camera::external::common::Size;
+using ::android::hardware::camera::external::common::SizeHasher;
+using ::android::hardware::graphics::common::V1_0::BufferUsage;
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::sp;
+using ::android::Mutex;
+using ::android::base::unique_fd;
+
+using ::android::hardware::camera::device::V3_4::implementation::SupportedV4L2Format;
+using ::android::hardware::camera::device::V3_4::implementation::CroppingType;
+using ::android::hardware::camera::device::V3_4::implementation::CirculatingBuffers;
+using ::android::hardware::camera::device::V3_4::implementation::HalRequest;
+using ::android::hardware::camera::device::V3_4::implementation::OutputThreadInterface;
+
+struct ExternalCameraOfflineSession : public virtual RefBase,
+ public virtual OutputThreadInterface {
+
+ ExternalCameraOfflineSession(
+ const CroppingType& croppingType,
+ const common::V1_0::helper::CameraMetadata& chars,
+ const std::string& cameraId,
+ const std::string& exifMake,
+ const std::string& exifModel,
+ uint32_t blobBufferSize,
+ bool afTrigger,
+ const hidl_vec<Stream>& offlineStreams,
+ std::deque<std::shared_ptr<HalRequest>>& offlineReqs,
+ const std::map<int, CirculatingBuffers>& circulatingBuffers);
+
+ bool initialize();
+
+ virtual ~ExternalCameraOfflineSession();
+
+ // Retrieve the HIDL interface, split into its own class to avoid inheritance issues when
+ // dealing with minor version revs and simultaneous implementation and interface inheritance
+ virtual sp<V3_6::ICameraOfflineSession> getInterface() {
+ return new TrampolineSessionInterface_3_6(this);
+ }
+
+protected:
+
+ // Methods from OutputThreadInterface
+ virtual Status importBuffer(int32_t streamId,
+ uint64_t bufId, buffer_handle_t buf,
+ /*out*/buffer_handle_t** outBufPtr,
+ bool allowEmptyBuf) override;
+
+ virtual Status processCaptureResult(std::shared_ptr<HalRequest>&) override;
+
+ virtual Status processCaptureRequestError(const std::shared_ptr<HalRequest>&,
+ /*out*/std::vector<NotifyMsg>* msgs = nullptr,
+ /*out*/std::vector<CaptureResult>* results = nullptr) override;
+
+ virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const override;
+
+ virtual void notifyError(uint32_t frameNumber, int32_t streamId, ErrorCode ec) override;
+ // End of OutputThreadInterface methods
+
+ class OutputThread : public V3_5::implementation::ExternalCameraDeviceSession::OutputThread {
+ public:
+ OutputThread(
+ wp<OutputThreadInterface> parent, CroppingType ct,
+ const common::V1_0::helper::CameraMetadata& chars,
+ sp<V3_5::implementation::ExternalCameraDeviceSession::BufferRequestThread> bufReqThread,
+ std::deque<std::shared_ptr<HalRequest>>& offlineReqs) :
+ V3_5::implementation::ExternalCameraDeviceSession::OutputThread(
+ parent, ct, chars, bufReqThread),
+ mOfflineReqs(offlineReqs) {}
+
+ virtual bool threadLoop() override;
+
+ protected:
+ std::deque<std::shared_ptr<HalRequest>> mOfflineReqs;
+ }; // OutputThread
+
+
+ Return<void> setCallback(const sp<ICameraDeviceCallback>& cb);
+
+ Return<void> getCaptureResultMetadataQueue(
+ V3_3::ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb);
+
+ Return<void> close();
+
+ void initOutputThread();
+
+ void invokeProcessCaptureResultCallback(
+ hidl_vec<CaptureResult> &results, bool tryWriteFmq);
+
+ status_t fillCaptureResult(common::V1_0::helper::CameraMetadata& md, nsecs_t timestamp);
+
+ void cleanupBuffersLocked(int id);
+
+ // Protect (most of) HIDL interface methods from synchronized-entering
+ mutable Mutex mInterfaceLock;
+
+ mutable Mutex mLock; // Protect all data members except otherwise noted
+
+ bool mClosed = false;
+ const CroppingType mCroppingType;
+ const common::V1_0::helper::CameraMetadata mChars;
+ const std::string mCameraId;
+ const std::string mExifMake;
+ const std::string mExifModel;
+ const uint32_t mBlobBufferSize;
+
+ std::mutex mAfTriggerLock; // protect mAfTrigger
+ bool mAfTrigger;
+
+ const hidl_vec<Stream> mOfflineStreams;
+ std::deque<std::shared_ptr<HalRequest>> mOfflineReqs;
+
+ // Protect mCirculatingBuffers, must not lock mLock after acquiring this lock
+ mutable Mutex mCbsLock;
+ std::map<int, CirculatingBuffers> mCirculatingBuffers;
+
+ static HandleImporter sHandleImporter;
+
+ using ResultMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+ std::shared_ptr<ResultMetadataQueue> mResultMetadataQueue;
+
+ // Protect against invokeProcessCaptureResultCallback()
+ Mutex mProcessCaptureResultLock;
+
+ sp<ICameraDeviceCallback> mCallback;
+
+ sp<V3_5::implementation::ExternalCameraDeviceSession::BufferRequestThread> mBufferRequestThread;
+ sp<OutputThread> mOutputThread;
+private:
+
+ struct TrampolineSessionInterface_3_6 : public ICameraOfflineSession {
+ TrampolineSessionInterface_3_6(sp<ExternalCameraOfflineSession> parent) :
+ mParent(parent) {}
+
+ virtual Return<void> setCallback(const sp<ICameraDeviceCallback>& cb) override {
+ return mParent->setCallback(cb);
+ }
+
+ virtual Return<void> getCaptureResultMetadataQueue(
+ V3_3::ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb) override {
+ return mParent->getCaptureResultMetadataQueue(_hidl_cb);
+ }
+
+ virtual Return<void> close() override {
+ return mParent->close();
+ }
+
+ private:
+ sp<ExternalCameraOfflineSession> mParent;
+ };
+};
+
+} // namespace implementation
+} // namespace V3_6
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_6_EXTCAMERAOFFLINESESSION_H
diff --git a/camera/provider/2.4/default/Android.bp b/camera/provider/2.4/default/Android.bp
index 9203b8d..627ddf4 100644
--- a/camera/provider/2.4/default/Android.bp
+++ b/camera/provider/2.4/default/Android.bp
@@ -49,6 +49,7 @@
"android.hardware.camera.device@3.3",
"android.hardware.camera.device@3.4",
"android.hardware.camera.device@3.5",
+ "android.hardware.camera.device@3.6",
"android.hardware.camera.provider@2.4",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
@@ -60,6 +61,7 @@
"camera.device@3.4-impl",
"camera.device@3.5-external-impl",
"camera.device@3.5-impl",
+ "camera.device@3.6-external-impl",
"libcamera_metadata",
"libcutils",
"libhardware",
@@ -73,7 +75,8 @@
],
header_libs: [
"camera.device@3.4-external-impl_headers",
- "camera.device@3.5-external-impl_headers"
+ "camera.device@3.5-external-impl_headers",
+ "camera.device@3.6-external-impl_headers"
],
export_include_dirs: ["."],
}
diff --git a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
index a6fd288..2bfced2 100644
--- a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
+++ b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
@@ -26,6 +26,7 @@
#include "ExternalCameraProviderImpl_2_4.h"
#include "ExternalCameraDevice_3_4.h"
#include "ExternalCameraDevice_3_5.h"
+#include "ExternalCameraDevice_3_6.h"
namespace android {
namespace hardware {
@@ -73,6 +74,7 @@
switch(mPreferredHal3MinorVersion) {
case 4:
case 5:
+ case 6:
// OK
break;
default:
@@ -171,6 +173,12 @@
cameraId, mCfg);
break;
}
+ case 6: {
+ ALOGV("Constructing v3.6 external camera device");
+ deviceImpl = new device::V3_6::implementation::ExternalCameraDevice(
+ cameraId, mCfg);
+ break;
+ }
default:
ALOGE("%s: Unknown HAL minor version %d!", __FUNCTION__, mPreferredHal3MinorVersion);
_hidl_cb(Status::INTERNAL_ERROR, nullptr);
@@ -202,7 +210,9 @@
ALOGI("ExtCam: adding %s to External Camera HAL!", devName);
Mutex::Autolock _l(mLock);
std::string deviceName;
- if (mPreferredHal3MinorVersion == 5) {
+ if (mPreferredHal3MinorVersion == 6) {
+ deviceName = std::string("device@3.6/external/") + devName;
+ } else if (mPreferredHal3MinorVersion == 5) {
deviceName = std::string("device@3.5/external/") + devName;
} else {
deviceName = std::string("device@3.4/external/") + devName;
@@ -249,7 +259,9 @@
void ExternalCameraProviderImpl_2_4::deviceRemoved(const char* devName) {
Mutex::Autolock _l(mLock);
std::string deviceName;
- if (mPreferredHal3MinorVersion == 5) {
+ if (mPreferredHal3MinorVersion == 6) {
+ deviceName = std::string("device@3.6/external/") + devName;
+ } else if (mPreferredHal3MinorVersion == 5) {
deviceName = std::string("device@3.5/external/") + devName;
} else {
deviceName = std::string("device@3.4/external/") + devName;
diff --git a/camera/provider/2.4/vts/functional/Android.bp b/camera/provider/2.4/vts/functional/Android.bp
index 15f5c9a..4b9d6f1 100644
--- a/camera/provider/2.4/vts/functional/Android.bp
+++ b/camera/provider/2.4/vts/functional/Android.bp
@@ -38,7 +38,8 @@
"android.hardware.camera.device@3.3",
"android.hardware.camera.device@3.4",
"android.hardware.camera.device@3.5",
- "android.hardware.camera.metadata@3.4",
+ "android.hardware.camera.device@3.6",
+ "android.hardware.camera.metadata@3.4",
"android.hardware.camera.provider@2.4",
"android.hardware.camera.provider@2.5",
"android.hardware.camera.provider@2.6",
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index b4092ca..5c73aa2 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -37,6 +37,8 @@
#include <android/hardware/camera/device/3.5/ICameraDevice.h>
#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
#include <android/hardware/camera/device/3.5/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.6/ICameraDevice.h>
+#include <android/hardware/camera/device/3.6/ICameraDeviceSession.h>
#include <android/hardware/camera/metadata/3.4/types.h>
#include <android/hardware/camera/provider/2.4/ICameraProvider.h>
#include <android/hardware/camera/provider/2.5/ICameraProvider.h>
@@ -133,6 +135,8 @@
const uint32_t kMaxPreviewWidth = 1920;
const uint32_t kMaxPreviewHeight = 1080;
+const uint32_t kMaxStillWidth = 2048;
+const uint32_t kMaxStillHeight = 1536;
const uint32_t kMaxVideoWidth = 4096;
const uint32_t kMaxVideoHeight = 2160;
const int64_t kStreamBufferTimeoutSec = 3;
@@ -162,11 +166,13 @@
namespace {
// "device@<version>/legacy/<id>"
const char *kDeviceNameRE = "device@([0-9]+\\.[0-9]+)/%s/(.+)";
+ const int CAMERA_DEVICE_API_VERSION_3_6 = 0x306;
const int CAMERA_DEVICE_API_VERSION_3_5 = 0x305;
const int CAMERA_DEVICE_API_VERSION_3_4 = 0x304;
const int CAMERA_DEVICE_API_VERSION_3_3 = 0x303;
const int CAMERA_DEVICE_API_VERSION_3_2 = 0x302;
const int CAMERA_DEVICE_API_VERSION_1_0 = 0x100;
+ const char *kHAL3_6 = "3.6";
const char *kHAL3_5 = "3.5";
const char *kHAL3_4 = "3.4";
const char *kHAL3_3 = "3.3";
@@ -202,7 +208,9 @@
return -1;
}
- if (version.compare(kHAL3_5) == 0) {
+ if (version.compare(kHAL3_6) == 0) {
+ return CAMERA_DEVICE_API_VERSION_3_6;
+ } else if (version.compare(kHAL3_5) == 0) {
return CAMERA_DEVICE_API_VERSION_3_5;
} else if (version.compare(kHAL3_4) == 0) {
return CAMERA_DEVICE_API_VERSION_3_4;
@@ -717,7 +725,8 @@
void castSession(const sp<ICameraDeviceSession> &session, int32_t deviceVersion,
sp<device::V3_3::ICameraDeviceSession> *session3_3 /*out*/,
sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/,
- sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/);
+ sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/,
+ sp<device::V3_6::ICameraDeviceSession> *session3_6 /*out*/);
void castDevice(const sp<device::V3_2::ICameraDevice> &device, int32_t deviceVersion,
sp<device::V3_5::ICameraDevice> *device3_5/*out*/);
void createStreamConfiguration(const ::android::hardware::hidl_vec<V3_2::Stream>& streams3_2,
@@ -727,6 +736,17 @@
::android::hardware::camera::device::V3_5::StreamConfiguration *config3_5,
uint32_t jpegBufferSize = 0);
+ void configureOfflineStillStream(const std::string &name, int32_t deviceVersion,
+ sp<ICameraProvider> provider,
+ const AvailableStream *threshold,
+ sp<device::V3_6::ICameraDeviceSession> *session/*out*/,
+ V3_2::Stream *stream /*out*/,
+ device::V3_6::HalStreamConfiguration *halStreamConfig /*out*/,
+ bool *supportsPartialResults /*out*/,
+ uint32_t *partialResultCount /*out*/,
+ sp<DeviceCb> *outCb /*out*/,
+ uint32_t *jpegBufferSize /*out*/,
+ bool *useHalBufManager /*out*/);
void configurePreviewStreams3_4(const std::string &name, int32_t deviceVersion,
sp<ICameraProvider> provider,
const AvailableStream *previewThreshold,
@@ -792,6 +812,7 @@
uint32_t* outBufSize);
static Status isConstrainedModeAvailable(camera_metadata_t *staticMeta);
static Status isLogicalMultiCamera(const camera_metadata_t *staticMeta);
+ static Status isOfflineSessionSupported(const camera_metadata_t *staticMeta);
static Status getPhysicalCameraIds(const camera_metadata_t *staticMeta,
std::unordered_set<std::string> *physicalIds/*out*/);
static Status getSupportedKeys(camera_metadata_t *staticMeta,
@@ -818,6 +839,9 @@
CameraParameters &cameraParams, const char *mode) ;
static Status isMonochromeCamera(const camera_metadata_t *staticMeta);
+ // Used by switchToOffline where a new result queue is created for offline reqs
+ void updateInflightResultQueue(std::shared_ptr<ResultMetadataQueue> resultQueue);
+
protected:
// In-flight queue for tracking completion of capture requests.
@@ -1442,7 +1466,13 @@
hidl_vec<StreamBuffer> tmpRetBuffers(bufReq.numBuffersRequested);
for (size_t j = 0; j < bufReq.numBuffersRequested; j++) {
hidl_handle buffer_handle;
- mParent->allocateGraphicBuffer(stream.v3_2.width, stream.v3_2.height,
+ uint32_t w = stream.v3_2.width;
+ uint32_t h = stream.v3_2.height;
+ if (stream.v3_2.format == PixelFormat::BLOB) {
+ w = stream.bufferSize;
+ h = 1;
+ }
+ mParent->allocateGraphicBuffer(w, h,
android_convertGralloc1To0Usage(
halStream.producerUsage, halStream.consumerUsage),
halStream.overrideFormat, &buffer_handle);
@@ -1706,6 +1736,7 @@
for (const auto& name : cameraDeviceNames) {
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
switch (deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_3_6:
case CAMERA_DEVICE_API_VERSION_3_5:
case CAMERA_DEVICE_API_VERSION_3_4:
case CAMERA_DEVICE_API_VERSION_3_3:
@@ -1748,6 +1779,7 @@
for (const auto& name : cameraDeviceNames) {
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
switch (deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_3_6:
case CAMERA_DEVICE_API_VERSION_3_5:
case CAMERA_DEVICE_API_VERSION_3_4:
case CAMERA_DEVICE_API_VERSION_3_3:
@@ -2489,6 +2521,7 @@
for (const auto& name : cameraDeviceNames) {
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
switch (deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_3_6:
case CAMERA_DEVICE_API_VERSION_3_5:
case CAMERA_DEVICE_API_VERSION_3_4:
case CAMERA_DEVICE_API_VERSION_3_3:
@@ -2568,6 +2601,7 @@
for (const auto& name : cameraDeviceNames) {
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
switch (deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_3_6:
case CAMERA_DEVICE_API_VERSION_3_5:
case CAMERA_DEVICE_API_VERSION_3_4:
case CAMERA_DEVICE_API_VERSION_3_3:
@@ -2694,6 +2728,7 @@
for (const auto& name : cameraDeviceNames) {
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
switch (deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_3_6:
case CAMERA_DEVICE_API_VERSION_3_5:
case CAMERA_DEVICE_API_VERSION_3_4:
case CAMERA_DEVICE_API_VERSION_3_3:
@@ -2759,6 +2794,7 @@
for (const auto& name : cameraDeviceNames) {
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
switch (deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_3_6:
case CAMERA_DEVICE_API_VERSION_3_5:
case CAMERA_DEVICE_API_VERSION_3_4:
case CAMERA_DEVICE_API_VERSION_3_3:
@@ -2788,8 +2824,12 @@
sp<device::V3_3::ICameraDeviceSession> sessionV3_3;
sp<device::V3_4::ICameraDeviceSession> sessionV3_4;
sp<device::V3_5::ICameraDeviceSession> sessionV3_5;
- castSession(session, deviceVersion, &sessionV3_3, &sessionV3_4, &sessionV3_5);
- if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_5) {
+ sp<device::V3_6::ICameraDeviceSession> sessionV3_6;
+ castSession(session, deviceVersion, &sessionV3_3,
+ &sessionV3_4, &sessionV3_5, &sessionV3_6);
+ if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_6) {
+ ASSERT_TRUE(sessionV3_6.get() != nullptr);
+ } else if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_5) {
ASSERT_TRUE(sessionV3_5.get() != nullptr);
} else if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_4) {
ASSERT_TRUE(sessionV3_4.get() != nullptr);
@@ -2851,6 +2891,7 @@
for (const auto& name : cameraDeviceNames) {
int deviceVersion = getCameraDeviceVersion(name, mProviderType);
switch (deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_3_6:
case CAMERA_DEVICE_API_VERSION_3_5:
case CAMERA_DEVICE_API_VERSION_3_4:
case CAMERA_DEVICE_API_VERSION_3_3:
@@ -2949,11 +2990,12 @@
sp<device::V3_3::ICameraDeviceSession> session3_3;
sp<device::V3_4::ICameraDeviceSession> session3_4;
sp<device::V3_5::ICameraDeviceSession> session3_5;
+ sp<device::V3_6::ICameraDeviceSession> session3_6;
sp<device::V3_2::ICameraDevice> cameraDevice;
sp<device::V3_5::ICameraDevice> cameraDevice3_5;
openEmptyDeviceSession(name, mProvider,
&session /*out*/, &staticMeta /*out*/, &cameraDevice /*out*/);
- castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+ castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
outputStreams.clear();
@@ -3057,11 +3099,12 @@
sp<device::V3_3::ICameraDeviceSession> session3_3;
sp<device::V3_4::ICameraDeviceSession> session3_4;
sp<device::V3_5::ICameraDeviceSession> session3_5;
+ sp<device::V3_6::ICameraDeviceSession> session3_6;
sp<device::V3_2::ICameraDevice> cameraDevice;
sp<device::V3_5::ICameraDevice> cameraDevice3_5;
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
&cameraDevice /*out*/);
- castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+ castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
outputStreams.clear();
@@ -3254,11 +3297,12 @@
sp<device::V3_3::ICameraDeviceSession> session3_3;
sp<device::V3_4::ICameraDeviceSession> session3_4;
sp<device::V3_5::ICameraDeviceSession> session3_5;
+ sp<device::V3_6::ICameraDeviceSession> session3_6;
sp<device::V3_2::ICameraDevice> cameraDevice;
sp<device::V3_5::ICameraDevice> cameraDevice3_5;
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
&cameraDevice /*out*/);
- castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+ castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
Status rc = isZSLModeAvailable(staticMeta);
@@ -3421,8 +3465,9 @@
sp<device::V3_3::ICameraDeviceSession> session3_3;
sp<device::V3_4::ICameraDeviceSession> session3_4;
sp<device::V3_5::ICameraDeviceSession> session3_5;
+ sp<device::V3_6::ICameraDeviceSession> session3_6;
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMetaBuffer /*out*/);
- castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+ castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_4) {
ASSERT_NE(session3_4, nullptr);
} else {
@@ -3543,11 +3588,12 @@
sp<device::V3_3::ICameraDeviceSession> session3_3;
sp<device::V3_4::ICameraDeviceSession> session3_4;
sp<device::V3_5::ICameraDeviceSession> session3_5;
+ sp<device::V3_6::ICameraDeviceSession> session3_6;
sp<device::V3_2::ICameraDevice> cameraDevice;
sp<device::V3_5::ICameraDevice> cameraDevice3_5;
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
&cameraDevice /*out*/);
- castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+ castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
// Check if camera support depth only
@@ -3660,11 +3706,12 @@
sp<device::V3_3::ICameraDeviceSession> session3_3;
sp<device::V3_4::ICameraDeviceSession> session3_4;
sp<device::V3_5::ICameraDeviceSession> session3_5;
+ sp<device::V3_6::ICameraDeviceSession> session3_6;
sp<device::V3_2::ICameraDevice> cameraDevice;
sp<device::V3_5::ICameraDevice> cameraDevice3_5;
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
&cameraDevice /*out*/);
- castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+ castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
Status rc = isConstrainedModeAvailable(staticMeta);
@@ -3871,11 +3918,12 @@
sp<device::V3_3::ICameraDeviceSession> session3_3;
sp<device::V3_4::ICameraDeviceSession> session3_4;
sp<device::V3_5::ICameraDeviceSession> session3_5;
+ sp<device::V3_6::ICameraDeviceSession> session3_6;
sp<device::V3_2::ICameraDevice> cameraDevice;
sp<device::V3_5::ICameraDevice> cameraDevice3_5;
openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMeta /*out*/,
&cameraDevice /*out*/);
- castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+ castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
castDevice(cameraDevice, deviceVersion, &cameraDevice3_5);
// Check if camera support depth only
@@ -4613,6 +4661,207 @@
}
}
+// Verify camera offline session behavior
+TEST_P(CameraHidlTest, switchToOffline) {
+ hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
+ AvailableStream threshold = {kMaxStillWidth, kMaxStillHeight,
+ static_cast<int32_t>(PixelFormat::BLOB)};
+ uint64_t bufferId = 1;
+ uint32_t frameNumber = 1;
+ ::android::hardware::hidl_vec<uint8_t> settings;
+
+ for (const auto& name : cameraDeviceNames) {
+ int deviceVersion = getCameraDeviceVersion(name, mProviderType);
+ if (deviceVersion == CAMERA_DEVICE_API_VERSION_1_0) {
+ continue;
+ } else if (deviceVersion <= 0) {
+ ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
+ ADD_FAILURE();
+ return;
+ }
+
+ camera_metadata_t* staticMetaBuffer;
+ {
+ Return<void> ret;
+ sp<ICameraDeviceSession> session;
+ openEmptyDeviceSession(name, mProvider, &session /*out*/, &staticMetaBuffer /*out*/);
+ ::android::hardware::camera::common::V1_0::helper::CameraMetadata staticMeta(
+ staticMetaBuffer);
+
+ if (isOfflineSessionSupported(staticMetaBuffer) != Status::OK) {
+ ret = session->close();
+ ASSERT_TRUE(ret.isOk());
+ continue;
+ }
+ ret = session->close();
+ ASSERT_TRUE(ret.isOk());
+ }
+
+ bool supportsPartialResults = false;
+ uint32_t partialResultCount = 0;
+ V3_2::Stream stream;
+ V3_6::HalStreamConfiguration halStreamConfig;
+ sp<V3_6::ICameraDeviceSession> session;
+ sp<DeviceCb> cb;
+ uint32_t jpegBufferSize;
+ bool useHalBufManager;
+ configureOfflineStillStream(name, deviceVersion, mProvider, &threshold,
+ &session /*out*/, &stream /*out*/, &halStreamConfig /*out*/,
+ &supportsPartialResults /*out*/, &partialResultCount /*out*/, &cb /*out*/,
+ &jpegBufferSize /*out*/, &useHalBufManager /*out*/);
+
+ auto ret = session->constructDefaultRequestSettings(RequestTemplate::STILL_CAPTURE,
+ [&](auto status, const auto& req) {
+ ASSERT_EQ(Status::OK, status);
+ settings = req; });
+ ASSERT_TRUE(ret.isOk());
+
+ std::shared_ptr<ResultMetadataQueue> resultQueue;
+ auto resultQueueRet =
+ session->getCaptureResultMetadataQueue(
+ [&resultQueue](const auto& descriptor) {
+ resultQueue = std::make_shared<ResultMetadataQueue>(
+ descriptor);
+ if (!resultQueue->isValid() ||
+ resultQueue->availableToWrite() <= 0) {
+ ALOGE("%s: HAL returns empty result metadata fmq,"
+ " not use it", __func__);
+ resultQueue = nullptr;
+ // Don't use the queue onwards.
+ }
+ });
+ ASSERT_TRUE(resultQueueRet.isOk());
+
+ ::android::hardware::camera::common::V1_0::helper::CameraMetadata requestMeta;
+ StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr};
+ hidl_handle buffers[kBurstFrameCount];
+ StreamBuffer outputBuffers[kBurstFrameCount];
+ CaptureRequest requests[kBurstFrameCount];
+ InFlightRequest inflightReqs[kBurstFrameCount];
+ hidl_vec<uint8_t> requestSettings[kBurstFrameCount];
+ auto halStreamConfig3_2 = halStreamConfig.streams[0].v3_4.v3_3.v3_2;
+ for (uint32_t i = 0; i < kBurstFrameCount; i++) {
+ std::unique_lock<std::mutex> l(mLock);
+
+ if (useHalBufManager) {
+ outputBuffers[i] = {halStreamConfig3_2.id, /*bufferId*/ 0,
+ buffers[i], BufferStatus::OK, nullptr, nullptr};
+ } else {
+ // jpeg buffer (w,h) = (blobLen, 1)
+ allocateGraphicBuffer(jpegBufferSize, /*height*/1,
+ android_convertGralloc1To0Usage(halStreamConfig3_2.producerUsage,
+ halStreamConfig3_2.consumerUsage),
+ halStreamConfig3_2.overrideFormat, &buffers[i]);
+ outputBuffers[i] = {halStreamConfig3_2.id, bufferId + i,
+ buffers[i], BufferStatus::OK, nullptr, nullptr};
+ }
+
+ requestMeta.clear();
+ requestMeta.append(reinterpret_cast<camera_metadata_t *> (settings.data()));
+
+ camera_metadata_t *metaBuffer = requestMeta.release();
+ requestSettings[i].setToExternal(reinterpret_cast<uint8_t *> (metaBuffer),
+ get_camera_metadata_size(metaBuffer), true);
+
+ requests[i] = {frameNumber + i, 0 /* fmqSettingsSize */, requestSettings[i],
+ emptyInputBuffer, {outputBuffers[i]}};
+
+ inflightReqs[i] = {1, false, supportsPartialResults, partialResultCount,
+ resultQueue};
+ mInflightMap.add(frameNumber + i, &inflightReqs[i]);
+ }
+
+ Status status = Status::INTERNAL_ERROR;
+ uint32_t numRequestProcessed = 0;
+ hidl_vec<BufferCache> cachesToRemove;
+ hidl_vec<CaptureRequest> burstRequest;
+ burstRequest.setToExternal(requests, kBurstFrameCount);
+ Return<void> returnStatus = session->processCaptureRequest(burstRequest, cachesToRemove,
+ [&status, &numRequestProcessed] (auto s, uint32_t n) {
+ status = s;
+ numRequestProcessed = n;
+ });
+ ASSERT_TRUE(returnStatus.isOk());
+ ASSERT_EQ(Status::OK, status);
+ ASSERT_EQ(numRequestProcessed, kBurstFrameCount);
+
+ hidl_vec<int32_t> offlineStreamIds = {halStreamConfig3_2.id};
+ V3_6::CameraOfflineSessionInfo offlineSessionInfo;
+ sp<device::V3_6::ICameraOfflineSession> offlineSession;
+ returnStatus = session->switchToOffline(offlineStreamIds,
+ [&status, &offlineSessionInfo, &offlineSession] (auto stat, auto info,
+ auto offSession) {
+ status = stat;
+ offlineSessionInfo = info;
+ offlineSession = offSession;
+ });
+ ASSERT_TRUE(returnStatus.isOk());
+
+ if (!halStreamConfig.streams[0].supportOffline) {
+ ASSERT_EQ(status, Status::ILLEGAL_ARGUMENT);
+ ret = session->close();
+ ASSERT_TRUE(ret.isOk());
+ continue;
+ }
+
+ ASSERT_EQ(status, Status::OK);
+ // Hal might be unable to find any requests qualified for offline mode.
+ if (offlineSession == nullptr) {
+ ret = session->close();
+ ASSERT_TRUE(ret.isOk());
+ continue;
+ }
+
+ ASSERT_EQ(offlineSessionInfo.offlineStreams.size(), 1u);
+ ASSERT_EQ(offlineSessionInfo.offlineStreams[0].id, halStreamConfig3_2.id);
+ ASSERT_NE(offlineSessionInfo.offlineRequests.size(), 0u);
+
+ // close device session to make sure offline session does not rely on it
+ ret = session->close();
+ ASSERT_TRUE(ret.isOk());
+
+ std::shared_ptr<ResultMetadataQueue> offlineResultQueue;
+ auto offlineResultQueueRet =
+ offlineSession->getCaptureResultMetadataQueue(
+ [&offlineResultQueue](const auto& descriptor) {
+ offlineResultQueue = std::make_shared<ResultMetadataQueue>(
+ descriptor);
+ if (!offlineResultQueue->isValid() ||
+ offlineResultQueue->availableToWrite() <= 0) {
+ ALOGE("%s: offline session returns empty result metadata fmq,"
+ " not use it", __func__);
+ offlineResultQueue = nullptr;
+ // Don't use the queue onwards.
+ }
+ });
+ ASSERT_TRUE(offlineResultQueueRet.isOk());
+
+ updateInflightResultQueue(offlineResultQueue);
+
+ ret = offlineSession->setCallback(cb);
+ ASSERT_TRUE(ret.isOk());
+
+ for (size_t i = 0; i < kBurstFrameCount; i++) {
+ std::unique_lock<std::mutex> l(mLock);
+ while (!inflightReqs[i].errorCodeValid && ((0 < inflightReqs[i].numBuffersLeft) ||
+ (!inflightReqs[i].haveResultMetadata))) {
+ auto timeout = std::chrono::system_clock::now() +
+ std::chrono::seconds(kStreamBufferTimeoutSec);
+ ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout));
+ }
+
+ ASSERT_FALSE(inflightReqs[i].errorCodeValid);
+ ASSERT_NE(inflightReqs[i].resultOutputBuffers.size(), 0u);
+ ASSERT_EQ(stream.id, inflightReqs[i].resultOutputBuffers[0].streamId);
+ ASSERT_FALSE(inflightReqs[i].collectedResult.isEmpty());
+ }
+
+
+ ret = offlineSession->close();
+ ASSERT_TRUE(ret.isOk());
+ }
+}
+
// Check whether an invalid capture request with missing output buffers
// will be reported correctly.
TEST_P(CameraHidlTest, processCaptureRequestInvalidBuffer) {
@@ -4972,6 +5221,30 @@
return ret;
}
+// Check if the camera device has logical multi-camera capability.
+Status CameraHidlTest::isOfflineSessionSupported(const camera_metadata_t *staticMeta) {
+ Status ret = Status::METHOD_NOT_SUPPORTED;
+ if (nullptr == staticMeta) {
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ camera_metadata_ro_entry entry;
+ int rc = find_camera_metadata_ro_entry(staticMeta,
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
+ if (0 != rc) {
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ for (size_t i = 0; i < entry.count; i++) {
+ if (ANDROID_REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING == entry.data.u8[i]) {
+ ret = Status::OK;
+ break;
+ }
+ }
+
+ return ret;
+}
+
// Generate a list of physical camera ids backing a logical multi-camera.
Status CameraHidlTest::getPhysicalCameraIds(const camera_metadata_t *staticMeta,
std::unordered_set<std::string> *physicalIds) {
@@ -5330,7 +5603,8 @@
*outCb = cb;
sp<device::V3_3::ICameraDeviceSession> session3_3;
- castSession(session, deviceVersion, &session3_3, session3_4, session3_5);
+ sp<device::V3_6::ICameraDeviceSession> session3_6;
+ castSession(session, deviceVersion, &session3_3, session3_4, session3_5, &session3_6);
ASSERT_NE(nullptr, (*session3_4).get());
*useHalBufManager = false;
@@ -5423,6 +5697,144 @@
ASSERT_TRUE(ret.isOk());
}
+// Configure preview stream with possible offline session support
+void CameraHidlTest::configureOfflineStillStream(const std::string &name,
+ int32_t deviceVersion,
+ sp<ICameraProvider> provider,
+ const AvailableStream *threshold,
+ sp<device::V3_6::ICameraDeviceSession> *session/*out*/,
+ V3_2::Stream *stream /*out*/,
+ device::V3_6::HalStreamConfiguration *halStreamConfig /*out*/,
+ bool *supportsPartialResults /*out*/,
+ uint32_t *partialResultCount /*out*/,
+ sp<DeviceCb> *outCb /*out*/,
+ uint32_t *jpegBufferSize /*out*/,
+ bool *useHalBufManager /*out*/) {
+ ASSERT_NE(nullptr, session);
+ ASSERT_NE(nullptr, halStreamConfig);
+ ASSERT_NE(nullptr, stream);
+ ASSERT_NE(nullptr, supportsPartialResults);
+ ASSERT_NE(nullptr, partialResultCount);
+ ASSERT_NE(nullptr, outCb);
+ ASSERT_NE(nullptr, jpegBufferSize);
+ ASSERT_NE(nullptr, useHalBufManager);
+
+ std::vector<AvailableStream> outputStreams;
+ ::android::sp<device::V3_6::ICameraDevice> cameraDevice;
+ ALOGI("configureStreams: Testing camera device %s", name.c_str());
+ Return<void> ret;
+ ret = provider->getCameraDeviceInterface_V3_x(
+ name,
+ [&cameraDevice](auto status, const auto& device) {
+ ALOGI("getCameraDeviceInterface_V3_x returns status:%d",
+ (int)status);
+ ASSERT_EQ(Status::OK, status);
+ ASSERT_NE(device, nullptr);
+ auto castResult = device::V3_6::ICameraDevice::castFrom(device);
+ ASSERT_TRUE(castResult.isOk());
+ cameraDevice = castResult;
+ });
+ ASSERT_TRUE(ret.isOk());
+
+ camera_metadata_t *staticMeta;
+ ret = cameraDevice->getCameraCharacteristics([&] (Status s,
+ CameraMetadata metadata) {
+ ASSERT_EQ(Status::OK, s);
+ staticMeta = clone_camera_metadata(
+ reinterpret_cast<const camera_metadata_t*>(metadata.data()));
+ ASSERT_NE(nullptr, staticMeta);
+ });
+ ASSERT_TRUE(ret.isOk());
+
+ camera_metadata_ro_entry entry;
+ auto status = find_camera_metadata_ro_entry(staticMeta,
+ ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &entry);
+ if ((0 == status) && (entry.count > 0)) {
+ *partialResultCount = entry.data.i32[0];
+ *supportsPartialResults = (*partialResultCount > 1);
+ }
+
+ *useHalBufManager = false;
+ status = find_camera_metadata_ro_entry(staticMeta,
+ ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &entry);
+ if ((0 == status) && (entry.count == 1)) {
+ *useHalBufManager = (entry.data.u8[0] ==
+ ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
+ }
+
+ auto st = getJpegBufferSize(staticMeta, jpegBufferSize);
+ ASSERT_EQ(st, Status::OK);
+
+ sp<DeviceCb> cb = new DeviceCb(this, deviceVersion, staticMeta);
+ ret = cameraDevice->open(cb, [&session](auto status, const auto& newSession) {
+ ALOGI("device::open returns status:%d", (int)status);
+ ASSERT_EQ(Status::OK, status);
+ ASSERT_NE(newSession, nullptr);
+ auto castResult = device::V3_6::ICameraDeviceSession::castFrom(newSession);
+ ASSERT_TRUE(castResult.isOk());
+ *session = castResult;
+ });
+ ASSERT_TRUE(ret.isOk());
+ *outCb = cb;
+
+ outputStreams.clear();
+ auto rc = getAvailableOutputStreams(staticMeta,
+ outputStreams, threshold);
+ size_t idx = 0;
+ int currLargest = outputStreams[0].width * outputStreams[0].height;
+ for (size_t i = 0; i < outputStreams.size(); i++) {
+ int area = outputStreams[i].width * outputStreams[i].height;
+ if (area > currLargest) {
+ idx = i;
+ currLargest = area;
+ }
+ }
+ free_camera_metadata(staticMeta);
+ ASSERT_EQ(Status::OK, rc);
+ ASSERT_FALSE(outputStreams.empty());
+
+ V3_2::DataspaceFlags dataspaceFlag = 0;
+ switch (static_cast<PixelFormat>(outputStreams[idx].format)) {
+ case PixelFormat::BLOB:
+ dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::V0_JFIF);
+ break;
+ case PixelFormat::Y16:
+ dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::DEPTH);
+ break;
+ default:
+ dataspaceFlag = static_cast<V3_2::DataspaceFlags>(Dataspace::UNKNOWN);
+ }
+
+ ::android::hardware::hidl_vec<V3_4::Stream> streams3_4(/*size*/1);
+ V3_4::Stream stream3_4 = {{ 0 /*streamId*/, StreamType::OUTPUT,
+ static_cast<uint32_t> (outputStreams[idx].width),
+ static_cast<uint32_t> (outputStreams[idx].height),
+ static_cast<PixelFormat> (outputStreams[idx].format),
+ GRALLOC1_CONSUMER_USAGE_CPU_READ, dataspaceFlag, StreamRotation::ROTATION_0},
+ nullptr /*physicalId*/, /*bufferSize*/ *jpegBufferSize};
+ streams3_4[0] = stream3_4;
+
+ ::android::hardware::camera::device::V3_4::StreamConfiguration config3_4;
+ ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
+ config3_4 = {streams3_4, StreamConfigurationMode::NORMAL_MODE, {}};
+
+ config3_5.v3_4 = config3_4;
+ config3_5.streamConfigCounter = 0;
+ ret = (*session)->configureStreams_3_6(config3_5,
+ [&] (Status s, device::V3_6::HalStreamConfiguration halConfig) {
+ ASSERT_EQ(Status::OK, s);
+ *halStreamConfig = halConfig;
+
+ if (*useHalBufManager) {
+ hidl_vec<V3_2::HalStream> halStreams3_2(1);
+ halStreams3_2[0] = halConfig.streams[0].v3_4.v3_3.v3_2;
+ cb->setCurrentStreamConfig(streams3_4, halStreams3_2);
+ }
+ });
+ *stream = streams3_4[0].v3_2;
+ ASSERT_TRUE(ret.isOk());
+}
+
bool CameraHidlTest::isDepthOnly(camera_metadata_t* staticMeta) {
camera_metadata_ro_entry scalarEntry;
camera_metadata_ro_entry depthEntry;
@@ -5454,6 +5866,14 @@
return false;
}
+void CameraHidlTest::updateInflightResultQueue(std::shared_ptr<ResultMetadataQueue> resultQueue) {
+ std::unique_lock<std::mutex> l(mLock);
+ for (size_t i = 0; i < mInflightMap.size(); i++) {
+ auto& req = mInflightMap.editValueAt(i);
+ req->resultQueue = resultQueue;
+ }
+}
+
// Open a device session and configure a preview stream.
void CameraHidlTest::configurePreviewStream(const std::string &name, int32_t deviceVersion,
sp<ICameraProvider> provider,
@@ -5522,7 +5942,8 @@
sp<device::V3_3::ICameraDeviceSession> session3_3;
sp<device::V3_4::ICameraDeviceSession> session3_4;
sp<device::V3_5::ICameraDeviceSession> session3_5;
- castSession(*session, deviceVersion, &session3_3, &session3_4, &session3_5);
+ sp<device::V3_6::ICameraDeviceSession> session3_6;
+ castSession(*session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
*useHalBufManager = false;
status = find_camera_metadata_ro_entry(staticMeta,
@@ -5657,12 +6078,20 @@
void CameraHidlTest::castSession(const sp<ICameraDeviceSession> &session, int32_t deviceVersion,
sp<device::V3_3::ICameraDeviceSession> *session3_3 /*out*/,
sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/,
- sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/) {
+ sp<device::V3_5::ICameraDeviceSession> *session3_5 /*out*/,
+ sp<device::V3_6::ICameraDeviceSession> *session3_6 /*out*/) {
ASSERT_NE(nullptr, session3_3);
ASSERT_NE(nullptr, session3_4);
ASSERT_NE(nullptr, session3_5);
+ ASSERT_NE(nullptr, session3_6);
switch (deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_3_6: {
+ auto castResult = device::V3_6::ICameraDeviceSession::castFrom(session);
+ ASSERT_TRUE(castResult.isOk());
+ *session3_6 = castResult;
+ }
+ [[fallthrough]];
case CAMERA_DEVICE_API_VERSION_3_5: {
auto castResult = device::V3_5::ICameraDeviceSession::castFrom(session);
ASSERT_TRUE(castResult.isOk());
@@ -6261,7 +6690,8 @@
sp<device::V3_3::ICameraDeviceSession> session3_3;
sp<device::V3_4::ICameraDeviceSession> session3_4;
sp<device::V3_5::ICameraDeviceSession> session3_5;
- castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5);
+ sp<device::V3_6::ICameraDeviceSession> session3_6;
+ castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6);
ASSERT_NE(nullptr, session3_5.get());
hidl_vec<int32_t> streamIds(1);
diff --git a/camera/provider/2.5/default/Android.bp b/camera/provider/2.5/default/Android.bp
index 4563362..9ddf651 100644
--- a/camera/provider/2.5/default/Android.bp
+++ b/camera/provider/2.5/default/Android.bp
@@ -52,6 +52,8 @@
"android.hardware.camera.provider@2.4-external",
"android.hardware.camera.provider@2.5",
"android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
"camera.device@3.3-impl",
@@ -72,7 +74,8 @@
],
header_libs: [
"camera.device@3.4-external-impl_headers",
- "camera.device@3.5-external-impl_headers"
+ "camera.device@3.5-external-impl_headers",
+ "camera.device@3.6-external-impl_headers"
],
export_include_dirs: ["."],
}
@@ -165,7 +168,10 @@
"android.hardware.camera.provider@2.5",
"android.hardware.camera.provider@2.5-external",
"android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
"libbinder",
+ "libcamera_metadata",
"libhidlbase",
"liblog",
"libtinyxml2",
@@ -179,5 +185,6 @@
"camera.device@3.4-impl_headers",
"camera.device@3.5-external-impl_headers",
"camera.device@3.5-impl_headers",
+ "camera.device@3.6-external-impl_headers",
],
}
diff --git a/camera/provider/2.6/ICameraProvider.hal b/camera/provider/2.6/ICameraProvider.hal
index 0948db6..9c46ba0 100644
--- a/camera/provider/2.6/ICameraProvider.hal
+++ b/camera/provider/2.6/ICameraProvider.hal
@@ -38,20 +38,29 @@
* streaming concurrently with the other camera ids in the combination.
*
* Target 1 Target 2
- * ---------------------------------------------
- * | Type | Size | Type | Size |
- * ---------------------------------------------
- * | YUV | 1280 X 720 | |
- * ---------------------------------------------
- * | PRIV | 1280 X 720 | |
- * ---------------------------------------------
- * | YUV | 1280 X 720 | YUV |1280 X 720|
- * ---------------------------------------------
- * | PRIV | 1280 X 720 | PRIV |1280 X 720|
- * ---------------------------------------------
- * | PRIV | 1280 X 720 | YUV |1280 X 720|
- * ---------------------------------------------
-
+ * -----------------------------------------------------
+ * | Type | Size | Type | Size |
+ * -----------------------------------------------------
+ * | YUV | s1440p | |
+ * -----------------------------------------------------
+ * | JPEG | s1440p | |
+ * -----------------------------------------------------
+ * | PRIV | s1440p | |
+ * -----------------------------------------------------
+ * | YUV / PRIV | s720p | YUV / PRIV | s1440p |
+ * -----------------------------------------------------
+ * | YUV / PRIV | s720p | JPEG | s1440p |
+ * -----------------------------------------------------
+ *
+ * where:
+ * s720p - min (max output resolution for the given format, 1280 X 720)
+ * s1440p - min (max output resolution for the given format, 1920 X 1440)
+ *
+ * The camera framework must call this method whenever it gets a
+ * cameraDeviceStatusChange callback adding a new camera device or removing
+ * a camera device known to it. This is so that the camera framework can get new combinations
+ * of camera ids that can stream concurrently, that might have potentially appeared.
+ *
* @return status Status code for the operation
* @return cameraIds a list of camera id combinations that support
* concurrent stream configurations with the minimum guarantees
diff --git a/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp b/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp
index 7e5a61a..1b5797b 100644
--- a/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp
+++ b/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp
@@ -16,8 +16,6 @@
#define LOG_TAG "mediacas_hidl_hal_test"
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
#include <android-base/logging.h>
#include <android/hardware/cas/1.0/IDescramblerBase.h>
#include <android/hardware/cas/1.0/types.h>
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index b9d3001..f1db89d 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -45,6 +45,7 @@
<interface>
<name>IEvsEnumerator</name>
<instance>default</instance>
+ <regex-instance>[a-z]+/[0-9]+</regex-instance>
</interface>
</hal>
<hal format="aidl" optional="true">
@@ -152,7 +153,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.contexthub</name>
- <version>1.0</version>
+ <version>1.0-1</version>
<interface>
<name>IContexthub</name>
<instance>default</instance>
@@ -160,7 +161,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.drm</name>
- <version>1.0-3</version>
+ <version>1.3</version>
<interface>
<name>ICryptoFactory</name>
<regex-instance>.*</regex-instance>
@@ -279,14 +280,6 @@
<instance>strongbox</instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.light</name>
- <version>2.0</version>
- <interface>
- <name>ILight</name>
- <instance>default</instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.light</name>
<interface>
@@ -347,7 +340,7 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="aidl" optional="true">
+ <hal format="aidl" optional="false">
<name>android.hardware.power</name>
<interface>
<name>IPower</name>
@@ -382,7 +375,10 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.radio.config</name>
- <version>1.3</version>
+ <!--
+ See compatibility_matrix.4.xml on versioning of radio config HAL.
+ -->
+ <version>1.1</version>
<interface>
<name>IRadioConfig</name>
<instance>default</instance>
@@ -415,7 +411,7 @@
<hal format="hidl" optional="true">
<name>android.hardware.sensors</name>
<version>1.0</version>
- <version>2.0</version>
+ <version>2.0-1</version>
<interface>
<name>ISensors</name>
<instance>default</instance>
@@ -494,14 +490,6 @@
</interface>
</hal>
<hal format="hidl" optional="true">
- <name>android.hardware.vibrator</name>
- <version>1.0-3</version>
- <interface>
- <name>IVibrator</name>
- <instance>default</instance>
- </interface>
- </hal>
- <hal format="hidl" optional="true">
<name>android.hardware.vr</name>
<version>1.0</version>
<interface>
diff --git a/contexthub/1.0/vts/functional/Android.bp b/contexthub/1.0/vts/functional/Android.bp
index 9e99c33..d51c966 100644
--- a/contexthub/1.0/vts/functional/Android.bp
+++ b/contexthub/1.0/vts/functional/Android.bp
@@ -18,7 +18,10 @@
name: "VtsHalContexthubV1_0TargetTest",
defaults: ["VtsHalTargetTestDefaults"],
srcs: ["VtsHalContexthubV1_0TargetTest.cpp"],
- static_libs: ["android.hardware.contexthub@1.0"],
+ static_libs: [
+ "android.hardware.contexthub@1.0",
+ "VtsHalContexthubUtils",
+ ],
test_suites: [
"general-tests",
"vts-core",
diff --git a/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp b/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp
index a1d173b..ada232b 100644
--- a/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp
+++ b/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp
@@ -16,6 +16,10 @@
#define LOG_TAG "contexthub_hidl_hal_test"
+#include "ContexthubCallbackBase.h"
+#include "ContexthubHidlTestBase.h"
+#include "VtsHalContexthubUtils.h"
+
#include <android-base/logging.h>
#include <android/hardware/contexthub/1.0/IContexthub.h>
#include <android/hardware/contexthub/1.0/IContexthubCallback.h>
@@ -23,17 +27,17 @@
#include <android/log.h>
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
#include <log/log.h>
#include <cinttypes>
#include <future>
#include <utility>
-using ::android::hardware::Return;
-using ::android::hardware::Void;
+using ::android::sp;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
using ::android::hardware::contexthub::V1_0::AsyncEventType;
using ::android::hardware::contexthub::V1_0::ContextHub;
using ::android::hardware::contexthub::V1_0::ContextHubMsg;
@@ -43,10 +47,11 @@
using ::android::hardware::contexthub::V1_0::NanoAppBinary;
using ::android::hardware::contexthub::V1_0::Result;
using ::android::hardware::contexthub::V1_0::TransactionResult;
-using ::android::sp;
-
-#define ASSERT_OK(result) ASSERT_EQ(result, Result::OK)
-#define EXPECT_OK(result) EXPECT_EQ(result, Result::OK)
+using ::android::hardware::contexthub::vts_utils::asBaseType;
+using ::android::hardware::contexthub::vts_utils::ContexthubCallbackBase;
+using ::android::hardware::contexthub::vts_utils::ContexthubHidlTestBase;
+using ::android::hardware::contexthub::vts_utils::getHalAndHubIdList;
+using ::android::hardware::contexthub::vts_utils::getHubsSync;
namespace {
@@ -54,132 +59,37 @@
// app ID is reserved and must never appear in the list of loaded apps.
constexpr uint64_t kNonExistentAppId = 0x476f6f6754555555;
-// Helper that does explicit conversion of an enum class to its underlying/base
-// type. Useful for stream output of enum values.
-template<typename EnumType>
-constexpr typename std::underlying_type<EnumType>::type asBaseType(
- EnumType value) {
- return static_cast<typename std::underlying_type<EnumType>::type>(value);
-}
+const std::vector<std::tuple<std::string, std::string>> kTestParameters =
+ getHalAndHubIdList<IContexthub>();
-// Synchronously queries IContexthub::getHubs() and returns the result
-hidl_vec<ContextHub> getHubsSync(sp<IContexthub> hubApi) {
- hidl_vec<ContextHub> hubList;
- std::promise<void> barrier;
-
- hubApi->getHubs([&hubList, &barrier](const hidl_vec<ContextHub>& hubs) {
- hubList = hubs;
- barrier.set_value();
- });
- barrier.get_future().wait_for(std::chrono::seconds(1));
-
- return hubList;
-}
-
-// Gets a list of valid hub IDs in the system
-std::vector<std::string> getHubIds(const std::string& service_name) {
- std::vector<std::string> hubIds;
-
- sp<IContexthub> hubApi = IContexthub::getService(service_name);
-
- if (hubApi != nullptr) {
- for (const ContextHub& hub : getHubsSync(hubApi)) {
- hubIds.push_back(std::to_string(hub.hubId));
- }
- }
-
- ALOGD("Running tests against all %zu reported hubs for service %s", hubIds.size(),
- service_name.c_str());
- return hubIds;
-}
-
-// Test fixture parameterized by hub ID, initializes the HAL and makes the context hub API handle
-// available.
-class ContexthubHidlTest : public ::testing::TestWithParam<std::tuple<std::string, std::string>> {
- public:
- virtual void SetUp() override {
- hubApi = IContexthub::getService(std::get<0>(GetParam()));
- ASSERT_NE(hubApi, nullptr);
-
- // getHubs() must be called at least once for proper initialization of the
- // HAL implementation
- getHubsSync(hubApi);
- }
-
- uint32_t getHubId() { return std::stoi(std::get<1>(GetParam())); }
-
- Result registerCallback(sp<IContexthubCallback> cb) {
- Result result = hubApi->registerCallback(getHubId(), cb);
- ALOGD("Registered callback, result %" PRIu32, result);
- return result;
- }
-
- sp<IContexthub> hubApi;
-};
-
-// Base callback implementation that just logs all callbacks by default
-class ContexthubCallbackBase : public IContexthubCallback {
- public:
- virtual Return<void> handleClientMsg(const ContextHubMsg& /*msg*/) override {
- ALOGD("Got client message callback");
- return Void();
- }
-
- virtual Return<void> handleTxnResult(
- uint32_t txnId, TransactionResult result) override {
- ALOGD("Got transaction result callback for txnId %" PRIu32 " with result %"
- PRId32, txnId, result);
- return Void();
- }
-
- virtual Return<void> handleHubEvent(AsyncEventType evt) override {
- ALOGD("Got hub event callback for event type %" PRIu32, evt);
- return Void();
- }
-
- virtual Return<void> handleAppAbort(uint64_t appId, uint32_t abortCode)
- override {
- ALOGD("Got app abort notification for appId 0x%" PRIx64 " with abort code "
- "0x%" PRIx32, appId, abortCode);
- return Void();
- }
-
- virtual Return<void> handleAppsInfo(const hidl_vec<HubAppInfo>& /*appInfo*/)
- override {
- ALOGD("Got app info callback");
- return Void();
- }
-};
+class ContexthubHidlTest : public ContexthubHidlTestBase<IContexthub> {};
// Wait for a callback to occur (signaled by the given future) up to the
// provided timeout. If the future is invalid or the callback does not come
// within the given time, returns false.
-template<class ReturnType>
-bool waitForCallback(
- std::future<ReturnType> future,
- ReturnType *result,
- std::chrono::milliseconds timeout = std::chrono::seconds(5)) {
- auto expiration = std::chrono::system_clock::now() + timeout;
+template <class ReturnType>
+bool waitForCallback(std::future<ReturnType> future, ReturnType* result,
+ std::chrono::milliseconds timeout = std::chrono::seconds(5)) {
+ auto expiration = std::chrono::system_clock::now() + timeout;
- EXPECT_NE(result, nullptr);
- EXPECT_TRUE(future.valid());
- if (result != nullptr && future.valid()) {
- std::future_status status = future.wait_until(expiration);
- EXPECT_NE(status, std::future_status::timeout)
- << "Timed out waiting for callback";
+ EXPECT_NE(result, nullptr);
+ EXPECT_TRUE(future.valid());
+ if (result != nullptr && future.valid()) {
+ std::future_status status = future.wait_until(expiration);
+ EXPECT_NE(status, std::future_status::timeout) << "Timed out waiting for callback";
- if (status == std::future_status::ready) {
- *result = future.get();
- return true;
+ if (status == std::future_status::ready) {
+ *result = future.get();
+ return true;
+ }
}
- }
- return false;
+ return false;
}
// Ensures that the metadata reported in getHubs() is sane
TEST_P(ContexthubHidlTest, TestGetHubs) {
- hidl_vec<ContextHub> hubs = getHubsSync(hubApi);
+ hidl_vec<ContextHub> hubs = getHubsSync(hubApi.get());
ALOGD("System reports %zu hubs", hubs.size());
for (const ContextHub& hub : hubs) {
@@ -199,189 +109,158 @@
}
TEST_P(ContexthubHidlTest, TestRegisterCallback) {
- ALOGD("TestRegisterCallback called, hubId %" PRIu32, getHubId());
- ASSERT_OK(registerCallback(new ContexthubCallbackBase()));
+ ALOGD("TestRegisterCallback called, hubId %" PRIu32, getHubId());
+ ASSERT_OK(registerCallback(new ContexthubCallbackBase()));
}
TEST_P(ContexthubHidlTest, TestRegisterNullCallback) {
- ALOGD("TestRegisterNullCallback called, hubId %" PRIu32, getHubId());
- ASSERT_OK(registerCallback(nullptr));
+ ALOGD("TestRegisterNullCallback called, hubId %" PRIu32, getHubId());
+ ASSERT_OK(registerCallback(nullptr));
}
// Helper callback that puts the async appInfo callback data into a promise
class QueryAppsCallback : public ContexthubCallbackBase {
- public:
- virtual Return<void> handleAppsInfo(const hidl_vec<HubAppInfo>& appInfo)
- override {
- ALOGD("Got app info callback with %zu apps", appInfo.size());
- promise.set_value(appInfo);
- return Void();
- }
+ public:
+ virtual Return<void> handleAppsInfo(const hidl_vec<HubAppInfo>& appInfo) override {
+ ALOGD("Got app info callback with %zu apps", appInfo.size());
+ promise.set_value(appInfo);
+ return Void();
+ }
- std::promise<hidl_vec<HubAppInfo>> promise;
+ std::promise<hidl_vec<HubAppInfo>> promise;
};
// Calls queryApps() and checks the returned metadata
TEST_P(ContexthubHidlTest, TestQueryApps) {
- ALOGD("TestQueryApps called, hubId %u", getHubId());
- sp<QueryAppsCallback> cb = new QueryAppsCallback();
- ASSERT_OK(registerCallback(cb));
+ ALOGD("TestQueryApps called, hubId %u", getHubId());
+ sp<QueryAppsCallback> cb = new QueryAppsCallback();
+ ASSERT_OK(registerCallback(cb));
- Result result = hubApi->queryApps(getHubId());
- ASSERT_OK(result);
+ Result result = hubApi->queryApps(getHubId());
+ ASSERT_OK(result);
- ALOGD("Waiting for app info callback");
- hidl_vec<HubAppInfo> appList;
- ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &appList));
- for (const HubAppInfo &appInfo : appList) {
- EXPECT_NE(appInfo.appId, UINT64_C(0));
- EXPECT_NE(appInfo.appId, kNonExistentAppId);
- }
+ ALOGD("Waiting for app info callback");
+ hidl_vec<HubAppInfo> appList;
+ ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &appList));
+ for (const HubAppInfo& appInfo : appList) {
+ EXPECT_NE(appInfo.appId, UINT64_C(0));
+ EXPECT_NE(appInfo.appId, kNonExistentAppId);
+ }
}
// Helper callback that puts the TransactionResult for the expectedTxnId into a
// promise
class TxnResultCallback : public ContexthubCallbackBase {
- public:
- virtual Return<void> handleTxnResult(
- uint32_t txnId, TransactionResult result) override {
- ALOGD("Got transaction result callback for txnId %" PRIu32 " (expecting %"
- PRIu32 ") with result %" PRId32, txnId, expectedTxnId, result);
- if (txnId == expectedTxnId) {
- promise.set_value(result);
+ public:
+ virtual Return<void> handleTxnResult(uint32_t txnId, TransactionResult result) override {
+ ALOGD("Got transaction result callback for txnId %" PRIu32 " (expecting %" PRIu32
+ ") with result %" PRId32,
+ txnId, expectedTxnId, result);
+ if (txnId == expectedTxnId) {
+ promise.set_value(result);
+ }
+ return Void();
}
- return Void();
- }
- uint32_t expectedTxnId = 0;
- std::promise<TransactionResult> promise;
+ uint32_t expectedTxnId = 0;
+ std::promise<TransactionResult> promise;
};
// Parameterized fixture that sets the callback to TxnResultCallback
class ContexthubTxnTest : public ContexthubHidlTest {
- public:
- virtual void SetUp() override {
- ContexthubHidlTest::SetUp();
- ASSERT_OK(registerCallback(cb));
- }
+ public:
+ virtual void SetUp() override {
+ ContexthubHidlTest::SetUp();
+ ASSERT_OK(registerCallback(cb));
+ }
- sp<TxnResultCallback> cb = new TxnResultCallback();
+ sp<TxnResultCallback> cb = new TxnResultCallback();
};
-
// Checks cases where the hub implementation is expected to return an error, but
// that error can be returned either synchronously or in the asynchronous
// transaction callback. Returns an AssertionResult that can be used in
// ASSERT/EXPECT_TRUE. Allows checking the sync result against 1 additional
// allowed error code apart from OK and TRANSACTION_FAILED, which are always
// allowed.
-::testing::AssertionResult checkFailureSyncOrAsync(
- Result result, Result allowedSyncResult,
- std::future<TransactionResult>&& future) {
- if (result == Result::OK) {
- // No error reported synchronously - this is OK, but then we should get an
- // async callback with a failure status
- TransactionResult asyncResult;
- if (!waitForCallback(std::forward<std::future<TransactionResult>>(future),
- &asyncResult)) {
- return ::testing::AssertionFailure()
- << "Got successful sync result, then failed to receive async cb";
- } else if (asyncResult == TransactionResult::SUCCESS) {
- return ::testing::AssertionFailure()
- << "Got successful sync result, then unexpected successful async "
- "result";
+::testing::AssertionResult checkFailureSyncOrAsync(Result result, Result allowedSyncResult,
+ std::future<TransactionResult>&& future) {
+ if (result == Result::OK) {
+ // No error reported synchronously - this is OK, but then we should get an
+ // async callback with a failure status
+ TransactionResult asyncResult;
+ if (!waitForCallback(std::forward<std::future<TransactionResult>>(future), &asyncResult)) {
+ return ::testing::AssertionFailure()
+ << "Got successful sync result, then failed to receive async cb";
+ } else if (asyncResult == TransactionResult::SUCCESS) {
+ return ::testing::AssertionFailure()
+ << "Got successful sync result, then unexpected successful async "
+ "result";
+ }
+ } else if (result != allowedSyncResult && result != Result::TRANSACTION_FAILED) {
+ return ::testing::AssertionFailure()
+ << "Got sync result " << asBaseType(result) << ", expected TRANSACTION_FAILED or "
+ << asBaseType(allowedSyncResult);
}
- } else if (result != allowedSyncResult &&
- result != Result::TRANSACTION_FAILED) {
- return ::testing::AssertionFailure() << "Got sync result "
- << asBaseType(result) << ", expected TRANSACTION_FAILED or "
- << asBaseType(allowedSyncResult);
- }
- return ::testing::AssertionSuccess();
+ return ::testing::AssertionSuccess();
}
TEST_P(ContexthubTxnTest, TestSendMessageToNonExistentNanoApp) {
- ContextHubMsg msg;
- msg.appName = kNonExistentAppId;
- msg.msgType = 1;
- msg.msg.resize(4);
- std::fill(msg.msg.begin(), msg.msg.end(), 0);
+ ContextHubMsg msg;
+ msg.appName = kNonExistentAppId;
+ msg.msgType = 1;
+ msg.msg.resize(4);
+ std::fill(msg.msg.begin(), msg.msg.end(), 0);
- ALOGD("Sending message to non-existent nanoapp");
- Result result = hubApi->sendMessageToHub(getHubId(), msg);
- if (result != Result::OK &&
- result != Result::BAD_PARAMS &&
- result != Result::TRANSACTION_FAILED) {
- FAIL() << "Got result " << asBaseType(result) << ", expected OK, BAD_PARAMS"
- << ", or TRANSACTION_FAILED";
- }
+ ALOGD("Sending message to non-existent nanoapp");
+ Result result = hubApi->sendMessageToHub(getHubId(), msg);
+ if (result != Result::OK && result != Result::BAD_PARAMS &&
+ result != Result::TRANSACTION_FAILED) {
+ FAIL() << "Got result " << asBaseType(result) << ", expected OK, BAD_PARAMS"
+ << ", or TRANSACTION_FAILED";
+ }
}
TEST_P(ContexthubTxnTest, TestLoadEmptyNanoApp) {
- cb->expectedTxnId = 0123;
- NanoAppBinary emptyApp;
+ cb->expectedTxnId = 0123;
+ NanoAppBinary emptyApp;
- emptyApp.appId = kNonExistentAppId;
- emptyApp.appVersion = 1;
- emptyApp.flags = 0;
- emptyApp.targetChreApiMajorVersion = 1;
- emptyApp.targetChreApiMinorVersion = 0;
+ emptyApp.appId = kNonExistentAppId;
+ emptyApp.appVersion = 1;
+ emptyApp.flags = 0;
+ emptyApp.targetChreApiMajorVersion = 1;
+ emptyApp.targetChreApiMinorVersion = 0;
- ALOGD("Loading empty nanoapp");
- Result result = hubApi->loadNanoApp(getHubId(), emptyApp, cb->expectedTxnId);
- EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS,
- cb->promise.get_future()));
+ ALOGD("Loading empty nanoapp");
+ Result result = hubApi->loadNanoApp(getHubId(), emptyApp, cb->expectedTxnId);
+ EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS, cb->promise.get_future()));
}
TEST_P(ContexthubTxnTest, TestUnloadNonexistentNanoApp) {
- cb->expectedTxnId = 1234;
+ cb->expectedTxnId = 1234;
- ALOGD("Unloading nonexistent nanoapp");
- Result result = hubApi->unloadNanoApp(getHubId(), kNonExistentAppId,
- cb->expectedTxnId);
- EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS,
- cb->promise.get_future()));
+ ALOGD("Unloading nonexistent nanoapp");
+ Result result = hubApi->unloadNanoApp(getHubId(), kNonExistentAppId, cb->expectedTxnId);
+ EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS, cb->promise.get_future()));
}
TEST_P(ContexthubTxnTest, TestEnableNonexistentNanoApp) {
- cb->expectedTxnId = 2345;
+ cb->expectedTxnId = 2345;
- ALOGD("Enabling nonexistent nanoapp");
- Result result = hubApi->enableNanoApp(getHubId(), kNonExistentAppId,
- cb->expectedTxnId);
- EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS,
- cb->promise.get_future()));
+ ALOGD("Enabling nonexistent nanoapp");
+ Result result = hubApi->enableNanoApp(getHubId(), kNonExistentAppId, cb->expectedTxnId);
+ EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS, cb->promise.get_future()));
}
TEST_P(ContexthubTxnTest, TestDisableNonexistentNanoApp) {
- cb->expectedTxnId = 3456;
+ cb->expectedTxnId = 3456;
- ALOGD("Disabling nonexistent nanoapp");
- Result result = hubApi->disableNanoApp(getHubId(), kNonExistentAppId,
- cb->expectedTxnId);
- EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS,
- cb->promise.get_future()));
+ ALOGD("Disabling nonexistent nanoapp");
+ Result result = hubApi->disableNanoApp(getHubId(), kNonExistentAppId, cb->expectedTxnId);
+ EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS, cb->promise.get_future()));
}
-// Return the test parameters of a vecter of tuples for all IContexthub services and each of its hub
-// id: <service name of IContexthub, hub id of the IContexthub service>
-static std::vector<std::tuple<std::string, std::string>> get_parameters() {
- std::vector<std::tuple<std::string, std::string>> parameters;
- std::vector<std::string> service_names =
- android::hardware::getAllHalInstanceNames(IContexthub::descriptor);
- for (const std::string& service_name : service_names) {
- std::vector<std::string> ids = getHubIds(service_name);
- for (const std::string& id : ids) {
- parameters.push_back(std::make_tuple(service_name, id));
- }
- }
-
- return parameters;
-}
-
-static std::vector<std::tuple<std::string, std::string>> kTestParameters = get_parameters();
-
INSTANTIATE_TEST_SUITE_P(HubIdSpecificTests, ContexthubHidlTest, testing::ValuesIn(kTestParameters),
android::hardware::PrintInstanceTupleNameToString<>);
diff --git a/contexthub/1.1/Android.bp b/contexthub/1.1/Android.bp
new file mode 100644
index 0000000..649f1db
--- /dev/null
+++ b/contexthub/1.1/Android.bp
@@ -0,0 +1,18 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.contexthub@1.1",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "IContexthub.hal",
+ ],
+ interfaces: [
+ "android.hardware.contexthub@1.0",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/contexthub/1.1/IContexthub.hal b/contexthub/1.1/IContexthub.hal
new file mode 100644
index 0000000..a3b4bd4
--- /dev/null
+++ b/contexthub/1.1/IContexthub.hal
@@ -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.contexthub@1.1;
+
+import @1.0::IContexthub;
+import @1.0::Result;
+
+interface IContexthub extends @1.0::IContexthub {
+ /**
+ * Notification sent by the framework to indicate that the user
+ * has changed a setting.
+ *
+ * @param setting User setting that has been modified.
+ * @param newValue The update value of the user setting.
+ */
+ onSettingChanged(Setting setting, SettingValue newValue);
+};
\ No newline at end of file
diff --git a/contexthub/1.1/default/Android.bp b/contexthub/1.1/default/Android.bp
new file mode 100644
index 0000000..86858c0
--- /dev/null
+++ b/contexthub/1.1/default/Android.bp
@@ -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.
+ */
+
+cc_binary {
+ name: "android.hardware.contexthub@1.1-service.mock",
+ defaults: ["hidl_defaults"],
+ vendor: true,
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.contexthub@1.1-service.rc"],
+ srcs: [
+ "Contexthub.cpp",
+ "service.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "android.hardware.contexthub@1.0",
+ "android.hardware.contexthub@1.1",
+ "libbase",
+ "libcutils",
+ "libhardware",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ ],
+ vintf_fragments: ["android.hardware.contexthub@1.1.xml"],
+}
diff --git a/contexthub/1.1/default/Contexthub.cpp b/contexthub/1.1/default/Contexthub.cpp
new file mode 100644
index 0000000..19cc262
--- /dev/null
+++ b/contexthub/1.1/default/Contexthub.cpp
@@ -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.
+ */
+#include "Contexthub.h"
+
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace contexthub {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::contexthub::V1_0::ContextHub;
+using ::android::hardware::contexthub::V1_0::HubAppInfo;
+using ::android::hardware::contexthub::V1_0::Result;
+
+namespace {
+
+constexpr uint32_t kMockHubId = 0;
+
+} // anonymous namespace
+
+Return<void> Contexthub::getHubs(getHubs_cb _hidl_cb) {
+ ContextHub hub = {};
+ hub.name = "Mock Context Hub";
+ hub.vendor = "AOSP";
+ hub.toolchain = "n/a";
+ hub.platformVersion = 1;
+ hub.toolchainVersion = 1;
+ hub.hubId = kMockHubId;
+ hub.peakMips = 1;
+ hub.peakPowerDrawMw = 1;
+ hub.maxSupportedMsgLen = 4096;
+ hub.chrePlatformId = UINT64_C(0x476f6f6754000000);
+ hub.chreApiMajorVersion = 1;
+ hub.chreApiMinorVersion = 4;
+
+ // Report a single mock hub
+ std::vector<ContextHub> hubs;
+ hubs.push_back(hub);
+
+ _hidl_cb(hubs);
+ return Void();
+}
+
+Return<Result> Contexthub::registerCallback(uint32_t hubId, const sp<IContexthubCallback>& cb) {
+ if (hubId == kMockHubId) {
+ mCallback = cb;
+ return Result::OK;
+ }
+ return Result::BAD_PARAMS;
+}
+
+// We don't expose any nanoapps, therefore all nanoapp-related API calls return with BAD_PARAMS
+Return<Result> Contexthub::sendMessageToHub(uint32_t /*hubId*/, const ContextHubMsg& /*msg*/) {
+ return Result::BAD_PARAMS;
+}
+
+Return<Result> Contexthub::loadNanoApp(uint32_t /*hubId*/, const NanoAppBinary& /*appBinary*/,
+ uint32_t /*transactionId*/) {
+ return Result::BAD_PARAMS;
+}
+
+Return<Result> Contexthub::unloadNanoApp(uint32_t /*hubId*/, uint64_t /*appId*/,
+ uint32_t /*transactionId*/) {
+ return Result::BAD_PARAMS;
+}
+
+Return<Result> Contexthub::enableNanoApp(uint32_t /*hubId*/, uint64_t /*appId*/,
+ uint32_t /*transactionId*/) {
+ return Result::BAD_PARAMS;
+}
+
+Return<Result> Contexthub::disableNanoApp(uint32_t /*hubId*/, uint64_t /*appId*/,
+ uint32_t /*transactionId*/) {
+ return Result::BAD_PARAMS;
+}
+
+Return<Result> Contexthub::queryApps(uint32_t hubId) {
+ if (hubId == kMockHubId && mCallback != nullptr) {
+ std::vector<HubAppInfo> nanoapps;
+ mCallback->handleAppsInfo(nanoapps);
+ return Result::OK;
+ }
+ return Result::BAD_PARAMS;
+}
+
+Return<void> Contexthub::onSettingChanged(Setting /*setting*/, SettingValue /*newValue*/) {
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace contexthub
+} // namespace hardware
+} // namespace android
diff --git a/contexthub/1.1/default/Contexthub.h b/contexthub/1.1/default/Contexthub.h
new file mode 100644
index 0000000..0da61d1
--- /dev/null
+++ b/contexthub/1.1/default/Contexthub.h
@@ -0,0 +1,56 @@
+/*
+ * 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/contexthub/1.1/IContexthub.h>
+
+namespace android {
+namespace hardware {
+namespace contexthub {
+namespace V1_1 {
+namespace implementation {
+
+class Contexthub : public V1_1::IContexthub {
+ using ContextHubMsg = ::android::hardware::contexthub::V1_0::ContextHubMsg;
+ using IContexthubCallback = ::android::hardware::contexthub::V1_0::IContexthubCallback;
+ using NanoAppBinary = ::android::hardware::contexthub::V1_0::NanoAppBinary;
+ using Result = ::android::hardware::contexthub::V1_0::Result;
+
+ public:
+ // Methods from V1_0::IContexthub
+ Return<void> getHubs(getHubs_cb _hidl_cb) override;
+ Return<Result> registerCallback(uint32_t hubId,
+ const ::android::sp<IContexthubCallback>& cb) override;
+ Return<Result> sendMessageToHub(uint32_t hubId, const ContextHubMsg& msg) override;
+ Return<Result> loadNanoApp(uint32_t hubId, const NanoAppBinary& appBinary,
+ uint32_t transactionId) override;
+ Return<Result> unloadNanoApp(uint32_t hubId, uint64_t appId, uint32_t transactionId) override;
+ Return<Result> enableNanoApp(uint32_t hubId, uint64_t appId, uint32_t transactionId) override;
+ Return<Result> disableNanoApp(uint32_t hubId, uint64_t appId, uint32_t transactionId) override;
+ Return<Result> queryApps(uint32_t hubId) override;
+
+ // Methods from V1_1::IContexthub
+ Return<void> onSettingChanged(Setting setting, SettingValue newValue) override;
+
+ private:
+ sp<IContexthubCallback> mCallback;
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace contexthub
+} // namespace hardware
+} // namespace android
diff --git a/contexthub/1.1/default/OWNERS b/contexthub/1.1/default/OWNERS
new file mode 100644
index 0000000..90c2330
--- /dev/null
+++ b/contexthub/1.1/default/OWNERS
@@ -0,0 +1,3 @@
+arthuri@google.com
+bduddie@google.com
+stange@google.com
diff --git a/contexthub/1.1/default/android.hardware.contexthub@1.1-service.rc b/contexthub/1.1/default/android.hardware.contexthub@1.1-service.rc
new file mode 100644
index 0000000..9db00f9
--- /dev/null
+++ b/contexthub/1.1/default/android.hardware.contexthub@1.1-service.rc
@@ -0,0 +1,6 @@
+service vendor.contexthub-hal-1-1-mock /vendor/bin/hw/android.hardware.contexthub@1.1-service.mock
+ interface android.hardware.contexthub@1.0::IContexthub default
+ interface android.hardware.contexthub@1.1::IContexthub default
+ class hal
+ user system
+ group system
diff --git a/contexthub/1.1/default/android.hardware.contexthub@1.1.xml b/contexthub/1.1/default/android.hardware.contexthub@1.1.xml
new file mode 100644
index 0000000..388f781
--- /dev/null
+++ b/contexthub/1.1/default/android.hardware.contexthub@1.1.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.contexthub</name>
+ <transport>hwbinder</transport>
+ <version>1.1</version>
+ <interface>
+ <name>IContexthub</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/contexthub/1.1/default/service.cpp b/contexthub/1.1/default/service.cpp
new file mode 100644
index 0000000..c5643f1
--- /dev/null
+++ b/contexthub/1.1/default/service.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.
+ */
+
+#define LOG_TAG "android.hardware.contexthub@1.1-service"
+
+#include <android/hardware/contexthub/1.1/IContexthub.h>
+#include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+#include "Contexthub.h"
+
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::joinRpcThreadpool;
+using ::android::hardware::contexthub::V1_1::IContexthub;
+using ::android::hardware::contexthub::V1_1::implementation::Contexthub;
+
+int main() {
+ configureRpcThreadpool(1, true /* callerWillJoin */);
+
+ ::android::sp<IContexthub> contexthub = new Contexthub();
+ if (contexthub->registerAsService() != ::android::OK) {
+ ALOGE("Failed to register Contexthub HAL instance");
+ return 1;
+ }
+
+ joinRpcThreadpool();
+ ALOGE("Service exited");
+ return 1;
+}
diff --git a/radio/config/1.3/IRadioConfigIndication.hal b/contexthub/1.1/types.hal
similarity index 64%
copy from radio/config/1.3/IRadioConfigIndication.hal
copy to contexthub/1.1/types.hal
index 9ef496c..885cf32 100644
--- a/radio/config/1.3/IRadioConfigIndication.hal
+++ b/contexthub/1.1/types.hal
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 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.
@@ -14,13 +14,19 @@
* limitations under the License.
*/
-package android.hardware.radio.config@1.3;
-
-import @1.2::IRadioConfigIndication;
+package android.hardware.contexthub@1.1;
/**
- * Interface declaring unsolicited radio config indications.
+ * Used to indicate the type of user setting that has changed.
*/
-interface IRadioConfigIndication extends @1.2::IRadioConfigIndication {
-
+enum Setting : uint8_t {
+ LOCATION,
};
+
+/**
+ * Used to indicate the value of a user setting.
+ */
+enum SettingValue : uint8_t {
+ DISABLED,
+ ENABLED,
+};
\ No newline at end of file
diff --git a/identity/1.0/vts/functional/Android.bp b/contexthub/1.1/vts/functional/Android.bp
similarity index 65%
rename from identity/1.0/vts/functional/Android.bp
rename to contexthub/1.1/vts/functional/Android.bp
index 03b49de..f1625a6 100644
--- a/identity/1.0/vts/functional/Android.bp
+++ b/contexthub/1.1/vts/functional/Android.bp
@@ -1,5 +1,5 @@
//
-// Copyright (C) 2019 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.
@@ -15,19 +15,13 @@
//
cc_test {
- name: "VtsHalIdentityCredentialTargetTest",
+ name: "VtsHalContexthubV1_1TargetTest",
defaults: ["VtsHalTargetTestDefaults"],
- srcs: [
- "VtsHalIdentityCredentialTargetTest.cpp",
- ],
+ srcs: ["VtsHalContexthubV1_1TargetTest.cpp"],
static_libs: [
- "android.hardware.identity@1.0",
- "android.hardware.identity-support-lib",
- "android.hardware.keymaster@4.0",
- "libcppbor",
- ],
- shared_libs: [
- "libcrypto",
+ "android.hardware.contexthub@1.0",
+ "android.hardware.contexthub@1.1",
+ "VtsHalContexthubUtils",
],
test_suites: [
"general-tests",
diff --git a/contexthub/1.1/vts/functional/OWNERS b/contexthub/1.1/vts/functional/OWNERS
new file mode 100644
index 0000000..161b2f0
--- /dev/null
+++ b/contexthub/1.1/vts/functional/OWNERS
@@ -0,0 +1,8 @@
+#Context Hub team
+arthuri@google.com
+bduddie@google.com
+stange@google.com
+
+#VTS team
+dshi@google.com
+trong@google.com
diff --git a/contexthub/1.1/vts/functional/VtsHalContexthubV1_1TargetTest.cpp b/contexthub/1.1/vts/functional/VtsHalContexthubV1_1TargetTest.cpp
new file mode 100644
index 0000000..f2fcdfc
--- /dev/null
+++ b/contexthub/1.1/vts/functional/VtsHalContexthubV1_1TargetTest.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 "contexthub_hidl_hal_test"
+
+#include "ContexthubCallbackBase.h"
+#include "ContexthubHidlTestBase.h"
+#include "VtsHalContexthubUtils.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/contexthub/1.0/IContexthub.h>
+#include <android/hardware/contexthub/1.1/IContexthub.h>
+#include <android/log.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include <cinttypes>
+
+using ::android::hardware::contexthub::V1_1::IContexthub;
+using ::android::hardware::contexthub::V1_1::Setting;
+using ::android::hardware::contexthub::V1_1::SettingValue;
+using ::android::hardware::contexthub::vts_utils::ContexthubCallbackBase;
+using ::android::hardware::contexthub::vts_utils::ContexthubHidlTestBase;
+using ::android::hardware::contexthub::vts_utils::getHalAndHubIdList;
+
+namespace {
+
+const std::vector<std::tuple<std::string, std::string>> kTestParameters =
+ getHalAndHubIdList<IContexthub>();
+
+class ContexthubHidlTest : public ContexthubHidlTestBase<IContexthub> {};
+
+TEST_P(ContexthubHidlTest, TestOnSettingChanged) {
+ // In VTS, we only test that sending the values doesn't cause things to blow up - other test
+ // suites verify the expected E2E behavior in CHRE
+ ASSERT_OK(registerCallback(new ContexthubCallbackBase()));
+ hubApi->onSettingChanged(Setting::LOCATION, SettingValue::DISABLED);
+ hubApi->onSettingChanged(Setting::LOCATION, SettingValue::ENABLED);
+ ASSERT_OK(registerCallback(nullptr));
+}
+
+INSTANTIATE_TEST_SUITE_P(HubIdSpecificTests, ContexthubHidlTest, testing::ValuesIn(kTestParameters),
+ android::hardware::PrintInstanceTupleNameToString<>);
+
+} // anonymous namespace
diff --git a/radio/config/1.3/IRadioConfigIndication.hal b/contexthub/common/vts/Android.bp
similarity index 61%
copy from radio/config/1.3/IRadioConfigIndication.hal
copy to contexthub/common/vts/Android.bp
index 9ef496c..3d5196a 100644
--- a/radio/config/1.3/IRadioConfigIndication.hal
+++ b/contexthub/common/vts/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 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.
@@ -14,13 +14,17 @@
* limitations under the License.
*/
-package android.hardware.radio.config@1.3;
-
-import @1.2::IRadioConfigIndication;
-
-/**
- * Interface declaring unsolicited radio config indications.
- */
-interface IRadioConfigIndication extends @1.2::IRadioConfigIndication {
-
-};
+cc_test_library {
+ name: "VtsHalContexthubUtils",
+ srcs: [
+ "VtsHalContexthubUtils.cpp",
+ ],
+ export_include_dirs: ["."],
+ shared_libs: [
+ "android.hardware.contexthub@1.0",
+ "libhardware",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/contexthub/common/vts/ContexthubCallbackBase.h b/contexthub/common/vts/ContexthubCallbackBase.h
new file mode 100644
index 0000000..124a116
--- /dev/null
+++ b/contexthub/common/vts/ContexthubCallbackBase.h
@@ -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.
+ */
+#pragma once
+
+#include <android/hardware/contexthub/1.0/IContexthubCallback.h>
+#include <log/log.h>
+
+#include <cinttypes>
+
+namespace android {
+namespace hardware {
+namespace contexthub {
+namespace vts_utils {
+
+// Base callback implementation that just logs all callbacks by default, but
+// records a failure if
+class ContexthubCallbackBase : public V1_0::IContexthubCallback {
+ public:
+ virtual Return<void> handleClientMsg(const V1_0::ContextHubMsg& /*msg*/) override {
+ ALOGD("Got client message callback");
+ return Void();
+ }
+
+ virtual Return<void> handleTxnResult(uint32_t txnId, V1_0::TransactionResult result) override {
+ ALOGD("Got transaction result callback for txnId %" PRIu32 " with result %" PRId32, txnId,
+ result);
+ return Void();
+ }
+
+ virtual Return<void> handleHubEvent(V1_0::AsyncEventType evt) override {
+ ALOGD("Got hub event callback for event type %" PRIu32, evt);
+ return Void();
+ }
+
+ virtual Return<void> handleAppAbort(uint64_t appId, uint32_t abortCode) override {
+ ALOGD("Got app abort notification for appId 0x%" PRIx64 " with abort code 0x%" PRIx32,
+ appId, abortCode);
+ return Void();
+ }
+
+ virtual Return<void> handleAppsInfo(const hidl_vec<V1_0::HubAppInfo>& /*appInfo*/) override {
+ ALOGD("Got app info callback");
+ return Void();
+ }
+};
+
+} // namespace vts_utils
+} // namespace contexthub
+} // namespace hardware
+} // namespace android
diff --git a/contexthub/common/vts/ContexthubHidlTestBase.h b/contexthub/common/vts/ContexthubHidlTestBase.h
new file mode 100644
index 0000000..ee5b7d3
--- /dev/null
+++ b/contexthub/common/vts/ContexthubHidlTestBase.h
@@ -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.
+ */
+#pragma once
+
+#include <android/hardware/contexthub/1.0/IContexthubCallback.h>
+#include <gtest/gtest.h>
+
+#include <string>
+#include <tuple>
+
+namespace android {
+namespace hardware {
+namespace contexthub {
+namespace vts_utils {
+
+// Base fixture for Context Hub HAL tests. Parameterized by service name and hub ID.
+template <class IContexthubVersion>
+class ContexthubHidlTestBase
+ : public ::testing::TestWithParam<std::tuple<std::string, std::string>> {
+ public:
+ virtual void SetUp() override { fetchHubApi(); }
+
+ void fetchHubApi() {
+ hubApi = IContexthubVersion::getService(std::get<0>(GetParam()));
+ ASSERT_NE(hubApi, nullptr);
+ }
+
+ uint32_t getHubId() { return std::stoi(std::get<1>(GetParam())); }
+
+ V1_0::Result registerCallback(sp<V1_0::IContexthubCallback> cb) {
+ return hubApi->registerCallback(getHubId(), cb);
+ }
+
+ sp<IContexthubVersion> hubApi;
+};
+
+} // namespace vts_utils
+} // namespace contexthub
+} // namespace hardware
+} // namespace android
diff --git a/contexthub/common/vts/OWNERS b/contexthub/common/vts/OWNERS
new file mode 100644
index 0000000..161b2f0
--- /dev/null
+++ b/contexthub/common/vts/OWNERS
@@ -0,0 +1,8 @@
+#Context Hub team
+arthuri@google.com
+bduddie@google.com
+stange@google.com
+
+#VTS team
+dshi@google.com
+trong@google.com
diff --git a/contexthub/common/vts/VtsHalContexthubUtils.cpp b/contexthub/common/vts/VtsHalContexthubUtils.cpp
new file mode 100644
index 0000000..5033b41
--- /dev/null
+++ b/contexthub/common/vts/VtsHalContexthubUtils.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.
+ */
+
+#include "VtsHalContexthubUtils.h"
+
+#include <chrono>
+#include <future>
+
+namespace android {
+namespace hardware {
+namespace contexthub {
+namespace vts_utils {
+
+using ::android::hardware::contexthub::V1_0::ContextHub;
+using ::android::hardware::contexthub::V1_0::IContexthub;
+
+// Synchronously queries IContexthub::getHubs() and returns the result
+hidl_vec<ContextHub> getHubsSync(IContexthub* hubApi) {
+ hidl_vec<ContextHub> hubList;
+ std::promise<void> barrier;
+
+ hubApi->getHubs([&hubList, &barrier](const hidl_vec<ContextHub>& hubs) {
+ hubList = hubs;
+ barrier.set_value();
+ });
+ barrier.get_future().wait_for(std::chrono::seconds(1));
+
+ return hubList;
+}
+
+} // namespace vts_utils
+} // namespace contexthub
+} // namespace hardware
+} // namespace android
diff --git a/contexthub/common/vts/VtsHalContexthubUtils.h b/contexthub/common/vts/VtsHalContexthubUtils.h
new file mode 100644
index 0000000..8f9b694
--- /dev/null
+++ b/contexthub/common/vts/VtsHalContexthubUtils.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.
+ */
+#pragma once
+
+#include <android/hardware/contexthub/1.0/IContexthub.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <utils/StrongPointer.h>
+
+#include <chrono>
+#include <future>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace contexthub {
+namespace vts_utils {
+
+#define ASSERT_OK(result) ASSERT_EQ(result, ::android::hardware::contexthub::V1_0::Result::OK)
+#define EXPECT_OK(result) EXPECT_EQ(result, ::android::hardware::contexthub::V1_0::Result::OK)
+
+// Helper that does explicit conversion of an enum class to its underlying/base
+// type. Useful for stream output of enum values.
+template <typename EnumType>
+inline constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) {
+ return static_cast<typename std::underlying_type<EnumType>::type>(value);
+}
+
+// Synchronously queries IContexthub::getHubs() and returns the result
+hidl_vec<V1_0::ContextHub> getHubsSync(V1_0::IContexthub* hubApi);
+
+// Create a vector of tuples that include each IContexthub service paired with each hub ID it
+// exposes via getHubs(). Each tuple represents a test target that we should run the VTS suite
+// against.
+template <class IContexthubVersion>
+static std::vector<std::tuple<std::string, std::string>> getHalAndHubIdList() {
+ std::vector<std::tuple<std::string, std::string>> parameters;
+ std::vector<std::string> serviceNames =
+ ::android::hardware::getAllHalInstanceNames(IContexthubVersion::descriptor);
+ for (const std::string& serviceName : serviceNames) {
+ sp<IContexthubVersion> hubApi = IContexthubVersion::getService(serviceName);
+ if (hubApi != nullptr) {
+ hidl_vec<V1_0::ContextHub> hubs = getHubsSync(hubApi.get());
+ for (const V1_0::ContextHub& hub : hubs) {
+ parameters.push_back(std::make_tuple(serviceName, std::to_string(hub.hubId)));
+ }
+ }
+ }
+
+ return parameters;
+}
+
+} // namespace vts_utils
+} // namespace contexthub
+} // namespace hardware
+} // namespace android
diff --git a/current.txt b/current.txt
index 75975f2..09fdd69 100644
--- a/current.txt
+++ b/current.txt
@@ -588,6 +588,7 @@
cd06a7911b9acd4a653bbf7133888878fbcb3f84be177c7a3f1becaae3d8618f android.hardware.camera.metadata@3.2::types
a05277065c28ebecd58118bd240fb8c55757361e8648c01f7c4dacdb7f2a95dc android.hardware.camera.metadata@3.3::types
9cb3df2bde2c6cd5fd96b7c41555420cacd7e276a556c684af91b7461c86460f android.hardware.gnss@1.0::IGnssCallback
+af334f1fc85c62b343f84b74d0495eed6f495f7fecedb53463db10c202310058 android.hardware.gnss.measurement_corrections@1.0::types
bceee81ec1b59324abd05932b5620fda5a6589597c9cb3953ba7f3ea02cccd3e android.hardware.camera.provider@2.4::ICameraProvider
2ce820dc4f3c6d85721b65150ed2157c6e2e2055f866fb6c6ba4790f14408d66 android.hardware.camera.provider@2.4::ICameraProviderCallback
b69a7615c508acf5c5201efd1bfa3262167874fc3594e2db5a3ff93addd8ac75 android.hardware.keymaster@4.0::IKeymasterDevice
@@ -596,21 +597,23 @@
5f6d3097ba84cb63c430787123f4de1b31c11f90b531b98eae9a8623a5ae962a android.hardware.neuralnetworks@1.1::types
fb382e986c10b8fbb797a8546e8f9ea6d1107bfe6f3fb7e57f6bbbf1f807a906 android.hardware.neuralnetworks@1.2::IDevice
40e71cd693de5b832325c5d8f081f2ff20a7ba2b89d401cee5b4b3eb0e241681 android.hardware.neuralnetworks@1.2::IPreparedModel
-7f7ef383268c95a1b8fe4e55c662bc806bb0ac11a154f6b049a113a44b0f024f android.hardware.neuralnetworks@1.2::types
+00649d29680f2c47edf60000c3ae7ae906ba638f0616947147e3676a83cf36fa android.hardware.neuralnetworks@1.2::types
a785a57447a81e9c130eef6904c3a5c256076c6a04588c40620ebd6fa2660d77 android.hardware.radio@1.2::types
1a6e2bd289f22931c526b21916910f1d4c436b7acb9556e4243de4ce8e6cc2e4 android.hardware.soundtrigger@2.0::ISoundTriggerHwCallback
fd65298e1e09e0e3c781ab18305920d757dbe55a3b459ce17814ec5cf6dfee99 android.hardware.wifi@1.0::IWifiP2pIface
+ff5dd821c2c7a9c78607159c4d788960b725487263c49d956ca5fa3d37008b45 android.hardware.wifi@1.2::IWifiStaIface
+5751f230e86a36111e7c5b995577cbf89d8df76c8e6c7641199198f3db3a93f7 android.hardware.wifi@1.3::IWifiStaIface
# HALs released in Android R
e966a3437d6a98d9d9e14e9d672088771716031900c0deb55a0946c751a03a44 android.hardware.audio@6.0::types
-dd3e9280be60a5e042331c1046d13938e2cc323dc4b267cc74d544bf62fc0314 android.hardware.audio@6.0::IDevice
+7241bd4596a927cd46d4b82f5e29e2cbe57f194aa1b25555f1d1d352e8b15c61 android.hardware.audio@6.0::IDevice
2402876cbc23c0de3690a665eca84fd3857d1808dba5cad25ce272f81ecef8c9 android.hardware.audio@6.0::IDevicesFactory
bca5379d5065e2e08b6ad7308ffc8a71a972fc0698bec678ea32eea786d01cb5 android.hardware.audio@6.0::IPrimaryDevice
fd1f1b29f26b42e886220f04a08086c00e5ade9d7b53f095438e578ab9d42a93 android.hardware.audio@6.0::IStream
2df5d5866b37776f25079c0e54b54350a2abe4e025a59c9e02a7d3abe8ca00e8 android.hardware.audio@6.0::IStreamIn
78e4138cc8307c11fc777c3bd376e581ba4ba48196b05ca1d7cdfa515c87b48a android.hardware.audio@6.0::IStreamOut
997fdaad7a9d17ee7e01feb7031a753e2365e72ad30b11d950e9183fabdf3844 android.hardware.audio@6.0::IStreamOutCallback
-43a3303378f5b9852c2da9ac2c1d440965aab7ba02a800229e7b3c84e2167e06 android.hardware.audio.common@6.0::types
+167ed5cfb7d91db2e2bf20f1320c1a9004eeb768e26f535e0f7db94a21867d21 android.hardware.audio.common@6.0::types
817930d58412d662cb45e641c50cb62c727e4a3e3ffe7029a53cad9677b97d58 android.hardware.audio.effect@6.0::types
525bec6b44f1103869c269a128d51b8dccd73af5340ba863c8886c68357c7faf android.hardware.audio.effect@6.0::IAcousticEchoCancelerEffect
8d76bbe3719d051a8e9a1dcf9244f37f5b0a491feb249fa48391edf7cb4f3131 android.hardware.audio.effect@6.0::IAutomaticGainControlEffect
@@ -634,57 +637,67 @@
40ab2c6866c18d32baf6e49e3053949e79601f56963a791e93e68b9ee18f718d android.hardware.bluetooth@1.1::IBluetoothHciCallbacks
07d0a252b2d8fa35887908a996ba395cf392968395fc30afab791f46e0c22a52 android.hardware.boot@1.1::IBootControl
74049a402be913963edfdd80828a53736570e9d8124a1bf18166b6ed46a6b0ab android.hardware.boot@1.1::types
+b8c63679e1a3874b356f3e691aecce1191d38f59063cf2ed2dce8a9d4cabf00e android.hardware.camera.device@3.6::ICameraDevice
+daad72a2f482d8a1f660d0e99ac1b78fedb414a4cfbe3fa56b2cd480e5d6f0cb android.hardware.camera.provider@2.6::ICameraProvider
+8f8d9463508ff9cae88eb35c429fd0e2dbca0ca8f5de7fdf836cc0c4370becb6 android.hardware.camera.provider@2.6::ICameraProviderCallback
c1aa508d00b66ed5feefea398fd5edf28fa651ac89773adad7dfda4e0a73a952 android.hardware.cas@1.2::ICas
9811f867def49b420d8c707f7e38d3bdd64f835244e1d2a5e9762ab9835672dc android.hardware.cas@1.2::ICasListener
f18695dd36ee205640b8326a17453858a7b4596653aaa6ef0016b0aef1bd4dac android.hardware.cas@1.2::IMediaCasService
4d85e814f94949dae4dc6cb82bbd7d6bb24ffafda6ddb2eac928d2a4fc2e21ce android.hardware.cas@1.2::types
+8351cc01eed4c0b4482d9572b5c7ddfd17874d8edb51d6761d348116fc91dd18 android.hardware.contexthub@1.1::IContexthub
+3581d0ba61663cdd45807494dcd697d01c074f27587df9140655f94346969cfe android.hardware.contexthub@1.1::types
66931c2506fbb5af61f20138cb05e0a09e7bf67d6964c231d27c648933bb33ec android.hardware.drm@1.3::ICryptoFactory
994d08ab27d613022c258a9ec48cece7adf2a305e92df5d76ef923e2c6665f64 android.hardware.drm@1.3::IDrmFactory
-881aa8720fb1d69aa9843bfab69d810ab7654a61d2f5ab5e2626cbf240f24eaf android.hardware.dumpstate@1.1::types
-13b33f623521ded51a6c0f7ea5b77e97066d0aa1e38a83c2873f08ad67294f89 android.hardware.dumpstate@1.1::IDumpstateDevice
+d9df99be0f59d8f33a9699fe316c67bfd11818aa69440bb1123ba43e717cea85 android.hardware.dumpstate@1.1::types
+186bc152ae189ab48f3a761a44ddf5edd0d483073c5b6ca1f802f8b50488b754 android.hardware.dumpstate@1.1::IDumpstateDevice
769d346927a94fd40ee80a5a976d8d15cf022ef99c5900738f4a82f26c0ed229 android.hardware.gnss@2.1::types
-88371e0edf69a1f72bfc45ecb2335e9b145e87339d3eecc92664a1fb200213ba android.hardware.gnss@2.1::IGnss
-ba62e1e8993bfb9f27fa04816fa0f2241ae2d01edfa3d0c04182e2e5de80045c android.hardware.gnss@2.1::IGnssCallback
-ccdf3c0fb2c02a6d4dc57afb276c3497ae8172b80b00ebc0bf8a0238dd38b01d android.hardware.gnss@2.1::IGnssConfiguration
-5a125c49ca83629e22afc8c39e865509343bfa2c38f0baea9a186bbac103492d android.hardware.gnss@2.1::IGnssMeasurement
-d7bf37660a0946de9599dcbae997b077ee3e604fc2044534d40d3da04297a5d3 android.hardware.gnss@2.1::IGnssMeasurementCallback
+c319e68b03829958404402c2d9c682019678087d60495807c0a7444e0a6af981 android.hardware.gnss@2.1::IGnss
+ba5ac712b2a656dc07c83ab4a7a2c2f3bee1bbcb752e8b8ffa9b672f3b5b0728 android.hardware.gnss@2.1::IGnssAntennaInfo
+0bc3ed97cbc3f6abc89c68f4e9f4d124f9f723431997dc88c2186cf4d2ad47ee android.hardware.gnss@2.1::IGnssAntennaInfoCallback
+3541d83adfeac16ee3e45d183a58dffe06012ccb5aa5bcd2e4f6eeae269f69cd android.hardware.gnss@2.1::IGnssCallback
+737d750017738f0753d13ba01a3310e0161f294b8ae80b3fd63eaa227e9d9c66 android.hardware.gnss@2.1::IGnssConfiguration
+7913a11206a577b12ade86a7cf3f95c2639cb514d086673f279bf99238c9917e android.hardware.gnss@2.1::IGnssMeasurement
+0a16e5913e94d995cfcf959a1c6f10b0b8e9dfdb5f45ac6e7244711ddd740272 android.hardware.gnss@2.1::IGnssMeasurementCallback
6670e7780803a8c696c6391fda5589a334b1b37dc7be9393792ed35035413633 android.hardware.gnss.measurement_corrections@1.1::IMeasurementCorrections
-a3f439b782a6a92aaf3c0250f3526e94e8bf8844c3d578f0815e21b12c431346 android.hardware.gnss.measurement_corrections@1.1::types
+956c1576ca0d6f11b42980ef59052062836b6763fe973af6cb709da50787f710 android.hardware.gnss.measurement_corrections@1.1::types
ce8dbe76eb9ee94b46ef98f725be992e760a5751073d4f4912484026541371f3 android.hardware.health@2.1::IHealth
26f04510a0b57aba5167c5c0a7c2f077c2acbb98b81902a072517829fd9fd67f android.hardware.health@2.1::IHealthInfoCallback
-db47f4ceceb1f06c656f39caa70c557b0f8471ef59fd58611bea667ffca20101 android.hardware.health@2.1::types
+e2f8bc1868fd4a3fd587c172773ea5a8c2f5a3deaf7958394102ca455252b255 android.hardware.health@2.1::types
0589e410f519e36514e7ece18f283f022df0f70efd2c12821d822f67f74aba98 android.hardware.identity@1.0::types
bbeee9604128ede83ee755b67e73b5ad29e6e1dbac9ec41fea6ffe2745b0c50a android.hardware.identity@1.0::IIdentityCredential
96ce8aad80f4c476f25261f790d357c117e79e18474c7dadd850dac704bbe65e android.hardware.identity@1.0::IIdentityCredentialStore
-6e1e28a96c90ba78d47257faea3f3bb4e6360affbbfa5822f0dc31211f9266ff android.hardware.identity@1.0::IWritableIdentityCredential
+8da9c938e58f7d636ddd2f92c646f99d9a9e79612e6441b6380ab12744251873 android.hardware.identity@1.0::IWritableIdentityCredential
27ae3724053940462114228872b3ffaf0b8e6177d5ba97f5a76339d12b8a99dd android.hardware.keymaster@4.1::IKeymasterDevice
adb0efdf1462e9b2e742c0dcadd598666aac551f178be06e755bfcdf5797abd0 android.hardware.keymaster@4.1::IOperation
ddcf89cd8ee2df0d32aee55050826446fb64f7aafde0a7cd946c64f61b1a364c android.hardware.keymaster@4.1::types
df9c79c4fdde2821550c6d5c3d07f5ec0adfb1b702561ce543c906ddef698703 android.hardware.media.c2@1.1::IComponent
a3eddd9bbdc87e8c22764070037dd1154f1cf006e6fba93364c4f85d4c134a19 android.hardware.media.c2@1.1::IComponentStore
65c16331e57f6dd68b3971f06f78fe9e3209afb60630c31705aa355f9a52bf0d android.hardware.neuralnetworks@1.3::IBuffer
-d1f382d14e1384b907d5bb5780df7f01934650d556fedbed2f15a90773c657d6 android.hardware.neuralnetworks@1.3::IDevice
-4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback
-29e26e83399b69c7998b787bd30426dd5baa2da350effca76bbee1ba877355c9 android.hardware.neuralnetworks@1.3::IFencedExecutionCallback
-384fd9fd6e4d43ea11d407e52ea81da5242c3c5f4b458b8707d8feb652a13e36 android.hardware.neuralnetworks@1.3::IPreparedModel
-0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback
-5f1a4e0c29fc686ed476f9f04eed35e4405d21288cb2746b978d6891de5cc37d android.hardware.neuralnetworks@1.3::types
+278817920bfd5292a7713f97f1832cca53de3de640f7670e413d97c6e7fd581c android.hardware.neuralnetworks@1.3::IDevice
+127ba11efb8220dc3aec9a8f441b59eaf1c68d7f03f577833e1824de75a36b17 android.hardware.neuralnetworks@1.3::IExecutionCallback
+6e904be0ddca5ae1de8eba020e6c38ed935ea7d80cd08f47787f137a0ca58555 android.hardware.neuralnetworks@1.3::IFencedExecutionCallback
+2b0b10d2ea7a18a4048cd0eb83d35c19a817aeee95f65807fc31f4ef21381397 android.hardware.neuralnetworks@1.3::IPreparedModel
+eee3430cc86c97c7b407495863d8fb61da6f1a64b7721e77b9b4909b11b174e9 android.hardware.neuralnetworks@1.3::IPreparedModelCallback
+dd39887aa4fb60ce60ea9cc043edeadbbae6e922d09d3946311b0b410024ae14 android.hardware.neuralnetworks@1.3::types
3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi
c67aaf26a7a40d14ea61e70e20afacbd0bb906df1704d585ac8599fbb69dd44b android.hardware.wifi.hostapd@1.2::IHostapd
-11f6448d15336361180391c8ebcdfd2d7cf77b3782d577e594d583aadc9c2877 android.hardware.wifi.hostapd@1.2::types
+2b5a7ea572b736030c64a3b4043af244425477c4672301780fe15aba5ed393d9 android.hardware.wifi.hostapd@1.2::types
a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardware.wifi.supplicant@1.3::ISupplicant
-8aed0a8e03e7a67bfdfb78ad7529a9ae95bea36e6060473b204c89d772522126 android.hardware.wifi.supplicant@1.3::ISupplicantStaIface
-def77c7db95d374f11a111bfc4ed60f92451303642a43276c4e291988fcee625 android.hardware.wifi.supplicant@1.3::ISupplicantStaIfaceCallback
-62cf050c593c1ec34b49178b5bdde72dd9b80d9bad3eb184e4f0cd564d28678c android.hardware.wifi.supplicant@1.3::ISupplicantStaNetwork
+159d48c9efb881f44d5deda8917b89fb4da26837f019446d6d73b73ea5010eca android.hardware.wifi.supplicant@1.3::ISupplicantStaIface
+2ce1f7fb52e49f80b13a9b153d491bce530552f02357ea729acae922a8659f93 android.hardware.wifi.supplicant@1.3::ISupplicantStaIfaceCallback
+77531c8d048f8f8ae532babd0ca86332a865ec9aace1b051226ef2b21123e645 android.hardware.wifi.supplicant@1.3::ISupplicantStaNetwork
98592d193a717066facf91428426e5abe211e3bd718bc372e29fb944ddbe6e7c android.hardware.wifi.supplicant@1.3::types
-50e22cd55ad5499e68e81541bbc67bd10e59c1b9f3ff8cc7ba70dcb0d2918381 android.hardware.radio@1.5::types
-8cc3306e8cd755d04521d1611b217b9d13a2a76d2af57cbea8f875333b3363f7 android.hardware.radio@1.5::IRadio
+99f5c26b952271d1246c957e1d0271fa39445ee65cc93aa7c187834f98914a33 android.hardware.radio@1.5::types
+7fefa2cc5b3b3be10b5cff5c5dc195385f491d4bf23ca65f9c6b3c30c8753a33 android.hardware.radio@1.5::IRadio
e96ae1c3a9c0689002ec2318e9c587f4f607c16a75a3cd38788b77eb91072021 android.hardware.radio@1.5::IRadioIndication
-7b77721a7716e163f5cc5f2830ed5616b953fcf0e5406f69de0fde5ce95e4184 android.hardware.radio@1.5::IRadioResponse
-2fd107f3de1b7e36825e241a88dfae8edf3a77c166cb746f00ddf6440ab78db1 android.hardware.radio.config@1.3::types
+829d3827eeb5a8f563e80fe627419b3231012fc02bc2e79782ec5e9ad9f799a4 android.hardware.radio@1.5::IRadioResponse
+4c4ce691df02faa28c0729e2a033ec464e1d72699be8bcde4dfb141313dbeba8 android.hardware.radio.config@1.3::types
a2977755bc5f1ef47f04b7f2400632efda6218e1515dba847da487145cfabc4f android.hardware.radio.config@1.3::IRadioConfig
742360c775313438b0f82256eac62fb5bbc76a6ae6f388573f3aa142fb2c1eea android.hardware.radio.config@1.3::IRadioConfigIndication
0006ab8e8b0910cbd3bbb08d5f17d5fac7d65a2bdad5f2334e4851db9d1e6fa8 android.hardware.radio.config@1.3::IRadioConfigResponse
-51d1c8d285e0456da2a3fdfbf4700c6277165d5e83219894d651c8ea0e39aa8b android.hardware.soundtrigger@2.3::types
-12d7533ff0754f45bf59ab300799074570a99a676545652c2c23abc73cb4515d android.hardware.soundtrigger@2.3::ISoundTriggerHw
+3ca6616381080bdd6c08141ad12775a94ae868c58b02b1274ae3326f7de724ab android.hardware.sensors@2.1::ISensors
+3d4141c6373cd9ca02fe221a7d12343840de2255d032c38248fe8e35816b58b2 android.hardware.sensors@2.1::ISensorsCallback
+8051cc50fc90ed447f058a8b15d81f35a65f1bd9004b1de4f127edeb89b47978 android.hardware.sensors@2.1::types
+4a6517ea4ad807855428b0101d8e1a486497bd88ab4300ba3b2be43d46d32580 android.hardware.soundtrigger@2.3::types
+b37f78e3fdc79af8b32a545b2b426f1fd1355b359d9e7835f3bf1ed0aa4518d8 android.hardware.soundtrigger@2.3::ISoundTriggerHw
7746fda1fbf9c7c132bae701cc5a161309e4f5e7f3e8065811045975ee86196d android.hardware.usb.gadget@1.1::IUsbGadget
diff --git a/drm/1.0/default/OWNERS b/drm/1.0/default/OWNERS
new file mode 100644
index 0000000..ecb421c
--- /dev/null
+++ b/drm/1.0/default/OWNERS
@@ -0,0 +1,6 @@
+edwinwong@google.com
+fredgc@google.com
+jtinker@google.com
+kylealexander@google.com
+rfrias@google.com
+robertshih@google.com
diff --git a/drm/1.0/vts/OWNERS b/drm/1.0/vts/OWNERS
new file mode 100644
index 0000000..ecb421c
--- /dev/null
+++ b/drm/1.0/vts/OWNERS
@@ -0,0 +1,6 @@
+edwinwong@google.com
+fredgc@google.com
+jtinker@google.com
+kylealexander@google.com
+rfrias@google.com
+robertshih@google.com
diff --git a/drm/1.0/vts/functional/Android.bp b/drm/1.0/vts/functional/Android.bp
index 4fbd54d..235bfb4 100644
--- a/drm/1.0/vts/functional/Android.bp
+++ b/drm/1.0/vts/functional/Android.bp
@@ -14,23 +14,75 @@
// limitations under the License.
//
-cc_test {
- name: "VtsHalDrmV1_0TargetTest",
+cc_library_static {
+ name: "libdrmvtshelper",
defaults: ["VtsHalTargetTestDefaults"],
+ local_include_dirs: [
+ "include",
+ ],
srcs: [
- "drm_hal_clearkey_test.cpp",
- "drm_hal_vendor_test.cpp",
"vendor_modules.cpp",
],
static_libs: [
+ "android.hardware.drm@1.0-helper",
+ ],
+ export_include_dirs: ["include"],
+}
+
+cc_library_static {
+ name: "android.hardware.drm@1.0-vts",
+ defaults: ["VtsHalTargetTestDefaults"],
+ local_include_dirs: [
+ "include",
+ ],
+ srcs: [
+ "drm_hal_clearkey_test.cpp",
+ "drm_hal_vendor_test.cpp",
+ ],
+ shared_libs: [
"android.hardware.drm@1.0",
- "android.hardware.drm@1.0-helper",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
"libhidlmemory",
"libnativehelper",
- "libssl",
+ ],
+ static_libs: [
+ "android.hardware.drm@1.0-helper",
"libcrypto_static",
+ "libdrmvtshelper",
+ ],
+ export_shared_lib_headers: [
+ "android.hardware.drm@1.0",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libhidlmemory",
+ "libnativehelper",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+}
+
+cc_test {
+ name: "VtsHalDrmV1_0TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "drm_hal_test_main.cpp",
+ ],
+ whole_static_libs: [
+ "android.hardware.drm@1.0-vts",
+ ],
+ shared_libs: [
+ "android.hardware.drm@1.0",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libhidlmemory",
+ "libnativehelper",
+ ],
+ static_libs: [
+ "android.hardware.drm@1.0-helper",
+ "libcrypto_static",
+ "libdrmvtshelper",
],
test_suites: [
"general-tests",
diff --git a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
index 5713d2e..ebdc2d7 100644
--- a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
+++ b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
@@ -16,127 +16,26 @@
#define LOG_TAG "drm_hal_clearkey_test@1.0"
-#include <android/hardware/drm/1.0/ICryptoFactory.h>
-#include <android/hardware/drm/1.0/ICryptoPlugin.h>
-#include <android/hardware/drm/1.0/IDrmFactory.h>
-#include <android/hardware/drm/1.0/IDrmPlugin.h>
-#include <android/hardware/drm/1.0/types.h>
-#include <android/hidl/allocator/1.0/IAllocator.h>
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/HidlSupport.h>
-#include <hidl/ServiceManagement.h>
-#include <hidlmemory/mapping.h>
-#include <log/log.h>
#include <openssl/aes.h>
-#include <memory>
#include <random>
-using ::android::hardware::drm::V1_0::BufferType;
-using ::android::hardware::drm::V1_0::DestinationBuffer;
-using ::android::hardware::drm::V1_0::ICryptoFactory;
-using ::android::hardware::drm::V1_0::ICryptoPlugin;
-using ::android::hardware::drm::V1_0::IDrmFactory;
-using ::android::hardware::drm::V1_0::IDrmPlugin;
-using ::android::hardware::drm::V1_0::KeyedVector;
-using ::android::hardware::drm::V1_0::KeyValue;
-using ::android::hardware::drm::V1_0::KeyRequestType;
-using ::android::hardware::drm::V1_0::KeyType;
-using ::android::hardware::drm::V1_0::Mode;
-using ::android::hardware::drm::V1_0::Pattern;
-using ::android::hardware::drm::V1_0::SecureStop;
-using ::android::hardware::drm::V1_0::SecureStopId;
-using ::android::hardware::drm::V1_0::SessionId;
-using ::android::hardware::drm::V1_0::SharedBuffer;
-using ::android::hardware::drm::V1_0::Status;
-using ::android::hardware::drm::V1_0::SubSample;
-
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hidl::allocator::V1_0::IAllocator;
-using ::android::hidl::memory::V1_0::IMemory;
-using ::android::sp;
+#include "android/hardware/drm/1.0/vts/drm_hal_clearkey_test.h"
using std::string;
-using std::unique_ptr;
using std::random_device;
using std::map;
using std::mt19937;
using std::vector;
-/**
- * These clearkey tests use white box knowledge of the legacy clearkey
- * plugin to verify that the HIDL HAL services and interfaces are working.
- * It is not intended to verify any vendor's HAL implementation. If you
- * are looking for vendor HAL tests, see drm_hal_vendor_test.cpp
- */
-#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
-#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
-
-static const uint8_t kCommonPsshBoxUUID[16] = {
- 0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02,
- 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B};
-
-// To be used in mpd to specify drm scheme for players
-static const uint8_t kClearKeyUUID[16] = {
- 0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9,
- 0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E};
-
static const uint8_t kInvalidUUID[16] = {
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
-class DrmHalClearkeyFactoryTest : public ::testing::TestWithParam<std::string> {
- public:
- void SetUp() override {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Running test %s.%s", test_info->test_case_name(),
- test_info->name());
-
- const std::string instanceName = GetParam();
- drmFactory = IDrmFactory::getService(instanceName);
- ASSERT_NE(nullptr, drmFactory.get());
- cryptoFactory = ICryptoFactory::getService(instanceName);
- ASSERT_NE(nullptr, cryptoFactory.get());
-
- const bool drmClearKey = drmFactory->isCryptoSchemeSupported(kClearKeyUUID);
- const bool cryptoClearKey = cryptoFactory->isCryptoSchemeSupported(kClearKeyUUID);
- EXPECT_EQ(drmClearKey, cryptoClearKey);
- const bool supportsClearKey = drmClearKey && cryptoClearKey;
-
- const bool drmCommonPsshBox = drmFactory->isCryptoSchemeSupported(kCommonPsshBoxUUID);
- const bool cryptoCommonPsshBox = cryptoFactory->isCryptoSchemeSupported(kCommonPsshBoxUUID);
- EXPECT_EQ(drmCommonPsshBox, cryptoCommonPsshBox);
- const bool supportsCommonPsshBox = drmCommonPsshBox && cryptoCommonPsshBox;
-
- EXPECT_EQ(supportsClearKey, supportsCommonPsshBox);
- correspondsToThisTest = supportsClearKey && supportsCommonPsshBox;
-
- if (instanceName == "clearkey") {
- EXPECT_TRUE(correspondsToThisTest);
-
- // TODO(b/147449315)
- // Only the clearkey plugged into the "default" instance supports
- // this test. Currently the "clearkey" instance fails some tests
- // here.
- GTEST_SKIP() << "Clearkey tests don't work with 'clearkey' instance yet.";
- }
-
- if (!correspondsToThisTest) {
- GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules";
- }
- }
-
- protected:
- sp<IDrmFactory> drmFactory;
- sp<ICryptoFactory> cryptoFactory;
-
- bool correspondsToThisTest;
-};
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_0 {
+namespace vts {
/**
* Ensure the factory doesn't support an invalid scheme UUID
@@ -264,48 +163,6 @@
EXPECT_OK(res);
}
-class DrmHalClearkeyPluginTest : public DrmHalClearkeyFactoryTest {
- public:
- virtual void SetUp() override {
- // Create factories
- DrmHalClearkeyFactoryTest::SetUp();
-
- if (!correspondsToThisTest) {
- GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules";
- }
-
- ASSERT_NE(nullptr, drmFactory.get());
- hidl_string packageName("android.hardware.drm.test");
- auto res = drmFactory->createPlugin(
- kClearKeyUUID, packageName,
- [this](Status status, const sp<IDrmPlugin>& plugin) {
- EXPECT_EQ(Status::OK, status);
- ASSERT_NE(nullptr, plugin.get());
- drmPlugin = plugin;
- });
- ASSERT_OK(res);
-
- hidl_vec<uint8_t> initVec;
- res = cryptoFactory->createPlugin(
- kClearKeyUUID, initVec,
- [this](Status status, const sp<ICryptoPlugin>& plugin) {
- EXPECT_EQ(Status::OK, status);
- ASSERT_NE(nullptr, plugin.get());
- cryptoPlugin = plugin;
- });
- ASSERT_OK(res);
- }
-
- SessionId openSession();
- void closeSession(const SessionId& sessionId);
- hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, const KeyType& type);
- sp<IMemory> getDecryptMemory(size_t size, size_t index);
-
- protected:
- sp<IDrmPlugin> drmPlugin;
- sp<ICryptoPlugin> cryptoPlugin;
-};
-
/**
* DrmPlugin tests
*/
@@ -966,30 +823,6 @@
* Decrypt tests
*/
-class DrmHalClearkeyDecryptTest : public DrmHalClearkeyPluginTest {
- public:
- void SetUp() override {
- DrmHalClearkeyPluginTest::SetUp();
-
- if (!correspondsToThisTest) {
- GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules";
- }
- }
- void fillRandom(const sp<IMemory>& memory);
- hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) {
- EXPECT_EQ(16u, vec.size());
- return hidl_array<uint8_t, 16>(&vec[0]);
- }
- uint32_t decrypt(Mode mode, uint8_t* iv, const hidl_vec<SubSample>& subSamples,
- const Pattern& pattern, Status status);
- void aes_ctr_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
- const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
- void aes_cbc_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
- const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
- void decryptWithInvalidKeys(hidl_vec<uint8_t>& invalidResponse,
- vector<uint8_t>& iv, const Pattern& noPattern, const vector<SubSample>& subSamples);
-};
-
void DrmHalClearkeyDecryptTest::fillRandom(const sp<IMemory>& memory) {
random_device rd;
mt19937 rand(rd());
@@ -1300,20 +1133,8 @@
decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples);
}
-static const std::set<std::string> kAllInstances = [] {
- std::vector<std::string> drmInstances =
- android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
- std::vector<std::string> cryptoInstances =
- android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
- std::set<std::string> allInstances;
- allInstances.insert(drmInstances.begin(), drmInstances.end());
- allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
- return allInstances;
-}();
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyFactoryTest, testing::ValuesIn(kAllInstances),
- android::hardware::PrintInstanceNameToString);
-INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyPluginTest, testing::ValuesIn(kAllInstances),
- android::hardware::PrintInstanceNameToString);
-INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyDecryptTest, testing::ValuesIn(kAllInstances),
- android::hardware::PrintInstanceNameToString);
+} // namespace vts
+} // namespace V1_0
+} // namespace drm
+} // namespace hardware
+} // namespace android
diff --git a/drm/1.0/vts/functional/drm_hal_test_main.cpp b/drm/1.0/vts/functional/drm_hal_test_main.cpp
new file mode 100644
index 0000000..fd2538b
--- /dev/null
+++ b/drm/1.0/vts/functional/drm_hal_test_main.cpp
@@ -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.
+ */
+
+#define LOG_TAG "drm_hal_vendor_test@1.0"
+
+#include "vendor_modules.h"
+#include "android/hardware/drm/1.0/vts/drm_hal_vendor_test.h"
+#include "android/hardware/drm/1.0/vts/drm_hal_clearkey_test.h"
+
+using ::android::hardware::drm::V1_0::ICryptoFactory;
+using ::android::hardware::drm::V1_0::IDrmFactory;
+
+using ::android::hardware::drm::V1_0::vts::DrmHalClearkeyFactoryTest;
+using ::android::hardware::drm::V1_0::vts::DrmHalClearkeyPluginTest;
+using ::android::hardware::drm::V1_0::vts::DrmHalClearkeyDecryptTest;
+
+using ::android::hardware::drm::V1_0::vts::DrmHalVendorFactoryTest;
+using ::android::hardware::drm::V1_0::vts::DrmHalVendorPluginTest;
+using ::android::hardware::drm::V1_0::vts::DrmHalVendorDecryptTest;
+
+/**
+ * Instantiate the set of test cases for each vendor module
+ */
+
+static const std::vector<DrmHalTestParam> kAllInstances = [] {
+ std::vector<std::string> drmInstances =
+ android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
+ std::vector<std::string> cryptoInstances =
+ android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
+ std::set<std::string> allInstances;
+ allInstances.insert(drmInstances.begin(), drmInstances.end());
+ allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
+
+ std::vector<DrmHalTestParam> allInstanceUuidCombos;
+ auto noUUID = [](std::string s) { return DrmHalTestParam(s); };
+ std::transform(allInstances.begin(), allInstances.end(),
+ std::back_inserter(allInstanceUuidCombos), noUUID);
+ return allInstanceUuidCombos;
+}();
+
+INSTANTIATE_TEST_CASE_P(DrmHalVendorFactoryTestCases, DrmHalVendorFactoryTest,
+ testing::ValuesIn(kAllInstances),
+ drm_vts::PrintParamInstanceToString);
+
+INSTANTIATE_TEST_CASE_P(DrmHalVendorPluginTestCases, DrmHalVendorPluginTest,
+ testing::ValuesIn(kAllInstances),
+ drm_vts::PrintParamInstanceToString);
+
+INSTANTIATE_TEST_CASE_P(DrmHalVendorDecryptTestCases, DrmHalVendorDecryptTest,
+ testing::ValuesIn(kAllInstances),
+ drm_vts::PrintParamInstanceToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyFactoryTest, testing::ValuesIn(kAllInstances),
+ drm_vts::PrintParamInstanceToString);
+INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyPluginTest, testing::ValuesIn(kAllInstances),
+ drm_vts::PrintParamInstanceToString);
+INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyDecryptTest, testing::ValuesIn(kAllInstances),
+ drm_vts::PrintParamInstanceToString);
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_module_api.h b/drm/1.0/vts/functional/drm_hal_vendor_module_api.h
new file mode 120000
index 0000000..a8b5ade
--- /dev/null
+++ b/drm/1.0/vts/functional/drm_hal_vendor_module_api.h
@@ -0,0 +1 @@
+include/drm_hal_vendor_module_api.h
\ No newline at end of file
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
index 2259985..5c6c98b 100644
--- a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
+++ b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
@@ -16,138 +16,46 @@
#define LOG_TAG "drm_hal_vendor_test@1.0"
-#include <android/hardware/drm/1.0/ICryptoFactory.h>
-#include <android/hardware/drm/1.0/ICryptoPlugin.h>
-#include <android/hardware/drm/1.0/IDrmFactory.h>
-#include <android/hardware/drm/1.0/IDrmPlugin.h>
-#include <android/hardware/drm/1.0/IDrmPluginListener.h>
-#include <android/hardware/drm/1.0/types.h>
-#include <android/hidl/allocator/1.0/IAllocator.h>
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-#include <hidlmemory/mapping.h>
-#include <log/log.h>
#include <openssl/aes.h>
-#include <memory>
#include <random>
#include "drm_hal_vendor_module_api.h"
#include "vendor_modules.h"
#include <VtsHalHidlTargetCallbackBase.h>
-using ::android::hardware::drm::V1_0::BufferType;
-using ::android::hardware::drm::V1_0::DestinationBuffer;
-using ::android::hardware::drm::V1_0::EventType;
-using ::android::hardware::drm::V1_0::ICryptoFactory;
-using ::android::hardware::drm::V1_0::ICryptoPlugin;
-using ::android::hardware::drm::V1_0::IDrmFactory;
-using ::android::hardware::drm::V1_0::IDrmPlugin;
-using ::android::hardware::drm::V1_0::IDrmPluginListener;
-using ::android::hardware::drm::V1_0::KeyedVector;
-using ::android::hardware::drm::V1_0::KeyRequestType;
-using ::android::hardware::drm::V1_0::KeyStatus;
-using ::android::hardware::drm::V1_0::KeyStatusType;
-using ::android::hardware::drm::V1_0::KeyType;
-using ::android::hardware::drm::V1_0::KeyValue;
-using ::android::hardware::drm::V1_0::Mode;
-using ::android::hardware::drm::V1_0::Pattern;
-using ::android::hardware::drm::V1_0::SecureStop;
-using ::android::hardware::drm::V1_0::SecureStopId;
-using ::android::hardware::drm::V1_0::SessionId;
-using ::android::hardware::drm::V1_0::SharedBuffer;
-using ::android::hardware::drm::V1_0::Status;
-using ::android::hardware::drm::V1_0::SubSample;
+#include "android/hardware/drm/1.0/vts/drm_hal_vendor_test.h"
-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::hidl::allocator::V1_0::IAllocator;
-using ::android::hidl::memory::V1_0::IMemory;
-using ::android::sp;
-
-using std::string;
-using std::unique_ptr;
using std::random_device;
-using std::map;
using std::mt19937;
-using std::vector;
-
-using ContentConfiguration = ::DrmHalVTSVendorModule_V1::ContentConfiguration;
-using Key = ::DrmHalVTSVendorModule_V1::ContentConfiguration::Key;
-
-#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
-#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
-
-#define RETURN_IF_SKIPPED \
- if (vendorModule == nullptr || !vendorModule->isInstalled()) { \
- GTEST_SKIP() << "This drm scheme not supported." \
- << " library:" << GetParam() << " service-name:" \
- << (vendorModule == nullptr ? "N/A" : vendorModule->getServiceName()) \
- << std::endl; \
- return; \
- }
static const uint8_t kInvalidUUID[16] = {
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
};
-static drm_vts::VendorModules* gVendorModules = nullptr;
-
-class DrmHalVendorFactoryTest : public testing::TestWithParam<std::string> {
- public:
- DrmHalVendorFactoryTest()
- : vendorModule(
- static_cast<DrmHalVTSVendorModule_V1*>(gVendorModules->getModule(GetParam()))) {}
-
- virtual ~DrmHalVendorFactoryTest() {}
-
- virtual void SetUp() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("Running test %s.%s from vendor module %s", test_info->test_case_name(),
- test_info->name(), GetParam().c_str());
-
- const std::string instance = GetParam();
- if (instance == "widevine") {
- ASSERT_NE(nullptr, vendorModule.get());
- }
-
- if (vendorModule == nullptr) {
- GTEST_SKIP() << "No vendor module available";
- } else {
- ASSERT_EQ(instance, vendorModule->getServiceName());
- contentConfigurations = vendorModule->getContentConfigurations();
- }
-
- drmFactory = IDrmFactory::getService(instance);
- ASSERT_NE(nullptr, drmFactory.get());
- cryptoFactory = ICryptoFactory::getService(instance);
- ASSERT_NE(nullptr, cryptoFactory.get());
-
- // If drm scheme not installed skip subsequent tests
- if (!drmFactory->isCryptoSchemeSupported(getVendorUUID())) {
- // no GTEST_SKIP since only some tests require the module
- vendorModule->setInstalled(false);
- }
- }
-
- protected:
- hidl_array<uint8_t, 16> getVendorUUID() {
- if (vendorModule == nullptr) return {};
- vector<uint8_t> uuid = vendorModule->getUUID();
- return hidl_array<uint8_t, 16>(&uuid[0]);
+static drm_vts::VendorModules* gVendorModules = [] {
+#if defined(__LP64__)
+ const char* kModulePath = "/data/local/tmp/64/lib";
+#else
+ const char* kModulePath = "/data/local/tmp/32/lib";
+#endif
+ auto modules = new drm_vts::VendorModules(kModulePath);
+ if (modules->getPathList().size() == 0) {
+ std::cerr << "WARNING: No vendor modules found in " << kModulePath <<
+ ", all vendor tests will be skipped" << std::endl;
}
+ return modules;
+}();
- sp<IDrmFactory> drmFactory;
- sp<ICryptoFactory> cryptoFactory;
- unique_ptr<DrmHalVTSVendorModule_V1> vendorModule;
- vector<ContentConfiguration> contentConfigurations;
-};
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_0 {
+namespace vts {
+
+DrmHalVendorFactoryTest::DrmHalVendorFactoryTest()
+ : vendorModule(static_cast<DrmHalVTSVendorModule_V1*>(
+ gVendorModules->getModuleByName(GetParam().instance_))) {} // getModuleByName
TEST_P(DrmHalVendorFactoryTest, ValidateConfigurations) {
const char* kVendorStr = "Vendor module ";
@@ -195,8 +103,8 @@
*/
TEST_P(DrmHalVendorFactoryTest, PluginConfigUUIDSupported) {
RETURN_IF_SKIPPED;
- EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(getVendorUUID()));
- EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(getVendorUUID()));
+ EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(getUUID()));
+ EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(getUUID()));
}
/**
@@ -232,7 +140,7 @@
RETURN_IF_SKIPPED;
hidl_string packageName("android.hardware.drm.test");
auto res = drmFactory->createPlugin(
- getVendorUUID(), packageName,
+ getUUID(), packageName,
[&](Status status, const sp<IDrmPlugin>& plugin) {
EXPECT_EQ(Status::OK, status);
EXPECT_NE(nullptr, plugin.get());
@@ -247,7 +155,7 @@
RETURN_IF_SKIPPED;
hidl_vec<uint8_t> initVec;
auto res = cryptoFactory->createPlugin(
- getVendorUUID(), initVec,
+ getUUID(), initVec,
[&](Status status, const sp<ICryptoPlugin>& plugin) {
EXPECT_EQ(Status::OK, status);
EXPECT_NE(nullptr, plugin.get());
@@ -285,50 +193,6 @@
EXPECT_OK(res);
}
-class DrmHalVendorPluginTest : public DrmHalVendorFactoryTest {
- public:
- virtual ~DrmHalVendorPluginTest() {}
- virtual void SetUp() override {
- // Create factories
- DrmHalVendorFactoryTest::SetUp();
- RETURN_IF_SKIPPED;
-
- hidl_string packageName("android.hardware.drm.test");
- auto res = drmFactory->createPlugin(
- getVendorUUID(), packageName,
- [this](Status status, const sp<IDrmPlugin>& plugin) {
- EXPECT_EQ(Status::OK, status);
- ASSERT_NE(nullptr, plugin.get());
- drmPlugin = plugin;
- });
- ASSERT_OK(res);
-
- hidl_vec<uint8_t> initVec;
- res = cryptoFactory->createPlugin(
- getVendorUUID(), initVec,
- [this](Status status, const sp<ICryptoPlugin>& plugin) {
- EXPECT_EQ(Status::OK, status);
- ASSERT_NE(nullptr, plugin.get());
- cryptoPlugin = plugin;
- });
- ASSERT_OK(res);
- }
-
- virtual void TearDown() override {}
-
- SessionId openSession();
- void closeSession(const SessionId& sessionId);
- sp<IMemory> getDecryptMemory(size_t size, size_t index);
- KeyedVector toHidlKeyedVector(const map<string, string>& params);
- hidl_vec<uint8_t> loadKeys(const SessionId& sessionId,
- const ContentConfiguration& configuration,
- const KeyType& type);
-
- protected:
- sp<IDrmPlugin> drmPlugin;
- sp<ICryptoPlugin> cryptoPlugin;
-};
-
/**
* DrmPlugin tests
*/
@@ -1193,7 +1057,7 @@
EXPECT_OK(res);
- sp<IMemory> mappedMemory = mapMemory(hidlMemory);
+ sp<IMemory> mappedMemory = android::hardware::mapMemory(hidlMemory);
EXPECT_NE(nullptr, mappedMemory.get());
res = cryptoPlugin->setSharedBufferBase(hidlMemory, index);
EXPECT_OK(res);
@@ -1237,29 +1101,6 @@
* Decrypt tests
*/
-class DrmHalVendorDecryptTest : public DrmHalVendorPluginTest {
- public:
- DrmHalVendorDecryptTest() = default;
- virtual ~DrmHalVendorDecryptTest() {}
-
- protected:
- void fillRandom(const sp<IMemory>& memory);
- hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) {
- EXPECT_EQ(vec.size(), 16u);
- return hidl_array<uint8_t, 16>(&vec[0]);
- }
- hidl_vec<KeyValue> queryKeyStatus(SessionId sessionId);
- void removeKeys(SessionId sessionId);
- uint32_t decrypt(Mode mode, bool isSecure,
- const hidl_array<uint8_t, 16>& keyId, uint8_t* iv,
- const hidl_vec<SubSample>& subSamples, const Pattern& pattern,
- const vector<uint8_t>& key, Status expectedStatus);
- void aes_ctr_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
- const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
- void aes_cbc_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
- const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
-};
-
void DrmHalVendorDecryptTest::fillRandom(const sp<IMemory>& memory) {
random_device rd;
mt19937 rand(rd());
@@ -1566,47 +1407,8 @@
}
}
-
-/**
- * Instantiate the set of test cases for each vendor module
- */
-
-static const std::set<std::string> kAllInstances = [] {
- std::vector<std::string> drmInstances =
- android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
- std::vector<std::string> cryptoInstances =
- android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
- std::set<std::string> allInstances;
- allInstances.insert(drmInstances.begin(), drmInstances.end());
- allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
- return allInstances;
-}();
-
-INSTANTIATE_TEST_CASE_P(DrmHalVendorFactoryTestCases, DrmHalVendorFactoryTest,
- testing::ValuesIn(kAllInstances),
- android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_CASE_P(DrmHalVendorPluginTestCases, DrmHalVendorPluginTest,
- testing::ValuesIn(kAllInstances),
- android::hardware::PrintInstanceNameToString);
-
-INSTANTIATE_TEST_CASE_P(DrmHalVendorDecryptTestCases, DrmHalVendorDecryptTest,
- testing::ValuesIn(kAllInstances),
- android::hardware::PrintInstanceNameToString);
-
-int main(int argc, char** argv) {
-#if defined(__LP64__)
- const char* kModulePath = "/data/local/tmp/64/lib";
-#else
- const char* kModulePath = "/data/local/tmp/32/lib";
-#endif
- gVendorModules = new drm_vts::VendorModules(kModulePath);
- if (gVendorModules->getPathList().size() == 0) {
- std::cerr << "WARNING: No vendor modules found in " << kModulePath <<
- ", all vendor tests will be skipped" << std::endl;
- }
- ::testing::InitGoogleTest(&argc, argv);
- int status = RUN_ALL_TESTS();
- ALOGI("Test result = %d", status);
- return status;
-}
+} // namespace vts
+} // namespace V1_0
+} // namespace drm
+} // namespace hardware
+} // namespace android
diff --git a/drm/1.0/vts/functional/include/android/hardware/drm/1.0/vts/drm_hal_clearkey_test.h b/drm/1.0/vts/functional/include/android/hardware/drm/1.0/vts/drm_hal_clearkey_test.h
new file mode 100644
index 0000000..ca707b8
--- /dev/null
+++ b/drm/1.0/vts/functional/include/android/hardware/drm/1.0/vts/drm_hal_clearkey_test.h
@@ -0,0 +1,201 @@
+/*
+ * 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 DRM_HAL_CLEARKEY_TEST_H
+#define DRM_HAL_CLEARKEY_TEST_H
+
+#include <android/hardware/drm/1.0/ICryptoFactory.h>
+#include <android/hardware/drm/1.0/ICryptoPlugin.h>
+#include <android/hardware/drm/1.0/IDrmFactory.h>
+#include <android/hardware/drm/1.0/IDrmPlugin.h>
+#include <android/hardware/drm/1.0/types.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <hidlmemory/mapping.h>
+#include <log/log.h>
+
+#include "drm_vts_helper.h"
+
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+
+using ::drm_vts::DrmHalTestParam;
+using ::drm_vts::PrintParamInstanceToString;
+
+using std::string;
+using std::map;
+using std::vector;
+
+/**
+ * These clearkey tests use white box knowledge of the legacy clearkey
+ * plugin to verify that the HIDL HAL services and interfaces are working.
+ * It is not intended to verify any vendor's HAL implementation. If you
+ * are looking for vendor HAL tests, see drm_hal_vendor_test.cpp
+ */
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_0 {
+namespace vts {
+
+class DrmHalClearkeyFactoryTest : public ::testing::TestWithParam<DrmHalTestParam> {
+ public:
+ void SetUp() override {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("Running test %s.%s", test_info->test_case_name(),
+ test_info->name());
+
+ const std::string instanceName = GetParam().instance_;
+ drmFactory = IDrmFactory::getService(instanceName);
+ ASSERT_NE(nullptr, drmFactory.get());
+ cryptoFactory = ICryptoFactory::getService(instanceName);
+ ASSERT_NE(nullptr, cryptoFactory.get());
+
+ const bool drmClearKey = drmFactory->isCryptoSchemeSupported(kClearKeyUUID);
+ const bool cryptoClearKey = cryptoFactory->isCryptoSchemeSupported(kClearKeyUUID);
+ EXPECT_EQ(drmClearKey, cryptoClearKey);
+ const bool supportsClearKey = drmClearKey && cryptoClearKey;
+
+ const bool drmCommonPsshBox = drmFactory->isCryptoSchemeSupported(kCommonPsshBoxUUID);
+ const bool cryptoCommonPsshBox = cryptoFactory->isCryptoSchemeSupported(kCommonPsshBoxUUID);
+ EXPECT_EQ(drmCommonPsshBox, cryptoCommonPsshBox);
+ const bool supportsCommonPsshBox = drmCommonPsshBox && cryptoCommonPsshBox;
+
+ EXPECT_EQ(supportsClearKey, supportsCommonPsshBox);
+ correspondsToThisTest = supportsClearKey && supportsCommonPsshBox;
+
+ if (instanceName == "clearkey") {
+ EXPECT_TRUE(correspondsToThisTest);
+
+ // TODO(b/147449315)
+ // Only the clearkey plugged into the "default" instance supports
+ // this test. Currently the "clearkey" instance fails some tests
+ // here.
+ GTEST_SKIP() << "Clearkey tests don't work with 'clearkey' instance yet.";
+ }
+
+ if (!correspondsToThisTest) {
+ GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules";
+ }
+ }
+
+ protected:
+ static constexpr uint8_t kCommonPsshBoxUUID[16] = {
+ 0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02,
+ 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B};
+
+ // To be used in mpd to specify drm scheme for players
+ static constexpr uint8_t kClearKeyUUID[16] = {
+ 0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9,
+ 0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E};
+
+ sp<IDrmFactory> drmFactory;
+ sp<ICryptoFactory> cryptoFactory;
+
+ bool correspondsToThisTest;
+};
+
+class DrmHalClearkeyPluginTest : public DrmHalClearkeyFactoryTest {
+ public:
+ virtual void SetUp() override {
+ // Create factories
+ DrmHalClearkeyFactoryTest::SetUp();
+
+ if (!correspondsToThisTest) {
+ GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules";
+ }
+
+ ASSERT_NE(nullptr, drmFactory.get());
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ getUUID(), packageName,
+ [this](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ ASSERT_NE(nullptr, plugin.get());
+ drmPlugin = plugin;
+ });
+ ASSERT_OK(res);
+
+ hidl_vec<uint8_t> initVec;
+ res = cryptoFactory->createPlugin(
+ getUUID(), initVec,
+ [this](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ ASSERT_NE(nullptr, plugin.get());
+ cryptoPlugin = plugin;
+ });
+ ASSERT_OK(res);
+ }
+
+ SessionId openSession();
+ void closeSession(const SessionId& sessionId);
+ hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, const KeyType& type);
+ sp<IMemory> getDecryptMemory(size_t size, size_t index);
+
+ protected:
+ hidl_array<uint8_t, 16> getUUID() {
+ if (GetParamUUID() == hidl_array<uint8_t, 16>()) {
+ return kClearKeyUUID;
+ }
+ return GetParamUUID();
+ }
+
+ hidl_array<uint8_t, 16> GetParamUUID() {
+ return GetParam().scheme_;
+ }
+
+ sp<IDrmPlugin> drmPlugin;
+ sp<ICryptoPlugin> cryptoPlugin;
+};
+
+class DrmHalClearkeyDecryptTest : public DrmHalClearkeyPluginTest {
+ public:
+ void SetUp() override {
+ DrmHalClearkeyPluginTest::SetUp();
+
+ if (!correspondsToThisTest) {
+ GTEST_SKIP() << "Cannot test clearkey features on non-clearkey DRM modules";
+ }
+ }
+ void fillRandom(const sp<IMemory>& memory);
+ hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) {
+ EXPECT_EQ(16u, vec.size());
+ return hidl_array<uint8_t, 16>(&vec[0]);
+ }
+ uint32_t decrypt(Mode mode, uint8_t* iv, const hidl_vec<SubSample>& subSamples,
+ const Pattern& pattern, Status status);
+ void aes_ctr_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
+ const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
+ void aes_cbc_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
+ const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
+ void decryptWithInvalidKeys(hidl_vec<uint8_t>& invalidResponse,
+ vector<uint8_t>& iv, const Pattern& noPattern, const vector<SubSample>& subSamples);
+};
+
+} // namespace vts
+} // namespace V1_0
+} // namespace drm
+} // namespace hardware
+} // namespace android
+
+#endif // DRM_HAL_CLEARKEY_TEST_H
diff --git a/drm/1.0/vts/functional/include/android/hardware/drm/1.0/vts/drm_hal_vendor_test.h b/drm/1.0/vts/functional/include/android/hardware/drm/1.0/vts/drm_hal_vendor_test.h
new file mode 100644
index 0000000..468d335
--- /dev/null
+++ b/drm/1.0/vts/functional/include/android/hardware/drm/1.0/vts/drm_hal_vendor_test.h
@@ -0,0 +1,209 @@
+/*
+ * 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 DRM_HAL_VENDOR_TEST_H
+#define DRM_HAL_VENDOR_TEST_H
+
+#include <android/hardware/drm/1.0/ICryptoFactory.h>
+#include <android/hardware/drm/1.0/ICryptoPlugin.h>
+#include <android/hardware/drm/1.0/IDrmFactory.h>
+#include <android/hardware/drm/1.0/IDrmPlugin.h>
+#include <android/hardware/drm/1.0/IDrmPluginListener.h>
+#include <android/hardware/drm/1.0/types.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <hidlmemory/mapping.h>
+#include <log/log.h>
+
+#include <memory>
+#include <set>
+#include <vector>
+
+#include "drm_hal_vendor_module_api.h"
+#include "drm_vts_helper.h"
+#include "vendor_modules.h"
+#include <VtsHalHidlTargetCallbackBase.h>
+
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+
+using ::drm_vts::DrmHalTestParam;
+using ::drm_vts::PrintParamInstanceToString;
+
+using std::string;
+using std::unique_ptr;
+using std::map;
+using std::vector;
+
+using ContentConfiguration = ::DrmHalVTSVendorModule_V1::ContentConfiguration;
+using Key = ::DrmHalVTSVendorModule_V1::ContentConfiguration::Key;
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+#define RETURN_IF_SKIPPED \
+ if (vendorModule == nullptr || !vendorModule->isInstalled()) { \
+ GTEST_SKIP() << "This drm scheme not supported." \
+ << " library:" << GetParam() << " service-name:" \
+ << (vendorModule == nullptr ? "N/A" : vendorModule->getServiceName()) \
+ << std::endl; \
+ return; \
+ }
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_0 {
+namespace vts {
+
+class DrmHalVendorFactoryTest : public testing::TestWithParam<DrmHalTestParam> {
+ public:
+ DrmHalVendorFactoryTest();
+ virtual ~DrmHalVendorFactoryTest() {}
+
+ virtual void SetUp() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("Running test %s.%s from vendor module %s", test_info->test_case_name(),
+ test_info->name(), GetParam().instance_.c_str());
+
+ const std::string instance = GetParam().instance_;
+ if (instance == "widevine") {
+ ASSERT_NE(nullptr, vendorModule.get());
+ }
+
+ if (vendorModule == nullptr) {
+ GTEST_SKIP() << "No vendor module available";
+ } else {
+ ASSERT_EQ(instance, vendorModule->getServiceName());
+ contentConfigurations = vendorModule->getContentConfigurations();
+ }
+
+ drmFactory = IDrmFactory::getService(instance);
+ ASSERT_NE(nullptr, drmFactory.get());
+ cryptoFactory = ICryptoFactory::getService(instance);
+ ASSERT_NE(nullptr, cryptoFactory.get());
+
+ // If drm scheme not installed skip subsequent tests
+ if (!drmFactory->isCryptoSchemeSupported(getUUID())) {
+ // no GTEST_SKIP since only some tests require the module
+ vendorModule->setInstalled(false);
+ hidl_array<uint8_t, 16> noUUID;
+ ASSERT_EQ(GetParamUUID(), noUUID) << "param uuid unsupported";
+ }
+ }
+
+ protected:
+ hidl_array<uint8_t, 16> getUUID() {
+ if (GetParamUUID() == hidl_array<uint8_t, 16>()) {
+ return getVendorUUID();
+ }
+ return GetParamUUID();
+ }
+
+ hidl_array<uint8_t, 16> getVendorUUID() {
+ if (vendorModule == nullptr) return {};
+ vector<uint8_t> uuid = vendorModule->getUUID();
+ return hidl_array<uint8_t, 16>(&uuid[0]);
+ }
+
+ hidl_array<uint8_t, 16> GetParamUUID() {
+ return GetParam().scheme_;
+ }
+
+ sp<IDrmFactory> drmFactory;
+ sp<ICryptoFactory> cryptoFactory;
+ unique_ptr<DrmHalVTSVendorModule_V1> vendorModule;
+ vector<ContentConfiguration> contentConfigurations;
+};
+
+class DrmHalVendorPluginTest : public DrmHalVendorFactoryTest {
+ public:
+ virtual ~DrmHalVendorPluginTest() {}
+ virtual void SetUp() override {
+ // Create factories
+ DrmHalVendorFactoryTest::SetUp();
+ RETURN_IF_SKIPPED;
+
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ getVendorUUID(), packageName,
+ [this](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ ASSERT_NE(nullptr, plugin.get());
+ drmPlugin = plugin;
+ });
+ ASSERT_OK(res);
+
+ hidl_vec<uint8_t> initVec;
+ res = cryptoFactory->createPlugin(
+ getVendorUUID(), initVec,
+ [this](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ ASSERT_NE(nullptr, plugin.get());
+ cryptoPlugin = plugin;
+ });
+ ASSERT_OK(res);
+ }
+
+ virtual void TearDown() override {}
+
+ SessionId openSession();
+ void closeSession(const SessionId& sessionId);
+ sp<IMemory> getDecryptMemory(size_t size, size_t index);
+ KeyedVector toHidlKeyedVector(const map<string, string>& params);
+ hidl_vec<uint8_t> loadKeys(const SessionId& sessionId,
+ const ContentConfiguration& configuration,
+ const KeyType& type);
+
+ protected:
+ sp<IDrmPlugin> drmPlugin;
+ sp<ICryptoPlugin> cryptoPlugin;
+};
+
+class DrmHalVendorDecryptTest : public DrmHalVendorPluginTest {
+ public:
+ DrmHalVendorDecryptTest() = default;
+ virtual ~DrmHalVendorDecryptTest() {}
+
+ protected:
+ void fillRandom(const sp<IMemory>& memory);
+ hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) {
+ EXPECT_EQ(vec.size(), 16u);
+ return hidl_array<uint8_t, 16>(&vec[0]);
+ }
+ hidl_vec<KeyValue> queryKeyStatus(SessionId sessionId);
+ void removeKeys(SessionId sessionId);
+ uint32_t decrypt(Mode mode, bool isSecure,
+ const hidl_array<uint8_t, 16>& keyId, uint8_t* iv,
+ const hidl_vec<SubSample>& subSamples, const Pattern& pattern,
+ const vector<uint8_t>& key, Status expectedStatus);
+ void aes_ctr_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
+ const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
+ void aes_cbc_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
+ const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
+};
+
+} // namespace vts
+} // namespace V1_0
+} // namespace drm
+} // namespace hardware
+} // namespace android
+
+#endif // DRM_HAL_VENDOR_TEST_H
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_module_api.h b/drm/1.0/vts/functional/include/drm_hal_vendor_module_api.h
similarity index 100%
rename from drm/1.0/vts/functional/drm_hal_vendor_module_api.h
rename to drm/1.0/vts/functional/include/drm_hal_vendor_module_api.h
diff --git a/drm/1.0/vts/functional/include/drm_vts_helper.h b/drm/1.0/vts/functional/include/drm_vts_helper.h
new file mode 100644
index 0000000..1f02af9
--- /dev/null
+++ b/drm/1.0/vts/functional/include/drm_vts_helper.h
@@ -0,0 +1,58 @@
+/*
+ * 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 DRM_VTS_HELPER_H
+#define DRM_VTS_HELPER_H
+
+#include <hidl/GtestPrinter.h>
+#include <hidl/HidlSupport.h>
+
+#include <array>
+#include <chrono>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace drm_vts {
+
+using ::android::hardware::hidl_array;
+
+struct DrmHalTestParam {
+ const std::string instance_;
+ const hidl_array<uint8_t, 16> scheme_{};
+ DrmHalTestParam(const std::string& instance) : instance_(instance) {}
+ DrmHalTestParam(const std::string& instance, const hidl_array<uint8_t, 16>& scheme)
+ : instance_(instance), scheme_(scheme) {}
+};
+
+inline std::ostream& operator<<(std::ostream& stream, const DrmHalTestParam& val) {
+ stream << val.instance_ << ", " << android::hardware::toString(val.scheme_);
+ return stream;
+}
+
+inline std::string PrintParamInstanceToString(
+ const testing::TestParamInfo<DrmHalTestParam>& info) {
+ // test names need to be unique -> index prefix
+ std::string name = std::to_string(info.index) + "/" + info.param.instance_;
+ return android::hardware::Sanitize(name);
+};
+
+} // namespace drm_vts
+
+#endif // DRM_VTS_HELPER_H
diff --git a/drm/1.2/vts/functional/vendor_modules.h b/drm/1.0/vts/functional/include/vendor_modules.h
similarity index 97%
rename from drm/1.2/vts/functional/vendor_modules.h
rename to drm/1.0/vts/functional/include/vendor_modules.h
index eacd2d1..3f6fa15 100644
--- a/drm/1.2/vts/functional/vendor_modules.h
+++ b/drm/1.0/vts/functional/include/vendor_modules.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -47,15 +47,15 @@
DrmHalVTSVendorModule* getModule(const std::string& path);
/**
- * Retrieve a DrmHalVTSVendorModule given a service name.
- */
- DrmHalVTSVendorModule* getModuleByName(const std::string& name);
-
- /**
* Return the list of paths to available vendor modules.
*/
std::vector<std::string> getPathList() const {return mPathList;}
+ /**
+ * Retrieve a DrmHalVTSVendorModule given a service name.
+ */
+ DrmHalVTSVendorModule* getModuleByName(const std::string& name);
+
private:
std::vector<std::string> mPathList;
std::map<std::string, std::unique_ptr<SharedLibrary>> mOpenLibraries;
diff --git a/drm/1.0/vts/functional/vendor_modules.cpp b/drm/1.0/vts/functional/vendor_modules.cpp
index 98430f5..53927bd 100644
--- a/drm/1.0/vts/functional/vendor_modules.cpp
+++ b/drm/1.0/vts/functional/vendor_modules.cpp
@@ -23,6 +23,7 @@
#include <utils/String8.h>
#include <SharedLibrary.h>
+#include "drm_hal_vendor_module_api.h"
#include "vendor_modules.h"
using std::string;
@@ -69,4 +70,15 @@
ModuleFactory moduleFactory = reinterpret_cast<ModuleFactory>(symbol);
return (*moduleFactory)();
}
+
+DrmHalVTSVendorModule* VendorModules::getModuleByName(const string& name) {
+ for (const auto &path : mPathList) {
+ auto module = getModule(path);
+ if (module->getServiceName() == name) {
+ return module;
+ }
+
+ }
+ return NULL;
+}
};
diff --git a/drm/1.0/vts/functional/vendor_modules.h b/drm/1.0/vts/functional/vendor_modules.h
deleted file mode 100644
index 8330b0a..0000000
--- a/drm/1.0/vts/functional/vendor_modules.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef VENDOR_MODULES_H
-#define VENDOR_MODULES_H
-
-#include <map>
-#include <vector>
-#include <string>
-
-#include <SharedLibrary.h>
-
-using ::android::hardware::drm::V1_0::helper::SharedLibrary;
-
-class DrmHalVTSVendorModule;
-
-namespace drm_vts {
-class VendorModules {
- public:
- /**
- * Initialize with a file system path where the shared libraries
- * are to be found.
- */
- explicit VendorModules(const std::string& dir) {
- scanModules(dir);
- }
- ~VendorModules() {}
-
- /**
- * Retrieve a DrmHalVTSVendorModule given its full path. The
- * getAPIVersion method can be used to determine the versioned
- * subclass type.
- */
- DrmHalVTSVendorModule* getModule(const std::string& path);
-
- /**
- * Return the list of paths to available vendor modules.
- */
- std::vector<std::string> getPathList() const {return mPathList;}
-
- private:
- std::vector<std::string> mPathList;
- std::map<std::string, std::unique_ptr<SharedLibrary>> mOpenLibraries;
-
- /**
- * Scan the list of paths to available vendor modules.
- */
- void scanModules(const std::string& dir);
-
- inline bool endsWith(const std::string& str, const std::string& suffix) const {
- if (suffix.size() > str.size()) return false;
- return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
- }
-
- VendorModules(const VendorModules&) = delete;
- void operator=(const VendorModules&) = delete;
-};
-};
-
-#endif // VENDOR_MODULES_H
diff --git a/drm/1.1/vts/OWNERS b/drm/1.1/vts/OWNERS
new file mode 100644
index 0000000..ecb421c
--- /dev/null
+++ b/drm/1.1/vts/OWNERS
@@ -0,0 +1,6 @@
+edwinwong@google.com
+fredgc@google.com
+jtinker@google.com
+kylealexander@google.com
+rfrias@google.com
+robertshih@google.com
diff --git a/drm/1.1/vts/functional/Android.bp b/drm/1.1/vts/functional/Android.bp
index fb09563..e08d760 100644
--- a/drm/1.1/vts/functional/Android.bp
+++ b/drm/1.1/vts/functional/Android.bp
@@ -14,21 +14,56 @@
// limitations under the License.
//
-cc_test {
- name: "VtsHalDrmV1_1TargetTest",
+cc_library_static {
+ name: "android.hardware.drm@1.1-vts",
defaults: ["VtsHalTargetTestDefaults"],
+ include_dirs: [
+ "hardware/interfaces/drm/1.0/vts/functional",
+ ],
+ local_include_dirs: [
+ "include",
+ ],
srcs: [
"drm_hal_clearkey_test.cpp",
],
- static_libs: [
+ shared_libs: [
"android.hardware.drm@1.0",
"android.hardware.drm@1.1",
- "android.hardware.drm@1.0-helper",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
"libhidlmemory",
"libnativehelper",
- "libssl",
+ ],
+ static_libs: [
+ "libdrmvtshelper",
+ ],
+ export_shared_lib_headers: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libhidlmemory",
+ "libnativehelper",
+ ],
+ export_static_lib_headers: [
+ "libdrmvtshelper",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+}
+
+cc_test {
+ name: "VtsHalDrmV1_1TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "drm_hal_test_main.cpp",
+ ],
+ whole_static_libs: [
+ "android.hardware.drm@1.1-vts"
+ ],
+ shared_libs: [
+ "android.hardware.drm@1.1",
],
test_suites: [
"general-tests",
diff --git a/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp
index bf99ef6..fba9733 100644
--- a/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp
+++ b/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp
@@ -16,205 +16,20 @@
#define LOG_TAG "drm_hal_clearkey_test@1.1"
-#include <android/hardware/drm/1.0/ICryptoPlugin.h>
-#include <android/hardware/drm/1.0/IDrmPlugin.h>
-#include <android/hardware/drm/1.0/types.h>
-#include <android/hardware/drm/1.1/ICryptoFactory.h>
-#include <android/hardware/drm/1.1/IDrmFactory.h>
-#include <android/hardware/drm/1.1/IDrmPlugin.h>
-#include <android/hardware/drm/1.1/types.h>
-#include <android/hidl/allocator/1.0/IAllocator.h>
-#include <android/hidl/manager/1.2/IServiceManager.h>
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/HidlSupport.h>
-#include <hidl/ServiceManagement.h>
-#include <hidlmemory/mapping.h>
#include <log/log.h>
-#include <openssl/aes.h>
-#include <memory>
-#include <random>
+#include <vector>
-namespace drm = ::android::hardware::drm;
-using ::android::hardware::drm::V1_0::BufferType;
-using ::android::hardware::drm::V1_0::DestinationBuffer;
-using ::android::hardware::drm::V1_0::ICryptoPlugin;
-using ::android::hardware::drm::V1_0::KeyedVector;
-using ::android::hardware::drm::V1_0::KeyValue;
-using ::android::hardware::drm::V1_0::KeyType;
-using ::android::hardware::drm::V1_0::Mode;
-using ::android::hardware::drm::V1_0::Pattern;
-using ::android::hardware::drm::V1_0::SecureStop;
-using ::android::hardware::drm::V1_0::SecureStopId;
-using ::android::hardware::drm::V1_0::SessionId;
-using ::android::hardware::drm::V1_0::SharedBuffer;
-using ::android::hardware::drm::V1_0::Status;
-using ::android::hardware::drm::V1_0::SubSample;
-using ::android::hardware::drm::V1_0::SubSample;
+#include "android/hardware/drm/1.1/vts/drm_hal_clearkey_test.h"
-using ::android::hardware::drm::V1_1::DrmMetricGroup;
-using ::android::hardware::drm::V1_1::HdcpLevel;
-using ::android::hardware::drm::V1_1::ICryptoFactory;
-using ::android::hardware::drm::V1_1::IDrmFactory;
-using ::android::hardware::drm::V1_1::IDrmPlugin;
-using ::android::hardware::drm::V1_1::KeyRequestType;
-using ::android::hardware::drm::V1_1::SecureStopRelease;
-using ::android::hardware::drm::V1_1::SecurityLevel;
-using ::android::hardware::drm::V1_1::SecurityLevel;
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_1 {
+namespace vts {
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hidl::allocator::V1_0::IAllocator;
-using ::android::hidl::memory::V1_0::IMemory;
-using ::android::sp;
-
-using std::string;
-using std::unique_ptr;
-using std::random_device;
-using std::map;
-using std::mt19937;
-using std::vector;
-
-/**
- * These clearkey tests use white box knowledge of the legacy clearkey
- * plugin to verify that the HIDL HAL services and interfaces are working.
- * It is not intended to verify any vendor's HAL implementation. If you
- * are looking for vendor HAL tests, see drm_hal_vendor_test.cpp
- */
-#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
-#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
-
-// To be used in mpd to specify drm scheme for players
-static const uint8_t kClearKeyUUID[16] = {
+const uint8_t kClearKeyUUID[16] = {
0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9,
- 0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E};
-
-class DrmHalClearkeyTest : public ::testing::TestWithParam<std::string> {
- public:
- void SetUp() override {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
-
- ALOGD("DrmHalClearkeyTest: Running test %s.%s", test_info->test_case_name(),
- test_info->name());
-
- const std::string instance = GetParam();
-
- sp<IDrmFactory> drmFactory = IDrmFactory::getService(instance);
- drmPlugin = createDrmPlugin(drmFactory);
- sp<ICryptoFactory> cryptoFactory = ICryptoFactory::getService(instance);
- cryptoPlugin = createCryptoPlugin(cryptoFactory);
-
- if (drmPlugin == nullptr || cryptoPlugin == nullptr) {
- if (instance == "clearkey") {
- ASSERT_NE(nullptr, drmPlugin.get()) << "Can't get clearkey drm@1.1 plugin";
- ASSERT_NE(nullptr, cryptoPlugin.get()) << "Can't get clearkey crypto@1.1 plugin";
- }
- GTEST_SKIP() << "Instance does not support clearkey";
- }
- }
-
- SessionId openSession();
- SessionId openSession(SecurityLevel level);
- void closeSession(const SessionId& sessionId);
- hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, const KeyType& type);
-
- private:
- sp<IDrmPlugin> createDrmPlugin(sp<IDrmFactory> drmFactory) {
- if (drmFactory == nullptr) {
- return nullptr;
- }
- sp<IDrmPlugin> plugin = nullptr;
- auto res = drmFactory->createPlugin(
- kClearKeyUUID, "", [&](Status status, const sp<drm::V1_0::IDrmPlugin>& pluginV1_0) {
- EXPECT_EQ(Status::OK == status, pluginV1_0 != nullptr);
- plugin = IDrmPlugin::castFrom(pluginV1_0);
- });
-
- if (!res.isOk()) {
- ALOGE("createDrmPlugin remote call failed");
- }
- return plugin;
- }
-
- sp<ICryptoPlugin> createCryptoPlugin(sp<ICryptoFactory> cryptoFactory) {
- if (cryptoFactory == nullptr) {
- return nullptr;
- }
- sp<ICryptoPlugin> plugin = nullptr;
- hidl_vec<uint8_t> initVec;
- auto res = cryptoFactory->createPlugin(
- kClearKeyUUID, initVec,
- [&](Status status, const sp<drm::V1_0::ICryptoPlugin>& pluginV1_0) {
- EXPECT_EQ(Status::OK == status, pluginV1_0 != nullptr);
- plugin = pluginV1_0;
- });
- if (!res.isOk()) {
- ALOGE("createCryptoPlugin remote call failed");
- }
- return plugin;
- }
-
-protected:
- template <typename CT>
- bool ValueEquals(DrmMetricGroup::ValueType type, const std::string& expected, const CT& actual) {
- return type == DrmMetricGroup::ValueType::STRING_TYPE && expected == actual.stringValue;
- }
-
- template <typename CT>
- bool ValueEquals(DrmMetricGroup::ValueType type, const int64_t expected, const CT& actual) {
- return type == DrmMetricGroup::ValueType::INT64_TYPE && expected == actual.int64Value;
- }
-
- template <typename CT>
- bool ValueEquals(DrmMetricGroup::ValueType type, const double expected, const CT& actual) {
- return type == DrmMetricGroup::ValueType::DOUBLE_TYPE && expected == actual.doubleValue;
- }
-
- template <typename AT, typename VT>
- bool ValidateMetricAttributeAndValue(const DrmMetricGroup::Metric& metric,
- const std::string& attributeName, const AT& attributeValue,
- const std::string& componentName, const VT& componentValue) {
- bool validAttribute = false;
- bool validComponent = false;
- for (const DrmMetricGroup::Attribute& attribute : metric.attributes) {
- if (attribute.name == attributeName &&
- ValueEquals(attribute.type, attributeValue, attribute)) {
- validAttribute = true;
- }
- }
- for (const DrmMetricGroup::Value& value : metric.values) {
- if (value.componentName == componentName &&
- ValueEquals(value.type, componentValue, value)) {
- validComponent = true;
- }
- }
- return validAttribute && validComponent;
- }
-
- template <typename AT, typename VT>
- bool ValidateMetricAttributeAndValue(const hidl_vec<DrmMetricGroup>& metricGroups,
- const std::string& metricName,
- const std::string& attributeName, const AT& attributeValue,
- const std::string& componentName, const VT& componentValue) {
- bool foundMetric = false;
- for (const auto& group : metricGroups) {
- for (const auto& metric : group.metrics) {
- if (metric.name == metricName) {
- foundMetric = foundMetric || ValidateMetricAttributeAndValue(
- metric, attributeName, attributeValue,
- componentName, componentValue);
- }
- }
- }
- return foundMetric;
- }
-
- sp<IDrmPlugin> drmPlugin;
- sp<ICryptoPlugin> cryptoPlugin;
+ 0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E
};
/**
@@ -812,16 +627,8 @@
EXPECT_OK(res);
}
-static const std::set<std::string> kAllInstances = [] {
- std::vector<std::string> drmInstances =
- android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
- std::vector<std::string> cryptoInstances =
- android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
- std::set<std::string> allInstances;
- allInstances.insert(drmInstances.begin(), drmInstances.end());
- allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
- return allInstances;
-}();
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyTest, testing::ValuesIn(kAllInstances),
- android::hardware::PrintInstanceNameToString);
+} // namespace vts
+} // namespace V1_1
+} // namespace drm
+} // namespace hardware
+} // namespace android
diff --git a/drm/1.1/vts/functional/drm_hal_test_main.cpp b/drm/1.1/vts/functional/drm_hal_test_main.cpp
new file mode 100644
index 0000000..c6965bd
--- /dev/null
+++ b/drm/1.1/vts/functional/drm_hal_test_main.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.
+ */
+
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+
+#include <algorithm>
+#include <iterator>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android/hardware/drm/1.1/vts/drm_hal_clearkey_test.h"
+
+using android::hardware::drm::V1_1::ICryptoFactory;
+using android::hardware::drm::V1_1::IDrmFactory;
+using android::hardware::drm::V1_1::vts::DrmHalClearkeyTest;
+using android::hardware::drm::V1_1::vts::kClearKeyUUID;
+
+static const std::vector<DrmHalTestParam> kAllInstances = [] {
+ std::vector<std::string> drmInstances =
+ android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
+ std::vector<std::string> cryptoInstances =
+ android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
+ std::set<std::string> allInstances;
+ allInstances.insert(drmInstances.begin(), drmInstances.end());
+ allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
+
+ std::vector<DrmHalTestParam> allInstancesWithClearKeyUuid;
+ std::transform(allInstances.begin(), allInstances.end(),
+ std::back_inserter(allInstancesWithClearKeyUuid),
+ [](std::string s) { return DrmHalTestParam(s, kClearKeyUUID); });
+ return allInstancesWithClearKeyUuid;
+}();
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyTest, testing::ValuesIn(kAllInstances),
+ PrintParamInstanceToString);
diff --git a/drm/1.1/vts/functional/include/android/hardware/drm/1.1/vts/drm_hal_clearkey_test.h b/drm/1.1/vts/functional/include/android/hardware/drm/1.1/vts/drm_hal_clearkey_test.h
new file mode 100644
index 0000000..e21a2c1
--- /dev/null
+++ b/drm/1.1/vts/functional/include/android/hardware/drm/1.1/vts/drm_hal_clearkey_test.h
@@ -0,0 +1,209 @@
+/*
+ * 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 V1_1_DRM_HAL_CLEARKEY_TEST_H
+#define V1_1_DRM_HAL_CLEARKEY_TEST_H
+
+#include <android/hardware/drm/1.0/ICryptoPlugin.h>
+#include <android/hardware/drm/1.0/IDrmPlugin.h>
+#include <android/hardware/drm/1.0/types.h>
+#include <android/hardware/drm/1.1/ICryptoFactory.h>
+#include <android/hardware/drm/1.1/IDrmFactory.h>
+#include <android/hardware/drm/1.1/IDrmPlugin.h>
+#include <android/hardware/drm/1.1/types.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include <string>
+
+#include "drm_vts_helper.h"
+
+namespace drm = ::android::hardware::drm;
+
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+
+using drm_vts::DrmHalTestParam;
+using drm_vts::PrintParamInstanceToString;
+
+/**
+ * These clearkey tests use white box knowledge of the legacy clearkey
+ * plugin to verify that the HIDL HAL services and interfaces are working.
+ * It is not intended to verify any vendor's HAL implementation. If you
+ * are looking for vendor HAL tests, see drm_hal_vendor_test.cpp
+ */
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_1 {
+namespace vts {
+
+using ::android::hardware::drm::V1_0::ICryptoPlugin;
+using ::android::hardware::drm::V1_0::KeyedVector;
+using ::android::hardware::drm::V1_0::KeyType;
+using ::android::hardware::drm::V1_0::Mode;
+using ::android::hardware::drm::V1_0::Pattern;
+using ::android::hardware::drm::V1_0::SecureStop;
+using ::android::hardware::drm::V1_0::SecureStopId;
+using ::android::hardware::drm::V1_0::SessionId;
+using ::android::hardware::drm::V1_0::Status;
+
+// To be used in mpd to specify drm scheme for players
+extern const uint8_t kClearKeyUUID[16];
+
+class DrmHalClearkeyTest : public ::testing::TestWithParam<DrmHalTestParam> {
+ public:
+ void SetUp() override {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ ALOGD("DrmHalClearkeyTest: Running test %s.%s", test_info->test_case_name(),
+ test_info->name());
+
+ const std::string instance = GetParam().instance_;
+
+ sp<IDrmFactory> drmFactory = IDrmFactory::getService(instance);
+ if (!drmFactory->isCryptoSchemeSupported(kClearKeyUUID)) {
+ GTEST_SKIP() << instance << " does not support clearkey";
+ }
+ drmPlugin = createDrmPlugin(drmFactory);
+ sp<ICryptoFactory> cryptoFactory = ICryptoFactory::getService(instance);
+ cryptoPlugin = createCryptoPlugin(cryptoFactory);
+
+ if (drmPlugin == nullptr || cryptoPlugin == nullptr) {
+ if (instance == "clearkey") {
+ ASSERT_NE(nullptr, drmPlugin.get()) << "Can't get clearkey drm@1.1 plugin";
+ ASSERT_NE(nullptr, cryptoPlugin.get()) << "Can't get clearkey crypto@1.1 plugin";
+ }
+ GTEST_SKIP() << "Instance does not support clearkey";
+ }
+ }
+
+ SessionId openSession();
+ SessionId openSession(SecurityLevel level);
+ void closeSession(const SessionId& sessionId);
+ hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, const KeyType& type);
+
+ private:
+ sp<IDrmPlugin> createDrmPlugin(sp<IDrmFactory> drmFactory) {
+ if (drmFactory == nullptr) {
+ return nullptr;
+ }
+ sp<IDrmPlugin> plugin = nullptr;
+ auto res = drmFactory->createPlugin(GetParam().scheme_, "",
+ [&](Status status, const sp<drm::V1_0::IDrmPlugin>& pluginV1_0) {
+ EXPECT_EQ(Status::OK == status, pluginV1_0 != nullptr);
+ plugin = IDrmPlugin::castFrom(pluginV1_0);
+ });
+
+ if (!res.isOk()) {
+ ALOGE("createDrmPlugin remote call failed");
+ }
+ return plugin;
+ }
+
+ sp<ICryptoPlugin> createCryptoPlugin(sp<ICryptoFactory> cryptoFactory) {
+ if (cryptoFactory == nullptr) {
+ return nullptr;
+ }
+ sp<ICryptoPlugin> plugin = nullptr;
+ hidl_vec<uint8_t> initVec;
+ auto res = cryptoFactory->createPlugin(
+ GetParam().scheme_, initVec,
+ [&](Status status, const sp<drm::V1_0::ICryptoPlugin>& pluginV1_0) {
+ EXPECT_EQ(Status::OK == status, pluginV1_0 != nullptr);
+ plugin = pluginV1_0;
+ });
+ if (!res.isOk()) {
+ ALOGE("createCryptoPlugin remote call failed");
+ }
+ return plugin;
+ }
+
+protected:
+ template <typename CT>
+ bool ValueEquals(DrmMetricGroup::ValueType type, const std::string& expected, const CT& actual) {
+ return type == DrmMetricGroup::ValueType::STRING_TYPE && expected == actual.stringValue;
+ }
+
+ template <typename CT>
+ bool ValueEquals(DrmMetricGroup::ValueType type, const int64_t expected, const CT& actual) {
+ return type == DrmMetricGroup::ValueType::INT64_TYPE && expected == actual.int64Value;
+ }
+
+ template <typename CT>
+ bool ValueEquals(DrmMetricGroup::ValueType type, const double expected, const CT& actual) {
+ return type == DrmMetricGroup::ValueType::DOUBLE_TYPE && expected == actual.doubleValue;
+ }
+
+ template <typename AT, typename VT>
+ bool ValidateMetricAttributeAndValue(const DrmMetricGroup::Metric& metric,
+ const std::string& attributeName, const AT& attributeValue,
+ const std::string& componentName, const VT& componentValue) {
+ bool validAttribute = false;
+ bool validComponent = false;
+ for (const DrmMetricGroup::Attribute& attribute : metric.attributes) {
+ if (attribute.name == attributeName &&
+ ValueEquals(attribute.type, attributeValue, attribute)) {
+ validAttribute = true;
+ }
+ }
+ for (const DrmMetricGroup::Value& value : metric.values) {
+ if (value.componentName == componentName &&
+ ValueEquals(value.type, componentValue, value)) {
+ validComponent = true;
+ }
+ }
+ return validAttribute && validComponent;
+ }
+
+ template <typename AT, typename VT>
+ bool ValidateMetricAttributeAndValue(const hidl_vec<DrmMetricGroup>& metricGroups,
+ const std::string& metricName,
+ const std::string& attributeName, const AT& attributeValue,
+ const std::string& componentName, const VT& componentValue) {
+ bool foundMetric = false;
+ for (const auto& group : metricGroups) {
+ for (const auto& metric : group.metrics) {
+ if (metric.name == metricName) {
+ foundMetric = foundMetric || ValidateMetricAttributeAndValue(
+ metric, attributeName, attributeValue,
+ componentName, componentValue);
+ }
+ }
+ }
+ return foundMetric;
+ }
+
+ sp<IDrmPlugin> drmPlugin;
+ sp<ICryptoPlugin> cryptoPlugin;
+};
+
+} // namespace vts
+} // namespace V1_1
+} // namespace drm
+} // namespace hardware
+} // namespace android
+
+#endif // V1_1_DRM_HAL_CLEARKEY_TEST_H
diff --git a/drm/1.2/vts/OWNERS b/drm/1.2/vts/OWNERS
new file mode 100644
index 0000000..ecb421c
--- /dev/null
+++ b/drm/1.2/vts/OWNERS
@@ -0,0 +1,6 @@
+edwinwong@google.com
+fredgc@google.com
+jtinker@google.com
+kylealexander@google.com
+rfrias@google.com
+robertshih@google.com
diff --git a/drm/1.2/vts/functional/Android.bp b/drm/1.2/vts/functional/Android.bp
index 83ea104..ecc7d6c 100644
--- a/drm/1.2/vts/functional/Android.bp
+++ b/drm/1.2/vts/functional/Android.bp
@@ -14,27 +14,61 @@
// limitations under the License.
//
-cc_test {
- name: "VtsHalDrmV1_2TargetTest",
+cc_library_static {
+ name: "android.hardware.drm@1.2-vts",
defaults: ["VtsHalTargetTestDefaults"],
- include_dirs: ["hardware/interfaces/drm/1.0/vts/functional"],
+ local_include_dirs: [
+ "include",
+ ],
srcs: [
"drm_hal_clearkey_module.cpp",
"drm_hal_common.cpp",
"drm_hal_test.cpp",
- "vendor_modules.cpp",
],
- static_libs: [
+ shared_libs: [
"android.hardware.drm@1.0",
"android.hardware.drm@1.1",
"android.hardware.drm@1.2",
- "android.hardware.drm@1.0-helper",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
"libhidlmemory",
"libnativehelper",
- "libssl",
+ ],
+ static_libs: [
+ "android.hardware.drm@1.0-helper",
"libcrypto_static",
+ "libdrmvtshelper",
+ ],
+ export_shared_lib_headers: [
+ "android.hardware.drm@1.2",
+ ],
+ export_static_lib_headers: [
+ "android.hardware.drm@1.0-helper",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+}
+
+cc_test {
+ name: "VtsHalDrmV1_2TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "drm_hal_test_main.cpp",
+ ],
+ whole_static_libs: [
+ "android.hardware.drm@1.2-vts",
+ ],
+ shared_libs: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.2",
+ "android.hidl.allocator@1.0",
+ "libhidlmemory",
+ ],
+ static_libs: [
+ "android.hardware.drm@1.0-helper",
+ "libcrypto_static",
+ "libdrmvtshelper",
],
test_suites: [
"general-tests",
diff --git a/drm/1.2/vts/functional/drm_hal_common.cpp b/drm/1.2/vts/functional/drm_hal_common.cpp
index 8a68608..d5e453c 100644
--- a/drm/1.2/vts/functional/drm_hal_common.cpp
+++ b/drm/1.2/vts/functional/drm_hal_common.cpp
@@ -26,7 +26,7 @@
#include <random>
#include "drm_hal_clearkey_module.h"
-#include "drm_hal_common.h"
+#include "android/hardware/drm/1.2/vts/drm_hal_common.h"
using ::android::hardware::drm::V1_0::BufferType;
using ::android::hardware::drm::V1_0::DestinationBuffer;
@@ -94,7 +94,7 @@
* DrmHalTest
*/
-DrmHalTest::DrmHalTest() : vendorModule(getModuleForInstance(GetParam())) {}
+DrmHalTest::DrmHalTest() : vendorModule(getModuleForInstance(GetParamService())) {}
void DrmHalTest::SetUp() {
const ::testing::TestInfo* const test_info =
@@ -102,9 +102,9 @@
ALOGD("Running test %s.%s from (vendor) module %s",
test_info->test_case_name(), test_info->name(),
- GetParam().c_str());
+ GetParamService().c_str());
- const string instance = GetParam();
+ const string instance = GetParamService();
drmFactory = IDrmFactory::getService(instance);
ASSERT_NE(drmFactory, nullptr);
@@ -124,8 +124,12 @@
contentConfigurations = vendorModule->getContentConfigurations();
// If drm scheme not installed skip subsequent tests
- if (!drmFactory->isCryptoSchemeSupported(getVendorUUID())) {
- GTEST_SKIP() << "vendor module drm scheme not supported";
+ if (!drmFactory->isCryptoSchemeSupported(getUUID())) {
+ if (GetParamUUID() == hidl_array<uint8_t, 16>()) {
+ GTEST_SKIP() << "vendor module drm scheme not supported";
+ } else {
+ FAIL() << "param scheme must be supported: " << android::hardware::toString(GetParamUUID());
+ }
}
ASSERT_NE(nullptr, drmPlugin.get()) << "Can't find " << vendorModule->getServiceName() << " drm@1.2 plugin";
@@ -140,7 +144,7 @@
sp<IDrmPlugin> plugin = nullptr;
hidl_string packageName("android.hardware.drm.test");
auto res =
- drmFactory->createPlugin(getVendorUUID(), packageName,
+ drmFactory->createPlugin(getUUID(), packageName,
[&](StatusV1_0 status, const sp<IDrmPluginV1_0>& pluginV1_0) {
EXPECT_EQ(StatusV1_0::OK == status, pluginV1_0 != nullptr);
plugin = IDrmPlugin::castFrom(pluginV1_0);
@@ -159,7 +163,7 @@
sp<ICryptoPlugin> plugin = nullptr;
hidl_vec<uint8_t> initVec;
auto res = cryptoFactory->createPlugin(
- getVendorUUID(), initVec,
+ getUUID(), initVec,
[&](StatusV1_0 status, const sp<ICryptoPluginV1_0>& pluginV1_0) {
EXPECT_EQ(StatusV1_0::OK == status, pluginV1_0 != nullptr);
plugin = ICryptoPlugin::castFrom(pluginV1_0);
@@ -170,6 +174,13 @@
return plugin;
}
+hidl_array<uint8_t, 16> DrmHalTest::getUUID() {
+ if (GetParamUUID() == hidl_array<uint8_t, 16>()) {
+ return getVendorUUID();
+ }
+ return GetParamUUID();
+}
+
hidl_array<uint8_t, 16> DrmHalTest::getVendorUUID() {
if (vendorModule == nullptr) return {};
vector<uint8_t> uuid = vendorModule->getUUID();
@@ -509,7 +520,7 @@
/**
* Helper method to test decryption with invalid keys is returned
*/
-void DrmHalClearkeyTest::decryptWithInvalidKeys(
+void DrmHalClearkeyTestV1_2::decryptWithInvalidKeys(
hidl_vec<uint8_t>& invalidResponse,
vector<uint8_t>& iv,
const Pattern& noPattern,
diff --git a/drm/1.2/vts/functional/drm_hal_test.cpp b/drm/1.2/vts/functional/drm_hal_test.cpp
index 71296dc..0dfff26 100644
--- a/drm/1.2/vts/functional/drm_hal_test.cpp
+++ b/drm/1.2/vts/functional/drm_hal_test.cpp
@@ -17,13 +17,13 @@
#define LOG_TAG "drm_hal_test@1.2"
#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
#include <hidl/HidlSupport.h>
#include <hidl/ServiceManagement.h>
#include <log/log.h>
#include <openssl/aes.h>
+#include <vector>
-#include "drm_hal_common.h"
+#include "android/hardware/drm/1.2/vts/drm_hal_common.h"
using ::android::hardware::drm::V1_0::Status;
using ::android::hardware::drm::V1_1::KeyRequestType;
@@ -34,12 +34,13 @@
using ::android::hardware::drm::V1_2::KeyStatusType;
using ::android::hardware::drm::V1_2::OfflineLicenseState;
-using ::android::hardware::drm::V1_2::vts::DrmHalClearkeyTest;
+using ::android::hardware::drm::V1_2::vts::DrmHalClearkeyTestV1_2;
using ::android::hardware::drm::V1_2::vts::DrmHalPluginListener;
using ::android::hardware::drm::V1_2::vts::DrmHalTest;
using ::android::hardware::drm::V1_2::vts::kCallbackLostState;
using ::android::hardware::drm::V1_2::vts::kCallbackKeysChange;
+using ::android::hardware::hidl_array;
using ::android::hardware::hidl_string;
static const char* const kVideoMp4 = "video/mp4";
@@ -54,7 +55,7 @@
* Ensure drm factory supports module UUID Scheme
*/
TEST_P(DrmHalTest, VendorUuidSupported) {
- auto res = drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kVideoMp4, kSwSecureCrypto);
+ auto res = drmFactory->isCryptoSchemeSupported_1_2(getUUID(), kVideoMp4, kSwSecureCrypto);
ALOGI("kVideoMp4 = %s res %d", kVideoMp4, (bool)res);
EXPECT_TRUE(res);
}
@@ -82,7 +83,7 @@
* Ensure drm factory doesn't support an invalid mime type
*/
TEST_P(DrmHalTest, BadMimeNotSupported) {
- EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kBadMime, kSwSecureCrypto));
+ EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getUUID(), kBadMime, kSwSecureCrypto));
}
/**
@@ -398,14 +399,14 @@
/**
* Ensure clearkey drm factory doesn't support security level higher than supported
*/
-TEST_P(DrmHalClearkeyTest, BadLevelNotSupported) {
- EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kVideoMp4, kHwSecureAll));
+TEST_P(DrmHalClearkeyTestV1_2, BadLevelNotSupported) {
+ EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getUUID(), kVideoMp4, kHwSecureAll));
}
/**
* Test resource contention during attempt to generate key request
*/
-TEST_P(DrmHalClearkeyTest, GetKeyRequestResourceContention) {
+TEST_P(DrmHalClearkeyTestV1_2, GetKeyRequestResourceContention) {
Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorResourceContention);
EXPECT_EQ(Status::OK, status);
auto sessionId = openSession();
@@ -426,7 +427,7 @@
/**
* Test clearkey plugin offline key with mock error
*/
-TEST_P(DrmHalClearkeyTest, OfflineLicenseInvalidState) {
+TEST_P(DrmHalClearkeyTestV1_2, OfflineLicenseInvalidState) {
auto sessionId = openSession();
hidl_vec<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorInvalidState);
@@ -447,7 +448,7 @@
/**
* Test SessionLostState is triggered on error
*/
-TEST_P(DrmHalClearkeyTest, SessionLostState) {
+TEST_P(DrmHalClearkeyTestV1_2, SessionLostState) {
sp<DrmHalPluginListener> listener = new DrmHalPluginListener();
auto res = drmPlugin->setListener(listener);
EXPECT_OK(res);
@@ -467,7 +468,7 @@
/**
* Negative decrypt test. Decrypt with invalid key.
*/
-TEST_P(DrmHalClearkeyTest, DecryptWithEmptyKey) {
+TEST_P(DrmHalClearkeyTestV1_2, DecryptWithEmptyKey) {
vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
const Pattern noPattern = {0, 0};
const uint32_t kClearBytes = 512;
@@ -504,7 +505,7 @@
/**
* Negative decrypt test. Decrypt with a key exceeds AES_BLOCK_SIZE.
*/
-TEST_P(DrmHalClearkeyTest, DecryptWithKeyTooLong) {
+TEST_P(DrmHalClearkeyTestV1_2, DecryptWithKeyTooLong) {
vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
const Pattern noPattern = {0, 0};
const uint32_t kClearBytes = 512;
@@ -531,43 +532,3 @@
memcpy(invalidResponse.data(), keyTooLongResponse.c_str(), kKeyTooLongResponseSize);
decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples);
}
-
-/**
- * Instantiate the set of test cases for each vendor module
- */
-
-static const std::set<std::string> kAllInstances = [] {
- using ::android::hardware::drm::V1_2::ICryptoFactory;
- using ::android::hardware::drm::V1_2::IDrmFactory;
-
- std::vector<std::string> drmInstances =
- android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
- std::vector<std::string> cryptoInstances =
- android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
- std::set<std::string> allInstances;
- allInstances.insert(drmInstances.begin(), drmInstances.end());
- allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
- return allInstances;
-}();
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalTest, testing::ValuesIn(kAllInstances),
- android::hardware::PrintInstanceNameToString);
-INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyTest, testing::ValuesIn(kAllInstances),
- android::hardware::PrintInstanceNameToString);
-
-int main(int argc, char** argv) {
-#if defined(__LP64__)
- const char* kModulePath = "/data/local/tmp/64/lib";
-#else
- const char* kModulePath = "/data/local/tmp/32/lib";
-#endif
- DrmHalTest::gVendorModules = new drm_vts::VendorModules(kModulePath);
- if (DrmHalTest::gVendorModules->getPathList().size() == 0) {
- std::cerr << "WARNING: No vendor modules found in " << kModulePath <<
- ", all vendor tests will be skipped" << std::endl;
- }
- ::testing::InitGoogleTest(&argc, argv);
- int status = RUN_ALL_TESTS();
- ALOGI("Test result = %d", status);
- return status;
-}
diff --git a/drm/1.2/vts/functional/drm_hal_test_main.cpp b/drm/1.2/vts/functional/drm_hal_test_main.cpp
new file mode 100644
index 0000000..ea6e63d
--- /dev/null
+++ b/drm/1.2/vts/functional/drm_hal_test_main.cpp
@@ -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.
+ */
+
+/**
+ * Instantiate the set of test cases for each vendor module
+ */
+
+#define LOG_TAG "drm_hal_test@1.2"
+
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android/hardware/drm/1.2/vts/drm_hal_common.h"
+
+using android::hardware::drm::V1_2::vts::DrmHalTest;
+using android::hardware::drm::V1_2::vts::DrmHalClearkeyTestV1_2;
+using drm_vts::DrmHalTestParam;
+using drm_vts::PrintParamInstanceToString;
+
+static const std::vector<DrmHalTestParam> kAllInstances = [] {
+ using ::android::hardware::drm::V1_2::ICryptoFactory;
+ using ::android::hardware::drm::V1_2::IDrmFactory;
+
+ std::vector<std::string> drmInstances =
+ android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
+ std::vector<std::string> cryptoInstances =
+ android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
+ std::set<std::string> allInstances;
+ allInstances.insert(drmInstances.begin(), drmInstances.end());
+ allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
+
+ std::vector<DrmHalTestParam> allInstanceUuidCombos;
+ auto noUUID = [](std::string s) { return DrmHalTestParam(s); };
+ std::transform(allInstances.begin(), allInstances.end(),
+ std::back_inserter(allInstanceUuidCombos), noUUID);
+ return allInstanceUuidCombos;
+}();
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalTest, testing::ValuesIn(kAllInstances),
+ PrintParamInstanceToString);
+INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyTestV1_2, testing::ValuesIn(kAllInstances),
+ PrintParamInstanceToString);
+
+int main(int argc, char** argv) {
+#if defined(__LP64__)
+ const char* kModulePath = "/data/local/tmp/64/lib";
+#else
+ const char* kModulePath = "/data/local/tmp/32/lib";
+#endif
+ DrmHalTest::gVendorModules = new drm_vts::VendorModules(kModulePath);
+ if (DrmHalTest::gVendorModules->getPathList().size() == 0) {
+ std::cerr << "WARNING: No vendor modules found in " << kModulePath <<
+ ", all vendor tests will be skipped" << std::endl;
+ }
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ ALOGI("Test result = %d", status);
+ return status;
+}
diff --git a/drm/1.2/vts/functional/drm_hal_common.h b/drm/1.2/vts/functional/include/android/hardware/drm/1.2/vts/drm_hal_common.h
similarity index 92%
rename from drm/1.2/vts/functional/drm_hal_common.h
rename to drm/1.2/vts/functional/include/android/hardware/drm/1.2/vts/drm_hal_common.h
index 6b71aa4..6fee6f4 100644
--- a/drm/1.2/vts/functional/drm_hal_common.h
+++ b/drm/1.2/vts/functional/include/android/hardware/drm/1.2/vts/drm_hal_common.h
@@ -23,14 +23,19 @@
#include <android/hardware/drm/1.2/IDrmPlugin.h>
#include <android/hardware/drm/1.2/IDrmPluginListener.h>
#include <android/hardware/drm/1.2/types.h>
+#include <hidl/HidlSupport.h>
+#include <array>
#include <chrono>
+# include <iostream>
#include <map>
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include "drm_hal_vendor_module_api.h"
+#include "drm_vts_helper.h"
#include "vendor_modules.h"
#include "VtsHalHidlTargetCallbackBase.h"
@@ -41,13 +46,10 @@
using ::android::hardware::drm::V1_0::Pattern;
using ::android::hardware::drm::V1_0::SessionId;
using ::android::hardware::drm::V1_0::SubSample;
+using ::android::hardware::drm::V1_1::SecurityLevel;
using KeyStatusV1_0 = ::android::hardware::drm::V1_0::KeyStatus;
using StatusV1_0 = ::android::hardware::drm::V1_0::Status;
-
-using ::android::hardware::drm::V1_1::ICryptoFactory;
-using ::android::hardware::drm::V1_1::SecurityLevel;
-
using StatusV1_2 = ::android::hardware::drm::V1_2::Status;
using ::android::hardware::hidl_array;
@@ -58,7 +60,11 @@
using ::android::hidl::memory::V1_0::IMemory;
using ::android::sp;
+using drm_vts::DrmHalTestParam;
+
+using std::array;
using std::map;
+using std::pair;
using std::string;
using std::unique_ptr;
using std::vector;
@@ -71,7 +77,7 @@
namespace V1_2 {
namespace vts {
-class DrmHalTest : public ::testing::TestWithParam<std::string> {
+class DrmHalTest : public ::testing::TestWithParam<DrmHalTestParam> {
public:
static drm_vts::VendorModules* gVendorModules;
DrmHalTest();
@@ -79,7 +85,10 @@
virtual void TearDown() override {}
protected:
+ hidl_array<uint8_t, 16> getUUID();
hidl_array<uint8_t, 16> getVendorUUID();
+ hidl_array<uint8_t, 16> GetParamUUID() { return GetParam().scheme_; }
+ string GetParamService() { return GetParam().instance_; }
void provision();
SessionId openSession(SecurityLevel level, StatusV1_0* err);
SessionId openSession();
@@ -124,7 +133,7 @@
};
-class DrmHalClearkeyTest : public DrmHalTest {
+class DrmHalClearkeyTestV1_2 : public DrmHalTest {
public:
virtual void SetUp() override {
DrmHalTest::SetUp();
@@ -132,7 +141,7 @@
0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9,
0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E};
if (!drmFactory->isCryptoSchemeSupported(kClearKeyUUID)) {
- GTEST_SKIP() << "ClearKey not supported by " << GetParam();
+ GTEST_SKIP() << "ClearKey not supported by " << GetParamService();
}
}
virtual void TearDown() override {}
diff --git a/drm/1.2/vts/functional/vendor_modules.cpp b/drm/1.2/vts/functional/vendor_modules.cpp
deleted file mode 100644
index 08131cf..0000000
--- a/drm/1.2/vts/functional/vendor_modules.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "drm-vts-vendor-modules"
-
-#include <dirent.h>
-#include <dlfcn.h>
-#include <log/log.h>
-#include <memory>
-#include <utils/String8.h>
-#include <SharedLibrary.h>
-
-#include "drm_hal_vendor_module_api.h"
-#include "vendor_modules.h"
-
-using std::string;
-using std::vector;
-using std::unique_ptr;
-using ::android::String8;
-using ::android::hardware::drm::V1_0::helper::SharedLibrary;
-
-namespace drm_vts {
-void VendorModules::scanModules(const std::string &directory) {
- DIR* dir = opendir(directory.c_str());
- if (dir == NULL) {
- ALOGE("Unable to open drm VTS vendor directory %s", directory.c_str());
- } else {
- struct dirent* entry;
- while ((entry = readdir(dir))) {
- ALOGD("checking file %s", entry->d_name);
- string fullpath = directory + "/" + entry->d_name;
- if (endsWith(fullpath, ".so")) {
- mPathList.push_back(fullpath);
- }
- }
- closedir(dir);
- }
-}
-
-DrmHalVTSVendorModule* VendorModules::getModule(const string& path) {
- if (mOpenLibraries.find(path) == mOpenLibraries.end()) {
- auto library = std::make_unique<SharedLibrary>(String8(path.c_str()));
- if (!library) {
- ALOGE("failed to map shared library %s", path.c_str());
- return NULL;
- }
- mOpenLibraries[path] = std::move(library);
- }
- const unique_ptr<SharedLibrary>& library = mOpenLibraries[path];
- void* symbol = library->lookup("vendorModuleFactory");
- if (symbol == NULL) {
- ALOGE("getVendorModule failed to lookup 'vendorModuleFactory' in %s: "
- "%s", path.c_str(), library->lastError());
- return NULL;
- }
- typedef DrmHalVTSVendorModule* (*ModuleFactory)();
- ModuleFactory moduleFactory = reinterpret_cast<ModuleFactory>(symbol);
- return (*moduleFactory)();
-}
-
-DrmHalVTSVendorModule* VendorModules::getModuleByName(const string& name) {
- for (const auto &path : mPathList) {
- auto module = getModule(path);
- if (module->getServiceName() == name) {
- return module;
- }
- }
- return NULL;
-}
-};
diff --git a/drm/1.3/vts/OWNERS b/drm/1.3/vts/OWNERS
new file mode 100644
index 0000000..3a0672e
--- /dev/null
+++ b/drm/1.3/vts/OWNERS
@@ -0,0 +1,9 @@
+conglin@google.com
+edwinwong@google.com
+fredgc@google.com
+jtinker@google.com
+juce@google.com
+kylealexander@google.com
+rfrias@google.com
+robertshih@google.com
+sigquit@google.com
diff --git a/drm/1.3/vts/functional/Android.bp b/drm/1.3/vts/functional/Android.bp
new file mode 100644
index 0000000..4be1575
--- /dev/null
+++ b/drm/1.3/vts/functional/Android.bp
@@ -0,0 +1,78 @@
+//
+// 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: "android.hardware.drm@1.3-vts",
+ defaults: ["VtsHalTargetTestDefaults"],
+ local_include_dirs: [
+ "include",
+ ],
+ srcs: [
+ "drm_hal_test.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "android.hardware.drm@1.2",
+ "android.hardware.drm@1.3",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libhidlmemory",
+ "libnativehelper",
+ ],
+ static_libs: [
+ "android.hardware.drm@1.0-helper",
+ "libcrypto_static",
+ "libdrmvtshelper",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+}
+
+cc_test {
+ name: "VtsHalDrmV1_3TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ include_dirs: ["hardware/interfaces/drm/1.0/vts/functional"],
+ srcs: [
+ "drm_hal_test_main.cpp",
+ ],
+ whole_static_libs: [
+ "android.hardware.drm@1.0-vts",
+ "android.hardware.drm@1.1-vts",
+ "android.hardware.drm@1.2-vts",
+ "android.hardware.drm@1.3-vts",
+ ],
+ shared_libs: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "android.hardware.drm@1.2",
+ "android.hardware.drm@1.3",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libhidlmemory",
+ "libnativehelper",
+ ],
+ static_libs: [
+ "android.hardware.drm@1.0-helper",
+ "libcrypto_static",
+ "libdrmvtshelper",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
+}
diff --git a/drm/1.3/vts/functional/drm_hal_test.cpp b/drm/1.3/vts/functional/drm_hal_test.cpp
new file mode 100644
index 0000000..738f5b2
--- /dev/null
+++ b/drm/1.3/vts/functional/drm_hal_test.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 "drm_hal_test@1.3"
+
+#include "android/hardware/drm/1.3/vts/drm_hal_test.h"
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_3 {
+namespace vts {
+
+TEST_P(DrmHalTestV1_3, SchemeSupported) {
+ EXPECT_TRUE(drmFactory_->isCryptoSchemeSupported(GetParam().scheme_));
+}
+
+TEST_P(DrmHalTestV1_3, SignRsaNotAllowed) {
+ hidl_array<uint8_t, 16> kWidevineUUID ({
+ 0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE,
+ 0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED
+ });
+
+ if (!drmFactory_->isCryptoSchemeSupported(kWidevineUUID)) {
+ GTEST_SKIP() << "Widevine only test";
+ }
+
+ // signRSA
+ const hidl_vec<uint8_t>& sessionId{};
+ const hidl_string& algorithm{};
+ const hidl_vec<uint8_t>& message{};
+ const hidl_vec<uint8_t>& wrappedKey{};
+ auto res = drmPlugin_->signRSA(
+ sessionId, algorithm, message, wrappedKey,
+ [&](StatusV1_0 status, const hidl_vec<uint8_t>& signature) {
+ EXPECT_EQ(status, StatusV1_0::ERROR_DRM_UNKNOWN);
+ EXPECT_EQ(signature.size(), 0);
+ }
+ );
+ EXPECT_TRUE(res.isOk());
+}
+
+} // namespace vts
+} // namespace V1_3
+} // namespace drm
+} // namespace hardware
+} // namespace android
diff --git a/drm/1.3/vts/functional/drm_hal_test_main.cpp b/drm/1.3/vts/functional/drm_hal_test_main.cpp
new file mode 100644
index 0000000..02b45ea
--- /dev/null
+++ b/drm/1.3/vts/functional/drm_hal_test_main.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+/**
+ * Instantiate the set of test cases for each vendor module
+ */
+
+#define LOG_TAG "drm_hal_test@1.3"
+
+#include <android/hardware/drm/1.3/ICryptoFactory.h>
+#include <android/hardware/drm/1.3/IDrmFactory.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android/hardware/drm/1.0/vts/drm_hal_clearkey_test.h" // V1_0 tests
+#include "android/hardware/drm/1.0/vts/drm_hal_vendor_test.h" // V1_0 tests
+#include "android/hardware/drm/1.1/vts/drm_hal_clearkey_test.h" // V1_1 tests
+#include "android/hardware/drm/1.2/vts/drm_hal_common.h" // V1_2 tests
+#include "android/hardware/drm/1.3/vts/drm_hal_test.h" // V1_3 tests
+
+using drm_vts::DrmHalTestParam;
+using drm_vts::PrintParamInstanceToString;
+
+using android::hardware::drm::V1_0::vts::DrmHalVendorFactoryTest;
+using android::hardware::drm::V1_0::vts::DrmHalVendorPluginTest;
+using android::hardware::drm::V1_0::vts::DrmHalVendorDecryptTest;
+using android::hardware::drm::V1_0::vts::DrmHalClearkeyFactoryTest;
+using android::hardware::drm::V1_0::vts::DrmHalClearkeyPluginTest;
+using android::hardware::drm::V1_0::vts::DrmHalClearkeyDecryptTest;
+using android::hardware::drm::V1_1::vts::DrmHalClearkeyTest;
+using android::hardware::drm::V1_2::vts::DrmHalTest;
+using android::hardware::drm::V1_2::vts::DrmHalClearkeyTestV1_2;
+using android::hardware::drm::V1_3::vts::DrmHalTestV1_3;
+
+static const std::vector<DrmHalTestParam> kAllInstances = [] {
+ using ::android::hardware::drm::V1_3::ICryptoFactory;
+ using ::android::hardware::drm::V1_3::IDrmFactory;
+
+ std::vector<std::string> drmInstances =
+ android::hardware::getAllHalInstanceNames(IDrmFactory::descriptor);
+ std::vector<std::string> cryptoInstances =
+ android::hardware::getAllHalInstanceNames(ICryptoFactory::descriptor);
+ std::set<std::string> allInstances;
+ allInstances.insert(drmInstances.begin(), drmInstances.end());
+ allInstances.insert(cryptoInstances.begin(), cryptoInstances.end());
+
+ std::vector<DrmHalTestParam> allInstanceUuidCombos;
+ for (const auto &instance : allInstances) {
+ auto drmFactory = IDrmFactory::getService(instance);
+ if (drmFactory == nullptr) {
+ continue;
+ }
+ drmFactory->getSupportedCryptoSchemes(
+ [&](const hidl_vec<hidl_array<uint8_t, 16>>& schemes) {
+ for (const auto &scheme : schemes) {
+ allInstanceUuidCombos.push_back(DrmHalTestParam(instance, scheme));
+ }
+ });
+ }
+ return allInstanceUuidCombos;
+}();
+
+INSTANTIATE_TEST_CASE_P(PerInstanceUuidV1_0, DrmHalVendorFactoryTest,
+ testing::ValuesIn(kAllInstances),
+ drm_vts::PrintParamInstanceToString);
+INSTANTIATE_TEST_CASE_P(PerInstanceUuidV1_0, DrmHalVendorPluginTest,
+ testing::ValuesIn(kAllInstances),
+ drm_vts::PrintParamInstanceToString);
+INSTANTIATE_TEST_CASE_P(PerInstanceUuidV1_0, DrmHalVendorDecryptTest,
+ testing::ValuesIn(kAllInstances),
+ drm_vts::PrintParamInstanceToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_0, DrmHalClearkeyFactoryTest,
+ testing::ValuesIn(kAllInstances),
+ drm_vts::PrintParamInstanceToString);
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_0, DrmHalClearkeyPluginTest,
+ testing::ValuesIn(kAllInstances),
+ drm_vts::PrintParamInstanceToString);
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_0, DrmHalClearkeyDecryptTest,
+ testing::ValuesIn(kAllInstances),
+ drm_vts::PrintParamInstanceToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_1, DrmHalClearkeyTest,
+ testing::ValuesIn(kAllInstances),
+ PrintParamInstanceToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_2, DrmHalTest,
+ testing::ValuesIn(kAllInstances),
+ PrintParamInstanceToString);
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_2, DrmHalClearkeyTestV1_2,
+ testing::ValuesIn(kAllInstances),
+ PrintParamInstanceToString);
+
+INSTANTIATE_TEST_SUITE_P(PerInstanceUuidV1_3, DrmHalTestV1_3,
+ testing::ValuesIn(kAllInstances),
+ PrintParamInstanceToString);
+
+int main(int argc, char** argv) {
+#if defined(__LP64__)
+ const char* kModulePath = "/data/local/tmp/64/lib";
+#else
+ const char* kModulePath = "/data/local/tmp/32/lib";
+#endif
+ DrmHalTest::gVendorModules = new drm_vts::VendorModules(kModulePath);
+ if (DrmHalTest::gVendorModules->getPathList().size() == 0) {
+ std::cerr << "WARNING: No vendor modules found in " << kModulePath <<
+ ", all vendor tests will be skipped" << std::endl;
+ }
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ ALOGI("Test result = %d", status);
+ return status;
+}
diff --git a/drm/1.3/vts/functional/include/android/hardware/drm/1.3/vts/drm_hal_test.h b/drm/1.3/vts/functional/include/android/hardware/drm/1.3/vts/drm_hal_test.h
new file mode 100644
index 0000000..d8f5277
--- /dev/null
+++ b/drm/1.3/vts/functional/include/android/hardware/drm/1.3/vts/drm_hal_test.h
@@ -0,0 +1,86 @@
+/*
+ * 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 DRM_HAL_TEST_V1_3_H
+#define DRM_HAL_TEST_V1_3_H
+
+#include <android/hardware/drm/1.3/ICryptoFactory.h>
+#include <android/hardware/drm/1.3/IDrmFactory.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "drm_hal_vendor_module_api.h"
+#include "drm_vts_helper.h"
+#include "vendor_modules.h"
+#include "VtsHalHidlTargetCallbackBase.h"
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_3 {
+namespace vts {
+
+using android::hardware::hidl_array;
+using android::hardware::hidl_string;
+
+using drm_vts::DrmHalTestParam;
+
+using IDrmFactoryV1_3 = android::hardware::drm::V1_3::IDrmFactory;
+using IDrmPluginV1_0 = android::hardware::drm::V1_0::IDrmPlugin;
+using StatusV1_0 = android::hardware::drm::V1_0::Status;
+
+class DrmHalTestV1_3 : public ::testing::TestWithParam<DrmHalTestParam> {
+public:
+ DrmHalTestV1_3()
+ : drmFactory_(IDrmFactoryV1_3::getService(GetParam().instance_)) {}
+
+ virtual void SetUp() override {
+ ASSERT_NE(drmFactory_, nullptr);
+
+ // create plugin
+ hidl_string packageName("android.hardware.drm.V1_3.vts");
+ auto res = drmFactory_->createPlugin(
+ GetParam().scheme_, packageName,
+ [&](StatusV1_0 status, const sp<IDrmPluginV1_0>& pluginV1_0) {
+ EXPECT_EQ(StatusV1_0::OK, status);
+ drmPlugin_ = pluginV1_0;
+ });
+ EXPECT_TRUE(res.isOk());
+ ASSERT_NE(drmPlugin_, nullptr);
+ }
+
+ virtual void TearDown() override {}
+
+protected:
+ sp<IDrmFactoryV1_3> drmFactory_;
+ sp<IDrmPluginV1_0> drmPlugin_;
+};
+
+} // namespace vts
+} // namespace V1_3
+} // namespace drm
+} // namespace hardware
+} // namespace android
+
+#endif // DRM_HAL_TEST_V1_3_H
diff --git a/dumpstate/1.1/IDumpstateDevice.hal b/dumpstate/1.1/IDumpstateDevice.hal
index 24831b3..183404d 100644
--- a/dumpstate/1.1/IDumpstateDevice.hal
+++ b/dumpstate/1.1/IDumpstateDevice.hal
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.hardware.dumpstate@1.1;
import @1.0::IDumpstateDevice;
@@ -28,17 +29,23 @@
* The 1.0 version of #dumpstateBoard(handle) should just delegate to this new method and pass
* DumpstateMode::DEFAULT and a timeout of 30,000ms (30 seconds).
*
+ * This method may still be called by the dumpstate routine even if getVerboseLoggingEnabled
+ * returns false. In this case, it may include essential information but must not include
+ * information that identifies the user.
+ *
* @param h A native handle with one or two valid file descriptors. The first FD is for text
* output, the second (if present) is for binary output.
* @param mode A mode value to restrict dumped content.
* @param timeoutMillis An approximate "budget" for how much time this call has been allotted.
* If execution runs longer than this, the IDumpstateDevice service may be killed and only
* partial information will be included in the report.
+ * @return status A DumpstateStatus value indicating the final result.
*/
- dumpstateBoard_1_1(handle h, DumpstateMode mode, uint64_t timeoutMillis);
+ dumpstateBoard_1_1(handle h, DumpstateMode mode, uint64_t timeoutMillis)
+ generates (DumpstateStatus status);
/**
- * Turns device vendor logging on or off.
+ * Turns verbose device vendor logging on or off.
*
* The setting should be persistent across reboots. Underlying implementations may need to start
* vendor logging daemons, set system properties, or change logging masks, for example. Given
@@ -46,8 +53,21 @@
* memory/storage/battery impacts, calling this method on a user build should only be done after
* user consent has been obtained, e.g. from a toggle in developer settings.
*
- * @param enable Whether to enable or disable device vendor logging.
- * @return success Whether or not the change took effect.
+ * Even if verbose logging has been disabled, dumpstateBoard may still be called by the
+ * dumpstate routine, and essential information that does not identify the user may be included.
+ *
+ * @param enable Whether to enable or disable verbose vendor logging.
*/
- setDeviceLoggingEnabled(bool enable) generates (bool success);
+ setVerboseLoggingEnabled(bool enable);
+
+ /**
+ * Queries the current state of verbose device logging. Primarily for UI and informative
+ * purposes.
+ *
+ * Even if verbose logging has been disabled, dumpstateBoard may still be called by the
+ * dumpstate routine, and essential information that does not identify the user may be included.
+ *
+ * @return enabled Whether or not verbose vendor logging is currently enabled.
+ */
+ getVerboseLoggingEnabled() generates (bool enabled);
};
diff --git a/dumpstate/1.1/types.hal b/dumpstate/1.1/types.hal
index a6f391a..c522f7c 100644
--- a/dumpstate/1.1/types.hal
+++ b/dumpstate/1.1/types.hal
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.hardware.dumpstate@1.1;
/**
@@ -23,22 +24,18 @@
* Takes a bug report without user interference.
*/
FULL = 0,
-
/**
* Interactive bug report, i.e. triggered by the user.
*/
INTERACTIVE = 1,
-
/**
* Remote bug report triggered by DevicePolicyManager, for example.
*/
REMOTE = 2,
-
/**
* Bug report triggered on a wear device.
*/
WEAR = 3,
-
/**
* Bug report limited to only connectivity info (cellular, wifi, and networking). Sometimes
* called "telephony" in legacy contexts.
@@ -49,14 +46,41 @@
* user application traffic.
*/
CONNECTIVITY = 4,
-
/**
* Bug report limited to only wifi info.
*/
WIFI = 5,
-
/**
- * Default mode.
+ * Default mode, essentially analogous to calling @1.0::IDumpstateDevice.dumpstateBoard(handle).
+ * This mode MUST be supported if the dumpstate HAL is implemented.
*/
- DEFAULT = 6
+ DEFAULT = 6,
+ /**
+ * Takes a report in protobuf.
+ *
+ * The content, if implemented, must be a binary protobuf message written to the first file
+ * descriptor of the native handle. The protobuf schema shall be defined by the vendor.
+ */
+ PROTO = 7,
+};
+
+/**
+ * A simple return enum for use with dumpstateBoard_1_1.
+ */
+enum DumpstateStatus : uint32_t {
+ OK = 0,
+ /**
+ * Returned for cases where the device doesn't support the given DumpstateMode (e.g. a phone
+ * trying to use DumpstateMode::WEAR).
+ */
+ UNSUPPORTED_MODE = 1,
+ /**
+ * Returned for cases where an IllegalArgumentException is typically appropriate, e.g. missing
+ * file descriptors.
+ */
+ ILLEGAL_ARGUMENT = 2,
+ /**
+ * Returned when device logging is not enabled.
+ */
+ DEVICE_LOGGING_NOT_ENABLED = 3,
};
diff --git a/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
index 3b6051c..cbdd87b 100644
--- a/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
+++ b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
@@ -18,6 +18,8 @@
#include <fcntl.h>
#include <unistd.h>
+
+#include <functional>
#include <vector>
#include <android/hardware/dumpstate/1.1/IDumpstateDevice.h>
@@ -28,24 +30,61 @@
#include <hidl/ServiceManagement.h>
#include <log/log.h>
+namespace {
+
using ::android::sp;
using ::android::hardware::Return;
using ::android::hardware::dumpstate::V1_1::DumpstateMode;
+using ::android::hardware::dumpstate::V1_1::DumpstateStatus;
using ::android::hardware::dumpstate::V1_1::IDumpstateDevice;
+using ::android::hardware::dumpstate::V1_1::toString;
class DumpstateHidl1_1Test : public ::testing::TestWithParam<std::string> {
- public:
- virtual void SetUp() override {
+ protected:
+ virtual void SetUp() override { GetService(); }
+
+ void GetService() {
dumpstate = IDumpstateDevice::getService(GetParam());
ASSERT_NE(dumpstate, nullptr) << "Could not get HIDL instance";
}
+ void ToggleVerboseLogging(bool enable) {
+ Return<void> status = dumpstate->setVerboseLoggingEnabled(enable);
+ ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+
+ if (!dumpstate->ping().isOk()) {
+ ALOGW("IDumpstateDevice service appears to have exited lazily, attempting to get "
+ "again");
+ GetService();
+ }
+
+ Return<bool> logging_enabled = dumpstate->getVerboseLoggingEnabled();
+ ASSERT_TRUE(logging_enabled.isOk())
+ << "Status should be ok: " << logging_enabled.description();
+ ASSERT_EQ(logging_enabled, enable)
+ << "Verbose logging should now be " << (enable ? "enabled" : "disabled");
+
+ if (!dumpstate->ping().isOk()) {
+ ALOGW("IDumpstateDevice service appears to have exited lazily, attempting to get "
+ "again");
+ GetService();
+ }
+ }
+
+ void EnableVerboseLogging() { ToggleVerboseLogging(true); }
+
+ void DisableVerboseLogging() { ToggleVerboseLogging(false); }
+
sp<IDumpstateDevice> dumpstate;
};
#define TEST_FOR_DUMPSTATE_MODE(name, body, mode) \
TEST_P(DumpstateHidl1_1Test, name##_##mode) { body(DumpstateMode::mode); }
+// We use a macro to define individual test cases instead of hidl_enum_range<> because some HAL
+// implementations are lazy and may call exit() at the end of dumpstateBoard(), which would cause
+// DEAD_OBJECT errors after the first iteration. Separate cases re-get the service each time as part
+// of SetUp(), and also provide better separation of concerns when specific modes are problematic.
#define TEST_FOR_ALL_DUMPSTATE_MODES(name, body) \
TEST_FOR_DUMPSTATE_MODE(name, body, FULL); \
TEST_FOR_DUMPSTATE_MODE(name, body, INTERACTIVE); \
@@ -53,25 +92,56 @@
TEST_FOR_DUMPSTATE_MODE(name, body, WEAR); \
TEST_FOR_DUMPSTATE_MODE(name, body, CONNECTIVITY); \
TEST_FOR_DUMPSTATE_MODE(name, body, WIFI); \
- TEST_FOR_DUMPSTATE_MODE(name, body, DEFAULT);
+ TEST_FOR_DUMPSTATE_MODE(name, body, DEFAULT); \
+ TEST_FOR_DUMPSTATE_MODE(name, body, PROTO);
-const uint64_t kDefaultTimeoutMillis = 30 * 1000; // 30 seconds
+constexpr uint64_t kDefaultTimeoutMillis = 30 * 1000; // 30 seconds
+
+// Will only execute additional_assertions when status == expected.
+void AssertStatusForMode(const DumpstateMode mode, const Return<DumpstateStatus>& status,
+ const DumpstateStatus expected,
+ std::function<void()> additional_assertions = nullptr) {
+ ASSERT_TRUE(status.isOk()) << "Status should be ok and return a more specific DumpstateStatus: "
+ << status.description();
+ if (mode == DumpstateMode::DEFAULT) {
+ ASSERT_EQ(expected, status) << "Required mode (DumpstateMode::" << toString(mode)
+ << "): status should be DumpstateStatus::" << toString(expected)
+ << ", but got DumpstateStatus::" << toString(status);
+ } else {
+ // The rest of the modes are optional to support, but they MUST return either the expected
+ // value or UNSUPPORTED_MODE.
+ ASSERT_TRUE(status == expected || status == DumpstateStatus::UNSUPPORTED_MODE)
+ << "Optional mode (DumpstateMode::" << toString(mode)
+ << "): status should be DumpstateStatus::" << toString(expected)
+ << " or DumpstateStatus::UNSUPPORTED_MODE, but got DumpstateStatus::"
+ << toString(status);
+ }
+ if (status == expected && additional_assertions != nullptr) {
+ additional_assertions();
+ }
+}
// Negative test: make sure dumpstateBoard() doesn't crash when passed a null pointer.
TEST_FOR_ALL_DUMPSTATE_MODES(TestNullHandle, [this](DumpstateMode mode) {
- Return<void> status = dumpstate->dumpstateBoard_1_1(nullptr, mode, kDefaultTimeoutMillis);
+ EnableVerboseLogging();
- ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+ Return<DumpstateStatus> status =
+ dumpstate->dumpstateBoard_1_1(nullptr, mode, kDefaultTimeoutMillis);
+
+ AssertStatusForMode(mode, status, DumpstateStatus::ILLEGAL_ARGUMENT);
});
// Negative test: make sure dumpstateBoard() ignores a handle with no FD.
TEST_FOR_ALL_DUMPSTATE_MODES(TestHandleWithNoFd, [this](DumpstateMode mode) {
+ EnableVerboseLogging();
+
native_handle_t* handle = native_handle_create(0, 0);
ASSERT_NE(handle, nullptr) << "Could not create native_handle";
- Return<void> status = dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+ Return<DumpstateStatus> status =
+ dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
- ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+ AssertStatusForMode(mode, status, DumpstateStatus::ILLEGAL_ARGUMENT);
native_handle_close(handle);
native_handle_delete(handle);
@@ -79,6 +149,8 @@
// Positive test: make sure dumpstateBoard() writes something to the FD.
TEST_FOR_ALL_DUMPSTATE_MODES(TestOk, [this](DumpstateMode mode) {
+ EnableVerboseLogging();
+
// Index 0 corresponds to the read end of the pipe; 1 to the write end.
int fds[2];
ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno;
@@ -87,12 +159,14 @@
ASSERT_NE(handle, nullptr) << "Could not create native_handle";
handle->data[0] = fds[1];
- Return<void> status = dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
- ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+ Return<DumpstateStatus> status =
+ dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
- // Check that at least one byte was written
- char buff;
- ASSERT_EQ(1, read(fds[0], &buff, 1)) << "dumped nothing";
+ AssertStatusForMode(mode, status, DumpstateStatus::OK, [&fds]() {
+ // Check that at least one byte was written.
+ char buff;
+ ASSERT_EQ(1, read(fds[0], &buff, 1)) << "Dumped nothing";
+ });
native_handle_close(handle);
native_handle_delete(handle);
@@ -100,6 +174,8 @@
// Positive test: make sure dumpstateBoard() doesn't crash with two FDs.
TEST_FOR_ALL_DUMPSTATE_MODES(TestHandleWithTwoFds, [this](DumpstateMode mode) {
+ EnableVerboseLogging();
+
int fds1[2];
int fds2[2];
ASSERT_EQ(0, pipe2(fds1, O_NONBLOCK)) << errno;
@@ -110,8 +186,17 @@
handle->data[0] = fds1[1];
handle->data[1] = fds2[1];
- Return<void> status = dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
- ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+ Return<DumpstateStatus> status =
+ dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+
+ AssertStatusForMode(mode, status, DumpstateStatus::OK, [&fds1, &fds2]() {
+ // Check that at least one byte was written to one of the FDs.
+ char buff;
+ size_t read1 = read(fds1[0], &buff, 1);
+ size_t read2 = read(fds2[0], &buff, 1);
+ // Sometimes read returns -1, so we can't just add them together and expect >= 1.
+ ASSERT_TRUE(read1 == 1 || read2 == 1) << "Dumped nothing";
+ });
native_handle_close(handle);
native_handle_delete(handle);
@@ -119,6 +204,8 @@
// Make sure dumpstateBoard_1_1 actually validates its arguments.
TEST_P(DumpstateHidl1_1Test, TestInvalidModeArgument_Negative) {
+ EnableVerboseLogging();
+
int fds[2];
ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno;
@@ -126,16 +213,21 @@
ASSERT_NE(handle, nullptr) << "Could not create native_handle";
handle->data[0] = fds[1];
- Return<void> status = dumpstate->dumpstateBoard_1_1(handle, static_cast<DumpstateMode>(-100),
- kDefaultTimeoutMillis);
- ASSERT_FALSE(status.isOk()) << "Status should not be ok with invalid mode param: "
- << status.description();
+ Return<DumpstateStatus> status = dumpstate->dumpstateBoard_1_1(
+ handle, static_cast<DumpstateMode>(-100), kDefaultTimeoutMillis);
+
+ ASSERT_TRUE(status.isOk()) << "Status should be ok and return a more specific DumpstateStatus: "
+ << status.description();
+ ASSERT_EQ(status, DumpstateStatus::ILLEGAL_ARGUMENT)
+ << "Should return DumpstateStatus::ILLEGAL_ARGUMENT for invalid mode param";
native_handle_close(handle);
native_handle_delete(handle);
}
TEST_P(DumpstateHidl1_1Test, TestInvalidModeArgument_Undefined) {
+ EnableVerboseLogging();
+
int fds[2];
ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno;
@@ -143,29 +235,89 @@
ASSERT_NE(handle, nullptr) << "Could not create native_handle";
handle->data[0] = fds[1];
- Return<void> status = dumpstate->dumpstateBoard_1_1(handle, static_cast<DumpstateMode>(9001),
- kDefaultTimeoutMillis);
- ASSERT_FALSE(status.isOk()) << "Status should not be ok with invalid mode param: "
- << status.description();
+ Return<DumpstateStatus> status = dumpstate->dumpstateBoard_1_1(
+ handle, static_cast<DumpstateMode>(9001), kDefaultTimeoutMillis);
+
+ ASSERT_TRUE(status.isOk()) << "Status should be ok and return a more specific DumpstateStatus: "
+ << status.description();
+ ASSERT_EQ(status, DumpstateStatus::ILLEGAL_ARGUMENT)
+ << "Should return DumpstateStatus::ILLEGAL_ARGUMENT for invalid mode param";
native_handle_close(handle);
native_handle_delete(handle);
}
-// Make sure toggling device logging doesn't crash.
-TEST_P(DumpstateHidl1_1Test, TestEnableDeviceLogging) {
- Return<bool> status = dumpstate->setDeviceLoggingEnabled(true);
+// Positive test: make sure dumpstateBoard() from 1.0 doesn't fail.
+TEST_P(DumpstateHidl1_1Test, Test1_0MethodOk) {
+ EnableVerboseLogging();
+
+ int fds[2];
+ ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno;
+
+ native_handle_t* handle = native_handle_create(1, 0);
+ ASSERT_NE(handle, nullptr) << "Could not create native_handle";
+ handle->data[0] = fds[1];
+
+ Return<void> status = dumpstate->dumpstateBoard(handle);
ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+
+ // Check that at least one byte was written.
+ char buff;
+ ASSERT_EQ(1, read(fds[0], &buff, 1)) << "Dumped nothing";
+
+ native_handle_close(handle);
+ native_handle_delete(handle);
}
-TEST_P(DumpstateHidl1_1Test, TestDisableDeviceLogging) {
- Return<bool> status = dumpstate->setDeviceLoggingEnabled(false);
+// Make sure disabling verbose logging behaves correctly. Some info is still allowed to be emitted,
+// but it can't have privacy/storage/battery impacts.
+TEST_FOR_ALL_DUMPSTATE_MODES(TestVerboseLoggingDisabled, [this](DumpstateMode mode) {
+ DisableVerboseLogging();
- ASSERT_TRUE(status.isOk()) << "Status should be ok: " << status.description();
+ // Index 0 corresponds to the read end of the pipe; 1 to the write end.
+ int fds[2];
+ ASSERT_EQ(0, pipe2(fds, O_NONBLOCK)) << errno;
+
+ native_handle_t* handle = native_handle_create(1, 0);
+ ASSERT_NE(handle, nullptr) << "Could not create native_handle";
+ handle->data[0] = fds[1];
+
+ Return<DumpstateStatus> status =
+ dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+
+ // We don't include additional assertions here about the file passed in. If verbose logging is
+ // disabled, the OEM may choose to include nothing at all, but it is allowed to include some
+ // essential information based on the mode as long as it isn't private user information.
+ AssertStatusForMode(mode, status, DumpstateStatus::OK);
+
+ native_handle_close(handle);
+ native_handle_delete(handle);
+});
+
+// Double-enable is perfectly valid, but the second call shouldn't do anything.
+TEST_P(DumpstateHidl1_1Test, TestRepeatedEnable) {
+ EnableVerboseLogging();
+ EnableVerboseLogging();
+}
+
+// Double-disable is perfectly valid, but the second call shouldn't do anything.
+TEST_P(DumpstateHidl1_1Test, TestRepeatedDisable) {
+ DisableVerboseLogging();
+ DisableVerboseLogging();
+}
+
+// Toggling in short order is perfectly valid.
+TEST_P(DumpstateHidl1_1Test, TestRepeatedToggle) {
+ EnableVerboseLogging();
+ DisableVerboseLogging();
+ EnableVerboseLogging();
+ DisableVerboseLogging();
}
INSTANTIATE_TEST_SUITE_P(
PerInstance, DumpstateHidl1_1Test,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(IDumpstateDevice::descriptor)),
android::hardware::PrintInstanceNameToString);
+
+} // namespace
diff --git a/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp b/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp
index 6183a1a..c04b4d0 100644
--- a/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp
+++ b/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp
@@ -26,6 +26,8 @@
#include <condition_variable>
#include <mutex>
+#include <cutils/properties.h>
+
using android::hardware::Return;
using android::hardware::Void;
@@ -37,6 +39,12 @@
using android::hardware::gnss::V1_0::IGnssMeasurement;
using android::sp;
+static bool IsAutomotiveDevice() {
+ char buffer[PROPERTY_VALUE_MAX] = {0};
+ property_get("ro.hardware.type", buffer, "");
+ return strncmp(buffer, "automotive", PROPERTY_VALUE_MAX) == 0;
+}
+
#define TIMEOUT_SEC 2 // for basic commands/responses
// for command line argument on how strictly to run the test
@@ -473,9 +481,9 @@
auto gnssDebug = gnss_hal_->getExtensionGnssDebug();
ASSERT_TRUE(gnssDebug.isOk());
- if (info_called_count_ > 0 && last_info_.yearOfHw >= 2017) {
- sp<IGnssDebug> iGnssDebug = gnssDebug;
- EXPECT_NE(iGnssDebug, nullptr);
+ if (!IsAutomotiveDevice() && info_called_count_ > 0 && last_info_.yearOfHw >= 2017) {
+ sp<IGnssDebug> iGnssDebug = gnssDebug;
+ EXPECT_NE(iGnssDebug, nullptr);
}
}
diff --git a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
index e0d8b54..8530ffb 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
@@ -19,6 +19,7 @@
#include <gnss_hal_test.h>
#include <android/hardware/gnss/1.1/IGnssConfiguration.h>
+#include <cutils/properties.h>
#include <gtest/gtest.h>
using android::hardware::hidl_vec;
@@ -32,6 +33,12 @@
using android::hardware::gnss::V1_1::IGnssConfiguration;
using android::hardware::gnss::V1_1::IGnssMeasurement;
+static bool IsAutomotiveDevice() {
+ char buffer[PROPERTY_VALUE_MAX] = {0};
+ property_get("ro.hardware.type", buffer, "");
+ return strncmp(buffer, "automotive", PROPERTY_VALUE_MAX) == 0;
+}
+
/*
* SetupTeardownCreateCleanup:
* Requests the gnss HAL then calls cleanup
@@ -524,7 +531,8 @@
TEST_P(GnssHalTest, GnssDebugValuesSanityTest) {
auto gnssDebug = gnss_hal_->getExtensionGnssDebug();
ASSERT_TRUE(gnssDebug.isOk());
- if (gnss_cb_->info_cbq_.calledCount() > 0 && gnss_cb_->last_info_.yearOfHw >= 2017) {
+ if (!IsAutomotiveDevice() && gnss_cb_->info_cbq_.calledCount() > 0 &&
+ gnss_cb_->last_info_.yearOfHw >= 2017) {
sp<IGnssDebug> iGnssDebug = gnssDebug;
EXPECT_NE(iGnssDebug, nullptr);
diff --git a/gnss/2.1/Android.bp b/gnss/2.1/Android.bp
index 7efc4a6..2122399 100644
--- a/gnss/2.1/Android.bp
+++ b/gnss/2.1/Android.bp
@@ -9,6 +9,8 @@
srcs: [
"types.hal",
"IGnss.hal",
+ "IGnssAntennaInfo.hal",
+ "IGnssAntennaInfoCallback.hal",
"IGnssCallback.hal",
"IGnssMeasurement.hal",
"IGnssMeasurementCallback.hal",
diff --git a/gnss/2.1/IGnss.hal b/gnss/2.1/IGnss.hal
index ce37647..a880b3f 100644
--- a/gnss/2.1/IGnss.hal
+++ b/gnss/2.1/IGnss.hal
@@ -18,10 +18,10 @@
import android.hardware.gnss.measurement_corrections@1.1::IMeasurementCorrections;
import @2.0::IGnss;
-
import IGnssCallback;
import IGnssMeasurement;
import IGnssConfiguration;
+import IGnssAntennaInfo;
/**
* Represents the standard GNSS (Global Navigation Satellite System) interface.
@@ -45,9 +45,9 @@
/**
* This method returns the IGnssMeasurement interface.
*
- * At least one of getExtensionGnssMeasurement(), getExtensionGnssMeasurement_1_1(),
+ * getExtensionGnssMeasurement(), getExtensionGnssMeasurement_1_1(),
* getExtensionGnssMeasurement_2_0(), and getExtensionGnssMeasurement_2_1() methods must return
- * a non-null handle, and the other methods must return nullptr.
+ * non-null. They can all return the same, latest version of IGnssMeasurement.
*
* @return gnssMeasurementIface Handle to the IGnssMeasurement interface.
*/
@@ -56,9 +56,9 @@
/**
* This method returns the IGnssConfiguration interface.
*
- * At least one of getExtensionGnssConfiguration(), getExtensionGnssConfiguration_1_1(),
+ * getExtensionGnssConfiguration(), getExtensionGnssConfiguration_1_1(),
* getExtensionGnssConfiguration_2_0(), and getExtensionGnssConfiguration_2_1() methods must
- * return a non-null handle, and the other methods must return nullptr.
+ * return non-null. They can all return the same, latest version of IGnssConfiguration.
*
* @return gnssConfigurationIface Handle to the IGnssConfiguration interface.
*/
@@ -72,5 +72,15 @@
*
* @return measurementCorrectionsIface Handle to the IMeasurementCorrections interface.
*/
- getExtensionMeasurementCorrections_1_1() generates (IMeasurementCorrections measurementCorrectionsIface);
-};
\ No newline at end of file
+ getExtensionMeasurementCorrections_1_1()
+ generates (IMeasurementCorrections measurementCorrectionsIface);
+
+ /**
+ * This method returns the IGnssAntennaInfo interface.
+ *
+ * This method must return non-null.
+ *
+ * @return gnssAntennaInfoIface Handle to the IGnssAntennaInfo interface.
+ */
+ getExtensionGnssAntennaInfo() generates (IGnssAntennaInfo gnssAntennaInfoIface);
+};
diff --git a/gnss/2.1/IGnssAntennaInfo.hal b/gnss/2.1/IGnssAntennaInfo.hal
new file mode 100644
index 0000000..f77d874
--- /dev/null
+++ b/gnss/2.1/IGnssAntennaInfo.hal
@@ -0,0 +1,46 @@
+/*
+ * 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@2.1;
+
+import IGnssAntennaInfoCallback;
+
+/**
+ * Extended interface for GNSS antenna information support.
+ */
+interface IGnssAntennaInfo {
+ enum GnssAntennaInfoStatus : int32_t {
+ SUCCESS = 0,
+ ERROR_ALREADY_INIT = -100,
+ ERROR_GENERIC = -101,
+ };
+
+ /**
+ * Registers the callback routines with the HAL.
+ *
+ * @param callback Handle to the GnssAntennaInfo callback interface.
+ */
+ setCallback(IGnssAntennaInfoCallback callback) generates (GnssAntennaInfoStatus initRet);
+
+ /**
+ * 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.
+ */
+ close();
+};
diff --git a/gnss/2.1/IGnssAntennaInfoCallback.hal b/gnss/2.1/IGnssAntennaInfoCallback.hal
new file mode 100644
index 0000000..883111e
--- /dev/null
+++ b/gnss/2.1/IGnssAntennaInfoCallback.hal
@@ -0,0 +1,136 @@
+/*
+ * 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@2.1;
+
+/**
+ * The callback interface to report GNSS antenna information from the HAL.
+ */
+interface IGnssAntennaInfoCallback {
+ /**
+ * A row of doubles. This is used to represent a row in a 2D array, which are used to
+ * characterize the phase center variation corrections and signal gain corrections.
+ */
+ struct Row {
+ vec<double> row;
+ };
+
+ /**
+ * A point in 3D space, with associated uncertainty.
+ */
+ struct Coord {
+ double x;
+
+ double xUncertainty;
+
+ double y;
+
+ double yUncertainty;
+
+ double z;
+
+ double zUncertainty;
+ };
+
+ struct GnssAntennaInfo {
+ /**
+ * The carrier frequency in MHz.
+ */
+ double carrierFrequencyMHz;
+
+ /**
+ * Phase center offset (PCO) with associated 1-sigma uncertainty. PCO is defined with
+ * respect to the origin of the Android sensor coordinate system, e.g., center of primary
+ * screen for mobiles - see sensor or form factor documents for details.
+ */
+ Coord phaseCenterOffsetCoordinateMillimeters;
+
+ /**
+ * 2D vectors representing the phase center variation (PCV) corrections, in
+ * millimeters, at regularly spaced azimuthal angle (theta) and zenith angle
+ * (phi). The PCV correction is added to the phase measurement to obtain the
+ * corrected value.
+ *
+ * The azimuthal angle, theta, is defined with respect to the X axis of the
+ * Android sensor coordinate system, increasing toward the Y axis. The zenith
+ * angle, phi, is defined with respect to the Z axis of the Android Sensor
+ * coordinate system, increasing toward the X-Y plane.
+ *
+ * Each row vector (outer vectors) represents a fixed theta. The first row
+ * corresponds to a theta angle of 0 degrees. The last row corresponds to a
+ * theta angle of (360 - deltaTheta) degrees, where deltaTheta is the regular
+ * spacing between azimuthal angles, i.e., deltaTheta = 360 / (number of rows).
+ *
+ * The columns (inner vectors) represent fixed zenith angles, beginning at 0
+ * degrees and ending at 180 degrees. They are separated by deltaPhi, the regular
+ * spacing between zenith angles, i.e., deltaPhi = 180 / (number of columns - 1).
+ *
+ * This field is optional, i.e., an empty vector.
+ */
+ vec<Row> phaseCenterVariationCorrectionMillimeters;
+
+ /**
+ * 2D vectors of 1-sigma uncertainty in millimeters associated with the PCV
+ * correction values.
+ *
+ * This field is optional, i.e., an empty vector.
+ */
+ vec<Row> phaseCenterVariationCorrectionUncertaintyMillimeters;
+
+ /**
+ * 2D vectors representing the signal gain corrections at regularly spaced
+ * azimuthal angle (theta) and zenith angle (phi). The values are calculated or
+ * measured at the antenna feed point without considering the radio and receiver
+ * noise figure and path loss contribution, in dBi, i.e., decibel over isotropic
+ * antenna with the same total power. The signal gain correction is added the
+ * signal gain measurement to obtain the corrected value.
+ *
+ * The azimuthal angle, theta, is defined with respect to the X axis of the
+ * Android sensor coordinate system, increasing toward the Y axis. The zenith
+ * angle, phi, is defined with respect to the Z axis of the Android Sensor
+ * coordinate system, increasing toward the X-Y plane.
+ *
+ * Each row vector (outer vectors) represents a fixed theta. The first row
+ * corresponds to a theta angle of 0 degrees. The last row corresponds to a
+ * theta angle of (360 - deltaTheta) degrees, where deltaTheta is the regular
+ * spacing between azimuthal angles, i.e., deltaTheta = 360 / (number of rows).
+ *
+ * The columns (inner vectors) represent fixed zenith angles, beginning at 0
+ * degrees and ending at 180 degrees. They are separated by deltaPhi, the regular
+ * spacing between zenith angles, i.e., deltaPhi = 180 / (number of columns - 1).
+ *
+ * This field is optional, i.e., an empty vector.
+ */
+ vec<Row> signalGainCorrectionDbi;
+
+ /**
+ * 2D vectors of 1-sigma uncertainty in dBi associated with the signal
+ * gain correction values.
+ *
+ * This field is optional, i.e., an empty vector.
+ */
+ vec<Row> signalGainCorrectionUncertaintyDbi;
+ };
+
+ /**
+ * Called when on connection, and on known-change to these values, such as upon a known
+ * GNSS RF antenna tuning change, or a foldable device state change.
+ *
+ * This is optional. It can never be called if the GNSS antenna information is not
+ * available.
+ */
+ gnssAntennaInfoCb(vec<GnssAntennaInfo> gnssAntennaInfos);
+};
diff --git a/gnss/2.1/IGnssCallback.hal b/gnss/2.1/IGnssCallback.hal
index da70742..94be915 100644
--- a/gnss/2.1/IGnssCallback.hal
+++ b/gnss/2.1/IGnssCallback.hal
@@ -24,8 +24,27 @@
* the interfaces and passes a handle to the HAL.
*/
interface IGnssCallback extends @2.0::IGnssCallback {
+ /**
+ * Flags for the gnssSetCapabilities callback.
+ */
+ @export(name = "", value_prefix = "GPS_CAPABILITY_")
+ enum Capabilities : @2.0::IGnssCallback.Capabilities {
+ /**
+ * GNSS supports measurement corrections
+ */
+ ANTENNA_INFO = 1 << 11,
+ };
- /** Extends a GnssSvInfo, adding a basebandCN0DbHz. */
+ /**
+ * Callback to inform framework of the GNSS HAL implementation's capabilities.
+ *
+ * @param capabilities Capability parameter is a bit field of the Capabilities enum.
+ */
+ gnssSetCapabilitiesCb_2_1(bitfield<Capabilities> capabilities);
+
+ /**
+ * Extends a GnssSvInfo, adding a basebandCN0DbHz.
+ */
struct GnssSvInfo {
/**
* GNSS satellite information for a single satellite and frequency.
diff --git a/gnss/2.1/IGnssConfiguration.hal b/gnss/2.1/IGnssConfiguration.hal
index 8360ba9..550f325 100644
--- a/gnss/2.1/IGnssConfiguration.hal
+++ b/gnss/2.1/IGnssConfiguration.hal
@@ -65,4 +65,4 @@
* @return success Whether the HAL accepts and abides by the provided blacklist.
*/
setBlacklist_2_1(vec<BlacklistedSource> blacklist) generates (bool success);
-};
\ No newline at end of file
+};
diff --git a/gnss/2.1/IGnssMeasurement.hal b/gnss/2.1/IGnssMeasurement.hal
index d2c76e6..49ba7eb 100644
--- a/gnss/2.1/IGnssMeasurement.hal
+++ b/gnss/2.1/IGnssMeasurement.hal
@@ -25,7 +25,6 @@
* Extended interface for GNSS Measurements support.
*/
interface IGnssMeasurement extends @2.0::IGnssMeasurement {
-
/**
* Initializes the interface and registers the callback routines with the HAL. After a
* successful call to 'setCallback_2_1' the HAL must begin to provide updates at an average
@@ -47,5 +46,5 @@
* error code.
*/
setCallback_2_1(IGnssMeasurementCallback callback, bool enableFullTracking)
- generates (GnssMeasurementStatus initRet);
+ generates (GnssMeasurementStatus initRet);
};
diff --git a/gnss/2.1/IGnssMeasurementCallback.hal b/gnss/2.1/IGnssMeasurementCallback.hal
index 0385abd..0e6abbd 100644
--- a/gnss/2.1/IGnssMeasurementCallback.hal
+++ b/gnss/2.1/IGnssMeasurementCallback.hal
@@ -21,36 +21,32 @@
import @2.0::ElapsedRealtime;
import GnssSignalType;
-/** The callback interface to report measurements from the HAL. */
+/**
+ * The callback interface to report measurements from the HAL.
+ */
interface IGnssMeasurementCallback extends @2.0::IGnssMeasurementCallback {
-
/**
* Flags to indicate what fields in GnssMeasurement are valid.
*/
- enum GnssMeasurementFlags : uint32_t {
- /** A valid 'snr' is stored in the data structure. */
- HAS_SNR = 1 << 0,
- /** A valid 'carrier frequency' is stored in the data structure. */
- HAS_CARRIER_FREQUENCY = 1 << 9,
- /** A valid 'carrier cycles' is stored in the data structure. */
- HAS_CARRIER_CYCLES = 1 << 10,
- /** A valid 'carrier phase' is stored in the data structure. */
- HAS_CARRIER_PHASE = 1 << 11,
- /** A valid 'carrier phase uncertainty' is stored in the data structure. */
- HAS_CARRIER_PHASE_UNCERTAINTY = 1 << 12,
- /** A valid automatic gain control is stored in the data structure. */
- HAS_AUTOMATIC_GAIN_CONTROL = 1 << 13,
- /** A valid receiver inter-signal bias is stored in the data structure. */
- HAS_RECEIVER_ISB = 1 << 16,
- /** A valid receiver inter-signal bias uncertainty is stored in the data structure. */
- HAS_RECEIVER_ISB_UNCERTAINTY = 1 << 17,
- /** A valid satellite inter-signal bias is stored in the data structure. */
- HAS_SATELLITE_ISB = 1 << 18,
- /** A valid satellite inter-signal bias uncertainty is stored in the data structure. */
- HAS_SATELLITE_ISB_UNCERTAINTY = 1 << 19
+ enum GnssMeasurementFlags : @1.0::IGnssMeasurementCallback.GnssMeasurementFlags {
+ /**
+ * A valid receiver inter-signal bias is stored in the data structure.
+ */
+ HAS_RECEIVER_ISB = 1 << 16,
+ /**
+ * A valid receiver inter-signal bias uncertainty is stored in the data structure.
+ */
+ HAS_RECEIVER_ISB_UNCERTAINTY = 1 << 17,
+ /**
+ * A valid satellite inter-signal bias is stored in the data structure.
+ */
+ HAS_SATELLITE_ISB = 1 << 18,
+ /**
+ * A valid satellite inter-signal bias uncertainty is stored in the data structure.
+ */
+ HAS_SATELLITE_ISB_UNCERTAINTY = 1 << 19,
};
-
/**
* Extends a GNSS Measurement, adding basebandCN0DbHz, GnssMeasurementFlags,
* receiverInterSignalBiasNs, receiverInterSignalBiasUncertaintyNs, satelliteInterSignalBiasNs
@@ -84,8 +80,8 @@
* The receiver inter-signal bias (ISB) in nanoseconds.
*
* This value is the estimated receiver-side inter-system (different from the constellation
- * in GnssClock.referenceSignalForIsb) bias and inter-frequency (different from the carrier
- * frequency in GnssClock.referenceSignalForIsb) bias. The reported receiver ISB
+ * in GnssClock.referenceSignalTypeForIsb) bias and inter-frequency (different from the
+ * carrier frequency in GnssClock.referenceSignalTypeForIsb) bias. The reported receiver ISB
* must include signal delays caused by
*
* - Receiver inter-constellation bias
@@ -94,7 +90,7 @@
*
* The value does not include the inter-frequency Ionospheric bias.
*
- * The receiver ISB of GnssClock.referenceSignalForIsb is defined to be 0.0 nanoseconds.
+ * The receiver ISB of GnssClock.referenceSignalTypeForIsb is defined to be 0.0 nanoseconds.
*/
double receiverInterSignalBiasNs;
@@ -107,8 +103,8 @@
* The satellite inter-signal bias in nanoseconds.
*
* This value is the satellite-and-control-segment-side inter-system (different from the
- * constellation in GnssClock.referenceSignalForIsb) bias and inter-frequency (different
- * from the carrier frequency in GnssClock.referenceSignalForIsb) bias, including:
+ * constellation in GnssClock.referenceSignalTypeForIsb) bias and inter-frequency (different
+ * from the carrier frequency in GnssClock.referenceSignalTypeForIsb) bias, including:
*
* - Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPT-UTC Time Offset (TauGps),
* BDS-GLO Time Offset (BGTO))
@@ -116,7 +112,7 @@
* - Satellite inter-signal bias, which includes satellite inter-frequency bias (GLO only),
* and satellite inter-code bias (e.g., Differential Code Bias (DCB)).
*
- * The receiver ISB of GnssClock.referenceSignalForIsb is defined to be 0.0 nanoseconds.
+ * The receiver ISB of GnssClock.referenceSignalTypeForIsb is defined to be 0.0 nanoseconds.
*/
double satelliteInterSignalBiasNs;
@@ -160,10 +156,14 @@
* Complete set of GNSS Measurement data, same as 2.0 with additional fields in measurements.
*/
struct GnssData {
- /** The full set of satellite measurement observations. */
+ /**
+ * The full set of satellite measurement observations.
+ */
vec<GnssMeasurement> measurements;
- /** The GNSS clock time reading. */
+ /**
+ * The GNSS clock time reading.
+ */
GnssClock clock;
/**
diff --git a/gnss/2.1/default/Android.bp b/gnss/2.1/default/Android.bp
index 1f1078e..c4dc8fd 100644
--- a/gnss/2.1/default/Android.bp
+++ b/gnss/2.1/default/Android.bp
@@ -22,6 +22,7 @@
vintf_fragments: ["android.hardware.gnss@2.1-service.xml"],
srcs: [
"Gnss.cpp",
+ "GnssAntennaInfo.cpp",
"GnssDebug.cpp",
"GnssMeasurement.cpp",
"GnssMeasurementCorrections.cpp",
diff --git a/gnss/2.1/default/Gnss.cpp b/gnss/2.1/default/Gnss.cpp
index 679eb35..2b327a9 100644
--- a/gnss/2.1/default/Gnss.cpp
+++ b/gnss/2.1/default/Gnss.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "Gnss"
#include "Gnss.h"
+#include "GnssAntennaInfo.h"
#include "GnssDebug.h"
#include "GnssMeasurement.h"
#include "GnssMeasurementCorrections.h"
@@ -334,10 +335,11 @@
sGnssCallback_2_1 = callback;
- using Capabilities = V2_0::IGnssCallback::Capabilities;
+ using Capabilities = V2_1::IGnssCallback::Capabilities;
const auto capabilities = Capabilities::MEASUREMENTS | Capabilities::MEASUREMENT_CORRECTIONS |
- Capabilities::LOW_POWER_MODE | Capabilities::SATELLITE_BLACKLIST;
- auto ret = sGnssCallback_2_1->gnssSetCapabilitiesCb_2_0(capabilities);
+ Capabilities::LOW_POWER_MODE | Capabilities::SATELLITE_BLACKLIST |
+ Capabilities::ANTENNA_INFO;
+ auto ret = sGnssCallback_2_1->gnssSetCapabilitiesCb_2_1(capabilities);
if (!ret.isOk()) {
ALOGE("%s: Unable to invoke callback", __func__);
}
@@ -374,6 +376,11 @@
return new GnssMeasurementCorrections();
}
+Return<sp<V2_1::IGnssAntennaInfo>> Gnss::getExtensionGnssAntennaInfo() {
+ ALOGD("Gnss::getExtensionGnssAntennaInfo");
+ return new GnssAntennaInfo();
+}
+
void Gnss::reportSvStatus(const hidl_vec<GnssSvInfo>& svInfoList) const {
std::unique_lock<std::mutex> lock(mMutex);
// TODO(skz): update this to call 2_0 callback if non-null
diff --git a/gnss/2.1/default/Gnss.h b/gnss/2.1/default/Gnss.h
index c47206a..bd5e6e8 100644
--- a/gnss/2.1/default/Gnss.h
+++ b/gnss/2.1/default/Gnss.h
@@ -22,6 +22,7 @@
#include <atomic>
#include <mutex>
#include <thread>
+#include "GnssAntennaInfo.h"
#include "GnssConfiguration.h"
namespace android {
@@ -91,6 +92,7 @@
Return<sp<V2_1::IGnssConfiguration>> getExtensionGnssConfiguration_2_1() override;
Return<sp<measurement_corrections::V1_1::IMeasurementCorrections>>
getExtensionMeasurementCorrections_1_1() override;
+ Return<sp<V2_1::IGnssAntennaInfo>> getExtensionGnssAntennaInfo() override;
private:
void reportLocation(const V2_0::GnssLocation&) const;
diff --git a/gnss/2.1/default/GnssAntennaInfo.cpp b/gnss/2.1/default/GnssAntennaInfo.cpp
new file mode 100644
index 0000000..d32a0af
--- /dev/null
+++ b/gnss/2.1/default/GnssAntennaInfo.cpp
@@ -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.
+ */
+
+#define LOG_TAG "GnssAntennaInfo"
+
+#include "GnssAntennaInfo.h"
+#include "Utils.h"
+
+#include <log/log.h>
+
+using ::android::hardware::gnss::common::Utils;
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace V2_1 {
+namespace implementation {
+
+sp<IGnssAntennaInfoCallback> GnssAntennaInfo::sCallback = nullptr;
+
+GnssAntennaInfo::GnssAntennaInfo() : mMinIntervalMillis(1000) {}
+
+GnssAntennaInfo::~GnssAntennaInfo() {
+ stop();
+}
+
+// Methods from ::android::hardware::gnss::V2_1::IGnssAntennaInfo follow.
+Return<GnssAntennaInfo::GnssAntennaInfoStatus> GnssAntennaInfo::setCallback(
+ const sp<IGnssAntennaInfoCallback>& callback) {
+ ALOGD("setCallback");
+ std::unique_lock<std::mutex> lock(mMutex);
+ sCallback = callback;
+
+ if (mIsActive) {
+ ALOGW("GnssAntennaInfo callback already set. Resetting the callback...");
+ stop();
+ }
+ start();
+
+ return GnssAntennaInfoStatus::SUCCESS;
+}
+
+Return<void> GnssAntennaInfo::close() {
+ ALOGD("close");
+ std::unique_lock<std::mutex> lock(mMutex);
+ stop();
+ sCallback = nullptr;
+ return Void();
+}
+
+// Private methods
+void GnssAntennaInfo::start() {
+ ALOGD("start");
+ mIsActive = true;
+ mThread = std::thread([this]() {
+ while (mIsActive == true) {
+ if (sCallback != nullptr) {
+ auto antennaInfos = Utils::getMockAntennaInfos();
+ this->reportAntennaInfo(antennaInfos);
+ }
+
+ /** For mock implementation this is good. On real device, we should only report
+ antennaInfo at start and when there is a configuration change. **/
+ std::this_thread::sleep_for(std::chrono::milliseconds(mMinIntervalMillis));
+ }
+ });
+}
+
+void GnssAntennaInfo::stop() {
+ ALOGD("stop");
+ mIsActive = false;
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void GnssAntennaInfo::reportAntennaInfo(
+ const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& antennaInfo) const {
+ std::unique_lock<std::mutex> lock(mMutex);
+
+ if (sCallback == nullptr) {
+ ALOGE("%s: No non-null callback", __func__);
+ return;
+ }
+
+ auto ret = sCallback->gnssAntennaInfoCb(antennaInfo);
+ if (!ret.isOk()) {
+ ALOGE("%s: Unable to invoke callback", __func__);
+ }
+}
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace gnss
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/gnss/2.1/default/GnssAntennaInfo.h b/gnss/2.1/default/GnssAntennaInfo.h
new file mode 100644
index 0000000..f4bfd24
--- /dev/null
+++ b/gnss/2.1/default/GnssAntennaInfo.h
@@ -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.
+ */
+
+#ifndef ANDROID_HARDWARE_GNSS_V2_1_GNSSANTENNAINFO_H
+#define ANDROID_HARDWARE_GNSS_V2_1_GNSSANTENNAINFO_H
+
+#include <android/hardware/gnss/2.1/IGnssAntennaInfo.h>
+
+#include <mutex>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+struct GnssAntennaInfo : public IGnssAntennaInfo {
+ GnssAntennaInfo();
+ ~GnssAntennaInfo();
+
+ // Methods from ::android::hardware::gnss::V2_1::IGnssAntennaInfo follow.
+ Return<GnssAntennaInfoStatus> setCallback(
+ const sp<IGnssAntennaInfoCallback>& callback) override;
+ Return<void> close() override;
+
+ private:
+ void start();
+ void stop();
+ void reportAntennaInfo(
+ const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& antennaInfo) const;
+
+ static sp<IGnssAntennaInfoCallback> sCallback;
+ std::atomic<long> mMinIntervalMillis;
+ std::atomic<bool> mIsActive;
+ std::thread mThread;
+ mutable std::mutex mMutex;
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace gnss
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_GNSS_V2_1_GNSSCONFIGURATION_H
\ No newline at end of file
diff --git a/gnss/2.1/default/GnssMeasurementCorrections.cpp b/gnss/2.1/default/GnssMeasurementCorrections.cpp
index 9dedbf6..accf62b 100644
--- a/gnss/2.1/default/GnssMeasurementCorrections.cpp
+++ b/gnss/2.1/default/GnssMeasurementCorrections.cpp
@@ -82,19 +82,20 @@
static_cast<int>(corrections.v1_0.satCorrections.size()),
corrections.hasEnvironmentBearing, corrections.environmentBearingDegrees,
corrections.environmentBearingUncertaintyDegrees);
- for (auto singleSatCorrection : corrections.v1_0.satCorrections) {
+ for (auto singleSatCorrection : corrections.satCorrections) {
ALOGD("singleSatCorrection = flags: %d, constellation: %d, svid: %d, cfHz: %f, probLos: %f,"
" epl: %f, eplUnc: %f",
- static_cast<int>(singleSatCorrection.singleSatCorrectionFlags),
+ static_cast<int>(singleSatCorrection.v1_0.singleSatCorrectionFlags),
static_cast<int>(singleSatCorrection.constellation),
- static_cast<int>(singleSatCorrection.svid), singleSatCorrection.carrierFrequencyHz,
- singleSatCorrection.probSatIsLos, singleSatCorrection.excessPathLengthMeters,
- singleSatCorrection.excessPathLengthUncertaintyMeters);
+ static_cast<int>(singleSatCorrection.v1_0.svid),
+ singleSatCorrection.v1_0.carrierFrequencyHz, singleSatCorrection.v1_0.probSatIsLos,
+ singleSatCorrection.v1_0.excessPathLengthMeters,
+ singleSatCorrection.v1_0.excessPathLengthUncertaintyMeters);
ALOGD("reflecting plane = lat: %f, lng: %f, alt: %f, azm: %f",
- singleSatCorrection.reflectingPlane.latitudeDegrees,
- singleSatCorrection.reflectingPlane.longitudeDegrees,
- singleSatCorrection.reflectingPlane.altitudeMeters,
- singleSatCorrection.reflectingPlane.azimuthDegrees);
+ singleSatCorrection.v1_0.reflectingPlane.latitudeDegrees,
+ singleSatCorrection.v1_0.reflectingPlane.longitudeDegrees,
+ singleSatCorrection.v1_0.reflectingPlane.altitudeMeters,
+ singleSatCorrection.v1_0.reflectingPlane.azimuthDegrees);
}
return true;
diff --git a/gnss/2.1/vts/functional/gnss_hal_test.cpp b/gnss/2.1/vts/functional/gnss_hal_test.cpp
index 93f89f5..da7a62b 100644
--- a/gnss/2.1/vts/functional/gnss_hal_test.cpp
+++ b/gnss/2.1/vts/functional/gnss_hal_test.cpp
@@ -215,6 +215,12 @@
return Void();
}
+Return<void> GnssHalTest::GnssCallback::gnssSetCapabilitiesCb_2_1(uint32_t capabilities) {
+ ALOGI("Capabilities (v2.1) received %d", capabilities);
+ capabilities_cbq_.store(capabilities);
+ return Void();
+}
+
Return<void> GnssHalTest::GnssCallback::gnssNameCb(const android::hardware::hidl_string& name) {
ALOGI("Name received: %s", name.c_str());
name_cbq_.store(name);
@@ -262,4 +268,11 @@
ALOGI("GnssMeasurementCorrectionsCallback capabilities received %d", capabilities);
capabilities_cbq_.store(capabilities);
return Void();
+}
+
+Return<void> GnssHalTest::GnssAntennaInfoCallback::gnssAntennaInfoCb(
+ const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+ ALOGD("GnssAntennaInfo v2.1 received. Size = %d", (int)gnssAntennaInfos.size());
+ antenna_info_cbq_.store(gnssAntennaInfos);
+ return Void();
}
\ No newline at end of file
diff --git a/gnss/2.1/vts/functional/gnss_hal_test.h b/gnss/2.1/vts/functional/gnss_hal_test.h
index b99cf23..9e6e162 100644
--- a/gnss/2.1/vts/functional/gnss_hal_test.h
+++ b/gnss/2.1/vts/functional/gnss_hal_test.h
@@ -31,6 +31,8 @@
using android::hardware::gnss::V1_0::GnssLocationFlags;
using android::hardware::gnss::V2_0::GnssConstellationType;
using android::hardware::gnss::V2_1::IGnss;
+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;
@@ -107,6 +109,7 @@
// New in v2.1
Return<void> gnssSvStatusCb_2_1(
const hidl_vec<IGnssCallback_2_1::GnssSvInfo>& svInfoList) override;
+ Return<void> gnssSetCapabilitiesCb_2_1(uint32_t capabilities) override;
private:
Return<void> gnssLocationCbImpl(const GnssLocation_2_0& location);
@@ -152,6 +155,20 @@
Return<void> setCapabilitiesCb(uint32_t capabilities) override;
};
+ /* Callback class for GnssAntennaInfo. */
+ class GnssAntennaInfoCallback : public IGnssAntennaInfoCallback {
+ public:
+ GnssCallbackEventQueue<hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>>
+ antenna_info_cbq_;
+
+ GnssAntennaInfoCallback() : antenna_info_cbq_("info"){};
+ virtual ~GnssAntennaInfoCallback() = default;
+
+ // Methods from V2_1::GnssAntennaInfoCallback follow.
+ Return<void> gnssAntennaInfoCb(
+ const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
+ };
+
/*
* SetUpGnssCallback:
* Set GnssCallback and verify the result.
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 9ac9436..7b054c0 100644
--- a/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
@@ -139,7 +139,7 @@
std::string codeType = lastMeasurement.clock.referenceSignalTypeForIsb.codeType;
ASSERT_TRUE(referenceConstellation >= GnssConstellationType::UNKNOWN &&
- referenceConstellation >= GnssConstellationType::IRNSS);
+ referenceConstellation <= GnssConstellationType::IRNSS);
ASSERT_TRUE(carrierFrequencyHz > 0);
ASSERT_TRUE(codeType != "");
@@ -154,6 +154,85 @@
}
/*
+ * TestGnssAntennaInfo:
+ * Sets a GnssAntennaInfoCallback, waits for report, and verifies
+ * 1. phaseCenterOffsetCoordinateMillimeters is valid
+ * 2. phaseCenterOffsetCoordinateUncertaintyMillimeters is valid.
+ * PhaseCenterVariationCorrections and SignalGainCorrections are optional.
+ */
+TEST_P(GnssHalTest, TestGnssAntennaInfo) {
+ const int kAntennaInfoTimeoutSeconds = 2;
+
+ auto gnssAntennaInfo = gnss_hal_->getExtensionGnssAntennaInfo();
+ ASSERT_TRUE(gnssAntennaInfo.isOk());
+
+ // Skip test if GnssAntennaInfo v2.1 is not supported
+ sp<IGnssAntennaInfo> iGnssAntennaInfo = gnssAntennaInfo;
+ if (!(gnss_cb_->last_capabilities_ & IGnssCallback_2_1::Capabilities::ANTENNA_INFO) ||
+ iGnssAntennaInfo == nullptr) {
+ ALOGD("GnssAntennaInfo v2.1 is not supported.");
+ return;
+ }
+
+ sp<GnssAntennaInfoCallback> callback = new GnssAntennaInfoCallback();
+ auto result = iGnssAntennaInfo->setCallback(callback);
+ ASSERT_TRUE(result.isOk());
+ EXPECT_EQ(result, IGnssAntennaInfo::GnssAntennaInfoStatus::SUCCESS);
+
+ hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo> antennaInfos;
+ ASSERT_TRUE(callback->antenna_info_cbq_.retrieve(antennaInfos, kAntennaInfoTimeoutSeconds));
+ EXPECT_EQ(callback->antenna_info_cbq_.calledCount(), 1);
+ ASSERT_TRUE(antennaInfos.size() > 0);
+
+ for (auto antennaInfo : antennaInfos) {
+ // Remaining fields are optional
+ if (antennaInfo.phaseCenterVariationCorrectionMillimeters != NULL) {
+ int numRows = antennaInfo.phaseCenterVariationCorrectionMillimeters.size();
+ int numColumns = antennaInfo.phaseCenterVariationCorrectionMillimeters[0].row.size();
+ // Must have at least 1 row and 2 columns
+ ASSERT_TRUE(numRows >= 1 && numColumns >= 2);
+
+ // Corrections and uncertainties must have same dimensions
+ ASSERT_TRUE(antennaInfo.phaseCenterVariationCorrectionMillimeters.size() ==
+ antennaInfo.phaseCenterVariationCorrectionUncertaintyMillimeters.size());
+ ASSERT_TRUE(
+ antennaInfo.phaseCenterVariationCorrectionMillimeters[0].row.size() ==
+ antennaInfo.phaseCenterVariationCorrectionUncertaintyMillimeters[0].row.size());
+
+ // Must be rectangular
+ for (auto row : antennaInfo.phaseCenterVariationCorrectionMillimeters) {
+ ASSERT_TRUE(row.row.size() == numColumns);
+ }
+ for (auto row : antennaInfo.phaseCenterVariationCorrectionUncertaintyMillimeters) {
+ ASSERT_TRUE(row.row.size() == numColumns);
+ }
+ }
+ if (antennaInfo.signalGainCorrectionDbi != NULL) {
+ int numRows = antennaInfo.signalGainCorrectionDbi.size();
+ int numColumns = antennaInfo.signalGainCorrectionUncertaintyDbi[0].row.size();
+ // Must have at least 1 row and 2 columns
+ ASSERT_TRUE(numRows >= 1 && numColumns >= 2);
+
+ // Corrections and uncertainties must have same dimensions
+ ASSERT_TRUE(antennaInfo.signalGainCorrectionDbi.size() ==
+ antennaInfo.signalGainCorrectionUncertaintyDbi.size());
+ ASSERT_TRUE(antennaInfo.signalGainCorrectionDbi[0].row.size() ==
+ antennaInfo.signalGainCorrectionUncertaintyDbi[0].row.size());
+
+ // Must be rectangular
+ for (auto row : antennaInfo.signalGainCorrectionDbi) {
+ ASSERT_TRUE(row.row.size() == numColumns);
+ }
+ for (auto row : antennaInfo.signalGainCorrectionUncertaintyDbi) {
+ ASSERT_TRUE(row.row.size() == numColumns);
+ }
+ }
+ }
+
+ iGnssAntennaInfo->close();
+}
+
+/*
* TestGnssSvInfoFields:
* Gets 1 location and a GnssSvInfo, and verifies
* 1. basebandCN0DbHz is valid.
diff --git a/gnss/common/utils/default/Utils.cpp b/gnss/common/utils/default/Utils.cpp
index 0cdc865..2e5b873 100644
--- a/gnss/common/utils/default/Utils.cpp
+++ b/gnss/common/utils/default/Utils.cpp
@@ -235,6 +235,58 @@
return svInfo;
}
+hidl_vec<GnssAntennaInfo> Utils::getMockAntennaInfos() {
+ GnssAntennaInfo mockAntennaInfo_1 = {
+ .carrierFrequencyMHz = 123412.12,
+ .phaseCenterOffsetCoordinateMillimeters = Coord{.x = 1,
+ .xUncertainty = 0.1,
+ .y = 2,
+ .yUncertainty = 0.1,
+ .z = 3,
+ .zUncertainty = 0.1},
+ .phaseCenterVariationCorrectionMillimeters =
+ {
+ Row{hidl_vec<double>{1, -1, 5, -2, 3, -1}},
+ Row{hidl_vec<double>{-2, 3, 2, 0, 1, 2}},
+ Row{hidl_vec<double>{1, 3, 2, -1, -3, 5}},
+ },
+ .phaseCenterVariationCorrectionUncertaintyMillimeters =
+ {
+ Row{hidl_vec<double>{0.1, 0.2, 0.4, 0.1, 0.2, 0.3}},
+ Row{hidl_vec<double>{0.3, 0.2, 0.3, 0.6, 0.1, 0.1}},
+ Row{hidl_vec<double>{0.1, 0.1, 0.4, 0.2, 0.5, 0.3}},
+ },
+ .signalGainCorrectionDbi =
+ {
+ Row{hidl_vec<double>{2, -3, 1, -3, 0, -4}},
+ Row{hidl_vec<double>{1, 0, -4, 1, 3, -2}},
+ Row{hidl_vec<double>{3, -2, 0, -2, 3, 0}},
+ },
+ .signalGainCorrectionUncertaintyDbi =
+ {
+ Row{hidl_vec<double>{0.3, 0.1, 0.2, 0.6, 0.1, 0.3}},
+ Row{hidl_vec<double>{0.1, 0.1, 0.5, 0.2, 0.3, 0.1}},
+ Row{hidl_vec<double>{0.2, 0.4, 0.2, 0.1, 0.1, 0.2}},
+ },
+ };
+
+ GnssAntennaInfo mockAntennaInfo_2 = {
+ .carrierFrequencyMHz = 532324.23,
+ .phaseCenterOffsetCoordinateMillimeters = Coord{.x = 5,
+ .xUncertainty = 0.1,
+ .y = 6,
+ .yUncertainty = 0.1,
+ .z = 7,
+ .zUncertainty = 0.1},
+ };
+
+ hidl_vec<GnssAntennaInfo> mockAntennaInfos = {
+ mockAntennaInfo_1,
+ mockAntennaInfo_2,
+ };
+ return mockAntennaInfos;
+}
+
} // namespace common
} // namespace gnss
} // namespace hardware
diff --git a/gnss/common/utils/default/include/Utils.h b/gnss/common/utils/default/include/Utils.h
index e0c61a4..d9ad5a5 100644
--- a/gnss/common/utils/default/include/Utils.h
+++ b/gnss/common/utils/default/include/Utils.h
@@ -33,6 +33,9 @@
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();
@@ -46,6 +49,7 @@
static GnssSvInfoV1_0 getMockSvInfoV1_0(int16_t svid, V1_0::GnssConstellationType type,
float cN0DbHz, float elevationDegrees,
float azimuthDegrees);
+ static hidl_vec<GnssAntennaInfo> getMockAntennaInfos();
};
} // namespace common
diff --git a/gnss/common/utils/vts/Utils.cpp b/gnss/common/utils/vts/Utils.cpp
index b6c3f5e..4b5a50f 100644
--- a/gnss/common/utils/vts/Utils.cpp
+++ b/gnss/common/utils/vts/Utils.cpp
@@ -22,7 +22,9 @@
namespace gnss {
namespace common {
-using V1_0::GnssConstellationType;
+using GnssConstellationType_V1_0 = V1_0::GnssConstellationType;
+using GnssConstellationType_V2_0 = V2_0::GnssConstellationType;
+
using V1_0::GnssLocationFlags;
void Utils::checkLocation(const GnssLocation& location, bool check_speed,
@@ -100,12 +102,12 @@
.azimuthDegrees = 203.0,
};
- SingleSatCorrection singleSatCorrection1 = {
+ SingleSatCorrection_V1_0 singleSatCorrection1 = {
.singleSatCorrectionFlags = GnssSingleSatCorrectionFlags::HAS_SAT_IS_LOS_PROBABILITY |
GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH |
GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH_UNC |
GnssSingleSatCorrectionFlags::HAS_REFLECTING_PLANE,
- .constellation = GnssConstellationType::GPS,
+ .constellation = GnssConstellationType_V1_0::GPS,
.svid = 12,
.carrierFrequencyHz = 1.59975e+09,
.probSatIsLos = 0.50001,
@@ -113,11 +115,11 @@
.excessPathLengthUncertaintyMeters = 25.5,
.reflectingPlane = reflectingPlane,
};
- SingleSatCorrection singleSatCorrection2 = {
+ SingleSatCorrection_V1_0 singleSatCorrection2 = {
.singleSatCorrectionFlags = GnssSingleSatCorrectionFlags::HAS_SAT_IS_LOS_PROBABILITY |
GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH |
GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH_UNC,
- .constellation = GnssConstellationType::GPS,
+ .constellation = GnssConstellationType_V1_0::GPS,
.svid = 9,
.carrierFrequencyHz = 1.59975e+09,
.probSatIsLos = 0.873,
@@ -125,8 +127,8 @@
.excessPathLengthUncertaintyMeters = 10.0,
};
- hidl_vec<SingleSatCorrection> singleSatCorrections = {singleSatCorrection1,
- singleSatCorrection2};
+ hidl_vec<SingleSatCorrection_V1_0> singleSatCorrections = {singleSatCorrection1,
+ singleSatCorrection2};
MeasurementCorrections_1_0 mockCorrections = {
.latitudeDegrees = 37.4219999,
.longitudeDegrees = -122.0840575,
@@ -142,11 +144,27 @@
const MeasurementCorrections_1_1 Utils::getMockMeasurementCorrections_1_1() {
MeasurementCorrections_1_0 mockCorrections_1_0 = getMockMeasurementCorrections();
+ SingleSatCorrection_V1_1 singleSatCorrection1 = {
+ .v1_0 = mockCorrections_1_0.satCorrections[0],
+ .constellation = GnssConstellationType_V2_0::IRNSS,
+ };
+ SingleSatCorrection_V1_1 singleSatCorrection2 = {
+ .v1_0 = mockCorrections_1_0.satCorrections[1],
+ .constellation = GnssConstellationType_V2_0::IRNSS,
+ };
+
+ mockCorrections_1_0.satCorrections[0].constellation = GnssConstellationType_V1_0::UNKNOWN;
+ mockCorrections_1_0.satCorrections[1].constellation = GnssConstellationType_V1_0::UNKNOWN;
+
+ hidl_vec<SingleSatCorrection_V1_1> singleSatCorrections = {singleSatCorrection1,
+ singleSatCorrection2};
+
MeasurementCorrections_1_1 mockCorrections_1_1 = {
.v1_0 = mockCorrections_1_0,
.hasEnvironmentBearing = true,
.environmentBearingDegrees = 45.0,
.environmentBearingUncertaintyDegrees = 4.0,
+ .satCorrections = singleSatCorrections,
};
return mockCorrections_1_1;
}
diff --git a/gnss/common/utils/vts/include/Utils.h b/gnss/common/utils/vts/include/Utils.h
index 781ad42..c3cdd18 100644
--- a/gnss/common/utils/vts/include/Utils.h
+++ b/gnss/common/utils/vts/include/Utils.h
@@ -29,6 +29,11 @@
using MeasurementCorrections_1_1 =
android::hardware::gnss::measurement_corrections::V1_1::MeasurementCorrections;
+using SingleSatCorrection_V1_0 =
+ android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection;
+using SingleSatCorrection_V1_1 =
+ android::hardware::gnss::measurement_corrections::V1_1::SingleSatCorrection;
+
namespace android {
namespace hardware {
namespace gnss {
diff --git a/gnss/measurement_corrections/1.0/types.hal b/gnss/measurement_corrections/1.0/types.hal
index edf26bf..3d7ab0f 100644
--- a/gnss/measurement_corrections/1.0/types.hal
+++ b/gnss/measurement_corrections/1.0/types.hal
@@ -92,16 +92,16 @@
double altitudeMeters;
/**
- * Represents the horizontal uncertainty (68% confidence) in meters on the device position at
- * which the corrections are provided.
+ * Represents the horizontal uncertainty (63% to 68% confidence) in meters on the device
+ * position at which the corrections are provided.
*
* This value is useful for example to judge how accurate the provided corrections are.
*/
double horizontalPositionUncertaintyMeters;
/**
- * Represents the vertical uncertainty (68% confidence) in meters on the device position at
- * which the corrections are provided.
+ * Represents the vertical uncertainty (63% to 68% confidence) in meters on the device position
+ * at which the corrections are provided.
*
* This value is useful for example to judge how accurate the provided corrections are.
*/
diff --git a/gnss/measurement_corrections/1.1/Android.bp b/gnss/measurement_corrections/1.1/Android.bp
index 1d69f20..d279af6 100644
--- a/gnss/measurement_corrections/1.1/Android.bp
+++ b/gnss/measurement_corrections/1.1/Android.bp
@@ -12,6 +12,7 @@
],
interfaces: [
"android.hardware.gnss.measurement_corrections@1.0",
+ "android.hardware.gnss@2.0",
"android.hardware.gnss@1.0",
"android.hidl.base@1.0",
],
diff --git a/gnss/measurement_corrections/1.1/types.hal b/gnss/measurement_corrections/1.1/types.hal
index 40b6f52..e98be13 100644
--- a/gnss/measurement_corrections/1.1/types.hal
+++ b/gnss/measurement_corrections/1.1/types.hal
@@ -17,11 +17,14 @@
package android.hardware.gnss.measurement_corrections@1.1;
import @1.0::MeasurementCorrections;
+import @1.0::SingleSatCorrection;
+import android.hardware.gnss@2.0::GnssConstellationType;
/**
* A struct containing a set of measurement corrections for all used GNSS satellites at the location
* specified by latitudeDegrees, longitudeDegrees, altitudeMeters and at the time of week specified
- * toaGpsNanosecondsOfWeek
+ * toaGpsNanosecondsOfWeek. The v1_0.satCorrections field is deprecated and is no longer used by
+ * framework.
*/
struct MeasurementCorrections {
@1.0::MeasurementCorrections v1_0;
@@ -48,11 +51,41 @@
* If the road is curved in the vicinity of the user location, then
* environmentBearingUncertaintyDegrees will include the amount by which the road direction
* changes in the area of position uncertainty.
+ *
+ * hasEnvironmentBearing should be checked to verify the environment bearing is available
+ * before calling this method. The value is undefined if hasEnvironmentBearing is false.
*/
float environmentBearingDegrees;
/**
- * Bearing uncertainty [0 to 180].
+ * Environment bearing uncertainty [0 to 180]. It represents the standard deviation of the
+ * physical structure in the circle of position uncertainty. hasEnvironmentBearing becomes false
+ * as the uncertainty value passes a predefined threshold depending on the physical structure
+ * around the user.
+ *
+ * hasEnvironmentBearing should be checked to verify the environment bearing is available
+ * before calling this method. The value is undefined if hasEnvironmentBearing is false.
*/
float environmentBearingUncertaintyDegrees;
-};
\ No newline at end of file
+
+ /**
+ * A set of SingleSatCorrection each containing measurement corrections for a satellite in view
+ */
+ vec<SingleSatCorrection> satCorrections;
+};
+
+/**
+ * A struct with measurement corrections for a single visible satellites, updating the
+ * GnssConstellationType to 2.0, which supports IRNSS. The v1_0.constellation is deprecated and is
+ * no longer used by framework.
+ *
+ * The bit mask singleSatCorrectionFlags indicates which correction values are valid in the struct
+ */
+struct SingleSatCorrection {
+ @1.0::SingleSatCorrection v1_0;
+
+ /**
+ * Defines the constellation of the given satellite.
+ */
+ GnssConstellationType constellation;
+};
diff --git a/graphics/composer/2.1/utils/vts/Android.bp b/graphics/composer/2.1/utils/vts/Android.bp
index cdc0f35..3b0911f 100644
--- a/graphics/composer/2.1/utils/vts/Android.bp
+++ b/graphics/composer/2.1/utils/vts/Android.bp
@@ -23,14 +23,13 @@
"TestCommandReader.cpp",
],
static_libs: [
- "VtsHalHidlTargetTestBase",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.mapper@2.0-vts",
"android.hardware.graphics.mapper@3.0-vts",
"android.hardware.graphics.mapper@4.0-vts",
+ "libgtest",
],
export_static_lib_headers: [
- "VtsHalHidlTargetTestBase",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.mapper@2.0-vts",
"android.hardware.graphics.mapper@3.0-vts",
diff --git a/graphics/composer/2.1/utils/vts/ComposerVts.cpp b/graphics/composer/2.1/utils/vts/ComposerVts.cpp
index a8e1480..4b6b7c8 100644
--- a/graphics/composer/2.1/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.1/utils/vts/ComposerVts.cpp
@@ -16,8 +16,6 @@
#include <composer-vts/2.1/ComposerVts.h>
-#include <VtsHalHidlTargetTestBase.h>
-
namespace android {
namespace hardware {
namespace graphics {
@@ -25,11 +23,6 @@
namespace V2_1 {
namespace vts {
-Composer::Composer() : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>()) {}
-
-Composer::Composer(const std::string& name)
- : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>(name)) {}
-
Composer::Composer(const sp<IComposer>& composer) : mComposer(composer) {
// ASSERT_* can only be used in functions returning void.
[this] {
diff --git a/graphics/composer/2.1/utils/vts/TestCommandReader.cpp b/graphics/composer/2.1/utils/vts/TestCommandReader.cpp
index 454a89c..0506f3a 100644
--- a/graphics/composer/2.1/utils/vts/TestCommandReader.cpp
+++ b/graphics/composer/2.1/utils/vts/TestCommandReader.cpp
@@ -29,63 +29,68 @@
mErrors.clear();
mCompositionChanges.clear();
while (!isEmpty()) {
- IComposerClient::Command command;
+ int32_t command;
uint16_t length;
ASSERT_TRUE(beginCommand(&command, &length));
- switch (command) {
- case IComposerClient::Command::SELECT_DISPLAY:
- ASSERT_EQ(2, length);
- read64(); // display
- break;
- case IComposerClient::Command::SET_ERROR: {
- ASSERT_EQ(2, length);
- auto loc = read();
- auto err = readSigned();
- std::pair<uint32_t, uint32_t> error(loc, err);
- mErrors.push_back(error);
- } break;
- case IComposerClient::Command::SET_CHANGED_COMPOSITION_TYPES:
- ASSERT_EQ(0, length % 3);
- for (uint16_t count = 0; count < length / 3; ++count) {
- uint64_t layerId = read64();
- uint32_t composition = read();
-
- std::pair<uint64_t, uint32_t> compositionChange(layerId, composition);
- mCompositionChanges.push_back(compositionChange);
- }
- break;
- case IComposerClient::Command::SET_DISPLAY_REQUESTS:
- ASSERT_EQ(1, length % 3);
- read(); // displayRequests, ignored for now
- for (uint16_t count = 0; count < (length - 1) / 3; ++count) {
- read64(); // layer
- // silently eat requests to clear the client target, since we won't be testing
- // client composition anyway
- ASSERT_EQ(1u, read());
- }
- break;
- case IComposerClient::Command::SET_PRESENT_FENCE:
- ASSERT_EQ(1, length);
- close(readFence());
- break;
- case IComposerClient::Command::SET_RELEASE_FENCES:
- ASSERT_EQ(0, length % 3);
- for (uint16_t count = 0; count < length / 3; ++count) {
- read64();
- close(readFence());
- }
- break;
- default:
- GTEST_FAIL() << "unexpected return command " << std::hex
- << static_cast<int>(command);
- break;
- }
+ parseSingleCommand(command, length);
endCommand();
}
}
+void TestCommandReader::parseSingleCommand(int32_t commandRaw, uint16_t length) {
+ IComposerClient::Command command = static_cast<IComposerClient::Command>(commandRaw);
+
+ switch (command) {
+ case IComposerClient::Command::SELECT_DISPLAY:
+ ASSERT_EQ(2, length);
+ read64(); // display
+ break;
+ case IComposerClient::Command::SET_ERROR: {
+ ASSERT_EQ(2, length);
+ auto loc = read();
+ auto err = readSigned();
+ std::pair<uint32_t, uint32_t> error(loc, err);
+ mErrors.push_back(error);
+ } break;
+ case IComposerClient::Command::SET_CHANGED_COMPOSITION_TYPES:
+ ASSERT_EQ(0, length % 3);
+ for (uint16_t count = 0; count < length / 3; ++count) {
+ uint64_t layerId = read64();
+ uint32_t composition = read();
+
+ std::pair<uint64_t, uint32_t> compositionChange(layerId, composition);
+ mCompositionChanges.push_back(compositionChange);
+ }
+ break;
+ case IComposerClient::Command::SET_DISPLAY_REQUESTS:
+ ASSERT_EQ(1, length % 3);
+ read(); // displayRequests, ignored for now
+ for (uint16_t count = 0; count < (length - 1) / 3; ++count) {
+ read64(); // layer
+ // silently eat requests to clear the client target, since we won't be testing
+ // client composition anyway
+ ASSERT_EQ(1u, read());
+ }
+ break;
+ case IComposerClient::Command::SET_PRESENT_FENCE:
+ ASSERT_EQ(1, length);
+ close(readFence());
+ break;
+ case IComposerClient::Command::SET_RELEASE_FENCES:
+ ASSERT_EQ(0, length % 3);
+ for (uint16_t count = 0; count < length / 3; ++count) {
+ read64();
+ close(readFence());
+ }
+ break;
+ default:
+ GTEST_FAIL() << "unexpected return command " << std::hex << static_cast<int>(command);
+ break;
+ }
+}
+
} // namespace vts
} // namespace V2_1
} // namespace composer
diff --git a/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/TestCommandReader.h b/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/TestCommandReader.h
index c12debe..40d347a 100644
--- a/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/TestCommandReader.h
+++ b/graphics/composer/2.1/utils/vts/include/composer-vts/2.1/TestCommandReader.h
@@ -29,12 +29,16 @@
// returned.
class TestCommandReader : public CommandReaderBase {
public:
- // Parse all commands in the return command queue. Call GTEST_FAIL() for
- // unexpected errors or commands.
- void parse();
+ virtual ~TestCommandReader() = default;
+ // Parse all commands in the return command queue. Call GTEST_FAIL() for
+ // unexpected errors or commands.
+ void parse();
- std::vector<std::pair<uint32_t, uint32_t>> mErrors;
- std::vector<std::pair<uint64_t, uint32_t>> mCompositionChanges;
+ std::vector<std::pair<uint32_t, uint32_t>> mErrors;
+ std::vector<std::pair<uint64_t, uint32_t>> mCompositionChanges;
+
+ protected:
+ virtual void parseSingleCommand(int32_t commandRaw, uint16_t length);
};
} // namespace vts
diff --git a/graphics/composer/2.2/utils/vts/Android.bp b/graphics/composer/2.2/utils/vts/Android.bp
index 5432882..a8bb1a2 100644
--- a/graphics/composer/2.2/utils/vts/Android.bp
+++ b/graphics/composer/2.2/utils/vts/Android.bp
@@ -26,11 +26,11 @@
"libui",
],
static_libs: [
- "VtsHalHidlTargetTestBase",
"android.hardware.graphics.composer@2.1-vts",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.mapper@2.1-vts",
"libarect",
+ "libgtest",
"libmath",
"libnativewindow",
"librenderengine",
@@ -40,7 +40,6 @@
"android.hardware.graphics.mapper@4.0-vts",
],
export_static_lib_headers: [
- "VtsHalHidlTargetTestBase",
"android.hardware.graphics.composer@2.1-vts",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.mapper@2.1-vts",
diff --git a/graphics/composer/2.2/utils/vts/ComposerVts.cpp b/graphics/composer/2.2/utils/vts/ComposerVts.cpp
index 93b67f0..a526137 100644
--- a/graphics/composer/2.2/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.2/utils/vts/ComposerVts.cpp
@@ -16,7 +16,6 @@
#include <composer-vts/2.2/ComposerVts.h>
-#include <VtsHalHidlTargetTestBase.h>
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <hidl/HidlTransportUtils.h>
diff --git a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
index a23d72c..c78c358 100644
--- a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
+++ b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
@@ -33,6 +33,10 @@
mRenderEngine = renderengine::RenderEngine::create(args);
}
+TestRenderEngine::~TestRenderEngine() {
+ mRenderEngine.release();
+}
+
void TestRenderEngine::setRenderLayers(std::vector<std::shared_ptr<TestLayer>> layers) {
sort(layers.begin(), layers.end(),
[](const std::shared_ptr<TestLayer>& lhs, const std::shared_ptr<TestLayer>& rhs) -> bool {
diff --git a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h
index 5d22305..6bc2732 100644
--- a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h
+++ b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ComposerVts.h
@@ -22,7 +22,6 @@
#include <unordered_set>
#include <vector>
-#include <VtsHalHidlTargetTestBase.h>
#include <android/hardware/graphics/composer/2.2/IComposer.h>
#include <android/hardware/graphics/composer/2.2/IComposerClient.h>
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
diff --git a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h
index 7519a64..d5eedf1 100644
--- a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h
+++ b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/ReadbackVts.h
@@ -142,7 +142,7 @@
const native_handle_t* mBufferHandle = nullptr;
};
-class ReadbackHelper : public ::testing::VtsHalHidlTargetTestBase {
+class ReadbackHelper {
public:
static std::string getColorModeString(ColorMode mode);
diff --git a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h
index b936cab..f2d5f19 100644
--- a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h
+++ b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h
@@ -24,8 +24,6 @@
#include <ui/Rect.h>
#include <ui/Region.h>
-#include <VtsHalHidlTargetTestBase.h>
-
namespace android {
namespace hardware {
namespace graphics {
@@ -43,7 +41,7 @@
static constexpr uint32_t sMaxFrameBufferAcquireBuffers = 2;
TestRenderEngine(const RenderEngineCreationArgs& args);
- ~TestRenderEngine() = default;
+ ~TestRenderEngine();
void setRenderLayers(std::vector<std::shared_ptr<TestLayer>> layers);
void initGraphicBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint64_t usage);
diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp
index f987516..e38af00 100644
--- a/graphics/composer/2.2/vts/functional/Android.bp
+++ b/graphics/composer/2.2/vts/functional/Android.bp
@@ -19,7 +19,7 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"VtsHalGraphicsComposerV2_2ReadbackTest.cpp",
- "VtsHalGraphicsComposerV2_2TargetTest.cpp"
+ "VtsHalGraphicsComposerV2_2TargetTest.cpp",
],
// TODO(b/64437680): Assume these libs are always available on the device.
@@ -51,12 +51,16 @@
"android.hardware.graphics.mapper@3.0-vts",
"android.hardware.graphics.mapper@4.0",
"android.hardware.graphics.mapper@4.0-vts",
- "librenderengine"
+ "libgtest",
+ "librenderengine",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
"android.hardware.graphics.composer@2.2-command-buffer",
],
disable_framework: true,
- test_suites: ["general-tests", "vts-core"],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
}
diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp
index 044bd96..cb43e64 100644
--- a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp
+++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp
@@ -932,7 +932,7 @@
class GraphicsBlendModeCompositionTest
: public GraphicsCompositionTestBase,
- public testing::WithParamInterface<std::tuple<string, string>> {
+ public testing::WithParamInterface<std::tuple<std::string, std::string>> {
public:
void SetUp() override {
SetUpBase(std::get<0>(GetParam()));
diff --git a/graphics/composer/2.3/utils/vts/Android.bp b/graphics/composer/2.3/utils/vts/Android.bp
index f65a9c4..3d81e8f 100644
--- a/graphics/composer/2.3/utils/vts/Android.bp
+++ b/graphics/composer/2.3/utils/vts/Android.bp
@@ -23,6 +23,7 @@
static_libs: [
"android.hardware.graphics.composer@2.2-vts",
"android.hardware.graphics.composer@2.3",
+ "libgtest",
],
export_static_lib_headers: [
"android.hardware.graphics.composer@2.2-vts",
diff --git a/graphics/composer/2.3/utils/vts/ComposerVts.cpp b/graphics/composer/2.3/utils/vts/ComposerVts.cpp
index d4f5b3a..d73a3b0 100644
--- a/graphics/composer/2.3/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.3/utils/vts/ComposerVts.cpp
@@ -16,8 +16,6 @@
#include <composer-vts/2.3/ComposerVts.h>
-#include <VtsHalHidlTargetTestBase.h>
-
namespace android {
namespace hardware {
namespace graphics {
@@ -27,11 +25,6 @@
using V2_1::Error;
-Composer::Composer() : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>()) {}
-
-Composer::Composer(const std::string& name)
- : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>(name)) {}
-
Composer::Composer(const sp<IComposer>& composer)
: V2_2::vts::Composer(composer), mComposer(composer) {}
diff --git a/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h b/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h
index e5ac842..dae9ab4 100644
--- a/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h
+++ b/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h
@@ -19,7 +19,6 @@
#include <memory>
#include <vector>
-#include <VtsHalHidlTargetTestBase.h>
#include <android/hardware/graphics/composer/2.3/IComposer.h>
#include <android/hardware/graphics/composer/2.3/IComposerClient.h>
#include <composer-vts/2.2/ComposerVts.h>
diff --git a/graphics/composer/2.4/utils/vts/Android.bp b/graphics/composer/2.4/utils/vts/Android.bp
index 673c15e..fc13c2a 100644
--- a/graphics/composer/2.4/utils/vts/Android.bp
+++ b/graphics/composer/2.4/utils/vts/Android.bp
@@ -20,19 +20,21 @@
srcs: [
"ComposerVts.cpp",
"GraphicsComposerCallback.cpp",
+ "TestCommandReader.cpp",
],
static_libs: [
- "VtsHalHidlTargetTestBase",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3-vts",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
+ "libgtest",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
"android.hardware.graphics.composer@2.2-command-buffer",
"android.hardware.graphics.composer@2.3-command-buffer",
+ "android.hardware.graphics.composer@2.4-command-buffer",
],
cflags: [
"-O0",
diff --git a/graphics/composer/2.4/utils/vts/ComposerVts.cpp b/graphics/composer/2.4/utils/vts/ComposerVts.cpp
index 8cdc452..b3f3374 100644
--- a/graphics/composer/2.4/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.4/utils/vts/ComposerVts.cpp
@@ -16,8 +16,6 @@
#include <composer-vts/2.4/ComposerVts.h>
-#include <VtsHalHidlTargetTestBase.h>
-
namespace android {
namespace hardware {
namespace graphics {
@@ -27,11 +25,6 @@
using V2_4::Error;
-Composer::Composer() : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>()) {}
-
-Composer::Composer(const std::string& name)
- : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>(name)) {}
-
Composer::Composer(const sp<IComposer>& composer)
: V2_3::vts::Composer(composer), mComposer(composer) {}
@@ -139,6 +132,38 @@
return error;
}
+void ComposerClient::execute(TestCommandReader* reader, CommandWriterBase* writer) {
+ bool queueChanged = false;
+ uint32_t commandLength = 0;
+ hidl_vec<hidl_handle> commandHandles;
+ ASSERT_TRUE(writer->writeQueue(&queueChanged, &commandLength, &commandHandles));
+
+ if (queueChanged) {
+ auto ret = mClient->setInputCommandQueue(*writer->getMQDescriptor());
+ ASSERT_EQ(V2_1::Error::NONE, ret);
+ }
+
+ mClient->executeCommands_2_3(
+ commandLength, commandHandles,
+ [&](const auto& tmpError, const auto& tmpOutQueueChanged, const auto& tmpOutLength,
+ const auto& tmpOutHandles) {
+ ASSERT_EQ(V2_1::Error::NONE, tmpError);
+
+ if (tmpOutQueueChanged) {
+ mClient->getOutputCommandQueue(
+ [&](const auto& tmpError, const auto& tmpDescriptor) {
+ ASSERT_EQ(V2_3::Error::NONE, tmpError);
+ reader->setMQDescriptor(tmpDescriptor);
+ });
+ }
+
+ ASSERT_TRUE(reader->readQueue(tmpOutLength, tmpOutHandles));
+ reader->parse();
+ });
+ reader->reset();
+ writer->reset();
+}
+
} // namespace vts
} // namespace V2_4
} // namespace composer
diff --git a/graphics/composer/2.4/utils/vts/TestCommandReader.cpp b/graphics/composer/2.4/utils/vts/TestCommandReader.cpp
new file mode 100644
index 0000000..a1ca628
--- /dev/null
+++ b/graphics/composer/2.4/utils/vts/TestCommandReader.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 <composer-vts/2.4/TestCommandReader.h>
+
+#include <gtest/gtest.h>
+
+namespace android::hardware::graphics::composer::V2_4::vts {
+
+void TestCommandReader::parseSingleCommand(int32_t commandRaw, uint16_t length) {
+ IComposerClient::Command command = static_cast<IComposerClient::Command>(commandRaw);
+
+ switch (command) {
+ case IComposerClient::Command::SET_CLIENT_TARGET_PROPERTY:
+ ASSERT_EQ(2, length);
+ read();
+ close(readFence());
+ break;
+ default:
+ return android::hardware::graphics::composer::V2_1::vts::TestCommandReader::
+ parseSingleCommand(commandRaw, length);
+ }
+}
+
+} // namespace android::hardware::graphics::composer::V2_4::vts
diff --git a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
index 3dd8e50..28e17b4 100644
--- a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
+++ b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
@@ -19,10 +19,10 @@
#include <memory>
#include <vector>
-#include <VtsHalHidlTargetTestBase.h>
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
#include <composer-vts/2.3/ComposerVts.h>
+#include <composer-vts/2.4/TestCommandReader.h>
#include <utils/StrongPointer.h>
namespace android {
@@ -93,6 +93,8 @@
Error setContentType(Display display, IComposerClient::ContentType contentType);
+ void execute(TestCommandReader* reader, CommandWriterBase* writer);
+
Error getLayerGenericMetadataKeys(
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys);
diff --git a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/TestCommandReader.h b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/TestCommandReader.h
new file mode 100644
index 0000000..5736fa6
--- /dev/null
+++ b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/TestCommandReader.h
@@ -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.
+ */
+
+#pragma once
+
+#include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
+#include <composer-vts/2.1/TestCommandReader.h>
+
+namespace android::hardware::graphics::composer::V2_4::vts {
+
+// A command parser that checks that no error nor unexpected commands are
+// returned.
+class TestCommandReader
+ : public android::hardware::graphics::composer::V2_1::vts::TestCommandReader {
+ protected:
+ void parseSingleCommand(int32_t commandRaw, uint16_t length) override;
+};
+
+} // namespace android::hardware::graphics::composer::V2_4::vts
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index f6b46ff..b6343d3 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -23,9 +23,9 @@
#include <android-base/logging.h>
#include <android/hardware/graphics/mapper/2.0/IMapper.h>
#include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
-#include <composer-vts/2.1/TestCommandReader.h>
#include <composer-vts/2.4/ComposerVts.h>
#include <composer-vts/2.4/GraphicsComposerCallback.h>
+#include <composer-vts/2.4/TestCommandReader.h>
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
@@ -42,6 +42,8 @@
namespace vts {
namespace {
+using namespace std::chrono_literals;
+
using common::V1_0::BufferUsage;
using common::V1_1::RenderIntent;
using common::V1_2::ColorMode;
@@ -75,7 +77,7 @@
mComposerCallback->setVsyncAllowed(false);
mWriter = std::make_unique<CommandWriterBase>(1024);
- mReader = std::make_unique<V2_1::vts::TestCommandReader>();
+ mReader = std::make_unique<TestCommandReader>();
}
void TearDown() override {
@@ -151,7 +153,7 @@
Display mPrimaryDisplay;
Display mInvalidDisplayId;
std::unique_ptr<CommandWriterBase> mWriter;
- std::unique_ptr<V2_1::vts::TestCommandReader> mReader;
+ std::unique_ptr<TestCommandReader> mReader;
private:
Display waitForFirstDisplay() {
@@ -182,7 +184,7 @@
mPrimaryDisplay, activeConfig, IComposerClient::Attribute::HEIGHT);
mWriter = std::make_unique<CommandWriterBase>(1024);
- mReader = std::make_unique<V2_1::vts::TestCommandReader>();
+ mReader = std::make_unique<TestCommandReader>();
}
void TearDown() override {
@@ -209,7 +211,7 @@
int64_t newPeriodNanos);
std::unique_ptr<CommandWriterBase> mWriter;
- std::unique_ptr<V2_1::vts::TestCommandReader> mReader;
+ std::unique_ptr<TestCommandReader> mReader;
int32_t mDisplayWidth;
int32_t mDisplayHeight;
@@ -526,7 +528,7 @@
EXPECT_EQ(Error::UNSUPPORTED,
mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, false));
GTEST_SUCCEED() << "Auto Low Latency Mode is not supported on display "
- << to_string(display) << ", skipping test";
+ << std::to_string(display) << ", skipping test";
return;
}
@@ -580,7 +582,7 @@
if (!contentTypeSupport) {
EXPECT_EQ(Error::UNSUPPORTED, mComposerClient->setContentType(display, contentType));
GTEST_SUCCEED() << contentTypeStr << " content type is not supported on display "
- << to_string(display) << ", skipping test";
+ << std::to_string(display) << ", skipping test";
return;
}
@@ -626,7 +628,7 @@
testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)),
android::hardware::PrintInstanceNameToString);
-TEST_F(GraphicsComposerHidlCommandTest, getLayerGenericMetadataKeys) {
+TEST_P(GraphicsComposerHidlCommandTest, getLayerGenericMetadataKeys) {
std::vector<IComposerClient::LayerGenericMetadataKey> keys;
mComposerClient->getLayerGenericMetadataKeys(&keys);
diff --git a/graphics/mapper/2.1/utils/passthrough/include/mapper-passthrough/2.1/Gralloc0Hal.h b/graphics/mapper/2.1/utils/passthrough/include/mapper-passthrough/2.1/Gralloc0Hal.h
index 8540068..13df3bc 100644
--- a/graphics/mapper/2.1/utils/passthrough/include/mapper-passthrough/2.1/Gralloc0Hal.h
+++ b/graphics/mapper/2.1/utils/passthrough/include/mapper-passthrough/2.1/Gralloc0Hal.h
@@ -49,7 +49,12 @@
mModule, bufferHandle, descriptorInfo.width, descriptorInfo.height,
static_cast<int32_t>(descriptorInfo.format),
static_cast<uint64_t>(descriptorInfo.usage), stride);
- return static_cast<Error>(ret);
+ if (ret == -EINVAL) {
+ return Error::BAD_BUFFER;
+ } else if (ret < 0) {
+ return Error::BAD_VALUE;
+ }
+ return Error::NONE;
}
Error getTransportSize(const native_handle_t* bufferHandle, uint32_t* outNumFds,
uint32_t* outNumInts) override {
diff --git a/health/2.0/default/Health.cpp b/health/2.0/default/Health.cpp
index 4225fd8..65eada8 100644
--- a/health/2.0/default/Health.cpp
+++ b/health/2.0/default/Health.cpp
@@ -156,7 +156,7 @@
const HealthInfo_1_0& health_info = battery_monitor_->getHealthInfo_1_0();
struct BatteryProperties props;
convertFromHealthInfo(health_info, &props);
- bool log = healthd_board_battery_update(&props);
+ bool log = (healthd_board_battery_update(&props) == 0);
if (log) {
battery_monitor_->logValues();
}
diff --git a/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp b/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp
index 49891b7..352a990 100644
--- a/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp
+++ b/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp
@@ -315,9 +315,7 @@
TEST_P(HealthHidlTest, getChargeStatus) {
SKIP_IF_SKIPPED();
EXPECT_OK(mHealth->getChargeStatus([](auto result, auto value) {
- EXPECT_VALID_OR_UNSUPPORTED_PROP(
- result, toString(value),
- value != BatteryStatus::UNKNOWN && verifyEnum<BatteryStatus>(value));
+ EXPECT_VALID_OR_UNSUPPORTED_PROP(result, toString(value), verifyEnum<BatteryStatus>(value));
}));
}
diff --git a/health/2.1/README.md b/health/2.1/README.md
index bfcf13b..8390570 100644
--- a/health/2.1/README.md
+++ b/health/2.1/README.md
@@ -24,6 +24,9 @@
```mk
# Install default passthrough implementation to vendor.
PRODUCT_PACKAGES += android.hardware.health@2.1-impl
+
+ # For non-A/B devices, install default passthrough implementation to recovery.
+ PRODUCT_PACKAGES += android.hardware.health@2.1-impl.recovery
```
You are done. Otherwise, go to the next step.
@@ -42,6 +45,8 @@
implementation, See
[Upgrading from Health HAL 2.0](#update-from-2-0).
+ 1. [Install the implementation](#install).
+
1. [Update necessary SELinux permissions](#selinux).
1. [Fix `/charger` symlink](#charger-symlink).
@@ -95,15 +100,22 @@
`HealthImpl::getHealthInfo` or `HealthImpl::getHealthInfo_2_1` because they call
`getDiskStats` and `getStorageInfo` to retrieve storage information.
+# Install the implementation {#install}
+
+In `device.mk`:
+
+```mk
+# Install the passthrough implementation to vendor.
+PRODUCT_PACKAGES += android.hardware.health@2.1-impl-<device>
+
+# For non-A/B devices, also install the passthrough implementation to recovery.
+PRODUCT_PACKAGES += android.hardware.health@2.1-impl-<device>.recovery
+```
+
# Update necessary SELinux permissions {#selinux}
For example (replace `<device>` with the device name):
```
-# device/<manufacturer>/<device>/sepolicy/vendor/file_contexts
-# Required for charger to open passthrough implementation. Replace <device> with the proper device
-# name. File name must be consistent with `stem` of the implementation module.
-/vendor/lib(64)?/hw/android\.hardware\.health@2\.0-impl-2\.1-<device>\.so u:object_r:same_process_hal_file:s0
-
# device/<manufacturer>/<device>/sepolicy/vendor/hal_health_default.te
# Add device specific permissions to hal_health_default domain, especially
# if a device-specific libhealthd is used and/or device-specific storage related
diff --git a/health/2.1/default/android.hardware.health@2.1-service.rc b/health/2.1/default/android.hardware.health@2.1-service.rc
index 917f1c2..b6d9e3b 100644
--- a/health/2.1/default/android.hardware.health@2.1-service.rc
+++ b/health/2.1/default/android.hardware.health@2.1-service.rc
@@ -1,5 +1,5 @@
service health-hal-2-1 /vendor/bin/hw/android.hardware.health@2.1-service
- class hal
+ class hal charger
user system
group system
capabilities WAKE_ALARM
diff --git a/health/2.1/types.hal b/health/2.1/types.hal
index efd8d6f..d775491 100644
--- a/health/2.1/types.hal
+++ b/health/2.1/types.hal
@@ -19,6 +19,10 @@
import @1.0::HealthConfig;
import @2.0::HealthInfo;
+enum Constants : int64_t {
+ BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED = -1,
+};
+
/**
* Battery capacity level. This enum provides additional information along side
* with the battery capacity.
@@ -27,11 +31,17 @@
*/
enum BatteryCapacityLevel : int32_t {
/**
+ * Battery capacity level is unsupported.
+ * Battery capacity level must be set to this value if and only if the
+ * implementation is unsupported.
+ */
+ UNSUPPORTED = -1,
+ /**
* Battery capacity level is unknown.
* Battery capacity level must be set to this value if and only if battery
- * is not present.
+ * is not present or the battery capacity level is unknown/uninitialized.
*/
- UNKNOWN = 0,
+ UNKNOWN,
/**
* Battery is at critical level. The Android framework must schedule a
* shutdown when it sees this value from the HAL.
@@ -78,18 +88,21 @@
/**
* Estimated time to fully charge the device (in seconds).
+ * Value must be BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED if and
+ * only if the implementation is unsupported.
* Value must be 0 if and only if batteryCapacityLevel is FULL or UNKNOWN.
* Otherwise, value must be positive.
*/
int64_t batteryChargeTimeToFullNowSeconds;
/**
- * Estimated battery full capacity (in microamp hours, uAh).
+ * Estimated battery full charge design capacity (in microamp hours, uAh).
* Value must be 0 if unknown.
- * Value must be positive if known, and must be between [50%, 120%] of
- * batteryFullCharge (the designed capacity).
+ * Value must be positive if known.
+ * Value must be greater than 100 000 uAh.
+ * Value must be less than 100 000 000 uAh.
*/
- int32_t batteryFullCapacityUah;
+ int32_t batteryFullChargeDesignCapacityUah;
};
/**
diff --git a/health/2.1/vts/functional/Android.bp b/health/2.1/vts/functional/Android.bp
index 5aa873a..17472fa 100644
--- a/health/2.1/vts/functional/Android.bp
+++ b/health/2.1/vts/functional/Android.bp
@@ -25,5 +25,8 @@
"android.hardware.health@2.0",
"android.hardware.health@2.1",
],
- test_suites: ["general-tests"],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
}
diff --git a/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp b/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp
index da9f5bb..deb1a29 100644
--- a/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp
+++ b/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp
@@ -219,6 +219,9 @@
return AssertionFailure() << static_cast<std::underlying_type_t<T>>(value) << " is not valid";
}
+#define FULL_CHARGE_DESIGN_CAP_MIN ((long)100 * 1000)
+#define FULL_CHARGE_DESIGN_CAP_MAX ((long)100000 * 1000)
+
/*
* Tests the values returned by getHealthInfo() from interface IHealth.
*/
@@ -228,14 +231,18 @@
return;
}
ASSERT_EQ(Result::SUCCESS, result);
- const auto& legacy = value.legacy.legacy;
EXPECT_TRUE(IsEnum(value.batteryCapacityLevel)) << " BatteryCapacityLevel";
EXPECT_GE(value.batteryChargeTimeToFullNowSeconds, 0);
- EXPECT_GE(value.batteryFullCapacityUah, 0) << "batteryFullCapacityUah is unknown";
- EXPECT_GE(value.batteryFullCapacityUah, legacy.batteryFullCharge * 0.50);
- EXPECT_LE(value.batteryFullCapacityUah, legacy.batteryFullCharge * 1.20);
+ EXPECT_GE(value.batteryFullChargeDesignCapacityUah, 0)
+ << "batteryFullChargeDesignCapacityUah should not be negative";
+
+ EXPECT_GT((long)value.batteryFullChargeDesignCapacityUah, FULL_CHARGE_DESIGN_CAP_MIN)
+ << "batteryFullChargeDesignCapacityUah should be greater than 100 mAh";
+
+ EXPECT_LT((long)value.batteryFullChargeDesignCapacityUah, FULL_CHARGE_DESIGN_CAP_MAX)
+ << "batteryFullChargeDesignCapacityUah should be less than 100,000 mAh";
})));
}
diff --git a/health/storage/1.0/vts/functional/Android.bp b/health/storage/1.0/vts/functional/Android.bp
index a30cdde..4c703c5 100644
--- a/health/storage/1.0/vts/functional/Android.bp
+++ b/health/storage/1.0/vts/functional/Android.bp
@@ -22,6 +22,9 @@
shared_libs: [
"libhidlbase",
],
- test_suites: ["general-tests", "vts-core"],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
+ test_config: "VtsHalHealthStorageV1_0TargetTest.config",
}
-
diff --git a/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.config b/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.config
new file mode 100644
index 0000000..0cd1c11
--- /dev/null
+++ b/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.config
@@ -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 VtsHalHealthStorageV1_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="VtsHalHealthStorageV1_0TargetTest->/data/local/tmp/VtsHalHealthStorageV1_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="VtsHalHealthStorageV1_0TargetTest" />
+ <option name="native-test-timeout" value="3m" />
+ </test>
+</configuration>
diff --git a/health/utils/libhealthloop/utils.cpp b/health/utils/libhealthloop/utils.cpp
index ebfd8d8..053fd19 100644
--- a/health/utils/libhealthloop/utils.cpp
+++ b/health/utils/libhealthloop/utils.cpp
@@ -42,6 +42,7 @@
.batteryCycleCountPath = String8(String8::kEmptyString),
.batteryCapacityLevelPath = String8(String8::kEmptyString),
.batteryChargeTimeToFullNowPath = String8(String8::kEmptyString),
+ .batteryFullChargeDesignCapacityUahPath = String8(String8::kEmptyString),
.energyCounter = NULL,
.boot_min_cap = 0,
.screen_on = NULL,
diff --git a/identity/1.0/Android.bp b/identity/1.0/Android.bp
deleted file mode 100644
index e0a6332..0000000
--- a/identity/1.0/Android.bp
+++ /dev/null
@@ -1,20 +0,0 @@
-// This file is autogenerated by hidl-gen -Landroidbp.
-
-hidl_interface {
- name: "android.hardware.identity@1.0",
- root: "android.hardware",
- vndk: {
- enabled: true,
- },
- srcs: [
- "types.hal",
- "IIdentityCredential.hal",
- "IIdentityCredentialStore.hal",
- "IWritableIdentityCredential.hal",
- ],
- interfaces: [
- "android.hardware.keymaster@4.0",
- "android.hidl.base@1.0",
- ],
- gen_java: false,
-}
diff --git a/identity/1.0/default/Android.bp b/identity/1.0/default/Android.bp
deleted file mode 100644
index d2b2966..0000000
--- a/identity/1.0/default/Android.bp
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// 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_binary {
- name: "android.hardware.identity@1.0-service.example",
- init_rc: ["android.hardware.identity@1.0-service.example.rc"],
- vendor: true,
- relative_install_path: "hw",
- cflags: [
- "-Wall",
- "-Wextra",
- ],
- srcs: [
- "service.cpp",
- "IdentityCredential.cpp",
- "IdentityCredentialStore.cpp",
- "WritableIdentityCredential.cpp",
- ],
- shared_libs: [
- "android.hardware.identity@1.0",
- "android.hardware.identity-support-lib",
- "android.hardware.keymaster@4.0",
- "libcppbor",
- "libcrypto",
- "libbase",
- "libhidlbase",
- "liblog",
- "libutils",
- ],
-}
diff --git a/identity/1.0/default/IdentityCredential.cpp b/identity/1.0/default/IdentityCredential.cpp
deleted file mode 100644
index b0a5e56..0000000
--- a/identity/1.0/default/IdentityCredential.cpp
+++ /dev/null
@@ -1,773 +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.
- */
-
-#define LOG_TAG "IdentityCredential"
-
-#include "IdentityCredential.h"
-#include "IdentityCredentialStore.h"
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <string.h>
-
-#include <android-base/logging.h>
-
-#include <cppbor.h>
-#include <cppbor_parse.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-using ::android::hardware::keymaster::V4_0::Timestamp;
-using ::std::optional;
-
-Return<void> IdentityCredential::deleteCredential(deleteCredential_cb _hidl_cb) {
- cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
- vector<uint8_t> proofOfDeletion = array.encode();
-
- optional<vector<uint8_t>> proofOfDeletionSignature =
- support::coseSignEcDsa(credentialPrivKey_,
- proofOfDeletion, // payload
- {}, // additionalData
- {}); // certificateChain
- if (!proofOfDeletionSignature) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {});
- return Void();
- }
-
- _hidl_cb(support::resultOK(), proofOfDeletionSignature.value());
- return Void();
-}
-
-Return<void> IdentityCredential::createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) {
- optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
- if (!keyPair) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error creating ephemeral key pair"), {});
- return Void();
- }
-
- // Stash public key of this key-pair for later check in startRetrieval().
- optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPair.value());
- if (!publicKey) {
- _hidl_cb(support::result(ResultCode::FAILED,
- "Error getting public part of ephemeral key pair"),
- {});
- return Void();
- }
- ephemeralPublicKey_ = publicKey.value();
-
- _hidl_cb(support::resultOK(), keyPair.value());
- return Void();
-}
-
-Return<void> IdentityCredential::setReaderEphemeralPublicKey(
- const hidl_vec<uint8_t>& publicKey, setReaderEphemeralPublicKey_cb _hidl_cb) {
- readerPublicKey_ = publicKey;
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-ResultCode IdentityCredential::initialize() {
- auto [item, _, message] = cppbor::parse(credentialData_);
- if (item == nullptr) {
- LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
- return ResultCode::INVALID_DATA;
- }
-
- const cppbor::Array* arrayItem = item->asArray();
- if (arrayItem == nullptr || arrayItem->size() != 3) {
- LOG(ERROR) << "CredentialData is not an array with three elements";
- return ResultCode::INVALID_DATA;
- }
-
- const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
- const cppbor::Bool* testCredentialItem =
- ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
- : nullptr);
- const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
- if (docTypeItem == nullptr || testCredentialItem == nullptr ||
- encryptedCredentialKeysItem == nullptr) {
- LOG(ERROR) << "CredentialData unexpected item types";
- return ResultCode::INVALID_DATA;
- }
-
- docType_ = docTypeItem->value();
- testCredential_ = testCredentialItem->value();
-
- vector<uint8_t> hardwareBoundKey;
- if (testCredential_) {
- hardwareBoundKey = support::getTestHardwareBoundKey();
- } else {
- hardwareBoundKey = support::getHardwareBoundKey();
- }
-
- const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
- const vector<uint8_t> docTypeVec(docType_.begin(), docType_.end());
- optional<vector<uint8_t>> decryptedCredentialKeys =
- support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
- if (!decryptedCredentialKeys) {
- LOG(ERROR) << "Error decrypting CredentialKeys";
- return ResultCode::INVALID_DATA;
- }
-
- auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
- if (dckItem == nullptr) {
- LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
- return ResultCode::INVALID_DATA;
- }
- const cppbor::Array* dckArrayItem = dckItem->asArray();
- if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
- LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
- return ResultCode::INVALID_DATA;
- }
- const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
- const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
- if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
- LOG(ERROR) << "CredentialKeys unexpected item types";
- return ResultCode::INVALID_DATA;
- }
- storageKey_ = storageKeyItem->value();
- credentialPrivKey_ = credentialPrivKeyItem->value();
-
- return ResultCode::OK;
-}
-
-Return<void> IdentityCredential::createAuthChallenge(createAuthChallenge_cb _hidl_cb) {
- uint64_t challenge = 0;
- while (challenge == 0) {
- optional<vector<uint8_t>> bytes = support::getRandom(8);
- if (!bytes) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting random data for challenge"),
- 0);
- return Void();
- }
-
- challenge = 0;
- for (size_t n = 0; n < bytes.value().size(); n++) {
- challenge |= ((bytes.value())[n] << (n * 8));
- }
- }
-
- authChallenge_ = challenge;
- _hidl_cb(support::resultOK(), challenge);
- return Void();
-}
-
-// TODO: this could be a lot faster if we did all the splitting and pubkey extraction
-// ahead of time.
-bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
- const vector<uint8_t>& readerCertificateChain) {
- optional<vector<uint8_t>> acpPubKey =
- support::certificateChainGetTopMostKey(profile.readerCertificate);
- if (!acpPubKey) {
- LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
- return false;
- }
-
- optional<vector<vector<uint8_t>>> certificatesInChain =
- support::certificateChainSplit(readerCertificateChain);
- if (!certificatesInChain) {
- LOG(ERROR) << "Error splitting readerCertificateChain";
- return false;
- }
- for (const vector<uint8_t>& certInChain : certificatesInChain.value()) {
- optional<vector<uint8_t>> certPubKey = support::certificateChainGetTopMostKey(certInChain);
- if (!certPubKey) {
- LOG(ERROR)
- << "Error extracting public key from certificate in chain presented by reader";
- return false;
- }
- if (acpPubKey.value() == certPubKey.value()) {
- return true;
- }
- }
- return false;
-}
-
-Timestamp clockGetTime() {
- struct timespec time;
- clock_gettime(CLOCK_MONOTONIC, &time);
- return time.tv_sec * 1000 + time.tv_nsec / 1000000;
-}
-
-bool checkUserAuthentication(const SecureAccessControlProfile& profile,
- const HardwareAuthToken& authToken, uint64_t authChallenge) {
- if (profile.secureUserId != authToken.userId) {
- LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId
- << ") differs from userId in authToken (" << authToken.userId << ")";
- return false;
- }
-
- if (profile.timeoutMillis == 0) {
- if (authToken.challenge == 0) {
- LOG(ERROR) << "No challenge in authToken";
- return false;
- }
-
- if (authToken.challenge != authChallenge) {
- LOG(ERROR) << "Challenge in authToken doesn't match the challenge we created";
- return false;
- }
- return true;
- }
-
- // Note that the Epoch for timestamps in HardwareAuthToken is at the
- // discretion of the vendor:
- //
- // "[...] since some starting point (generally the most recent device
- // boot) which all of the applications within one secure environment
- // must agree upon."
- //
- // Therefore, if this software implementation is used on a device which isn't
- // the emulator then the assumption that the epoch is the same as used in
- // clockGetTime above will not hold. This is OK as this software
- // implementation should never be used on a real device.
- //
- Timestamp now = clockGetTime();
- if (authToken.timestamp > now) {
- LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp
- << ") is in the future (now: " << now << ")";
- return false;
- }
- if (now > authToken.timestamp + profile.timeoutMillis) {
- LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp << " + "
- << profile.timeoutMillis << " = "
- << (authToken.timestamp + profile.timeoutMillis)
- << ") is in the past (now: " << now << ")";
- return false;
- }
-
- return true;
-}
-
-Return<void> IdentityCredential::startRetrieval(
- const hidl_vec<SecureAccessControlProfile>& accessControlProfiles,
- const HardwareAuthToken& authToken, const hidl_vec<uint8_t>& itemsRequest,
- const hidl_vec<uint8_t>& sessionTranscript, const hidl_vec<uint8_t>& readerSignature,
- const hidl_vec<uint16_t>& requestCounts, startRetrieval_cb _hidl_cb) {
- if (sessionTranscript.size() > 0) {
- auto [item, _, message] = cppbor::parse(sessionTranscript);
- if (item == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "SessionTranscript contains invalid CBOR"));
- return Void();
- }
- sessionTranscriptItem_ = std::move(item);
- }
- if (numStartRetrievalCalls_ > 0) {
- if (sessionTranscript_ != vector<uint8_t>(sessionTranscript)) {
- _hidl_cb(support::result(
- ResultCode::SESSION_TRANSCRIPT_MISMATCH,
- "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
- return Void();
- }
- }
- sessionTranscript_ = sessionTranscript;
-
- // If there is a signature, validate that it was made with the top-most key in the
- // certificate chain embedded in the COSE_Sign1 structure.
- optional<vector<uint8_t>> readerCertificateChain;
- if (readerSignature.size() > 0) {
- readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
- if (!readerCertificateChain) {
- _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
- "Unable to get reader certificate chain from COSE_Sign1"));
- return Void();
- }
-
- if (!support::certificateChainValidate(readerCertificateChain.value())) {
- _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
- "Error validating reader certificate chain"));
- return Void();
- }
-
- optional<vector<uint8_t>> readerPublicKey =
- support::certificateChainGetTopMostKey(readerCertificateChain.value());
- if (!readerPublicKey) {
- _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
- "Unable to get public key from reader certificate chain"));
- return Void();
- }
-
- const vector<uint8_t>& itemsRequestBytes = itemsRequest;
- vector<uint8_t> dataThatWasSigned = cppbor::Array()
- .add("ReaderAuthentication")
- .add(sessionTranscriptItem_->clone())
- .add(cppbor::Semantic(24, itemsRequestBytes))
- .encode();
- if (!support::coseCheckEcDsaSignature(readerSignature,
- dataThatWasSigned, // detached content
- readerPublicKey.value())) {
- _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
- "readerSignature check failed"));
- return Void();
- }
- }
-
- // Here's where we would validate the passed-in |authToken| to assure ourselves
- // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
- //
- // However this involves calculating the MAC. However this requires access
- // to the key needed to a pre-shared key which we don't have...
- //
-
- // To prevent replay-attacks, we check that the public part of the ephemeral
- // key we previously created, is present in the DeviceEngagement part of
- // SessionTranscript as a COSE_Key, in uncompressed form.
- //
- // We do this by just searching for the X and Y coordinates.
- if (sessionTranscript.size() > 0) {
- const cppbor::Array* array = sessionTranscriptItem_->asArray();
- if (array == nullptr || array->size() != 2) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "SessionTranscript is not an array with two items"));
- return Void();
- }
- const cppbor::Semantic* taggedEncodedDE = (*array)[0]->asSemantic();
- if (taggedEncodedDE == nullptr || taggedEncodedDE->value() != 24) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "First item in SessionTranscript array is not a "
- "semantic with value 24"));
- return Void();
- }
- const cppbor::Bstr* encodedDE = (taggedEncodedDE->child())->asBstr();
- if (encodedDE == nullptr) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "Child of semantic in first item in SessionTranscript "
- "array is not a bstr"));
- return Void();
- }
- const vector<uint8_t>& bytesDE = encodedDE->value();
-
- auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
- if (!getXYSuccess) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "Error extracting X and Y from ePub"));
- return Void();
- }
- if (sessionTranscript.size() > 0 &&
- !(memmem(bytesDE.data(), bytesDE.size(), ePubX.data(), ePubX.size()) != nullptr &&
- memmem(bytesDE.data(), bytesDE.size(), ePubY.data(), ePubY.size()) != nullptr)) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "Did not find ephemeral public key's X and Y coordinates in "
- "SessionTranscript (make sure leading zeroes are not used)"));
- return Void();
- }
- }
-
- // itemsRequest: If non-empty, contains request data that may be signed by the
- // reader. The content can be defined in the way appropriate for the
- // credential, but there are three requirements that must be met to work with
- // this HAL:
- if (itemsRequest.size() > 0) {
- // 1. The content must be a CBOR-encoded structure.
- auto [item, _, message] = cppbor::parse(itemsRequest);
- if (item == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "Error decoding CBOR in itemsRequest: %s", message.c_str()));
- return Void();
- }
-
- // 2. The CBOR structure must be a map.
- const cppbor::Map* map = item->asMap();
- if (map == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "itemsRequest is not a CBOR map"));
- return Void();
- }
-
- // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
- // the example below.
- //
- // NameSpaces = {
- // + NameSpace => DataElements ; Requested data elements for each NameSpace
- // }
- //
- // NameSpace = tstr
- //
- // DataElements = {
- // + DataElement => IntentToRetain
- // }
- //
- // DataElement = tstr
- // IntentToRetain = bool
- //
- // Here's an example of an |itemsRequest| CBOR value satisfying above requirements 1.
- // through 3.:
- //
- // {
- // 'docType' : 'org.iso.18013-5.2019',
- // 'nameSpaces' : {
- // 'org.iso.18013-5.2019' : {
- // 'Last name' : false,
- // 'Birth date' : false,
- // 'First name' : false,
- // 'Home address' : true
- // },
- // 'org.aamva.iso.18013-5.2019' : {
- // 'Real Id' : false
- // }
- // }
- // }
- //
- const cppbor::Map* nsMap = nullptr;
- for (size_t n = 0; n < map->size(); n++) {
- const auto& [keyItem, valueItem] = (*map)[n];
- if (keyItem->type() == cppbor::TSTR && keyItem->asTstr()->value() == "nameSpaces" &&
- valueItem->type() == cppbor::MAP) {
- nsMap = valueItem->asMap();
- break;
- }
- }
- if (nsMap == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "No nameSpaces map in top-most map"));
- return Void();
- }
-
- for (size_t n = 0; n < nsMap->size(); n++) {
- auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
- const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
- const cppbor::Map* nsInnerMap = nsValueItem->asMap();
- if (nsKey == nullptr || nsInnerMap == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "Type mismatch in nameSpaces map"));
- return Void();
- }
- string requestedNamespace = nsKey->value();
- vector<string> requestedKeys;
- for (size_t m = 0; m < nsInnerMap->size(); m++) {
- const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
- const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
- const cppbor::Simple* simple = innerMapValueItem->asSimple();
- const cppbor::Bool* intentToRetainItem =
- (simple != nullptr) ? simple->asBool() : nullptr;
- if (nameItem == nullptr || intentToRetainItem == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "Type mismatch in value in nameSpaces map"));
- return Void();
- }
- requestedKeys.push_back(nameItem->value());
- }
- requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
- }
- }
-
- // Finally, validate all the access control profiles in the requestData.
- bool haveAuthToken = (authToken.mac.size() > 0);
- for (const auto& profile : accessControlProfiles) {
- if (!support::secureAccessControlProfileCheckMac(profile, storageKey_)) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Error checking MAC for profile with id %d", int(profile.id)));
- return Void();
- }
- ResultCode accessControlCheck = ResultCode::OK;
- if (profile.userAuthenticationRequired) {
- if (!haveAuthToken || !checkUserAuthentication(profile, authToken, authChallenge_)) {
- accessControlCheck = ResultCode::USER_AUTHENTICATION_FAILED;
- }
- } else if (profile.readerCertificate.size() > 0) {
- if (!readerCertificateChain ||
- !checkReaderAuthentication(profile, readerCertificateChain.value())) {
- accessControlCheck = ResultCode::READER_AUTHENTICATION_FAILED;
- }
- }
- profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
- }
-
- deviceNameSpacesMap_ = cppbor::Map();
- currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
-
- requestCountsRemaining_ = requestCounts;
- currentNameSpace_ = "";
-
- itemsRequest_ = itemsRequest;
-
- numStartRetrievalCalls_ += 1;
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-Return<void> IdentityCredential::startRetrieveEntryValue(
- const hidl_string& nameSpace, const hidl_string& name, uint32_t entrySize,
- const hidl_vec<uint16_t>& accessControlProfileIds, startRetrieveEntryValue_cb _hidl_cb) {
- if (name.empty()) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Name cannot be empty"));
- return Void();
- }
- if (nameSpace.empty()) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Name space cannot be empty"));
- return Void();
- }
-
- if (requestCountsRemaining_.size() == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "No more name spaces left to go through"));
- return Void();
- }
-
- if (currentNameSpace_ == "") {
- // First call.
- currentNameSpace_ = nameSpace;
- }
-
- if (nameSpace == currentNameSpace_) {
- // Same namespace.
- if (requestCountsRemaining_[0] == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "No more entries to be retrieved in current name space"));
- return Void();
- }
- requestCountsRemaining_[0] -= 1;
- } else {
- // New namespace.
- if (requestCountsRemaining_[0] != 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Moved to new name space but %d entries need to be retrieved "
- "in current name space",
- int(requestCountsRemaining_[0])));
- return Void();
- }
- if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
- deviceNameSpacesMap_.add(currentNameSpace_,
- std::move(currentNameSpaceDeviceNameSpacesMap_));
- }
- currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
-
- requestCountsRemaining_.erase(requestCountsRemaining_.begin());
- currentNameSpace_ = nameSpace;
- }
-
- // It's permissible to have an empty itemsRequest... but if non-empty you can
- // only request what was specified in said itemsRequest. Enforce that.
- if (itemsRequest_.size() > 0) {
- const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
- if (it == requestedNameSpacesAndNames_.end()) {
- _hidl_cb(support::result(ResultCode::NOT_IN_REQUEST_MESSAGE,
- "Name space '%s' was not requested in startRetrieval",
- nameSpace.c_str()));
- return Void();
- }
- const auto& dataItemNames = it->second;
- if (std::find(dataItemNames.begin(), dataItemNames.end(), name) == dataItemNames.end()) {
- _hidl_cb(support::result(
- ResultCode::NOT_IN_REQUEST_MESSAGE,
- "Data item name '%s' in name space '%s' was not requested in startRetrieval",
- name.c_str(), nameSpace.c_str()));
- return Void();
- }
- }
-
- // Enforce access control.
- //
- // Access is granted if at least one of the profiles grants access.
- //
- // If an item is configured without any profiles, access is denied.
- //
- ResultCode accessControl = ResultCode::NO_ACCESS_CONTROL_PROFILES;
- for (auto id : accessControlProfileIds) {
- auto search = profileIdToAccessCheckResult_.find(id);
- if (search == profileIdToAccessCheckResult_.end()) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Requested entry with unvalidated profile id %d", (int(id))));
- return Void();
- }
- ResultCode accessControlForProfile = search->second;
- if (accessControlForProfile == ResultCode::OK) {
- accessControl = ResultCode::OK;
- break;
- }
- accessControl = accessControlForProfile;
- }
- if (accessControl != ResultCode::OK) {
- _hidl_cb(support::result(accessControl, "Access control check failed"));
- return Void();
- }
-
- entryAdditionalData_ =
- support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
-
- currentName_ = name;
- entryRemainingBytes_ = entrySize;
- entryValue_.resize(0);
-
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-Return<void> IdentityCredential::retrieveEntryValue(const hidl_vec<uint8_t>& encryptedContent,
- retrieveEntryValue_cb _hidl_cb) {
- optional<vector<uint8_t>> content =
- support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
- if (!content) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting data"), {});
- return Void();
- }
-
- size_t chunkSize = content.value().size();
-
- if (chunkSize > entryRemainingBytes_) {
- LOG(ERROR) << "Retrieved chunk of size " << chunkSize
- << " is bigger than remaining space of size " << entryRemainingBytes_;
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Retrieved chunk of size %zd is bigger than remaining space "
- "of size %zd",
- chunkSize, entryRemainingBytes_),
- {});
- return Void();
- }
-
- entryRemainingBytes_ -= chunkSize;
- if (entryRemainingBytes_ > 0) {
- if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Retrieved non-final chunk of size %zd but expected "
- "kGcmChunkSize which is %zd",
- chunkSize, IdentityCredentialStore::kGcmChunkSize),
- {});
- return Void();
- }
- }
-
- entryValue_.insert(entryValue_.end(), content.value().begin(), content.value().end());
-
- if (entryRemainingBytes_ == 0) {
- auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
- if (entryValueItem == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Retrieved data invalid CBOR"), {});
- return Void();
- }
- currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
- }
-
- _hidl_cb(support::resultOK(), content.value());
- return Void();
-}
-
-Return<void> IdentityCredential::finishRetrieval(const hidl_vec<uint8_t>& signingKeyBlob,
- finishRetrieval_cb _hidl_cb) {
- if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
- deviceNameSpacesMap_.add(currentNameSpace_,
- std::move(currentNameSpaceDeviceNameSpacesMap_));
- }
- vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
-
- // If there's no signing key or no sessionTranscript or no reader ephemeral
- // public key, we return the empty MAC.
- 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> encodedDeviceAuthentication = array.encode();
-
- vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
- optional<vector<uint8_t>> signingKey =
- support::decryptAes128Gcm(storageKey_, signingKeyBlob, docTypeAsBlob);
- if (!signingKey) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting signingKeyBlob"),
- {}, {});
- return Void();
- }
-
- optional<vector<uint8_t>> sharedSecret =
- support::ecdh(readerPublicKey_, signingKey.value());
- if (!sharedSecret) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error doing ECDH"), {}, {});
- return Void();
- }
-
- vector<uint8_t> salt = {0x00};
- vector<uint8_t> info = {};
- optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
- if (!derivedKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error deriving key from shared secret"),
- {}, {});
- return Void();
- }
-
- mac = support::coseMac0(derivedKey.value(), {}, // payload
- encodedDeviceAuthentication); // additionalData
- if (!mac) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error MACing data"), {}, {});
- return Void();
- }
- }
-
- _hidl_cb(support::resultOK(), mac.value_or(vector<uint8_t>({})), encodedDeviceNameSpaces);
- return Void();
-}
-
-Return<void> IdentityCredential::generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) {
- string serialDecimal = "0"; // TODO: set serial to something unique
- string issuer = "Android Open Source Project";
- string subject = "Android IdentityCredential Reference Implementation";
- time_t validityNotBefore = time(nullptr);
- time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
-
- optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
- if (!signingKeyPKCS8) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {});
- return Void();
- }
-
- optional<vector<uint8_t>> signingPublicKey =
- support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
- if (!signingPublicKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of signingKey"), {},
- {});
- return Void();
- }
-
- optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
- if (!signingKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting private part of signingKey"),
- {}, {});
- return Void();
- }
-
- optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
- signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
- if (!certificate) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {});
- return Void();
- }
-
- optional<vector<uint8_t>> nonce = support::getRandom(12);
- if (!nonce) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting random"), {}, {});
- return Void();
- }
- vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
- optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
- storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
- if (!encryptedSigningKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error encrypting signingKey"), {}, {});
- return Void();
- }
- _hidl_cb(support::resultOK(), encryptedSigningKey.value(), certificate.value());
- return Void();
-}
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
diff --git a/identity/1.0/default/IdentityCredential.h b/identity/1.0/default/IdentityCredential.h
deleted file mode 100644
index eb8787b..0000000
--- a/identity/1.0/default/IdentityCredential.h
+++ /dev/null
@@ -1,132 +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 ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
-#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
-
-#include <android/hardware/identity/1.0/IIdentityCredential.h>
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <cppbor/cppbor.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-using ::std::map;
-using ::std::string;
-using ::std::vector;
-
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::identity::V1_0::IIdentityCredential;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
-using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
-
-using MapStringToVectorOfStrings = map<string, vector<string>>;
-
-class IdentityCredential : public IIdentityCredential {
- public:
- IdentityCredential(const hidl_vec<uint8_t>& credentialData)
- : credentialData_(credentialData), numStartRetrievalCalls_(0), authChallenge_(0) {}
-
- // Parses and decrypts credentialData_, return false on failure. Must be
- // called right after construction.
- ResultCode initialize();
-
- // Methods from ::android::hardware::identity::IIdentityCredential follow.
-
- Return<void> deleteCredential(deleteCredential_cb _hidl_cb) override;
- Return<void> createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) override;
-
- Return<void> setReaderEphemeralPublicKey(const hidl_vec<uint8_t>& publicKey,
- setReaderEphemeralPublicKey_cb _hidl_cb) override;
-
- Return<void> createAuthChallenge(createAuthChallenge_cb _hidl_cb) override;
-
- Return<void> startRetrieval(const hidl_vec<SecureAccessControlProfile>& accessControlProfiles,
- const HardwareAuthToken& authToken,
- const hidl_vec<uint8_t>& itemsRequest,
- const hidl_vec<uint8_t>& sessionTranscript,
- const hidl_vec<uint8_t>& readerSignature,
- const hidl_vec<uint16_t>& requestCounts,
- startRetrieval_cb _hidl_cb) override;
- Return<void> startRetrieveEntryValue(const hidl_string& nameSpace, const hidl_string& name,
- uint32_t entrySize,
- const hidl_vec<uint16_t>& accessControlProfileIds,
- startRetrieveEntryValue_cb _hidl_cb) override;
- Return<void> retrieveEntryValue(const hidl_vec<uint8_t>& encryptedContent,
- retrieveEntryValue_cb _hidl_cb) override;
- Return<void> finishRetrieval(const hidl_vec<uint8_t>& signingKeyBlob,
- finishRetrieval_cb _hidl_cb) override;
-
- Return<void> generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) override;
-
- private:
- // Set by constructor
- vector<uint8_t> credentialData_;
- int numStartRetrievalCalls_;
-
- // Set by initialize()
- string docType_;
- bool testCredential_;
- vector<uint8_t> storageKey_;
- vector<uint8_t> credentialPrivKey_;
-
- // Set by createEphemeralKeyPair()
- vector<uint8_t> ephemeralPublicKey_;
-
- // Set by setReaderEphemeralPublicKey()
- vector<uint8_t> readerPublicKey_;
-
- // Set by createAuthChallenge()
- uint64_t authChallenge_;
-
- // Set at startRetrieval() time.
- map<uint16_t, ResultCode> profileIdToAccessCheckResult_;
- vector<uint8_t> sessionTranscript_;
- std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
- vector<uint8_t> itemsRequest_;
- vector<uint16_t> requestCountsRemaining_;
- MapStringToVectorOfStrings requestedNameSpacesAndNames_;
- cppbor::Map deviceNameSpacesMap_;
- cppbor::Map currentNameSpaceDeviceNameSpacesMap_;
-
- // Set at startRetrieveEntryValue() time.
- string currentNameSpace_;
- string currentName_;
- size_t entryRemainingBytes_;
- vector<uint8_t> entryValue_;
- vector<uint8_t> entryAdditionalData_;
-};
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
diff --git a/identity/1.0/default/IdentityCredentialStore.cpp b/identity/1.0/default/IdentityCredentialStore.cpp
deleted file mode 100644
index 9eb1e70..0000000
--- a/identity/1.0/default/IdentityCredentialStore.cpp
+++ /dev/null
@@ -1,68 +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.
- */
-
-#define LOG_TAG "IdentityCredentialStore"
-
-#include "IdentityCredentialStore.h"
-#include "IdentityCredential.h"
-#include "WritableIdentityCredential.h"
-
-#include <android-base/logging.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-// Methods from ::android::hardware::identity::IIdentityCredentialStore follow.
-
-Return<void> IdentityCredentialStore::getHardwareInformation(getHardwareInformation_cb _hidl_cb) {
- _hidl_cb(support::resultOK(), "IdentityCredential Reference Implementation", "Google",
- kGcmChunkSize, false /* isDirectAccess */, {} /* supportedDocTypes */);
- return Void();
-}
-
-Return<void> IdentityCredentialStore::createCredential(const hidl_string& docType,
- bool testCredential,
- createCredential_cb _hidl_cb) {
- auto writable_credential = new WritableIdentityCredential(docType, testCredential);
- if (!writable_credential->initialize()) {
- _hidl_cb(support::result(ResultCode::FAILED,
- "Error initializing WritableIdentityCredential"),
- writable_credential);
- return Void();
- }
- _hidl_cb(support::resultOK(), writable_credential);
- return Void();
-}
-
-Return<void> IdentityCredentialStore::getCredential(const hidl_vec<uint8_t>& credentialData,
- getCredential_cb _hidl_cb) {
- auto credential = new IdentityCredential(credentialData);
- // We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now.
- auto ret = credential->initialize();
- if (ret != ResultCode::OK) {
- _hidl_cb(support::result(ret, "Error initializing IdentityCredential"), credential);
- return Void();
- }
- _hidl_cb(support::resultOK(), credential);
- return Void();
-}
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
diff --git a/identity/1.0/default/IdentityCredentialStore.h b/identity/1.0/default/IdentityCredentialStore.h
deleted file mode 100644
index ad75360..0000000
--- a/identity/1.0/default/IdentityCredentialStore.h
+++ /dev/null
@@ -1,55 +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 ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
-#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
-
-#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-
-class IdentityCredentialStore : public IIdentityCredentialStore {
- public:
- IdentityCredentialStore() {}
-
- // The GCM chunk size used by this implementation is 64 KiB.
- static constexpr size_t kGcmChunkSize = 64 * 1024;
-
- // Methods from ::android::hardware::identity::IIdentityCredentialStore follow.
- Return<void> getHardwareInformation(getHardwareInformation_cb _hidl_cb) override;
- Return<void> createCredential(const hidl_string& docType, bool testCredential,
- createCredential_cb _hidl_cb) override;
- Return<void> getCredential(const hidl_vec<uint8_t>& credentialData,
- getCredential_cb _hidl_cb) override;
-};
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
diff --git a/identity/1.0/default/OWNERS b/identity/1.0/default/OWNERS
deleted file mode 100644
index 6969910..0000000
--- a/identity/1.0/default/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-swillden@google.com
-zeuthen@google.com
diff --git a/identity/1.0/default/WritableIdentityCredential.cpp b/identity/1.0/default/WritableIdentityCredential.cpp
deleted file mode 100644
index 548b4c0..0000000
--- a/identity/1.0/default/WritableIdentityCredential.cpp
+++ /dev/null
@@ -1,426 +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.
- */
-
-#define LOG_TAG "WritableIdentityCredential"
-
-#include "WritableIdentityCredential.h"
-#include "IdentityCredentialStore.h"
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <android-base/logging.h>
-
-#include <cppbor/cppbor.h>
-#include <cppbor/cppbor_parse.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-using ::std::optional;
-
-// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
-// |credentialPrivKey|.
-static bool generateCredentialKeys(const vector<uint8_t>& storageKey,
- const vector<uint8_t>& credentialPrivKey,
- vector<uint8_t>& credentialKeys) {
- if (storageKey.size() != 16) {
- LOG(ERROR) << "Size of storageKey is not 16";
- return false;
- }
-
- cppbor::Array array;
- array.add(cppbor::Bstr(storageKey));
- array.add(cppbor::Bstr(credentialPrivKey));
- credentialKeys = array.encode();
- return true;
-}
-
-// Writes CBOR-encoded structure to |credentialData| containing |docType|,
-// |testCredential| and |credentialKeys|. The latter element will be stored in
-// encrypted form, using |hardwareBoundKey| as the encryption key.
-bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType,
- bool testCredential, const vector<uint8_t>& credentialKeys,
- vector<uint8_t>& credentialData) {
- optional<vector<uint8_t>> nonce = support::getRandom(12);
- if (!nonce) {
- LOG(ERROR) << "Error getting random";
- return false;
- }
- vector<uint8_t> docTypeAsVec(docType.begin(), docType.end());
- optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm(
- hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec);
- if (!credentialBlob) {
- LOG(ERROR) << "Error encrypting CredentialKeys blob";
- return false;
- }
-
- cppbor::Array array;
- array.add(docType);
- array.add(testCredential);
- array.add(cppbor::Bstr(credentialBlob.value()));
- credentialData = array.encode();
- return true;
-}
-
-bool WritableIdentityCredential::initialize() {
- optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
- if (!keyPair) {
- LOG(ERROR) << "Error creating credentialKey";
- return false;
- }
-
- optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
- if (!pubKey) {
- LOG(ERROR) << "Error getting public part of credentialKey";
- return false;
- }
- credentialPubKey_ = pubKey.value();
-
- optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
- if (!privKey) {
- LOG(ERROR) << "Error getting private part of credentialKey";
- return false;
- }
- credentialPrivKey_ = privKey.value();
-
- optional<vector<uint8_t>> random = support::getRandom(16);
- if (!random) {
- LOG(ERROR) << "Error creating storageKey";
- return false;
- }
- storageKey_ = random.value();
-
- return true;
-}
-
-Return<void> WritableIdentityCredential::getAttestationCertificate(
- const hidl_vec<uint8_t>& /* attestationChallenge */,
- getAttestationCertificate_cb _hidl_cb) {
- // For now, we dynamically generate an attestion key on each and every
- // request and use that to sign CredentialKey. In a real implementation this
- // would look very differently.
- optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
- if (!attestationKeyPair) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error creating attestationKey"), {});
- return Void();
- }
-
- optional<vector<uint8_t>> attestationPubKey =
- support::ecKeyPairGetPublicKey(attestationKeyPair.value());
- if (!attestationPubKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of attestationKey"),
- {});
- return Void();
- }
-
- optional<vector<uint8_t>> attestationPrivKey =
- support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
- if (!attestationPrivKey) {
- _hidl_cb(
- support::result(ResultCode::FAILED, "Error getting private part of attestationKey"),
- {});
- return Void();
- }
-
- string serialDecimal;
- string issuer;
- string subject;
- time_t validityNotBefore = time(nullptr);
- time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
-
- // First create a certificate for |credentialPubKey| which is signed by
- // |attestationPrivKey|.
- //
- serialDecimal = "0"; // TODO: set serial to |attestationChallenge|
- issuer = "Android Open Source Project";
- subject = "Android IdentityCredential CredentialKey";
- optional<vector<uint8_t>> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate(
- credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
- if (!credentialPubKeyCertificate) {
- _hidl_cb(support::result(ResultCode::FAILED,
- "Error creating certificate for credentialPubKey"),
- {});
- return Void();
- }
-
- // This is followed by a certificate for |attestationPubKey| self-signed by
- // |attestationPrivKey|.
- serialDecimal = "0"; // TODO: set serial
- issuer = "Android Open Source Project";
- subject = "Android IdentityCredential AttestationKey";
- optional<vector<uint8_t>> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate(
- attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
- if (!attestationKeyCertificate) {
- _hidl_cb(support::result(ResultCode::FAILED,
- "Error creating certificate for attestationPubKey"),
- {});
- return Void();
- }
-
- // Concatenate the certificates to form the chain.
- vector<uint8_t> certificateChain;
- certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(),
- credentialPubKeyCertificate.value().end());
- certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
- attestationKeyCertificate.value().end());
-
- _hidl_cb(support::resultOK(), certificateChain);
- return Void();
-}
-
-Return<void> WritableIdentityCredential::startPersonalization(uint16_t accessControlProfileCount,
- const hidl_vec<uint16_t>& entryCounts,
- startPersonalization_cb _hidl_cb) {
- numAccessControlProfileRemaining_ = accessControlProfileCount;
- remainingEntryCounts_ = entryCounts;
- entryNameSpace_ = "";
-
- signedDataAccessControlProfiles_ = cppbor::Array();
- signedDataNamespaces_ = cppbor::Map();
- signedDataCurrentNamespace_ = cppbor::Array();
-
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-Return<void> WritableIdentityCredential::addAccessControlProfile(
- uint16_t id, const hidl_vec<uint8_t>& readerCertificate, bool userAuthenticationRequired,
- uint64_t timeoutMillis, uint64_t secureUserId, addAccessControlProfile_cb _hidl_cb) {
- SecureAccessControlProfile profile;
-
- if (numAccessControlProfileRemaining_ == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "numAccessControlProfileRemaining_ is 0 and expected non-zero"),
- profile);
- return Void();
- }
-
- // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
- // be zero.
- if (!userAuthenticationRequired && timeoutMillis != 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "userAuthenticationRequired is false but timeout is non-zero"),
- profile);
- return Void();
- }
-
- profile.id = id;
- profile.readerCertificate = readerCertificate;
- profile.userAuthenticationRequired = userAuthenticationRequired;
- profile.timeoutMillis = timeoutMillis;
- profile.secureUserId = secureUserId;
- optional<vector<uint8_t>> mac =
- support::secureAccessControlProfileCalcMac(profile, storageKey_);
- if (!mac) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error calculating MAC for profile"), profile);
- return Void();
- }
- profile.mac = mac.value();
-
- cppbor::Map profileMap;
- profileMap.add("id", profile.id);
- if (profile.readerCertificate.size() > 0) {
- profileMap.add("readerCertificate", cppbor::Bstr(profile.readerCertificate));
- }
- if (profile.userAuthenticationRequired) {
- profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
- profileMap.add("timeoutMillis", profile.timeoutMillis);
- }
- signedDataAccessControlProfiles_.add(std::move(profileMap));
-
- numAccessControlProfileRemaining_--;
-
- _hidl_cb(support::resultOK(), profile);
- return Void();
-}
-
-Return<void> WritableIdentityCredential::beginAddEntry(
- const hidl_vec<uint16_t>& accessControlProfileIds, const hidl_string& nameSpace,
- const hidl_string& name, uint32_t entrySize, beginAddEntry_cb _hidl_cb) {
- if (numAccessControlProfileRemaining_ != 0) {
- LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
- << " and expected zero";
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "numAccessControlProfileRemaining_ is %zd and expected zero",
- numAccessControlProfileRemaining_));
- return Void();
- }
-
- if (remainingEntryCounts_.size() == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "No more namespaces to add to"));
- return Void();
- }
-
- // Handle initial beginEntry() call.
- if (entryNameSpace_ == "") {
- entryNameSpace_ = nameSpace;
- }
-
- // If the namespace changed...
- if (nameSpace != entryNameSpace_) {
- // Then check that all entries in the previous namespace have been added..
- if (remainingEntryCounts_[0] != 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "New namespace but %d entries remain to be added",
- int(remainingEntryCounts_[0])));
- return Void();
- }
- remainingEntryCounts_.erase(remainingEntryCounts_.begin());
-
- if (signedDataCurrentNamespace_.size() > 0) {
- signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
- signedDataCurrentNamespace_ = cppbor::Array();
- }
- } else {
- // Same namespace...
- if (remainingEntryCounts_[0] == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Same namespace but no entries remain to be added"));
- return Void();
- }
- remainingEntryCounts_[0] -= 1;
- }
-
- entryAdditionalData_ =
- support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
-
- entryRemainingBytes_ = entrySize;
- entryNameSpace_ = nameSpace;
- entryName_ = name;
- entryAccessControlProfileIds_ = accessControlProfileIds;
- entryBytes_.resize(0);
- // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
-
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-Return<void> WritableIdentityCredential::addEntryValue(const hidl_vec<uint8_t>& content,
- addEntryValue_cb _hidl_cb) {
- size_t contentSize = content.size();
-
- if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
- _hidl_cb(support::result(
- ResultCode::INVALID_DATA,
- "Passed in chunk of size %zd is bigger than kGcmChunkSize which is %zd",
- contentSize, IdentityCredentialStore::kGcmChunkSize),
- {});
- return Void();
- }
- if (contentSize > entryRemainingBytes_) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Passed in chunk of size %zd is bigger than remaining space "
- "of size %zd",
- contentSize, entryRemainingBytes_),
- {});
- return Void();
- }
-
- entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
- entryRemainingBytes_ -= contentSize;
- if (entryRemainingBytes_ > 0) {
- if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Retrieved non-final chunk of size %zd but expected "
- "kGcmChunkSize which is %zd",
- contentSize, IdentityCredentialStore::kGcmChunkSize),
- {});
- return Void();
- }
- }
-
- optional<vector<uint8_t>> nonce = support::getRandom(12);
- if (!nonce) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting nonce"), {});
- return Void();
- }
- optional<vector<uint8_t>> encryptedContent =
- support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
- if (!encryptedContent) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error encrypting content"), {});
- return Void();
- }
-
- if (entryRemainingBytes_ == 0) {
- // TODO: ideally do do this without parsing the data (but still validate data is valid
- // CBOR).
- auto [item, _, message] = cppbor::parse(entryBytes_);
- if (item == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Data is not valid CBOR"), {});
- return Void();
- }
- cppbor::Map entryMap;
- entryMap.add("name", entryName_);
- entryMap.add("value", std::move(item));
- cppbor::Array profileIdArray;
- for (auto id : entryAccessControlProfileIds_) {
- profileIdArray.add(id);
- }
- entryMap.add("accessControlProfiles", std::move(profileIdArray));
- signedDataCurrentNamespace_.add(std::move(entryMap));
- }
-
- _hidl_cb(support::resultOK(), encryptedContent.value());
- return Void();
-}
-
-Return<void> WritableIdentityCredential::finishAddingEntries(finishAddingEntries_cb _hidl_cb) {
- if (signedDataCurrentNamespace_.size() > 0) {
- signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
- }
- cppbor::Array popArray;
- popArray.add("ProofOfProvisioning")
- .add(docType_)
- .add(std::move(signedDataAccessControlProfiles_))
- .add(std::move(signedDataNamespaces_))
- .add(testCredential_);
- vector<uint8_t> encodedCbor = popArray.encode();
-
- optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
- encodedCbor, // payload
- {}, // additionalData
- {}); // certificateChain
- if (!signature) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {}, {});
- return Void();
- }
-
- vector<uint8_t> credentialKeys;
- if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialKeys"), {}, {});
- return Void();
- }
-
- vector<uint8_t> credentialData;
- if (!generateCredentialData(testCredential_ ? support::getTestHardwareBoundKey()
- : support::getHardwareBoundKey(),
- docType_, testCredential_, credentialKeys, credentialData)) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialData"), {}, {});
- return Void();
- }
-
- _hidl_cb(support::resultOK(), credentialData, signature.value());
- return Void();
-}
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
diff --git a/identity/1.0/default/WritableIdentityCredential.h b/identity/1.0/default/WritableIdentityCredential.h
deleted file mode 100644
index 9f4e303..0000000
--- a/identity/1.0/default/WritableIdentityCredential.h
+++ /dev/null
@@ -1,105 +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 ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
-#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
-
-#include <android/hardware/identity/1.0/IWritableIdentityCredential.h>
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <cppbor.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-using ::std::string;
-using ::std::vector;
-
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
-
-class WritableIdentityCredential : public IWritableIdentityCredential {
- public:
- WritableIdentityCredential(const hidl_string& docType, bool testCredential)
- : docType_(docType), testCredential_(testCredential) {}
-
- // Creates the Credential Key. Returns false on failure. Must be called
- // right after construction.
- bool initialize();
-
- // Methods from ::android::hardware::identity::IWritableIdentityCredential
- // follow.
- Return<void> getAttestationCertificate(const hidl_vec<uint8_t>& attestationChallenge,
- getAttestationCertificate_cb _hidl_cb) override;
-
- Return<void> startPersonalization(uint16_t accessControlProfileCount,
- const hidl_vec<uint16_t>& entryCounts,
- startPersonalization_cb _hidl_cb) override;
-
- Return<void> addAccessControlProfile(uint16_t id, const hidl_vec<uint8_t>& readerCertificate,
- bool userAuthenticationRequired, uint64_t timeoutMillis,
- uint64_t secureUserId,
- addAccessControlProfile_cb _hidl_cb) override;
-
- Return<void> beginAddEntry(const hidl_vec<uint16_t>& accessControlProfileIds,
- const hidl_string& nameSpace, const hidl_string& name,
- uint32_t entrySize, beginAddEntry_cb _hidl_cb) override;
-
- Return<void> addEntryValue(const hidl_vec<uint8_t>& content,
- addEntryValue_cb _hidl_cb) override;
-
- Return<void> finishAddingEntries(finishAddingEntries_cb _hidl_cb) override;
-
- private:
- string docType_;
- bool testCredential_;
-
- // These are set in initialize().
- vector<uint8_t> storageKey_;
- vector<uint8_t> credentialPrivKey_;
- vector<uint8_t> credentialPubKey_;
-
- // These fields are initialized during startPersonalization()
- size_t numAccessControlProfileRemaining_;
- vector<uint16_t> remainingEntryCounts_;
- cppbor::Array signedDataAccessControlProfiles_;
- cppbor::Map signedDataNamespaces_;
- cppbor::Array signedDataCurrentNamespace_;
-
- // These fields are initialized during beginAddEntry()
- size_t entryRemainingBytes_;
- vector<uint8_t> entryAdditionalData_;
- string entryNameSpace_;
- string entryName_;
- vector<uint16_t> entryAccessControlProfileIds_;
- vector<uint8_t> entryBytes_;
-};
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
diff --git a/identity/1.0/default/android.hardware.identity@1.0-service.example.rc b/identity/1.0/default/android.hardware.identity@1.0-service.example.rc
deleted file mode 100644
index 1eb7319..0000000
--- a/identity/1.0/default/android.hardware.identity@1.0-service.example.rc
+++ /dev/null
@@ -1,3 +0,0 @@
-service vendor.identity-1-0 /vendor/bin/hw/android.hardware.identity@1.0-service.example
- class hal
- user nobody
diff --git a/identity/1.0/default/service.cpp b/identity/1.0/default/service.cpp
deleted file mode 100644
index 839e803..0000000
--- a/identity/1.0/default/service.cpp
+++ /dev/null
@@ -1,40 +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.
- */
-
-#define LOG_TAG "android.hardware.identity@1.0-service"
-
-#include <android-base/logging.h>
-#include <hidl/HidlTransportSupport.h>
-
-#include "IdentityCredentialStore.h"
-
-using android::hardware::joinRpcThreadpool;
-using android::hardware::identity::implementation::IdentityCredentialStore;
-
-int main(int /* argc */, char* argv[]) {
- ::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/);
-
- ::android::base::InitLogging(argv, &android::base::StderrLogger);
-
- auto identity_store = new IdentityCredentialStore();
- auto status = identity_store->registerAsService();
- if (status != android::OK) {
- LOG(FATAL) << "Could not register service for IdentityCredentialStore 1.0 (" << status
- << ")";
- }
- joinRpcThreadpool();
- return -1; // Should never get here.
-}
diff --git a/identity/1.0/types.hal b/identity/1.0/types.hal
deleted file mode 100644
index 5aedfea..0000000
--- a/identity/1.0/types.hal
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright 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.
- */
-
-package android.hardware.identity@1.0;
-
-/**
- * The ResultCode enumeration is used to convey the status of an operation.
- */
-enum ResultCode : int32_t {
- /**
- * Success.
- */
- OK = 0,
-
- /**
- * The operation failed. This is used as a generic catch-all for errors that don't belong
- * in other categories, including memory/resource allocation failures and I/O errors.
- */
- FAILED = 1,
-
- /**
- * The passed data was invalid. This is a generic catch all for errors that don't belong
- * in other categories related to parameter validation.
- */
- INVALID_DATA = 2,
-
- /**
- * The authToken parameter passed to IIdentityCredential.startRetrieval() is not valid.
- */
- INVALID_AUTH_TOKEN = 3,
-
- /**
- * The itemsRequest parameter passed to IIdentityCredential.startRetrieval() does not meet
- * the requirements described in the documentation for that method.
- */
- INVALID_ITEMS_REQUEST_MESSAGE = 4,
-
- /**
- * The readerSignature parameter in IIdentityCredential.startRetrieval() is invalid,
- * doesn't contain an embedded certificate chain, or the signature failed to
- * validate.
- */
- READER_SIGNATURE_CHECK_FAILED = 5,
-
- /**
- * The sessionTranscript passed to startRetrieval() did not contain the ephmeral public
- * key returned by createEphemeralPublicKey().
- */
- EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 6,
-
- /**
- * An access condition related to user authentication was not satisfied.
- */
- USER_AUTHENTICATION_FAILED = 7,
-
- /**
- * An access condition related to reader authentication was not satisfied.
- */
- READER_AUTHENTICATION_FAILED = 8,
-
- /**
- * The request data element has no access control profiles associated so it cannot be accessed.
- */
- NO_ACCESS_CONTROL_PROFILES = 9,
-
- /**
- * The requested data element is not in the provided non-empty itemsRequest message.
- */
- NOT_IN_REQUEST_MESSAGE = 10,
-
- /**
- * The passed-in sessionTranscript doesn't match the previously passed-in sessionTranscript.
- */
- SESSION_TRANSCRIPT_MISMATCH = 11,
-};
-
-/**
- * A result has a ResultCode and corresponding textual message.
- */
-struct Result {
- /**
- * The result code.
- *
- * Implementations must not use values not defined in the ResultCode enumeration.
- */
- ResultCode code;
-
- /**
- * A human-readable message in English conveying more detail about a failure.
- *
- * If code is ResultCode::OK this field must be set to the empty string.
- */
- string message;
-};
-
-struct SecureAccessControlProfile {
- /**
- * id is a numeric identifier that must be unique within the context of a Credential and may be
- * used to reference the profile.
- */
- uint16_t id;
-
- /**
- * readerCertificate, if non-empty, specifies a single X.509 certificate (not a chain
- * of certificates) that must be used to authenticate requests. For details about how
- * this is done, see the readerSignature paremter of IIdentityCredential.startRetrieval.
- */
- vec<uint8_t> readerCertificate;
-
- /**
- * if true, the user is required to authenticate to allow requests. Required authentication
- * fressness is specified by timeout below.
- *
- */
- bool userAuthenticationRequired;
-
- /**
- * Timeout specifies the amount of time, in milliseconds, for which a user authentication (see
- * above) is valid, if userAuthenticationRequired is set to true. If userAuthenticationRequired
- * is true and timout is zero then authentication is required for each reader session.
- *
- * If userAuthenticationRequired is false, timeout must be zero.
- */
- uint64_t timeoutMillis;
-
- /**
- * secureUserId must be non-zero if userAuthenticationRequired is true.
- * It is not related to any Android user ID or UID, but is created in the
- * Gatekeeper application in the secure environment.
- */
- uint64_t secureUserId;
-
- /**
- * The mac is used to authenticate the access control profile. It contains:
- *
- * AES-GCM-ENC(storageKey, R, {}, AccessControlProfile)
- *
- * where AccessControlProfile is the CBOR map:
- *
- * AccessControlProfile = {
- * "id": uint,
- * ? "readerCertificate" : bstr,
- * ? (
- * "userAuthenticationRequired" : bool,
- * "timeoutMillis" : uint,
- * "secureUserId" : uint
- * )
- * }
- */
- vec<uint8_t> mac;
-};
diff --git a/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp b/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp
deleted file mode 100644
index 903e912..0000000
--- a/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "IdentityCredentialHidlHalTest"
-
-#include <map>
-
-#include <android-base/logging.h>
-#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
-#include <android/hardware/identity/1.0/types.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <cppbor.h>
-#include <cppbor_parse.h>
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-
-using std::map;
-using std::optional;
-using std::string;
-using std::vector;
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace test {
-
-using ::android::hardware::identity::V1_0::IIdentityCredential;
-using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
-using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
-using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
-
-// ---------------------------------------------------------------------------
-// Test Data.
-// ---------------------------------------------------------------------------
-
-struct TestEntryData {
- TestEntryData(string nameSpace, string name, vector<uint16_t> profileIds)
- : nameSpace(nameSpace), name(name), profileIds(profileIds) {}
-
- TestEntryData(string nameSpace, string name, const string& value, vector<uint16_t> profileIds)
- : TestEntryData(nameSpace, name, profileIds) {
- valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
- }
- TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
- vector<uint16_t> profileIds)
- : TestEntryData(nameSpace, name, profileIds) {
- valueCbor = cppbor::Bstr(value).encode();
- }
- TestEntryData(string nameSpace, string name, bool value, vector<uint16_t> profileIds)
- : TestEntryData(nameSpace, name, profileIds) {
- valueCbor = cppbor::Bool(value).encode();
- }
- TestEntryData(string nameSpace, string name, int64_t value, vector<uint16_t> profileIds)
- : TestEntryData(nameSpace, name, profileIds) {
- if (value >= 0) {
- valueCbor = cppbor::Uint(value).encode();
- } else {
- valueCbor = cppbor::Nint(-value).encode();
- }
- }
-
- string nameSpace;
- string name;
- vector<uint8_t> valueCbor;
- vector<uint16_t> profileIds;
-};
-
-struct TestProfile {
- uint16_t id;
- hidl_vec<uint8_t> readerCertificate;
- bool userAuthenticationRequired;
- uint64_t timeoutMillis;
-};
-
-/************************************
- * TEST DATA FOR AUTHENTICATION
- ************************************/
-// Test authentication token for user authentication
-
-class IdentityCredentialStoreHidlTest : public ::testing::TestWithParam<std::string> {
- public:
- virtual void SetUp() override {
- string serviceName = GetParam();
- ASSERT_FALSE(serviceName.empty());
- credentialStore_ = IIdentityCredentialStore::getService(serviceName);
- ASSERT_NE(credentialStore_, nullptr);
-
- credentialStore_->getHardwareInformation(
- [&](const Result& result, const hidl_string& credentialStoreName,
- const hidl_string& credentialStoreAuthorName, uint32_t chunkSize,
- bool /* isDirectAccess */,
- const hidl_vec<hidl_string> /* supportedDocTypes */) {
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_GT(credentialStoreName.size(), 0u);
- ASSERT_GT(credentialStoreAuthorName.size(), 0u);
- ASSERT_GE(chunkSize, 256u); // Chunk sizes < APDU buffer won't be supported
- dataChunkSize_ = chunkSize;
- });
- }
- virtual void TearDown() override {}
-
- uint32_t dataChunkSize_ = 0;
-
- sp<IIdentityCredentialStore> credentialStore_;
-};
-
-TEST_P(IdentityCredentialStoreHidlTest, HardwareConfiguration) {
- credentialStore_->getHardwareInformation(
- [&](const Result& result, const hidl_string& credentialStoreName,
- const hidl_string& credentialStoreAuthorName, uint32_t chunkSize,
- bool /* isDirectAccess */, const hidl_vec<hidl_string> /* supportedDocTypes */) {
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_GT(credentialStoreName.size(), 0u);
- ASSERT_GT(credentialStoreAuthorName.size(), 0u);
- ASSERT_GE(chunkSize, 256u); // Chunk sizes < APDU buffer won't be supported
- });
-}
-
-TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
- // First, generate a key-pair for the reader since its public key will be
- // part of the request data.
- optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
- ASSERT_TRUE(readerKeyPKCS8);
- optional<vector<uint8_t>> readerPublicKey =
- support::ecKeyPairGetPublicKey(readerKeyPKCS8.value());
- optional<vector<uint8_t>> readerKey = support::ecKeyPairGetPrivateKey(readerKeyPKCS8.value());
- string serialDecimal = "1234";
- string issuer = "Android Open Source Project";
- string subject = "Android IdentityCredential VTS Test";
- time_t validityNotBefore = time(nullptr);
- time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
- optional<vector<uint8_t>> readerCertificate = support::ecPublicKeyGenerateCertificate(
- readerPublicKey.value(), readerKey.value(), serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
- ASSERT_TRUE(readerCertificate);
-
- // Make the portrait image really big (just shy of 256 KiB) to ensure that
- // the chunking code gets exercised.
- vector<uint8_t> portraitImage;
- portraitImage.resize(256 * 1024 - 10);
- for (size_t n = 0; n < portraitImage.size(); n++) {
- portraitImage[n] = (uint8_t)n;
- }
-
- // Access control profiles:
- const vector<TestProfile> testProfiles = {// Profile 0 (reader authentication)
- {0, readerCertificate.value(), false, 0},
- // Profile 1 (no authentication)
- {1, {}, false, 0}};
-
- HardwareAuthToken authToken = {};
-
- // Here's the actual test data:
- const vector<TestEntryData> testEntries = {
- {"PersonalData", "Last name", string("Turing"), vector<uint16_t>{0, 1}},
- {"PersonalData", "Birth date", string("19120623"), vector<uint16_t>{0, 1}},
- {"PersonalData", "First name", string("Alan"), vector<uint16_t>{0, 1}},
- {"PersonalData", "Home address", string("Maida Vale, London, England"),
- vector<uint16_t>{0}},
- {"Image", "Portrait image", portraitImage, vector<uint16_t>{0, 1}},
- };
- const vector<uint16_t> testEntriesEntryCounts = {static_cast<uint16_t>(testEntries.size() - 1),
- 1u};
-
- string cborPretty;
- sp<IWritableIdentityCredential> writableCredential;
-
- hidl_vec<uint8_t> empty{0};
-
- string docType = "org.iso.18013-5.2019.mdl";
- bool testCredential = true;
- Result result;
- credentialStore_->createCredential(
- docType, testCredential,
- [&](const Result& _result, const sp<IWritableIdentityCredential>& _writableCredential) {
- result = _result;
- writableCredential = _writableCredential;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_NE(writableCredential, nullptr);
-
- string challenge = "attestationChallenge";
- vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
- vector<uint8_t> attestationCertificate;
- writableCredential->getAttestationCertificate(
- attestationChallenge,
- [&](const Result& _result, const hidl_vec<uint8_t>& _attestationCertificate) {
- result = _result;
- attestationCertificate = _attestationCertificate;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts,
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- vector<SecureAccessControlProfile> returnedSecureProfiles;
- for (const auto& testProfile : testProfiles) {
- SecureAccessControlProfile profile;
- writableCredential->addAccessControlProfile(
- testProfile.id, testProfile.readerCertificate,
- testProfile.userAuthenticationRequired, testProfile.timeoutMillis,
- 0, // secureUserId
- [&](const Result& _result, const SecureAccessControlProfile& _profile) {
- result = _result;
- profile = _profile;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_EQ(testProfile.id, profile.id);
- ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate);
- ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
- ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
- ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
- returnedSecureProfiles.push_back(profile);
- }
-
- // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
- // is a little hacky but it works well enough.
- map<const TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
-
- for (const auto& entry : testEntries) {
- vector<vector<uint8_t>> chunks = support::chunkVector(entry.valueCbor, dataChunkSize_);
-
- writableCredential->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
- entry.valueCbor.size(),
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- vector<vector<uint8_t>> encryptedChunks;
- for (const auto& chunk : chunks) {
- writableCredential->addEntryValue(
- chunk, [&](const Result& result, hidl_vec<uint8_t> encryptedContent) {
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_GT(encryptedContent.size(), 0u);
- encryptedChunks.push_back(encryptedContent);
- });
- }
- encryptedBlobs[&entry] = encryptedChunks;
- }
-
- vector<uint8_t> credentialData;
- vector<uint8_t> proofOfProvisioningSignature;
- writableCredential->finishAddingEntries(
- [&](const Result& _result, const hidl_vec<uint8_t>& _credentialData,
- const hidl_vec<uint8_t>& _proofOfProvisioningSignature) {
- result = _result;
- credentialData = _credentialData;
- proofOfProvisioningSignature = _proofOfProvisioningSignature;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- optional<vector<uint8_t>> proofOfProvisioning =
- support::coseSignGetPayload(proofOfProvisioningSignature);
- ASSERT_TRUE(proofOfProvisioning);
- cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
- EXPECT_EQ(
- "[\n"
- " 'ProofOfProvisioning',\n"
- " 'org.iso.18013-5.2019.mdl',\n"
- " [\n"
- " {\n"
- " 'id' : 0,\n"
- " 'readerCertificate' : <not printed>,\n"
- " },\n"
- " {\n"
- " 'id' : 1,\n"
- " },\n"
- " ],\n"
- " {\n"
- " 'PersonalData' : [\n"
- " {\n"
- " 'name' : 'Last name',\n"
- " 'value' : 'Turing',\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
- " },\n"
- " {\n"
- " 'name' : 'Birth date',\n"
- " 'value' : '19120623',\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
- " },\n"
- " {\n"
- " 'name' : 'First name',\n"
- " 'value' : 'Alan',\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
- " },\n"
- " {\n"
- " 'name' : 'Home address',\n"
- " 'value' : 'Maida Vale, London, England',\n"
- " 'accessControlProfiles' : [0, ],\n"
- " },\n"
- " ],\n"
- " 'Image' : [\n"
- " {\n"
- " 'name' : 'Portrait image',\n"
- " 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
- " },\n"
- " ],\n"
- " },\n"
- " true,\n"
- "]",
- cborPretty);
-
- optional<vector<uint8_t>> credentialPubKey =
- support::certificateChainGetTopMostKey(attestationCertificate);
- ASSERT_TRUE(credentialPubKey);
- EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
- {}, // Additional data
- credentialPubKey.value()));
- writableCredential = nullptr;
-
- // Now that the credential has been provisioned, read it back and check the
- // correct data is returned.
- sp<IIdentityCredential> credential;
- credentialStore_->getCredential(
- credentialData, [&](const Result& _result, const sp<IIdentityCredential>& _credential) {
- result = _result;
- credential = _credential;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_NE(credential, nullptr);
-
- optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
- ASSERT_TRUE(readerEphemeralKeyPair);
- optional<vector<uint8_t>> readerEphemeralPublicKey =
- support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
- credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value(),
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- vector<uint8_t> ephemeralKeyPair;
- credential->createEphemeralKeyPair(
- [&](const Result& _result, const hidl_vec<uint8_t>& _ephemeralKeyPair) {
- result = _result;
- ephemeralKeyPair = _ephemeralKeyPair;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
-
- // Calculate requestData field and sign it with the reader key.
- auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
- ASSERT_TRUE(getXYSuccess);
- cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
- vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
- vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
- cppbor::Array sessionTranscript = cppbor::Array()
- .add(cppbor::Semantic(24, deviceEngagementBytes))
- .add(cppbor::Semantic(24, eReaderPubBytes));
- vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
-
- vector<uint8_t> itemsRequestBytes =
- cppbor::Map("nameSpaces",
- cppbor::Map()
- .add("PersonalData", cppbor::Map()
- .add("Last name", false)
- .add("Birth date", false)
- .add("First name", false)
- .add("Home address", true))
- .add("Image", cppbor::Map().add("Portrait image", false)))
- .encode();
- cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
- EXPECT_EQ(
- "{\n"
- " 'nameSpaces' : {\n"
- " 'PersonalData' : {\n"
- " 'Last name' : false,\n"
- " 'Birth date' : false,\n"
- " 'First name' : false,\n"
- " 'Home address' : true,\n"
- " },\n"
- " 'Image' : {\n"
- " 'Portrait image' : false,\n"
- " },\n"
- " },\n"
- "}",
- cborPretty);
- vector<uint8_t> dataToSign = cppbor::Array()
- .add("ReaderAuthentication")
- .add(sessionTranscript.clone())
- .add(cppbor::Semantic(24, itemsRequestBytes))
- .encode();
- optional<vector<uint8_t>> readerSignature =
- support::coseSignEcDsa(readerKey.value(), {}, // content
- dataToSign, // detached content
- readerCertificate.value());
- ASSERT_TRUE(readerSignature);
-
- credential->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes,
- sessionTranscriptBytes, readerSignature.value(),
- testEntriesEntryCounts,
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- for (const auto& entry : testEntries) {
- credential->startRetrieveEntryValue(entry.nameSpace, entry.name, entry.valueCbor.size(),
- entry.profileIds,
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- auto it = encryptedBlobs.find(&entry);
- ASSERT_NE(it, encryptedBlobs.end());
- const vector<vector<uint8_t>>& encryptedChunks = it->second;
-
- vector<uint8_t> content;
- for (const auto& encryptedChunk : encryptedChunks) {
- vector<uint8_t> chunk;
- credential->retrieveEntryValue(
- encryptedChunk, [&](const Result& _result, const hidl_vec<uint8_t>& _chunk) {
- result = _result;
- chunk = _chunk;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- content.insert(content.end(), chunk.begin(), chunk.end());
- }
- EXPECT_EQ(content, entry.valueCbor);
- }
-
- // Generate the key that will be used to sign AuthenticatedData.
- vector<uint8_t> signingKeyBlob;
- vector<uint8_t> signingKeyCertificate;
- credential->generateSigningKeyPair([&](const Result& _result,
- const hidl_vec<uint8_t> _signingKeyBlob,
- const hidl_vec<uint8_t> _signingKeyCertificate) {
- result = _result;
- signingKeyBlob = _signingKeyBlob;
- signingKeyCertificate = _signingKeyCertificate;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- vector<uint8_t> mac;
- vector<uint8_t> deviceNameSpacesBytes;
- credential->finishRetrieval(signingKeyBlob,
- [&](const Result& _result, const hidl_vec<uint8_t> _mac,
- const hidl_vec<uint8_t> _deviceNameSpacesBytes) {
- result = _result;
- mac = _mac;
- deviceNameSpacesBytes = _deviceNameSpacesBytes;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
- ASSERT_EQ(
- "{\n"
- " 'PersonalData' : {\n"
- " 'Last name' : 'Turing',\n"
- " 'Birth date' : '19120623',\n"
- " 'First name' : 'Alan',\n"
- " 'Home address' : 'Maida Vale, London, England',\n"
- " },\n"
- " 'Image' : {\n"
- " 'Portrait image' : <bstr size=262134 "
- "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
- " },\n"
- "}",
- cborPretty);
- // The data that is MACed is ["DeviceAuthentication", sessionTranscriptBytes, docType,
- // deviceNameSpacesBytes] so build up that structure
- cppbor::Array deviceAuthentication;
- deviceAuthentication.add("DeviceAuthentication");
- deviceAuthentication.add(sessionTranscript.clone());
- deviceAuthentication.add(docType);
- deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
- vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
- optional<vector<uint8_t>> signingPublicKey =
- support::certificateChainGetTopMostKey(signingKeyCertificate);
- EXPECT_TRUE(signingPublicKey);
-
- // Derive the key used for MACing.
- optional<vector<uint8_t>> readerEphemeralPrivateKey =
- support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
- optional<vector<uint8_t>> sharedSecret =
- support::ecdh(signingPublicKey.value(), readerEphemeralPrivateKey.value());
- ASSERT_TRUE(sharedSecret);
- vector<uint8_t> salt = {0x00};
- vector<uint8_t> info = {};
- optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
- ASSERT_TRUE(derivedKey);
- optional<vector<uint8_t>> calculatedMac =
- support::coseMac0(derivedKey.value(), {}, // payload
- encodedDeviceAuthentication); // detached content
- ASSERT_TRUE(calculatedMac);
- EXPECT_EQ(mac, calculatedMac);
-}
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, IdentityCredentialStoreHidlTest,
- testing::ValuesIn(android::hardware::getAllHalInstanceNames(
- IIdentityCredentialStore::descriptor)),
- android::hardware::PrintInstanceNameToString);
-
-} // namespace test
-} // namespace identity
-} // namespace hardware
-} // namespace android
diff --git a/identity/1.0/vts/OWNERS b/identity/OWNERS
similarity index 100%
rename from identity/1.0/vts/OWNERS
rename to identity/OWNERS
diff --git a/identity/aidl/Android.bp b/identity/aidl/Android.bp
new file mode 100644
index 0000000..72b19a1
--- /dev/null
+++ b/identity/aidl/Android.bp
@@ -0,0 +1,21 @@
+aidl_interface {
+ name: "android.hardware.identity",
+ vendor_available: true,
+ srcs: [
+ "android/hardware/identity/*.aidl",
+ ],
+ imports: [
+ "android.hardware.keymaster",
+ ],
+ stability: "vintf",
+ backend: {
+ java: {
+ platform_apis: true,
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
diff --git a/radio/config/1.3/IRadioConfigIndication.hal b/identity/aidl/android/hardware/identity/Certificate.aidl
similarity index 63%
copy from radio/config/1.3/IRadioConfigIndication.hal
copy to identity/aidl/android/hardware/identity/Certificate.aidl
index 9ef496c..5bbc17c 100644
--- a/radio/config/1.3/IRadioConfigIndication.hal
+++ b/identity/aidl/android/hardware/identity/Certificate.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package android.hardware.radio.config@1.3;
+package android.hardware.identity;
-import @1.2::IRadioConfigIndication;
-
-/**
- * Interface declaring unsolicited radio config indications.
- */
-interface IRadioConfigIndication extends @1.2::IRadioConfigIndication {
-
-};
+@VintfStability
+parcelable Certificate {
+ /**
+ * encodedCertificate contains the bytes of a DER-encoded X.509 certificate.
+ *
+ * If there is no certificate, this array is empty.
+ */
+ byte[] encodedCertificate;
+}
diff --git a/identity/aidl/android/hardware/identity/CipherSuite.aidl b/identity/aidl/android/hardware/identity/CipherSuite.aidl
new file mode 100644
index 0000000..20b02a8
--- /dev/null
+++ b/identity/aidl/android/hardware/identity/CipherSuite.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.identity;
+
+/**
+ * Cipher suites that can be used for communication between holder and reader devices.
+ */
+@VintfStability
+@Backing(type="int")
+enum CipherSuite {
+ /**
+ * Specifies that the cipher suite that will be used to secure communications between the reader
+ * is:
+ *
+ * - ECDHE with HKDF-SHA-256 for key agreement.
+ * - AES-256 with GCM block mode for authenticated encryption (nonces are incremented by
+ * one for every message).
+ * - ECDSA with SHA-256 for signing (used for signing session transcripts to defeat
+ * man-in-the-middle attacks), signing keys are not ephemeral.
+ *
+ * At present this is the only supported cipher suite and it is mandatory for all
+ * implementations to support it.
+ */
+ CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 = 1,
+}
diff --git a/identity/aidl/android/hardware/identity/HardwareInformation.aidl b/identity/aidl/android/hardware/identity/HardwareInformation.aidl
new file mode 100644
index 0000000..d67739d
--- /dev/null
+++ b/identity/aidl/android/hardware/identity/HardwareInformation.aidl
@@ -0,0 +1,54 @@
+/*
+ * 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.identity;
+
+@VintfStability
+parcelable HardwareInformation {
+ /**
+ * credentialStoreName is the name of the credential store implementation.
+ */
+ @utf8InCpp String credentialStoreName;
+
+ /**
+ * credentialStoreAuthorName is the name of the credential store author.
+ */
+ @utf8InCpp String credentialStoreAuthorName;
+
+ /**
+ * dataChunkSize is the size of data chunks to be used when sending and recieving data
+ * entries. All data chunks for a data item must be this size except for the last.
+ */
+ int dataChunkSize;
+
+ /**
+ * isDirectAccess specifies whether the provisioned credential is available through
+ * direct access. Credentials provisioned in credential stores with this set
+ * to true, should use reader authentication on all data elements.
+ */
+ boolean isDirectAccess;
+
+ /**
+ * supportedDocTypes if empty, then any document type is supported, otherwise
+ * only the document types returned are supported.
+ *
+ * Document types are defined in the relevant standard for the document, for example for the
+ * for Mobile Driving License as defined by ISO 18013-5 the document type is defined to
+ * be "org.iso.18013.5.1.mDL".
+ *
+ */
+ @utf8InCpp String[] supportedDocTypes;
+}
diff --git a/identity/1.0/IIdentityCredential.hal b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
similarity index 75%
rename from identity/1.0/IIdentityCredential.hal
rename to identity/aidl/android/hardware/identity/IIdentityCredential.aidl
index 75f6e18..10ce4c2 100644
--- a/identity/1.0/IIdentityCredential.hal
+++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
@@ -14,10 +14,13 @@
* limitations under the License.
*/
-package android.hardware.identity@1.0;
+package android.hardware.identity;
-import android.hardware.keymaster@4.0::HardwareAuthToken;
+import android.hardware.identity.Certificate;
+import android.hardware.identity.SecureAccessControlProfile;
+import android.hardware.keymaster.HardwareAuthToken;
+@VintfStability
interface IIdentityCredential {
/**
* Delete a credential.
@@ -35,10 +38,9 @@
* After this method has been called, the persistent storage used for credentialData should
* be deleted.
*
- * @return proofOfDeletionSignature is a COSE_Sign1 signature described above.
+ * @return a COSE_Sign1 signature described above.
*/
- deleteCredential()
- generates(Result result, vec<uint8_t> proofOfDeletionSignature);
+ byte[] deleteCredential();
/**
* Creates an ephemeral EC key pair, for use in establishing a seceure session with a reader.
@@ -46,39 +48,33 @@
* with the reader. The reason for generating the key pair in the secure environment is so that
* the secure environment knows what public key to expect to find in the session transcript.
*
- * This method may only be called once per instance. If called more than once, FAILED
+ * This method may only be called once per instance. If called more than once, STATUS_FAILED
* will be returned.
*
- * @return result is OK on success or FAILED if an error occurred.
- *
- * @return keyPair contains the unencrypted key-pair in PKCS#8 format.
+ * @return the unencrypted key-pair in PKCS#8 format.
*/
- createEphemeralKeyPair() generates (Result result, vec<uint8_t> keyPair);
+ byte[] createEphemeralKeyPair();
/**
* Sets the public part of the reader's ephemeral key pair.
*
- * This method may only be called once per instance. If called more than once, FAILED
+ * This method may only be called once per instance. If called more than once, STATUS_FAILED
* will be returned.
*
* @param publicKey contains the reader's ephemeral public key, in uncompressed form.
- *
- * @return result is OK on success or FAILED if an error occurred.
*/
- setReaderEphemeralPublicKey(vec<uint8_t> publicKey) generates (Result result);
+ void setReaderEphemeralPublicKey(in byte[] publicKey);
/**
* Creates a challenge value to be used for proving successful user authentication. This
* is included in the authToken passed to the startRetrieval() method.
*
- * This method may only be called once per instance. If called more than once, FAILED
+ * This method may only be called once per instance. If called more than once, STATUS_FAILED
* will be returned.
*
- * @return result is OK on success or FAILED if an error occurred.
- *
- * @return challenge on success, is a non-zero number.
+ * @return challenge, a non-zero number.
*/
- createAuthChallenge() generates (Result result, uint64_t challenge);
+ long createAuthChallenge();
/**
* Start an entry retrieval process.
@@ -92,12 +88,12 @@
* startRetrieval(), then multiple calls of startRetrieveEntryValue(), retrieveEntryValue(),
* then finally finishRetrieval()) but if this is done, the sessionTranscript parameter
* must be identical for each startRetrieval() invocation. If this is not the case, this call
- * fails with the SESSION_TRANSCRIPT_MISMATCH error.
+ * fails with the STATUS_SESSION_TRANSCRIPT_MISMATCH error.
*
- * If the provided authToken is not valid this method fails with INVALID_AUTH_TOKEN.
+ * If the provided authToken is not valid this method fails with STATUS_INVALID_AUTH_TOKEN.
*
* Each of the provided accessControlProfiles is checked in this call. If they are not
- * all valid, the call fails with INVALID_DATA.
+ * all valid, the call fails with STATUS_INVALID_DATA.
*
* For the itemsRequest parameter, the content can be defined in the way appropriate for
* the credential, but there are three requirements that must be met to work with this HAL:
@@ -108,7 +104,7 @@
* the example below.
*
* If these requirements are not met the startRetrieval() call fails with
- * INVALID_ITEMS_REQUEST_MESSAGE.
+ * STATUS_INVALID_ITEMS_REQUEST_MESSAGE.
*
* Here's an example of ItemsRequest CBOR which conforms to this requirement:
*
@@ -156,17 +152,18 @@
* 'x5chain' unprotected header element of the COSE_Sign1 structure (as as described
* in 'draft-ietf-cose-x509-04'). There will be at least one certificate in said element
* and there may be more (and if so, each certificate must be signed by its successor).
- * This is checked and if the check fails the call fails with READER_SIGNATURE_CHECK_FAILED.
+ * This is checked and if the check fails the call fails with
+ * STATUS_READER_SIGNATURE_CHECK_FAILED.
*
* The SessionTranscript CBOR is conveyed in the sessionTranscript parameter. It
* is permissible for this to be empty in which case the readerSignature parameter
- * must also be empty. If this is not the case, the call fails with FAILED.
+ * must also be empty. If this is not the case, the call fails with STATUS_FAILED.
*
* If the SessionTranscript CBOR is not empty, the X and Y coordinates of the public
* part of the key-pair previously generated by createEphemeralKeyPair() must appear
* somewhere in the bytes of DeviceEngagement structure. Both X and Y should be in
* uncompressed form. If this is not satisfied, the call fails with
- * EPHEMERAL_PUBLIC_KEY_NOT_FOUND.
+ * STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND.
*
* @param accessControlProfiles
* Access control profiles that are required to retrieve the entries that are going to be
@@ -196,16 +193,11 @@
* will succeed (i.e. that the access control profile checks will succeed). This means that
* it's the responsibility of the caller to determine which access control checks will fail
* and remove the corresponding requests from the counts.
- *
- * @return result is OK on success. If an error occurs one of the values described above
- * will be returned.
*/
- startRetrieval(vec<SecureAccessControlProfile> accessControlProfiles,
- HardwareAuthToken authToken,
- vec<uint8_t> itemsRequest,
- vec<uint8_t> sessionTranscript,
- vec<uint8_t> readerSignature,
- vec<uint16_t> requestCounts) generates(Result result);
+ void startRetrieval(in SecureAccessControlProfile[] accessControlProfiles,
+ in HardwareAuthToken authToken,
+ in byte[] itemsRequest,
+ in byte[] sessionTranscript, in byte[] readerSignature, in int[] requestCounts);
/**
* Starts retrieving an entry, subject to access control requirements. Entries must be
@@ -213,15 +205,15 @@
*
* If the requestData parameter as passed to startRetrieval() was non-empty
* this method must only be called with entries specified in that field. If this
- * requirement is not met, the call fails with NOT_IN_REQUEST_MESSAGE.
+ * requirement is not met, the call fails with STATUS_NOT_IN_REQUEST_MESSAGE.
*
- * If nameSpace or name is empty this call fails with INVALID_DATA.
+ * If nameSpace or name is empty this call fails with STATUS_INVALID_DATA.
*
* Each access control profile for the entry is checked. If user authentication
* is required and the supplied auth token doesn't provide it the call fails
- * with USER_AUTHENTICATION_FAILED. If reader authentication is required and
+ * with STATUS_USER_AUTHENTICATION_FAILED. If reader authentication is required and
* a suitable reader certificate chain isn't presented, the call fails with
- * READER_AUTHENTICATION_FAILED.
+ * STATUS_READER_AUTHENTICATION_FAILED.
*
* It is permissible to keep retrieving values if an access control check fails.
*
@@ -231,38 +223,29 @@
*
* @param entrySize is the size of the entry value, if it's a text string or a byte string.
* It must be zero if the entry value is an integer or boolean. If this requirement
- * is not met the call fails with INVALID_DATA.
+ * is not met the call fails with STATUS_INVALID_DATA.
*
* @param accessControlProfileIds specifies the set of access control profiles that can
* authorize access to the provisioned element. If an identifier of a profile
* is given and this profile wasn't passed to startRetrieval() this call fails
- * with INVALID_DATA.
- *
- * @return result is OK on success. Otherwise one of INVALID_DATA, FAILED,
- * USER_AUTHENTICATION_FAILED, READER_AUTHENTICATION_FAILED.
+ * with STATUS_INVALID_DATA.
*/
- startRetrieveEntryValue(string nameSpace, string name, uint32_t entrySize,
- vec<uint16_t> accessControlProfileIds)
- generates (Result result);
-
+ void startRetrieveEntryValue(in @utf8InCpp String nameSpace, in @utf8InCpp String name,
+ in int entrySize, in int[] accessControlProfileIds);
/**
* Retrieves an entry value, or part of one, if the entry value is larger than gcmChunkSize.
* May only be called after startRetrieveEntry().
*
* If the passed in data is not authentic, can't be decrypted, is of the wrong size, or can't
- * be decoded, this call fails with INVALID_DATA.
+ * be decoded, this call fails with STATUS_INVALID_DATA.
*
* @param encryptedContent contains the encrypted and MACed content.
*
- * @return result is OK on success, INVALID_DATA, or FAILED if an error occurred.
- *
- * @return content is the entry value as CBOR, or part of the entry value in the case the
+ * @return the entry value as CBOR, or part of the entry value in the case the
* content exceeds gcmChunkSize in length.
*/
- retrieveEntryValue(vec<uint8_t> encryptedContent)
- generates (Result result, vec<uint8_t> content);
-
+ byte[] retrieveEntryValue(in byte[] encryptedContent);
/**
* End retrieval of data, optionally returning a message authentication code over the
@@ -273,11 +256,9 @@
*
* @param signingKeyBlob is either empty or a signingKeyBlob (see generateSigningKeyPair(),
* below) containing the signing key to use to sign the data retrieved. If this
- * is not in the right format the call fails with INVALID_DATA.
+ * is not in the right format the call fails with STATUS_INVALID_DATA.
*
- * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
- *
- * @return mac is empty if signingKeyBlob or the sessionTranscript passed to
+ * @param out mac is empty if signingKeyBlob or the sessionTranscript passed to
* startRetrieval() is empty. Otherwise it is a COSE_Mac0 with empty payload
* and the detached content is set to DeviceAuthentication as defined below.
* The key used for the MAC operation is EMacKey and is derived as follows:
@@ -321,23 +302,17 @@
* DataItemValue = any
*
*
- * @return deviceNameSpaces the bytes of DeviceNameSpaces.
+ * @param out deviceNameSpaces the bytes of DeviceNameSpaces.
*/
- finishRetrieval(vec<uint8_t> signingKeyBlob)
- generates(Result result, vec<uint8_t> mac, vec<uint8_t> deviceNameSpaces);
-
+ void finishRetrieval(in byte[] signingKeyBlob, out byte[] mac, out byte[] deviceNameSpaces);
/**
* Generate a key pair to be used for signing session data and retrieved data items.
*
- * @return result is OK on success or FAILED if an error occurred.
+ * @param out signingKeyBlob contains an encrypted copy of the newly-generated private
+ * signing key.
*
- * @return signingKeyBlob contains an encrypted copy of the newly-generated private signing key.
- *
- * @return signingKeyCertificate contains an X.509 certificate for the new signing key, signed
- * by the credential key.
+ * @return an X.509 certificate for the new signing key, signed by the credential key.
*/
- generateSigningKeyPair()
- generates(Result result, vec<uint8_t> signingKeyBlob,
- vec<uint8_t> signingKeyCertificate);
-};
+ Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
+}
diff --git a/identity/1.0/IIdentityCredentialStore.hal b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
similarity index 67%
rename from identity/1.0/IIdentityCredentialStore.hal
rename to identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
index 118ca6f..23cb1b7 100644
--- a/identity/1.0/IIdentityCredentialStore.hal
+++ b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package android.hardware.identity@1.0;
+package android.hardware.identity;
-import IWritableIdentityCredential;
-import IIdentityCredential;
+import android.hardware.identity.IIdentityCredential;
+import android.hardware.identity.IWritableIdentityCredential;
+import android.hardware.identity.HardwareInformation;
+import android.hardware.identity.CipherSuite;
/**
* IIdentityCredentialStore provides an interface to a secure store for user identity documents.
@@ -98,37 +100,90 @@
* define appropriate encodings, those are used. For example, X.509 certificates. Where new
* encodings are needed, CBOR is used. CBOR maps are described in CDDL notation
* (https://tools.ietf.org/html/draft-ietf-cbor-cddl-06).
+ *
+ * All binder calls in the HAL may return a ServiceSpecificException with statuses from the
+ * STATUS_* integers defined in this interface. Each method states which status can be returned
+ * and under which circumstances.
*/
+@VintfStability
interface IIdentityCredentialStore {
+ /**
+ * Success.
+ */
+ const int STATUS_OK = 0;
+
+ /**
+ * The operation failed. This is used as a generic catch-all for errors that don't belong
+ * in other categories, including memory/resource allocation failures and I/O errors.
+ */
+ const int STATUS_FAILED = 1;
+
+ /**
+ * Unsupported cipher suite.
+ */
+ const int STATUS_CIPHER_SUITE_NOT_SUPPORTED = 2;
+
+ /**
+ * The passed data was invalid. This is a generic catch all for errors that don't belong
+ * in other categories related to parameter validation.
+ */
+ const int STATUS_INVALID_DATA = 3;
+
+ /**
+ * The authToken parameter passed to IIdentityCredential.startRetrieval() is not valid.
+ */
+ const int STATUS_INVALID_AUTH_TOKEN = 4;
+
+ /**
+ * The itemsRequest parameter passed to IIdentityCredential.startRetrieval() does not meet
+ * the requirements described in the documentation for that method.
+ */
+ const int STATUS_INVALID_ITEMS_REQUEST_MESSAGE = 5;
+
+ /**
+ * The readerSignature parameter in IIdentityCredential.startRetrieval() is invalid,
+ * doesn't contain an embedded certificate chain, or the signature failed to
+ * validate.
+ */
+ const int STATUS_READER_SIGNATURE_CHECK_FAILED = 6;
+
+ /**
+ * The sessionTranscript passed to startRetrieval() did not contain the ephmeral public
+ * key returned by createEphemeralPublicKey().
+ */
+ const int STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 7;
+
+ /**
+ * An access condition related to user authentication was not satisfied.
+ */
+ const int STATUS_USER_AUTHENTICATION_FAILED = 8;
+
+ /**
+ * An access condition related to reader authentication was not satisfied.
+ */
+ const int STATUS_READER_AUTHENTICATION_FAILED = 9;
+
+ /**
+ * The request data element has no access control profiles associated so it cannot be accessed.
+ */
+ const int STATUS_NO_ACCESS_CONTROL_PROFILES = 10;
+
+ /**
+ * The requested data element is not in the provided non-empty itemsRequest message.
+ */
+ const int STATUS_NOT_IN_REQUEST_MESSAGE = 11;
+
+ /**
+ * The passed-in sessionTranscript doesn't match the previously passed-in sessionTranscript.
+ */
+ const int STATUS_SESSION_TRANSCRIPT_MISMATCH = 12;
/**
* Returns information about hardware.
*
- * The isDirectAccess output parameter indicates whether this credential store
- * implementation is for direct access. Credentials provisioned in credential
- * stores with this set to true, should use reader authentication on all data elements.
- *
- * @return result is OK on success, FAILED if an error occurred.
- *
- * @return credentialStoreName the name of the credential store implementation.
- *
- * @return credentialStoreAuthorName the name of the credential store author.
- *
- * @return dataChunkSize the maximum size of data chunks.
- *
- * @return isDirectAccess whether the provisioned credential is available through
- * direct access.
- *
- * @return supportedDocTypes if empty, then any document type is supported, otherwise
- * only the document types returned are supported.
+ * @return a HardwareInformation with information about the hardware.
*/
- getHardwareInformation()
- generates(Result result,
- string credentialStoreName,
- string credentialStoreAuthorName,
- uint32_t dataChunkSize,
- bool isDirectAccess,
- vec<string> supportedDocTypes);
+ HardwareInformation getHardwareInformation();
/**
* createCredential creates a new Credential. When a Credential is created, two cryptographic
@@ -147,16 +202,14 @@
* all-zeros hardware-bound key (HBK) and must set the test bit in the
* personalizationReceipt (see finishAddingEntries(), in IWritableIdentityCredential).
*
- * @return result is OK on success, FAILED if an error occurred.
- *
- * @return writableCredential is an IWritableIdentityCredential HIDL interface that provides
- * operations to provision a credential.
+ * @return an IWritableIdentityCredential interface that provides operations to
+ * provision a credential.
*/
- createCredential(string docType, bool testCredential)
- generates(Result result, IWritableIdentityCredential writableCredential);
+ IWritableIdentityCredential createCredential(in @utf8InCpp String docType,
+ in boolean testCredential);
/**
- * getCredential retrieves an IIdentityCredential HIDL interface which allows use of a stored
+ * getCredential retrieves an IIdentityCredential interface which allows use of a stored
* Credential.
*
* The cipher suite used to communicate with the remote verifier must also be specified. Currently
@@ -170,16 +223,17 @@
*
* Support for other cipher suites may be added in a future version of this HAL.
*
+ * This method fails with STATUS_INVALID_DATA if the passed in credentialData cannot be
+ * decoded or decrypted.
+ *
+ * @param cipherSuite is the cipher suite to use.
+ *
* @param credentialData is a CBOR-encoded structure containing metadata about the credential
* and an encrypted byte array that contains data used to secure the credential. See the
- * return argument of the same name in finishAddingEntries(), in IWritableIdentityCredential.
+ * return argument of the same name in finishAddingEntries(), in
+ * IWritableIdentityCredential.
*
- * @return result is OK on success or INVALID_DATA if the passed in credentialData
- * cannot be decoded or decrypted.
- *
- * @return credential is an IIdentityCredential HIDL interface that provides operations on the
- * Credential.
+ * @return an IIdentityCredential HIDL interface that provides operations on the Credential.
*/
- getCredential(vec<uint8_t> credentialData)
- generates (Result result, IIdentityCredential credential);
-};
+ IIdentityCredential getCredential(in CipherSuite cipherSuite, in byte[] credentialData);
+}
diff --git a/identity/1.0/IWritableIdentityCredential.hal b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
similarity index 62%
rename from identity/1.0/IWritableIdentityCredential.hal
rename to identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
index b1ce00d..483b0c7 100644
--- a/identity/1.0/IWritableIdentityCredential.hal
+++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -14,50 +14,82 @@
* limitations under the License.
*/
-package android.hardware.identity@1.0;
+package android.hardware.identity;
+
+import android.hardware.identity.Certificate;
+import android.hardware.identity.SecureAccessControlProfile;
/**
* IWritableIdentityCredential is used to personalize a new identity credential. Credentials cannot
* be updated or modified after creation; any changes require deletion and re-creation.
*/
+@VintfStability
interface IWritableIdentityCredential {
/**
* Gets the certificate chain for credentialKey which can be used to prove the hardware
* characteristics to an issuing authority. Must not be called more than once.
*
* The certificate chain must be generated using Keymaster Attestation
- * (see https://source.android.com/security/keystore/attestation) and must also
- * have the Tag::IDENTITY_CREDENTIAL_KEY tag from KeyMaster 4.1 set. This tag indicates
- * that this 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).
+ * (see https://source.android.com/security/keystore/attestation) with the
+ * following additional requirements:
+ *
+ * - The attestationVersion field in the attestation extension must be at least 3.
+ *
+ * - 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).
+ *
+ * - 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 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 attestationChallenge field must be set to the passed-in challenge.
+ *
+ * - The uniqueId field must be empty.
+ *
+ * - 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.
+ *
+ * - The teeEnforced field in the attestation extension must include
+ * Tag::IDENTITY_CREDENTIAL_KEY. This tag 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).
+ *
+ * Additional authorizations may be needed in the softwareEnforced and teeEnforced
+ * fields - the above is not an exhaustive list.
+ *
+ * @param attestationApplicationId is the DER encoded value to be stored
+ * in Tag::ATTESTATION_APPLICATION_ID. This schema is described in
+ * https://developer.android.com/training/articles/security-key-attestation#certificate_schema_attestationid
*
* @param attestationChallenge a challenge set by the issuer to ensure freshness.
*
- * @return result is OK on success, FAILED if an error occurred.
- *
- * @return certificate is the X.509 certificate chain for the credentialKey
+ * @return the X.509 certificate chain for the credentialKey
*/
- getAttestationCertificate(vec<uint8_t> attestationChallenge)
- generates(Result result, vec<uint8_t> certificate);
+ Certificate[] getAttestationCertificate(in byte[] attestationApplicationId, in byte[] attestationChallenge);
/**
* Start the personalization process.
*
* startPersonalization must not be called more than once.
*
- * @param accessControlProfileCount specifies the number of access control profiles that will be
- * provisioned with addAccessControlProfile().
+ * @param accessControlProfileCount specifies the number of access control profiles that will
+ * be provisioned with addAccessControlProfile().
*
* @param entryCounts specifies the number of data entries that will be provisioned with
* beginAddEntry() and addEntry(). Each item in the array specifies how many entries
* will be added for each name space.
- *
- * @return result is OK on success, FAILED if an error occurred.
- *
*/
- startPersonalization(uint16_t accessControlProfileCount, vec<uint16_t> entryCounts)
- generates(Result result);
+ void startPersonalization(in int accessControlProfileCount, in int[] entryCounts);
/**
* Add an access control profile, which defines the requirements or retrieval of one or more
@@ -67,15 +99,15 @@
*
* This method must be called exactly as many times as specified in the startPersonalization()
* accessControlProfileCount parameter. If this is requirement is not met, the method fails
- * with INVALID_DATA.
+ * with STATUS_INVALID_DATA.
*
* @param id a numeric identifier that must be unique within the context of a Credential and may
* be used to reference the profile. If this is not satisfied the call fails with
- * INVALID_DATA.
+ * STATUS_INVALID_DATA.
*
- * @param readerCertificate if non-empty, specifies a X.509 certificate (or chain of certificates)
- * that must be used to authenticate requests (see the readerSignature parameter in
- * IIdentityCredential.startRetrieval).
+ * @param readerCertificate if non-empty, specifies a X.509 certificate (or chain of
+ * certificates) that must be used to authenticate requests (see the readerSignature
+ * parameter in IIdentityCredential.startRetrieval).
*
* @param userAuthenticationRequired if true, specifies that the user is required to
* authenticate to allow requests. Required authentication freshness is specified by
@@ -85,22 +117,18 @@
* authentication (see userAuthenticationRequired above) is valid, if
* userAuthenticationRequired is true. If the timout is zero then authentication is
* required for each reader session. If userAuthenticationRequired is false, the timeout
- * must be zero. If this requirement is not met the call fails with INVALID_DATA.
+ * must be zero. If this requirement is not met the call fails with STATUS_INVALID_DATA.
*
* @param secureUserId must be non-zero if userAuthenticationRequired is true. It is not
* related to any Android user ID or UID, but is created in the Gatekeeper application
* in the secure environment. If this requirement is not met the call fails with
- * INVALID_DATA.
+ * STATUS_INVALID_DATA.
*
- * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
- *
- * @return secureAccessControlProfile is a structure with the passed-in data and MAC created
- * with storageKey for authenticating the data at a later point in time.
+ * @return a structure with the passed-in data and MAC created with storageKey for authenticating
+ * the data at a later point in time.
*/
- addAccessControlProfile(uint16_t id, vec<uint8_t> readerCertificate,
- bool userAuthenticationRequired, uint64_t timeoutMillis,
- uint64_t secureUserId)
- generates(Result result, SecureAccessControlProfile secureAccessControlProfile);
+ SecureAccessControlProfile addAccessControlProfile(in int id, in Certificate readerCertificate,
+ in boolean userAuthenticationRequired, in long timeoutMillis, in long secureUserId);
/**
* Begins the process of adding an entry to the credential. All access control profiles must be
@@ -109,7 +137,7 @@
*
* This method must be called exactly as many times as the sum of the items in the entryCounts
* parameter specified in the startPersonalization(), and must be followed by one or more calls
- * to addEntryValue(). If this requirement is not met the method fails with INVALID_DATA.
+ * to addEntryValue(). If this requirement is not met the method fails with STATUS_INVALID_DATA.
*
* @param accessControlProfileIds specifies the set of access control profiles that can
* authorize access to the provisioned element.
@@ -119,13 +147,10 @@
* @param name is the name of the element.
*
* @param entrySize is the size of the entry value. If this requirement
- * is not met this method fails with INVALID_DATA.
- *
- * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
+ * is not met this method fails with STATUS_INVALID_DATA.
*/
- beginAddEntry(vec<uint16_t> accessControlProfileIds, string nameSpace,
- string name, uint32_t entrySize)
- generates(Result result);
+ void beginAddEntry(in int[] accessControlProfileIds, in @utf8InCpp String nameSpace,
+ in @utf8InCpp String name, in int entrySize);
/**
* Continues the process of adding an entry, providing a value or part of a value.
@@ -135,18 +160,13 @@
* (see IIdentityCredentialStore.getHardwareInformation()), the caller must provide the
* value in chunks. All chunks must be exactly gcmChunkSize except the last and the sum of all
* chunk sizes must equal the value of the beginAddEntry() entrySize argument. If this
- * requirement is not met the call fails with INVALID_DATA.
+ * requirement is not met the call fails with STATUS_INVALID_DATA.
*
* @param content is the entry value, encoded as CBOR. In the case the content exceeds gcmChunkSize,
* this may be partial content up to gcmChunkSize bytes long.
*
- * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
- *
- * @return encryptedContent contains the encrypted and MACed content. For directly-available
- * credentials the contents are implementation-defined but must not exceed 32 bytes in
- * length.
- *
- * For other credentials, encryptedContent contains:
+ * @return the encrypted and MACed content. For directly-available credentials the contents are
+ * implementation-defined. For other credentials, the result contains
*
* AES-GCM-ENC(storageKey, R, Data, AdditionalData)
*
@@ -160,18 +180,15 @@
* "AccessControlProfileIds" : [ + uint ],
* }
*/
- addEntryValue(vec<uint8_t> content)
- generates(Result result, vec<uint8_t> encryptedContent);
+ byte[] addEntryValue(in byte[] content);
/**
- * Finishes adding entries and returns a signature that an issuing authority may use to validate
- * that all data was provisioned correctly.
+ * Finishes adding entries and returns a signature that an issuing authority may use to
+ * validate that all data was provisioned correctly.
*
* After this method is called, the IWritableIdentityCredential is no longer usable.
*
- * @return result is OK on success or FAILED if an error occurred.
- *
- * @return credentialData is a CBOR-encoded structure (in CDDL notation):
+ * @param out credentialData is a CBOR-encoded structure (in CDDL notation):
*
* CredentialData = [
* tstr, ; docType, an optional name that identifies the type of credential
@@ -194,10 +211,10 @@
* bstr ; credentialPrivKey, the private key for credentialKey
* ]
*
- * @return proofOfProvisioningSignature proves to the IA that the credential was imported into the
- * secure hardware without alteration or error. When the final addEntry() call is made
- * (when the number of provisioned entries equals the sum of the items in
- * startPersonalization() entryCounts parameter), it a COSE_Sign1 structure
+ * @param out proofOfProvisioningSignature proves to the IA that the credential was imported
+ * into the secure hardware without alteration or error. When the final addEntry() call is
+ * made (when the number of provisioned entries equals the sum of the items in
+ * startPersonalization() entryCounts parameter), a COSE_Sign1 structure
* signed by CredentialKey with payload set to the ProofOfProvisioning CBOR below:
*
* ProofOfProvisioning = [
@@ -230,7 +247,6 @@
* "accessControlProfiles" : [ * uint ],
* }
*/
- finishAddingEntries()
- generates(Result result, vec<uint8_t> credentialData,
- vec<uint8_t> proofOfProvisioningSignature);
-};
+ void finishAddingEntries(out byte[] credentialData,
+ out byte[] proofOfProvisioningSignature);
+}
diff --git a/identity/aidl/android/hardware/identity/SecureAccessControlProfile.aidl b/identity/aidl/android/hardware/identity/SecureAccessControlProfile.aidl
new file mode 100644
index 0000000..01d312d
--- /dev/null
+++ b/identity/aidl/android/hardware/identity/SecureAccessControlProfile.aidl
@@ -0,0 +1,78 @@
+/*
+ * 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.identity;
+
+import android.hardware.identity.Certificate;
+
+@VintfStability
+parcelable SecureAccessControlProfile {
+ /**
+ * id is a numeric identifier that must be unique within the context of a Credential and may be
+ * used to reference the profile.
+ */
+ int id;
+
+ /**
+ * readerCertificate, if non-empty, specifies a single X.509 certificate (not a chain
+ * of certificates) that must be used to authenticate requests. For details about how
+ * this is done, see the readerSignature paremter of IIdentityCredential.startRetrieval.
+ */
+ Certificate readerCertificate;
+
+ /**
+ * if true, the user is required to authenticate to allow requests. Required authentication
+ * fressness is specified by timeout below.
+ *
+ */
+ boolean userAuthenticationRequired;
+
+ /**
+ * Timeout specifies the amount of time, in milliseconds, for which a user authentication (see
+ * above) is valid, if userAuthenticationRequired is set to true. If userAuthenticationRequired
+ * is true and timout is zero then authentication is required for each reader session.
+ *
+ * If userAuthenticationRequired is false, timeout must be zero.
+ */
+ long timeoutMillis;
+
+ /**
+ * secureUserId must be non-zero if userAuthenticationRequired is true.
+ * It is not related to any Android user ID or UID, but is created in the
+ * Gatekeeper application in the secure environment.
+ */
+ long secureUserId;
+
+ /**
+ * The mac is used to authenticate the access control profile. It contains:
+ *
+ * AES-GCM-ENC(storageKey, R, {}, AccessControlProfile)
+ *
+ * where AccessControlProfile is the CBOR map:
+ *
+ * AccessControlProfile = {
+ * "id": uint,
+ * ? "readerCertificate" : bstr,
+ * ? (
+ * "userAuthenticationRequired" : bool,
+ * "timeoutMillis" : uint,
+ * "secureUserId" : uint
+ * )
+ * }
+ */
+ byte[] mac;
+}
+
diff --git a/identity/aidl/default/Android.bp b/identity/aidl/default/Android.bp
new file mode 100644
index 0000000..2eb0faa
--- /dev/null
+++ b/identity/aidl/default/Android.bp
@@ -0,0 +1,29 @@
+cc_binary {
+ name: "android.hardware.identity-service.example",
+ relative_install_path: "hw",
+ init_rc: ["identity-default.rc"],
+ vintf_fragments: ["identity-default.xml"],
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcppbor",
+ "libcrypto",
+ "liblog",
+ "libutils",
+ "android.hardware.identity-support-lib",
+ "android.hardware.identity-ndk_platform",
+ "android.hardware.keymaster-ndk_platform",
+ ],
+ srcs: [
+ "IdentityCredential.cpp",
+ "IdentityCredentialStore.cpp",
+ "WritableIdentityCredential.cpp",
+ "Util.cpp",
+ "service.cpp",
+ ],
+}
diff --git a/identity/aidl/default/IdentityCredential.cpp b/identity/aidl/default/IdentityCredential.cpp
new file mode 100644
index 0000000..d5b3a0f
--- /dev/null
+++ b/identity/aidl/default/IdentityCredential.cpp
@@ -0,0 +1,768 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "IdentityCredential"
+
+#include "IdentityCredential.h"
+#include "IdentityCredentialStore.h"
+#include "Util.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <string.h>
+
+#include <android-base/logging.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::aidl::android::hardware::keymaster::Timestamp;
+using ::std::optional;
+
+using namespace ::android::hardware::identity;
+
+int IdentityCredential::initialize() {
+ auto [item, _, message] = cppbor::parse(credentialData_);
+ if (item == nullptr) {
+ LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ const cppbor::Array* arrayItem = item->asArray();
+ if (arrayItem == nullptr || arrayItem->size() != 3) {
+ LOG(ERROR) << "CredentialData is not an array with three elements";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
+ const cppbor::Bool* testCredentialItem =
+ ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
+ : nullptr);
+ const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
+ if (docTypeItem == nullptr || testCredentialItem == nullptr ||
+ encryptedCredentialKeysItem == nullptr) {
+ LOG(ERROR) << "CredentialData unexpected item types";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ docType_ = docTypeItem->value();
+ testCredential_ = testCredentialItem->value();
+
+ vector<uint8_t> hardwareBoundKey;
+ if (testCredential_) {
+ hardwareBoundKey = support::getTestHardwareBoundKey();
+ } else {
+ hardwareBoundKey = getHardwareBoundKey();
+ }
+
+ const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
+ const vector<uint8_t> docTypeVec(docType_.begin(), docType_.end());
+ optional<vector<uint8_t>> decryptedCredentialKeys =
+ support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
+ if (!decryptedCredentialKeys) {
+ LOG(ERROR) << "Error decrypting CredentialKeys";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
+ if (dckItem == nullptr) {
+ LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+ const cppbor::Array* dckArrayItem = dckItem->asArray();
+ if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
+ LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+ const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
+ const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
+ if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
+ LOG(ERROR) << "CredentialKeys unexpected item types";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+ storageKey_ = storageKeyItem->value();
+ credentialPrivKey_ = credentialPrivKeyItem->value();
+
+ return IIdentityCredentialStore::STATUS_OK;
+}
+
+ndk::ScopedAStatus IdentityCredential::deleteCredential(
+ vector<int8_t>* outProofOfDeletionSignature) {
+ cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
+ vector<uint8_t> proofOfDeletion = array.encode();
+
+ optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
+ proofOfDeletion, // payload
+ {}, // additionalData
+ {}); // certificateChain
+ if (!signature) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
+ }
+
+ *outProofOfDeletionSignature = byteStringToSigned(signature.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<int8_t>* outKeyPair) {
+ optional<vector<uint8_t>> kp = support::createEcKeyPair();
+ if (!kp) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key pair"));
+ }
+
+ // Stash public key of this key-pair for later check in startRetrieval().
+ optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(kp.value());
+ if (!publicKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting public part of ephemeral key pair"));
+ }
+ ephemeralPublicKey_ = publicKey.value();
+
+ *outKeyPair = byteStringToSigned(kp.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey(
+ const vector<int8_t>& publicKey) {
+ readerPublicKey_ = byteStringToUnsigned(publicKey);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) {
+ uint64_t challenge = 0;
+ while (challenge == 0) {
+ optional<vector<uint8_t>> bytes = support::getRandom(8);
+ if (!bytes) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting random data for challenge"));
+ }
+
+ challenge = 0;
+ for (size_t n = 0; n < bytes.value().size(); n++) {
+ challenge |= ((bytes.value())[n] << (n * 8));
+ }
+ }
+
+ *outChallenge = challenge;
+ return ndk::ScopedAStatus::ok();
+}
+
+// TODO: this could be a lot faster if we did all the splitting and pubkey extraction
+// ahead of time.
+bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
+ const vector<uint8_t>& readerCertificateChain) {
+ optional<vector<uint8_t>> acpPubKey = support::certificateChainGetTopMostKey(
+ byteStringToUnsigned(profile.readerCertificate.encodedCertificate));
+ if (!acpPubKey) {
+ LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
+ return false;
+ }
+
+ optional<vector<vector<uint8_t>>> certificatesInChain =
+ support::certificateChainSplit(readerCertificateChain);
+ if (!certificatesInChain) {
+ LOG(ERROR) << "Error splitting readerCertificateChain";
+ return false;
+ }
+ for (const vector<uint8_t>& certInChain : certificatesInChain.value()) {
+ optional<vector<uint8_t>> certPubKey = support::certificateChainGetTopMostKey(certInChain);
+ if (!certPubKey) {
+ LOG(ERROR)
+ << "Error extracting public key from certificate in chain presented by reader";
+ return false;
+ }
+ if (acpPubKey.value() == certPubKey.value()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+Timestamp clockGetTime() {
+ struct timespec time;
+ clock_gettime(CLOCK_MONOTONIC, &time);
+ Timestamp ts;
+ ts.milliSeconds = time.tv_sec * 1000 + time.tv_nsec / 1000000;
+ return ts;
+}
+
+bool checkUserAuthentication(const SecureAccessControlProfile& profile,
+ const HardwareAuthToken& authToken, uint64_t authChallenge) {
+ if (profile.secureUserId != authToken.userId) {
+ LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId
+ << ") differs from userId in authToken (" << authToken.userId << ")";
+ return false;
+ }
+
+ if (profile.timeoutMillis == 0) {
+ if (authToken.challenge == 0) {
+ LOG(ERROR) << "No challenge in authToken";
+ return false;
+ }
+
+ if (authToken.challenge != int64_t(authChallenge)) {
+ LOG(ERROR) << "Challenge in authToken doesn't match the challenge we created";
+ return false;
+ }
+ return true;
+ }
+
+ // Note that the Epoch for timestamps in HardwareAuthToken is at the
+ // discretion of the vendor:
+ //
+ // "[...] since some starting point (generally the most recent device
+ // boot) which all of the applications within one secure environment
+ // must agree upon."
+ //
+ // Therefore, if this software implementation is used on a device which isn't
+ // the emulator then the assumption that the epoch is the same as used in
+ // clockGetTime above will not hold. This is OK as this software
+ // implementation should never be used on a real device.
+ //
+ Timestamp now = clockGetTime();
+ if (authToken.timestamp.milliSeconds > now.milliSeconds) {
+ LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp.milliSeconds
+ << ") is in the future (now: " << now.milliSeconds << ")";
+ return false;
+ }
+ if (now.milliSeconds > authToken.timestamp.milliSeconds + profile.timeoutMillis) {
+ LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp.milliSeconds << " + "
+ << profile.timeoutMillis << " = "
+ << (authToken.timestamp.milliSeconds + profile.timeoutMillis)
+ << ") is in the past (now: " << now.milliSeconds << ")";
+ return false;
+ }
+ return true;
+}
+
+ndk::ScopedAStatus IdentityCredential::startRetrieval(
+ const vector<SecureAccessControlProfile>& accessControlProfiles,
+ const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequestS,
+ const vector<int8_t>& sessionTranscriptS, const vector<int8_t>& readerSignatureS,
+ const vector<int32_t>& requestCounts) {
+ auto sessionTranscript = byteStringToUnsigned(sessionTranscriptS);
+ auto itemsRequest = byteStringToUnsigned(itemsRequestS);
+ auto readerSignature = byteStringToUnsigned(readerSignatureS);
+
+ if (sessionTranscript.size() > 0) {
+ auto [item, _, message] = cppbor::parse(sessionTranscript);
+ if (item == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "SessionTranscript contains invalid CBOR"));
+ }
+ sessionTranscriptItem_ = std::move(item);
+ }
+ if (numStartRetrievalCalls_ > 0) {
+ if (sessionTranscript_ != sessionTranscript) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
+ "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
+ }
+ }
+ sessionTranscript_ = sessionTranscript;
+
+ // If there is a signature, validate that it was made with the top-most key in the
+ // certificate chain embedded in the COSE_Sign1 structure.
+ optional<vector<uint8_t>> readerCertificateChain;
+ if (readerSignature.size() > 0) {
+ readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
+ if (!readerCertificateChain) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Unable to get reader certificate chain from COSE_Sign1"));
+ }
+
+ if (!support::certificateChainValidate(readerCertificateChain.value())) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Error validating reader certificate chain"));
+ }
+
+ optional<vector<uint8_t>> readerPublicKey =
+ support::certificateChainGetTopMostKey(readerCertificateChain.value());
+ if (!readerPublicKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Unable to get public key from reader certificate chain"));
+ }
+
+ const vector<uint8_t>& itemsRequestBytes = itemsRequest;
+ vector<uint8_t> dataThatWasSigned = cppbor::Array()
+ .add("ReaderAuthentication")
+ .add(sessionTranscriptItem_->clone())
+ .add(cppbor::Semantic(24, itemsRequestBytes))
+ .encode();
+ if (!support::coseCheckEcDsaSignature(readerSignature,
+ dataThatWasSigned, // detached content
+ readerPublicKey.value())) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "readerSignature check failed"));
+ }
+ }
+
+ // Here's where we would validate the passed-in |authToken| to assure ourselves
+ // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
+ //
+ // However this involves calculating the MAC. However this requires access
+ // to the key needed to a pre-shared key which we don't have...
+ //
+
+ // To prevent replay-attacks, we check that the public part of the ephemeral
+ // key we previously created, is present in the DeviceEngagement part of
+ // SessionTranscript as a COSE_Key, in uncompressed form.
+ //
+ // We do this by just searching for the X and Y coordinates.
+ if (sessionTranscript.size() > 0) {
+ const cppbor::Array* array = sessionTranscriptItem_->asArray();
+ if (array == nullptr || array->size() != 2) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "SessionTranscript is not an array with two items"));
+ }
+ const cppbor::Semantic* taggedEncodedDE = (*array)[0]->asSemantic();
+ if (taggedEncodedDE == nullptr || taggedEncodedDE->value() != 24) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "First item in SessionTranscript array is not a "
+ "semantic with value 24"));
+ }
+ const cppbor::Bstr* encodedDE = (taggedEncodedDE->child())->asBstr();
+ if (encodedDE == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "Child of semantic in first item in SessionTranscript "
+ "array is not a bstr"));
+ }
+ const vector<uint8_t>& bytesDE = encodedDE->value();
+
+ auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
+ if (!getXYSuccess) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "Error extracting X and Y from ePub"));
+ }
+ if (sessionTranscript.size() > 0 &&
+ !(memmem(bytesDE.data(), bytesDE.size(), ePubX.data(), ePubX.size()) != nullptr &&
+ memmem(bytesDE.data(), bytesDE.size(), ePubY.data(), ePubY.size()) != nullptr)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "Did not find ephemeral public key's X and Y coordinates in "
+ "SessionTranscript (make sure leading zeroes are not used)"));
+ }
+ }
+
+ // itemsRequest: If non-empty, contains request data that may be signed by the
+ // reader. The content can be defined in the way appropriate for the
+ // credential, but there are three requirements that must be met to work with
+ // this HAL:
+ if (itemsRequest.size() > 0) {
+ // 1. The content must be a CBOR-encoded structure.
+ auto [item, _, message] = cppbor::parse(itemsRequest);
+ if (item == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "Error decoding CBOR in itemsRequest"));
+ }
+
+ // 2. The CBOR structure must be a map.
+ const cppbor::Map* map = item->asMap();
+ if (map == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "itemsRequest is not a CBOR map"));
+ }
+
+ // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
+ // the example below.
+ //
+ // NameSpaces = {
+ // + NameSpace => DataElements ; Requested data elements for each NameSpace
+ // }
+ //
+ // NameSpace = tstr
+ //
+ // DataElements = {
+ // + DataElement => IntentToRetain
+ // }
+ //
+ // DataElement = tstr
+ // IntentToRetain = bool
+ //
+ // Here's an example of an |itemsRequest| CBOR value satisfying above requirements 1.
+ // through 3.:
+ //
+ // {
+ // 'docType' : 'org.iso.18013-5.2019',
+ // 'nameSpaces' : {
+ // 'org.iso.18013-5.2019' : {
+ // 'Last name' : false,
+ // 'Birth date' : false,
+ // 'First name' : false,
+ // 'Home address' : true
+ // },
+ // 'org.aamva.iso.18013-5.2019' : {
+ // 'Real Id' : false
+ // }
+ // }
+ // }
+ //
+ const cppbor::Map* nsMap = nullptr;
+ for (size_t n = 0; n < map->size(); n++) {
+ const auto& [keyItem, valueItem] = (*map)[n];
+ if (keyItem->type() == cppbor::TSTR && keyItem->asTstr()->value() == "nameSpaces" &&
+ valueItem->type() == cppbor::MAP) {
+ nsMap = valueItem->asMap();
+ break;
+ }
+ }
+ if (nsMap == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "No nameSpaces map in top-most map"));
+ }
+
+ for (size_t n = 0; n < nsMap->size(); n++) {
+ auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
+ const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
+ const cppbor::Map* nsInnerMap = nsValueItem->asMap();
+ if (nsKey == nullptr || nsInnerMap == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "Type mismatch in nameSpaces map"));
+ }
+ string requestedNamespace = nsKey->value();
+ vector<string> requestedKeys;
+ for (size_t m = 0; m < nsInnerMap->size(); m++) {
+ const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
+ const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
+ const cppbor::Simple* simple = innerMapValueItem->asSimple();
+ const cppbor::Bool* intentToRetainItem =
+ (simple != nullptr) ? simple->asBool() : nullptr;
+ if (nameItem == nullptr || intentToRetainItem == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "Type mismatch in value in nameSpaces map"));
+ }
+ requestedKeys.push_back(nameItem->value());
+ }
+ requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
+ }
+ }
+
+ // Finally, validate all the access control profiles in the requestData.
+ bool haveAuthToken = (authToken.mac.size() > 0);
+ for (const auto& profile : accessControlProfiles) {
+ if (!secureAccessControlProfileCheckMac(profile, storageKey_)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Error checking MAC for profile"));
+ }
+ int accessControlCheck = IIdentityCredentialStore::STATUS_OK;
+ if (profile.userAuthenticationRequired) {
+ if (!haveAuthToken || !checkUserAuthentication(profile, authToken, authChallenge_)) {
+ accessControlCheck = IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED;
+ }
+ } else if (profile.readerCertificate.encodedCertificate.size() > 0) {
+ if (!readerCertificateChain ||
+ !checkReaderAuthentication(profile, readerCertificateChain.value())) {
+ accessControlCheck = IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED;
+ }
+ }
+ profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
+ }
+
+ deviceNameSpacesMap_ = cppbor::Map();
+ currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
+
+ requestCountsRemaining_ = requestCounts;
+ currentNameSpace_ = "";
+
+ itemsRequest_ = itemsRequest;
+
+ numStartRetrievalCalls_ += 1;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
+ const string& nameSpace, const string& name, int32_t entrySize,
+ const vector<int32_t>& accessControlProfileIds) {
+ if (name.empty()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty"));
+ }
+ if (nameSpace.empty()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Name space cannot be empty"));
+ }
+
+ if (requestCountsRemaining_.size() == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "No more name spaces left to go through"));
+ }
+
+ if (currentNameSpace_ == "") {
+ // First call.
+ currentNameSpace_ = nameSpace;
+ }
+
+ if (nameSpace == currentNameSpace_) {
+ // Same namespace.
+ if (requestCountsRemaining_[0] == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "No more entries to be retrieved in current name space"));
+ }
+ requestCountsRemaining_[0] -= 1;
+ } else {
+ // New namespace.
+ if (requestCountsRemaining_[0] != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Moved to new name space but one or more entries need to be retrieved "
+ "in current name space"));
+ }
+ if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
+ deviceNameSpacesMap_.add(currentNameSpace_,
+ std::move(currentNameSpaceDeviceNameSpacesMap_));
+ }
+ currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
+
+ requestCountsRemaining_.erase(requestCountsRemaining_.begin());
+ currentNameSpace_ = nameSpace;
+ }
+
+ // It's permissible to have an empty itemsRequest... but if non-empty you can
+ // only request what was specified in said itemsRequest. Enforce that.
+ if (itemsRequest_.size() > 0) {
+ const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
+ if (it == requestedNameSpacesAndNames_.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
+ "Name space was not requested in startRetrieval"));
+ }
+ const auto& dataItemNames = it->second;
+ if (std::find(dataItemNames.begin(), dataItemNames.end(), name) == dataItemNames.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
+ "Data item name in name space was not requested in startRetrieval"));
+ }
+ }
+
+ // Enforce access control.
+ //
+ // Access is granted if at least one of the profiles grants access.
+ //
+ // If an item is configured without any profiles, access is denied.
+ //
+ int accessControl = IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES;
+ for (auto id : accessControlProfileIds) {
+ auto search = profileIdToAccessCheckResult_.find(id);
+ if (search == profileIdToAccessCheckResult_.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Requested entry with unvalidated profile id"));
+ }
+ int accessControlForProfile = search->second;
+ if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
+ accessControl = IIdentityCredentialStore::STATUS_OK;
+ break;
+ }
+ accessControl = accessControlForProfile;
+ }
+ if (accessControl != IIdentityCredentialStore::STATUS_OK) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ int(accessControl), "Access control check failed"));
+ }
+
+ entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
+
+ currentName_ = name;
+ entryRemainingBytes_ = entrySize;
+ entryValue_.resize(0);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<int8_t>& encryptedContentS,
+ vector<int8_t>* outContent) {
+ auto encryptedContent = byteStringToUnsigned(encryptedContentS);
+
+ optional<vector<uint8_t>> content =
+ support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
+ if (!content) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Error decrypting data"));
+ }
+
+ size_t chunkSize = content.value().size();
+
+ if (chunkSize > entryRemainingBytes_) {
+ LOG(ERROR) << "Retrieved chunk of size " << chunkSize
+ << " is bigger than remaining space of size " << entryRemainingBytes_;
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved chunk is bigger than remaining space"));
+ }
+
+ entryRemainingBytes_ -= chunkSize;
+ if (entryRemainingBytes_ > 0) {
+ if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved non-final chunk of size which isn't kGcmChunkSize"));
+ }
+ }
+
+ entryValue_.insert(entryValue_.end(), content.value().begin(), content.value().end());
+
+ if (entryRemainingBytes_ == 0) {
+ auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
+ if (entryValueItem == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved data which is invalid CBOR"));
+ }
+ currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
+ }
+
+ *outContent = byteStringToSigned(content.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::finishRetrieval(const vector<int8_t>& signingKeyBlobS,
+ vector<int8_t>* outMac,
+ vector<int8_t>* outDeviceNameSpaces) {
+ auto signingKeyBlob = byteStringToUnsigned(signingKeyBlobS);
+
+ if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
+ deviceNameSpacesMap_.add(currentNameSpace_,
+ std::move(currentNameSpaceDeviceNameSpacesMap_));
+ }
+ vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
+
+ // If there's no signing key or no sessionTranscript or no reader ephemeral
+ // public key, we return the empty MAC.
+ 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> encodedDeviceAuthentication = array.encode();
+
+ vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
+ optional<vector<uint8_t>> signingKey =
+ support::decryptAes128Gcm(storageKey_, signingKeyBlob, docTypeAsBlob);
+ if (!signingKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "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"));
+ }
+
+ vector<uint8_t> salt = {0x00};
+ vector<uint8_t> info = {};
+ optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
+ if (!derivedKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error deriving key from shared secret"));
+ }
+
+ mac = support::coseMac0(derivedKey.value(), {}, // payload
+ encodedDeviceAuthentication); // additionalData
+ if (!mac) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));
+ }
+ }
+
+ *outMac = byteStringToSigned(mac.value_or(vector<uint8_t>({})));
+ *outDeviceNameSpaces = byteStringToSigned(encodedDeviceNameSpaces);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
+ vector<int8_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";
+ time_t validityNotBefore = time(nullptr);
+ time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+
+ optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
+ if (!signingKeyPKCS8) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
+ }
+
+ optional<vector<uint8_t>> signingPublicKey =
+ support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
+ if (!signingPublicKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting public part of signingKey"));
+ }
+
+ optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
+ if (!signingKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting private part of signingKey"));
+ }
+
+ optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
+ signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
+ validityNotBefore, validityNotAfter);
+ if (!certificate) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
+ }
+
+ optional<vector<uint8_t>> nonce = support::getRandom(12);
+ if (!nonce) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error getting random"));
+ }
+ vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
+ optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
+ storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
+ if (!encryptedSigningKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error encrypting signingKey"));
+ }
+ *outSigningKeyBlob = byteStringToSigned(encryptedSigningKey.value());
+ *outSigningKeyCertificate = Certificate();
+ outSigningKeyCertificate->encodedCertificate = byteStringToSigned(certificate.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/IdentityCredential.h b/identity/aidl/default/IdentityCredential.h
new file mode 100644
index 0000000..49ed0d4
--- /dev/null
+++ b/identity/aidl/default/IdentityCredential.h
@@ -0,0 +1,109 @@
+/*
+ * 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 ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
+#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
+
+#include <aidl/android/hardware/identity/BnIdentityCredential.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <cppbor/cppbor.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::aidl::android::hardware::keymaster::HardwareAuthToken;
+using ::std::map;
+using ::std::string;
+using ::std::vector;
+
+using MapStringToVectorOfStrings = map<string, vector<string>>;
+
+class IdentityCredential : public BnIdentityCredential {
+ public:
+ IdentityCredential(const vector<uint8_t>& credentialData)
+ : credentialData_(credentialData), numStartRetrievalCalls_(0), authChallenge_(0) {}
+
+ // Parses and decrypts credentialData_, return a status code from
+ // IIdentityCredentialStore. Must be called right after construction.
+ int initialize();
+
+ // Methods from IIdentityCredential follow.
+ ndk::ScopedAStatus deleteCredential(vector<int8_t>* outProofOfDeletionSignature) override;
+ ndk::ScopedAStatus createEphemeralKeyPair(vector<int8_t>* outKeyPair) override;
+ ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<int8_t>& publicKey) override;
+ ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override;
+ ndk::ScopedAStatus startRetrieval(
+ const vector<SecureAccessControlProfile>& accessControlProfiles,
+ const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequest,
+ const vector<int8_t>& sessionTranscript, const vector<int8_t>& readerSignature,
+ const vector<int32_t>& requestCounts) override;
+ ndk::ScopedAStatus startRetrieveEntryValue(
+ const string& nameSpace, const string& name, int32_t entrySize,
+ const vector<int32_t>& accessControlProfileIds) override;
+ ndk::ScopedAStatus retrieveEntryValue(const vector<int8_t>& encryptedContent,
+ vector<int8_t>* outContent) override;
+ ndk::ScopedAStatus finishRetrieval(const vector<int8_t>& signingKeyBlob, vector<int8_t>* outMac,
+ vector<int8_t>* outDeviceNameSpaces) override;
+ ndk::ScopedAStatus generateSigningKeyPair(vector<int8_t>* outSigningKeyBlob,
+ Certificate* outSigningKeyCertificate) override;
+
+ private:
+ // Set by constructor
+ vector<uint8_t> credentialData_;
+ int numStartRetrievalCalls_;
+
+ // Set by initialize()
+ string docType_;
+ bool testCredential_;
+ vector<uint8_t> storageKey_;
+ vector<uint8_t> credentialPrivKey_;
+
+ // Set by createEphemeralKeyPair()
+ vector<uint8_t> ephemeralPublicKey_;
+
+ // Set by setReaderEphemeralPublicKey()
+ vector<uint8_t> readerPublicKey_;
+
+ // Set by createAuthChallenge()
+ uint64_t authChallenge_;
+
+ // Set at startRetrieval() time.
+ map<int32_t, int> profileIdToAccessCheckResult_;
+ vector<uint8_t> sessionTranscript_;
+ std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
+ vector<uint8_t> itemsRequest_;
+ vector<int32_t> requestCountsRemaining_;
+ MapStringToVectorOfStrings requestedNameSpacesAndNames_;
+ cppbor::Map deviceNameSpacesMap_;
+ cppbor::Map currentNameSpaceDeviceNameSpacesMap_;
+
+ // Set at startRetrieveEntryValue() time.
+ string currentNameSpace_;
+ string currentName_;
+ size_t entryRemainingBytes_;
+ vector<uint8_t> entryValue_;
+ vector<uint8_t> entryAdditionalData_;
+};
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
diff --git a/identity/aidl/default/IdentityCredentialStore.cpp b/identity/aidl/default/IdentityCredentialStore.cpp
new file mode 100644
index 0000000..1efb4b4
--- /dev/null
+++ b/identity/aidl/default/IdentityCredentialStore.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "IdentityCredentialStore"
+
+#include <android-base/logging.h>
+
+#include "IdentityCredential.h"
+#include "IdentityCredentialStore.h"
+#include "WritableIdentityCredential.h"
+
+namespace aidl::android::hardware::identity {
+
+ndk::ScopedAStatus IdentityCredentialStore::getHardwareInformation(
+ HardwareInformation* hardwareInformation) {
+ HardwareInformation hw;
+ hw.credentialStoreName = "Identity Credential Reference Implementation";
+ hw.credentialStoreAuthorName = "Google";
+ hw.dataChunkSize = kGcmChunkSize;
+ hw.isDirectAccess = false;
+ hw.supportedDocTypes = {};
+ *hardwareInformation = hw;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredentialStore::createCredential(
+ const string& docType, bool testCredential,
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
+ shared_ptr<WritableIdentityCredential> wc =
+ ndk::SharedRefBase::make<WritableIdentityCredential>(docType, testCredential);
+ if (!wc->initialize()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error initializing WritableIdentityCredential"));
+ }
+ *outWritableCredential = wc;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredentialStore::getCredential(
+ CipherSuite cipherSuite, const vector<int8_t>& credentialData,
+ shared_ptr<IIdentityCredential>* outCredential) {
+ // We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now.
+ if (cipherSuite != CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED,
+ "Unsupported cipher suite"));
+ }
+
+ vector<uint8_t> data = vector<uint8_t>(credentialData.begin(), credentialData.end());
+ shared_ptr<IdentityCredential> credential = ndk::SharedRefBase::make<IdentityCredential>(data);
+ auto ret = credential->initialize();
+ if (ret != IIdentityCredentialStore::STATUS_OK) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ int(ret), "Error initializing IdentityCredential"));
+ }
+ *outCredential = credential;
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/IdentityCredentialStore.h b/identity/aidl/default/IdentityCredentialStore.h
new file mode 100644
index 0000000..a205113
--- /dev/null
+++ b/identity/aidl/default/IdentityCredentialStore.h
@@ -0,0 +1,48 @@
+/*
+ * 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 ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
+#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
+
+#include <aidl/android/hardware/identity/BnIdentityCredentialStore.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::std::shared_ptr;
+using ::std::string;
+using ::std::vector;
+
+class IdentityCredentialStore : public BnIdentityCredentialStore {
+ public:
+ IdentityCredentialStore() {}
+
+ // The GCM chunk size used by this implementation is 64 KiB.
+ static constexpr size_t kGcmChunkSize = 64 * 1024;
+
+ // Methods from IIdentityCredentialStore follow.
+ ndk::ScopedAStatus getHardwareInformation(HardwareInformation* hardwareInformation) override;
+
+ ndk::ScopedAStatus createCredential(
+ const string& docType, bool testCredential,
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) override;
+
+ ndk::ScopedAStatus getCredential(CipherSuite cipherSuite, const vector<int8_t>& credentialData,
+ shared_ptr<IIdentityCredential>* outCredential) override;
+};
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
diff --git a/identity/aidl/default/Util.cpp b/identity/aidl/default/Util.cpp
new file mode 100644
index 0000000..a0f86be
--- /dev/null
+++ b/identity/aidl/default/Util.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Util"
+
+#include "Util.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <string.h>
+
+#include <android-base/logging.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+namespace aidl::android::hardware::identity {
+
+using namespace ::android::hardware::identity;
+
+// This is not a very random HBK but that's OK because this is the SW
+// implementation where it can't be kept secret.
+vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+
+const vector<uint8_t>& getHardwareBoundKey() {
+ return hardwareBoundKey;
+}
+
+vector<uint8_t> byteStringToUnsigned(const vector<int8_t>& value) {
+ return vector<uint8_t>(value.begin(), value.end());
+}
+
+vector<int8_t> byteStringToSigned(const vector<uint8_t>& value) {
+ return vector<int8_t>(value.begin(), value.end());
+}
+
+vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) {
+ cppbor::Map map;
+ map.add("id", profile.id);
+
+ if (profile.readerCertificate.encodedCertificate.size() > 0) {
+ map.add("readerCertificate",
+ cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate)));
+ }
+
+ if (profile.userAuthenticationRequired) {
+ map.add("userAuthenticationRequired", profile.userAuthenticationRequired);
+ map.add("timeoutMillis", profile.timeoutMillis);
+ map.add("secureUserId", profile.secureUserId);
+ }
+
+ return map.encode();
+}
+
+optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
+ const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) {
+ vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
+
+ optional<vector<uint8_t>> nonce = support::getRandom(12);
+ if (!nonce) {
+ return {};
+ }
+ optional<vector<uint8_t>> macO =
+ support::encryptAes128Gcm(storageKey, nonce.value(), {}, cborData);
+ if (!macO) {
+ return {};
+ }
+ return macO.value();
+}
+
+bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
+ const vector<uint8_t>& storageKey) {
+ vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
+
+ if (profile.mac.size() < support::kAesGcmIvSize) {
+ return false;
+ }
+ vector<uint8_t> nonce =
+ vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + support::kAesGcmIvSize);
+ optional<vector<uint8_t>> mac = support::encryptAes128Gcm(storageKey, nonce, {}, cborData);
+ if (!mac) {
+ return false;
+ }
+ if (mac.value() != byteStringToUnsigned(profile.mac)) {
+ return false;
+ }
+ return true;
+}
+
+vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
+ const vector<int32_t> accessControlProfileIds) {
+ cppbor::Map map;
+ map.add("Namespace", nameSpace);
+ map.add("Name", name);
+
+ cppbor::Array acpIds;
+ for (auto id : accessControlProfileIds) {
+ acpIds.add(id);
+ }
+ map.add("AccessControlProfileIds", std::move(acpIds));
+ return map.encode();
+}
+
+} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/Util.h b/identity/aidl/default/Util.h
new file mode 100644
index 0000000..ee41ad1
--- /dev/null
+++ b/identity/aidl/default/Util.h
@@ -0,0 +1,58 @@
+/*
+ * 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 ANDROID_HARDWARE_IDENTITY_UTIL_H
+#define ANDROID_HARDWARE_IDENTITY_UTIL_H
+
+#include <aidl/android/hardware/identity/BnIdentityCredential.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <map>
+#include <optional>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <cppbor/cppbor.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::std::optional;
+using ::std::string;
+using ::std::vector;
+
+// Returns the hardware-bound AES-128 key.
+const vector<uint8_t>& getHardwareBoundKey();
+
+// Calculates the MAC for |profile| using |storageKey|.
+optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
+ const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey);
+
+// Checks authenticity of the MAC in |profile| using |storageKey|.
+bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
+ const vector<uint8_t>& storageKey);
+
+// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method.
+vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
+ const vector<int32_t> accessControlProfileIds);
+
+vector<uint8_t> byteStringToUnsigned(const vector<int8_t>& value);
+
+vector<int8_t> byteStringToSigned(const vector<uint8_t>& value);
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_UTIL_H
diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp
new file mode 100644
index 0000000..ba2062d
--- /dev/null
+++ b/identity/aidl/default/WritableIdentityCredential.cpp
@@ -0,0 +1,421 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "WritableIdentityCredential"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <android-base/logging.h>
+
+#include <cppbor/cppbor.h>
+#include <cppbor/cppbor_parse.h>
+
+#include "IdentityCredentialStore.h"
+#include "Util.h"
+#include "WritableIdentityCredential.h"
+
+namespace aidl::android::hardware::identity {
+
+using ::std::optional;
+using namespace ::android::hardware::identity;
+
+bool WritableIdentityCredential::initialize() {
+ optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
+ if (!keyPair) {
+ LOG(ERROR) << "Error creating credentialKey";
+ return false;
+ }
+
+ optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
+ if (!pubKey) {
+ LOG(ERROR) << "Error getting public part of credentialKey";
+ return false;
+ }
+ credentialPubKey_ = pubKey.value();
+
+ optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
+ if (!privKey) {
+ LOG(ERROR) << "Error getting private part of credentialKey";
+ return false;
+ }
+ credentialPrivKey_ = privKey.value();
+
+ optional<vector<uint8_t>> random = support::getRandom(16);
+ if (!random) {
+ LOG(ERROR) << "Error creating storageKey";
+ return false;
+ }
+ storageKey_ = random.value();
+
+ return true;
+}
+
+// TODO: use |attestationApplicationId| and |attestationChallenge| and also
+// ensure the returned certificate chain satisfy the requirements listed in
+// the docs for IWritableIdentityCredential::getAttestationCertificate()
+//
+ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
+ const vector<int8_t>& /*attestationApplicationId*/,
+ const vector<int8_t>& /*attestationChallenge*/, vector<Certificate>* outCertificateChain) {
+ // For now, we dynamically generate an attestion key on each and every
+ // request and use that to sign CredentialKey. In a real implementation this
+ // would look very differently.
+ optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
+ if (!attestationKeyPair) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating attestationKey"));
+ }
+
+ optional<vector<uint8_t>> attestationPubKey =
+ support::ecKeyPairGetPublicKey(attestationKeyPair.value());
+ if (!attestationPubKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting public part of attestationKey"));
+ }
+
+ optional<vector<uint8_t>> attestationPrivKey =
+ support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
+ if (!attestationPrivKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting private part of attestationKey"));
+ }
+
+ string serialDecimal;
+ string issuer;
+ string subject;
+ time_t validityNotBefore = time(nullptr);
+ time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+
+ // First create a certificate for |credentialPubKey| which is signed by
+ // |attestationPrivKey|.
+ //
+ serialDecimal = "0"; // TODO: set serial to |attestationChallenge|
+ issuer = "Android Open Source Project";
+ subject = "Android IdentityCredential CredentialKey";
+ optional<vector<uint8_t>> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate(
+ credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject,
+ validityNotBefore, validityNotAfter);
+ if (!credentialPubKeyCertificate) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error creating certificate for credentialPubKey"));
+ }
+
+ // This is followed by a certificate for |attestationPubKey| self-signed by
+ // |attestationPrivKey|.
+ serialDecimal = "0"; // TODO: set serial
+ issuer = "Android Open Source Project";
+ subject = "Android IdentityCredential AttestationKey";
+ optional<vector<uint8_t>> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate(
+ attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject,
+ validityNotBefore, validityNotAfter);
+ if (!attestationKeyCertificate) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error creating certificate for attestationPubKey"));
+ }
+
+ // Concatenate the certificates to form the chain.
+ vector<uint8_t> certificateChain;
+ certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(),
+ credentialPubKeyCertificate.value().end());
+ certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
+ attestationKeyCertificate.value().end());
+
+ optional<vector<vector<uint8_t>>> splitCertChain =
+ support::certificateChainSplit(certificateChain);
+ if (!splitCertChain) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error splitting certificate chain"));
+ }
+ *outCertificateChain = vector<Certificate>();
+ for (const vector<uint8_t>& cert : splitCertChain.value()) {
+ Certificate c = Certificate();
+ c.encodedCertificate = byteStringToSigned(cert);
+ outCertificateChain->push_back(std::move(c));
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
+ int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
+ numAccessControlProfileRemaining_ = accessControlProfileCount;
+ remainingEntryCounts_ = entryCounts;
+ entryNameSpace_ = "";
+
+ signedDataAccessControlProfiles_ = cppbor::Array();
+ signedDataNamespaces_ = cppbor::Map();
+ signedDataCurrentNamespace_ = cppbor::Array();
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
+ int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
+ int64_t timeoutMillis, int64_t secureUserId,
+ SecureAccessControlProfile* outSecureAccessControlProfile) {
+ SecureAccessControlProfile profile;
+
+ if (numAccessControlProfileRemaining_ == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
+ }
+
+ // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
+ // be zero.
+ if (!userAuthenticationRequired && timeoutMillis != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "userAuthenticationRequired is false but timeout is non-zero"));
+ }
+
+ profile.id = id;
+ profile.readerCertificate = readerCertificate;
+ profile.userAuthenticationRequired = userAuthenticationRequired;
+ profile.timeoutMillis = timeoutMillis;
+ profile.secureUserId = secureUserId;
+ optional<vector<uint8_t>> mac = secureAccessControlProfileCalcMac(profile, storageKey_);
+ if (!mac) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error calculating MAC for profile"));
+ }
+ profile.mac = byteStringToSigned(mac.value());
+
+ cppbor::Map profileMap;
+ profileMap.add("id", profile.id);
+ if (profile.readerCertificate.encodedCertificate.size() > 0) {
+ profileMap.add(
+ "readerCertificate",
+ cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate)));
+ }
+ if (profile.userAuthenticationRequired) {
+ profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
+ profileMap.add("timeoutMillis", profile.timeoutMillis);
+ }
+ signedDataAccessControlProfiles_.add(std::move(profileMap));
+
+ numAccessControlProfileRemaining_--;
+
+ *outSecureAccessControlProfile = profile;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
+ const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
+ int32_t entrySize) {
+ if (numAccessControlProfileRemaining_ != 0) {
+ LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
+ << " and expected zero";
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "numAccessControlProfileRemaining_ is not zero"));
+ }
+
+ if (remainingEntryCounts_.size() == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
+ }
+
+ // Handle initial beginEntry() call.
+ if (entryNameSpace_ == "") {
+ entryNameSpace_ = nameSpace;
+ }
+
+ // If the namespace changed...
+ if (nameSpace != entryNameSpace_) {
+ // Then check that all entries in the previous namespace have been added..
+ if (remainingEntryCounts_[0] != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "New namespace but a non-zero number of entries remain to be added"));
+ }
+ remainingEntryCounts_.erase(remainingEntryCounts_.begin());
+
+ if (signedDataCurrentNamespace_.size() > 0) {
+ signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
+ signedDataCurrentNamespace_ = cppbor::Array();
+ }
+ } else {
+ // Same namespace...
+ if (remainingEntryCounts_[0] == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Same namespace but no entries remain to be added"));
+ }
+ remainingEntryCounts_[0] -= 1;
+ }
+
+ entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
+
+ entryRemainingBytes_ = entrySize;
+ entryNameSpace_ = nameSpace;
+ entryName_ = name;
+ entryAccessControlProfileIds_ = accessControlProfileIds;
+ entryBytes_.resize(0);
+ // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<int8_t>& contentS,
+ vector<int8_t>* outEncryptedContent) {
+ auto content = byteStringToUnsigned(contentS);
+ size_t contentSize = content.size();
+
+ if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Passed in chunk of is bigger than kGcmChunkSize"));
+ }
+ if (contentSize > entryRemainingBytes_) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Passed in chunk is bigger than remaining space"));
+ }
+
+ entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
+ entryRemainingBytes_ -= contentSize;
+ if (entryRemainingBytes_ > 0) {
+ if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved non-final chunk which isn't kGcmChunkSize"));
+ }
+ }
+
+ optional<vector<uint8_t>> nonce = support::getRandom(12);
+ if (!nonce) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce"));
+ }
+ optional<vector<uint8_t>> encryptedContent =
+ support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
+ if (!encryptedContent) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content"));
+ }
+
+ if (entryRemainingBytes_ == 0) {
+ // TODO: ideally do do this without parsing the data (but still validate data is valid
+ // CBOR).
+ auto [item, _, message] = cppbor::parse(entryBytes_);
+ if (item == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
+ }
+ cppbor::Map entryMap;
+ entryMap.add("name", entryName_);
+ entryMap.add("value", std::move(item));
+ cppbor::Array profileIdArray;
+ for (auto id : entryAccessControlProfileIds_) {
+ profileIdArray.add(id);
+ }
+ entryMap.add("accessControlProfiles", std::move(profileIdArray));
+ signedDataCurrentNamespace_.add(std::move(entryMap));
+ }
+
+ *outEncryptedContent = byteStringToSigned(encryptedContent.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
+// |credentialPrivKey|.
+static bool generateCredentialKeys(const vector<uint8_t>& storageKey,
+ const vector<uint8_t>& credentialPrivKey,
+ vector<uint8_t>& credentialKeys) {
+ if (storageKey.size() != 16) {
+ LOG(ERROR) << "Size of storageKey is not 16";
+ return false;
+ }
+
+ cppbor::Array array;
+ array.add(cppbor::Bstr(storageKey));
+ array.add(cppbor::Bstr(credentialPrivKey));
+ credentialKeys = array.encode();
+ return true;
+}
+
+// Writes CBOR-encoded structure to |credentialData| containing |docType|,
+// |testCredential| and |credentialKeys|. The latter element will be stored in
+// encrypted form, using |hardwareBoundKey| as the encryption key.
+bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType,
+ bool testCredential, const vector<uint8_t>& credentialKeys,
+ vector<uint8_t>& credentialData) {
+ optional<vector<uint8_t>> nonce = support::getRandom(12);
+ if (!nonce) {
+ LOG(ERROR) << "Error getting random";
+ return false;
+ }
+ vector<uint8_t> docTypeAsVec(docType.begin(), docType.end());
+ optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm(
+ hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec);
+ if (!credentialBlob) {
+ LOG(ERROR) << "Error encrypting CredentialKeys blob";
+ return false;
+ }
+
+ cppbor::Array array;
+ array.add(docType);
+ array.add(testCredential);
+ array.add(cppbor::Bstr(credentialBlob.value()));
+ credentialData = array.encode();
+ return true;
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
+ vector<int8_t>* outCredentialData, vector<int8_t>* outProofOfProvisioningSignature) {
+ if (signedDataCurrentNamespace_.size() > 0) {
+ signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
+ }
+ cppbor::Array popArray;
+ popArray.add("ProofOfProvisioning")
+ .add(docType_)
+ .add(std::move(signedDataAccessControlProfiles_))
+ .add(std::move(signedDataNamespaces_))
+ .add(testCredential_);
+ vector<uint8_t> encodedCbor = popArray.encode();
+
+ optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
+ encodedCbor, // payload
+ {}, // additionalData
+ {}); // certificateChain
+ if (!signature) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
+ }
+
+ vector<uint8_t> credentialKeys;
+ if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialKeys"));
+ }
+
+ vector<uint8_t> credentialData;
+ if (!generateCredentialData(
+ testCredential_ ? support::getTestHardwareBoundKey() : getHardwareBoundKey(),
+ docType_, testCredential_, credentialKeys, credentialData)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialData"));
+ }
+
+ *outCredentialData = byteStringToSigned(credentialData);
+ *outProofOfProvisioningSignature = byteStringToSigned(signature.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/WritableIdentityCredential.h b/identity/aidl/default/WritableIdentityCredential.h
new file mode 100644
index 0000000..b380f89
--- /dev/null
+++ b/identity/aidl/default/WritableIdentityCredential.h
@@ -0,0 +1,90 @@
+/*
+ * 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 ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
+#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
+
+#include <aidl/android/hardware/identity/BnWritableIdentityCredential.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <cppbor.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::std::string;
+using ::std::vector;
+
+class WritableIdentityCredential : public BnWritableIdentityCredential {
+ public:
+ WritableIdentityCredential(const string& docType, bool testCredential)
+ : docType_(docType), testCredential_(testCredential) {}
+
+ // Creates the Credential Key. Returns false on failure. Must be called
+ // right after construction.
+ bool initialize();
+
+ // Methods from IWritableIdentityCredential follow.
+ ndk::ScopedAStatus getAttestationCertificate(const vector<int8_t>& attestationApplicationId,
+ const vector<int8_t>& attestationChallenge,
+ vector<Certificate>* outCertificateChain) override;
+
+ ndk::ScopedAStatus startPersonalization(int32_t accessControlProfileCount,
+ const vector<int32_t>& entryCounts) override;
+
+ ndk::ScopedAStatus addAccessControlProfile(
+ int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
+ int64_t timeoutMillis, int64_t secureUserId,
+ SecureAccessControlProfile* outSecureAccessControlProfile) override;
+
+ ndk::ScopedAStatus beginAddEntry(const vector<int32_t>& accessControlProfileIds,
+ const string& nameSpace, const string& name,
+ int32_t entrySize) override;
+
+ ndk::ScopedAStatus addEntryValue(const vector<int8_t>& content,
+ vector<int8_t>* outEncryptedContent) override;
+
+ ndk::ScopedAStatus finishAddingEntries(
+ vector<int8_t>* outCredentialData,
+ vector<int8_t>* outProofOfProvisioningSignature) override;
+
+ // private:
+ string docType_;
+ bool testCredential_;
+
+ // These are set in initialize().
+ vector<uint8_t> storageKey_;
+ vector<uint8_t> credentialPrivKey_;
+ vector<uint8_t> credentialPubKey_;
+
+ // These fields are initialized during startPersonalization()
+ size_t numAccessControlProfileRemaining_;
+ vector<int32_t> remainingEntryCounts_;
+ cppbor::Array signedDataAccessControlProfiles_;
+ cppbor::Map signedDataNamespaces_;
+ cppbor::Array signedDataCurrentNamespace_;
+
+ // These fields are initialized during beginAddEntry()
+ size_t entryRemainingBytes_;
+ vector<uint8_t> entryAdditionalData_;
+ string entryNameSpace_;
+ string entryName_;
+ vector<int32_t> entryAccessControlProfileIds_;
+ vector<uint8_t> entryBytes_;
+};
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
diff --git a/identity/aidl/default/identity-default.rc b/identity/aidl/default/identity-default.rc
new file mode 100644
index 0000000..d3b62c1
--- /dev/null
+++ b/identity/aidl/default/identity-default.rc
@@ -0,0 +1,3 @@
+service vendor.identity-default /vendor/bin/hw/android.hardware.identity-service.example
+ class hal
+ user nobody
diff --git a/identity/aidl/default/identity-default.xml b/identity/aidl/default/identity-default.xml
new file mode 100644
index 0000000..a47d354
--- /dev/null
+++ b/identity/aidl/default/identity-default.xml
@@ -0,0 +1,9 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.identity</name>
+ <interface>
+ <name>IIdentityCredentialStore</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/identity/aidl/default/service.cpp b/identity/aidl/default/service.cpp
new file mode 100644
index 0000000..f05c615
--- /dev/null
+++ b/identity/aidl/default/service.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.identity-service"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "IdentityCredentialStore.h"
+
+using aidl::android::hardware::identity::IdentityCredentialStore;
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<IdentityCredentialStore> store =
+ ndk::SharedRefBase::make<IdentityCredentialStore>();
+
+ const std::string instance = std::string() + IdentityCredentialStore::descriptor + "/default";
+ LOG(INFO) << "instance: " << instance;
+ binder_status_t status = AServiceManager_addService(store->asBinder().get(), instance.c_str());
+ CHECK(status == STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
new file mode 100644
index 0000000..21ff440
--- /dev/null
+++ b/identity/aidl/vts/Android.bp
@@ -0,0 +1,21 @@
+cc_test {
+ name: "VtsHalIdentityTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["VtsHalIdentityTargetTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libcppbor",
+ "android.hardware.identity-support-lib",
+ ],
+ static_libs: [
+ "android.hardware.identity-cpp",
+ "android.hardware.keymaster-cpp",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
+}
diff --git a/identity/aidl/vts/VtsHalIdentityTargetTest.cpp b/identity/aidl/vts/VtsHalIdentityTargetTest.cpp
new file mode 100644
index 0000000..5abe5a2
--- /dev/null
+++ b/identity/aidl/vts/VtsHalIdentityTargetTest.cpp
@@ -0,0 +1,446 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "VtsHalIdentityTargetTest"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+
+namespace android::hardware::identity {
+
+using std::map;
+using std::optional;
+using std::string;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+
+// ---------------------------------------------------------------------------
+// Test Data.
+// ---------------------------------------------------------------------------
+
+struct TestEntryData {
+ TestEntryData(string nameSpace, string name, vector<int32_t> profileIds)
+ : nameSpace(nameSpace), name(name), profileIds(profileIds) {}
+
+ TestEntryData(string nameSpace, string name, const string& value, vector<int32_t> profileIds)
+ : TestEntryData(nameSpace, name, profileIds) {
+ valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
+ }
+ TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
+ vector<int32_t> profileIds)
+ : TestEntryData(nameSpace, name, profileIds) {
+ valueCbor = cppbor::Bstr(value).encode();
+ }
+ TestEntryData(string nameSpace, string name, bool value, vector<int32_t> profileIds)
+ : TestEntryData(nameSpace, name, profileIds) {
+ valueCbor = cppbor::Bool(value).encode();
+ }
+ TestEntryData(string nameSpace, string name, int64_t value, vector<int32_t> profileIds)
+ : TestEntryData(nameSpace, name, profileIds) {
+ if (value >= 0) {
+ valueCbor = cppbor::Uint(value).encode();
+ } else {
+ valueCbor = cppbor::Nint(-value).encode();
+ }
+ }
+
+ string nameSpace;
+ string name;
+ vector<uint8_t> valueCbor;
+ vector<int32_t> profileIds;
+};
+
+struct TestProfile {
+ uint16_t id;
+ vector<uint8_t> readerCertificate;
+ bool userAuthenticationRequired;
+ uint64_t timeoutMillis;
+};
+
+// ----------------------------------------------------------------
+
+class IdentityAidl : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(GetParam().c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ }
+
+ sp<IIdentityCredentialStore> credentialStore_;
+};
+
+TEST_P(IdentityAidl, hardwareInformation) {
+ HardwareInformation info;
+ ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
+ ASSERT_GT(info.credentialStoreName.size(), 0);
+ ASSERT_GT(info.credentialStoreAuthorName.size(), 0);
+ ASSERT_GE(info.dataChunkSize, 256);
+}
+
+TEST_P(IdentityAidl, createAndRetrieveCredential) {
+ // First, generate a key-pair for the reader since its public key will be
+ // part of the request data.
+ optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
+ ASSERT_TRUE(readerKeyPKCS8);
+ optional<vector<uint8_t>> readerPublicKey =
+ support::ecKeyPairGetPublicKey(readerKeyPKCS8.value());
+ optional<vector<uint8_t>> readerKey = support::ecKeyPairGetPrivateKey(readerKeyPKCS8.value());
+ string serialDecimal = "1234";
+ string issuer = "Android Open Source Project";
+ string subject = "Android IdentityCredential VTS Test";
+ time_t validityNotBefore = time(nullptr);
+ time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+ optional<vector<uint8_t>> readerCertificate = support::ecPublicKeyGenerateCertificate(
+ readerPublicKey.value(), readerKey.value(), serialDecimal, issuer, subject,
+ validityNotBefore, validityNotAfter);
+ ASSERT_TRUE(readerCertificate);
+
+ // Make the portrait image really big (just shy of 256 KiB) to ensure that
+ // the chunking code gets exercised.
+ vector<uint8_t> portraitImage;
+ portraitImage.resize(256 * 1024 - 10);
+ for (size_t n = 0; n < portraitImage.size(); n++) {
+ portraitImage[n] = (uint8_t)n;
+ }
+
+ // Access control profiles:
+ const vector<TestProfile> testProfiles = {// Profile 0 (reader authentication)
+ {0, readerCertificate.value(), false, 0},
+ // Profile 1 (no authentication)
+ {1, {}, false, 0}};
+
+ HardwareAuthToken authToken;
+
+ // Here's the actual test data:
+ const vector<TestEntryData> testEntries = {
+ {"PersonalData", "Last name", string("Turing"), vector<int32_t>{0, 1}},
+ {"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
+ {"PersonalData", "First name", string("Alan"), vector<int32_t>{0, 1}},
+ {"PersonalData", "Home address", string("Maida Vale, London, England"),
+ vector<int32_t>{0}},
+ {"Image", "Portrait image", portraitImage, vector<int32_t>{0, 1}},
+ };
+ const vector<int32_t> testEntriesEntryCounts = {static_cast<int32_t>(testEntries.size() - 1),
+ 1u};
+ HardwareInformation hwInfo;
+ ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
+
+ string cborPretty;
+ sp<IWritableIdentityCredential> writableCredential;
+ string docType = "org.iso.18013-5.2019.mdl";
+ bool testCredential = true;
+ ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &writableCredential)
+ .isOk());
+ ASSERT_NE(writableCredential, nullptr);
+
+ string challenge = "attestationChallenge";
+ // TODO: set it to something random and check it's in the cert chain
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
+ vector<Certificate> attestationCertificates;
+ ASSERT_TRUE(writableCredential
+ ->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &attestationCertificates)
+ .isOk());
+ ASSERT_GE(attestationCertificates.size(), 2);
+
+ ASSERT_TRUE(
+ writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
+ .isOk());
+
+ vector<SecureAccessControlProfile> returnedSecureProfiles;
+ for (const auto& testProfile : testProfiles) {
+ SecureAccessControlProfile profile;
+ Certificate cert;
+ cert.encodedCertificate = testProfile.readerCertificate;
+ ASSERT_TRUE(writableCredential
+ ->addAccessControlProfile(testProfile.id, cert,
+ testProfile.userAuthenticationRequired,
+ testProfile.timeoutMillis,
+ 0, // secureUserId
+ &profile)
+ .isOk());
+ ASSERT_EQ(testProfile.id, profile.id);
+ ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate.encodedCertificate);
+ ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
+ ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
+ ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
+ returnedSecureProfiles.push_back(profile);
+ }
+
+ // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
+ // is a little hacky but it works well enough.
+ map<const TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
+
+ for (const auto& entry : testEntries) {
+ vector<vector<uint8_t>> chunks =
+ support::chunkVector(entry.valueCbor, hwInfo.dataChunkSize);
+
+ ASSERT_TRUE(writableCredential
+ ->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
+ entry.valueCbor.size())
+ .isOk());
+
+ vector<vector<uint8_t>> encryptedChunks;
+ for (const auto& chunk : chunks) {
+ vector<uint8_t> encryptedChunk;
+ ASSERT_TRUE(writableCredential->addEntryValue(chunk, &encryptedChunk).isOk());
+ encryptedChunks.push_back(encryptedChunk);
+ }
+ encryptedBlobs[&entry] = encryptedChunks;
+ }
+
+ vector<uint8_t> credentialData;
+ vector<uint8_t> proofOfProvisioningSignature;
+ ASSERT_TRUE(
+ writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
+ .isOk());
+
+ optional<vector<uint8_t>> proofOfProvisioning =
+ support::coseSignGetPayload(proofOfProvisioningSignature);
+ ASSERT_TRUE(proofOfProvisioning);
+ cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
+ EXPECT_EQ(
+ "[\n"
+ " 'ProofOfProvisioning',\n"
+ " 'org.iso.18013-5.2019.mdl',\n"
+ " [\n"
+ " {\n"
+ " 'id' : 0,\n"
+ " 'readerCertificate' : <not printed>,\n"
+ " },\n"
+ " {\n"
+ " 'id' : 1,\n"
+ " },\n"
+ " ],\n"
+ " {\n"
+ " 'PersonalData' : [\n"
+ " {\n"
+ " 'name' : 'Last name',\n"
+ " 'value' : 'Turing',\n"
+ " 'accessControlProfiles' : [0, 1, ],\n"
+ " },\n"
+ " {\n"
+ " 'name' : 'Birth date',\n"
+ " 'value' : '19120623',\n"
+ " 'accessControlProfiles' : [0, 1, ],\n"
+ " },\n"
+ " {\n"
+ " 'name' : 'First name',\n"
+ " 'value' : 'Alan',\n"
+ " 'accessControlProfiles' : [0, 1, ],\n"
+ " },\n"
+ " {\n"
+ " 'name' : 'Home address',\n"
+ " 'value' : 'Maida Vale, London, England',\n"
+ " 'accessControlProfiles' : [0, ],\n"
+ " },\n"
+ " ],\n"
+ " 'Image' : [\n"
+ " {\n"
+ " 'name' : 'Portrait image',\n"
+ " 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
+ " 'accessControlProfiles' : [0, 1, ],\n"
+ " },\n"
+ " ],\n"
+ " },\n"
+ " true,\n"
+ "]",
+ cborPretty);
+
+ optional<vector<uint8_t>> credentialPubKey =
+ support::certificateChainGetTopMostKey(attestationCertificates[0].encodedCertificate);
+ ASSERT_TRUE(credentialPubKey);
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+ {}, // Additional data
+ credentialPubKey.value()));
+ writableCredential = nullptr;
+
+ // Now that the credential has been provisioned, read it back and check the
+ // correct data is returned.
+ sp<IIdentityCredential> credential;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData, &credential)
+ .isOk());
+ ASSERT_NE(credential, nullptr);
+
+ optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
+ ASSERT_TRUE(readerEphemeralKeyPair);
+ optional<vector<uint8_t>> readerEphemeralPublicKey =
+ support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
+ ASSERT_TRUE(credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value()).isOk());
+
+ vector<uint8_t> ephemeralKeyPair;
+ ASSERT_TRUE(credential->createEphemeralKeyPair(&ephemeralKeyPair).isOk());
+ optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
+
+ // Calculate requestData field and sign it with the reader key.
+ auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
+ ASSERT_TRUE(getXYSuccess);
+ cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
+ vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
+ vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
+ cppbor::Array sessionTranscript = cppbor::Array()
+ .add(cppbor::Semantic(24, deviceEngagementBytes))
+ .add(cppbor::Semantic(24, eReaderPubBytes));
+ vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
+
+ vector<uint8_t> itemsRequestBytes =
+ cppbor::Map("nameSpaces",
+ cppbor::Map()
+ .add("PersonalData", cppbor::Map()
+ .add("Last name", false)
+ .add("Birth date", false)
+ .add("First name", false)
+ .add("Home address", true))
+ .add("Image", cppbor::Map().add("Portrait image", false)))
+ .encode();
+ cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
+ EXPECT_EQ(
+ "{\n"
+ " 'nameSpaces' : {\n"
+ " 'PersonalData' : {\n"
+ " 'Last name' : false,\n"
+ " 'Birth date' : false,\n"
+ " 'First name' : false,\n"
+ " 'Home address' : true,\n"
+ " },\n"
+ " 'Image' : {\n"
+ " 'Portrait image' : false,\n"
+ " },\n"
+ " },\n"
+ "}",
+ cborPretty);
+ vector<uint8_t> dataToSign = cppbor::Array()
+ .add("ReaderAuthentication")
+ .add(sessionTranscript.clone())
+ .add(cppbor::Semantic(24, itemsRequestBytes))
+ .encode();
+ optional<vector<uint8_t>> readerSignature =
+ support::coseSignEcDsa(readerKey.value(), {}, // content
+ dataToSign, // detached content
+ readerCertificate.value());
+ ASSERT_TRUE(readerSignature);
+
+ ASSERT_TRUE(credential
+ ->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes,
+ sessionTranscriptBytes, readerSignature.value(),
+ testEntriesEntryCounts)
+ .isOk());
+
+ for (const auto& entry : testEntries) {
+ ASSERT_TRUE(credential
+ ->startRetrieveEntryValue(entry.nameSpace, entry.name,
+ entry.valueCbor.size(), entry.profileIds)
+ .isOk());
+
+ auto it = encryptedBlobs.find(&entry);
+ ASSERT_NE(it, encryptedBlobs.end());
+ const vector<vector<uint8_t>>& encryptedChunks = it->second;
+
+ vector<uint8_t> content;
+ for (const auto& encryptedChunk : encryptedChunks) {
+ vector<uint8_t> chunk;
+ ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk());
+ content.insert(content.end(), chunk.begin(), chunk.end());
+ }
+ EXPECT_EQ(content, entry.valueCbor);
+ }
+
+ // Generate the key that will be used to sign AuthenticatedData.
+ vector<uint8_t> signingKeyBlob;
+ Certificate signingKeyCertificate;
+ ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+
+ vector<uint8_t> mac;
+ vector<uint8_t> deviceNameSpacesBytes;
+ ASSERT_TRUE(credential->finishRetrieval(signingKeyBlob, &mac, &deviceNameSpacesBytes).isOk());
+ cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
+ ASSERT_EQ(
+ "{\n"
+ " 'PersonalData' : {\n"
+ " 'Last name' : 'Turing',\n"
+ " 'Birth date' : '19120623',\n"
+ " 'First name' : 'Alan',\n"
+ " 'Home address' : 'Maida Vale, London, England',\n"
+ " },\n"
+ " 'Image' : {\n"
+ " 'Portrait image' : <bstr size=262134 "
+ "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
+ " },\n"
+ "}",
+ cborPretty);
+ // The data that is MACed is ["DeviceAuthentication", sessionTranscriptBytes, docType,
+ // deviceNameSpacesBytes] so build up that structure
+ cppbor::Array deviceAuthentication;
+ deviceAuthentication.add("DeviceAuthentication");
+ deviceAuthentication.add(sessionTranscript.clone());
+ deviceAuthentication.add(docType);
+ deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
+ vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
+ optional<vector<uint8_t>> signingPublicKey =
+ support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
+ EXPECT_TRUE(signingPublicKey);
+
+ // Derive the key used for MACing.
+ optional<vector<uint8_t>> readerEphemeralPrivateKey =
+ support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
+ optional<vector<uint8_t>> sharedSecret =
+ support::ecdh(signingPublicKey.value(), readerEphemeralPrivateKey.value());
+ ASSERT_TRUE(sharedSecret);
+ vector<uint8_t> salt = {0x00};
+ vector<uint8_t> info = {};
+ optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
+ ASSERT_TRUE(derivedKey);
+ optional<vector<uint8_t>> calculatedMac =
+ support::coseMac0(derivedKey.value(), {}, // payload
+ encodedDeviceAuthentication); // detached content
+ ASSERT_TRUE(calculatedMac);
+ EXPECT_EQ(mac, calculatedMac);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ Identity, IdentityAidl,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+// INSTANTIATE_TEST_SUITE_P(Identity, IdentityAidl,
+// testing::Values("android.hardware.identity.IIdentityCredentialStore/default"));
+
+} // namespace android::hardware::identity
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ ::android::ProcessState::self()->startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/identity/support/Android.bp b/identity/support/Android.bp
index 38dc10b..7b4546b 100644
--- a/identity/support/Android.bp
+++ b/identity/support/Android.bp
@@ -23,7 +23,6 @@
"include",
],
shared_libs: [
- "android.hardware.identity@1.0",
"libcrypto",
"libbase",
"libhidlbase",
@@ -41,7 +40,6 @@
],
shared_libs: [
"android.hardware.identity-support-lib",
- "android.hardware.identity@1.0",
"libcrypto",
"libbase",
"libhidlbase",
diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
index 485571a..4533ad9 100644
--- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
+++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
@@ -18,12 +18,11 @@
#define IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_
#include <cstdint>
+#include <optional>
#include <string>
#include <tuple>
#include <vector>
-#include <android/hardware/identity/1.0/types.h>
-
namespace android {
namespace hardware {
namespace identity {
@@ -34,10 +33,6 @@
using ::std::tuple;
using ::std::vector;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
-
// ---------------------------------------------------------------------------
// Miscellaneous utilities.
// ---------------------------------------------------------------------------
@@ -258,21 +253,11 @@
const vector<uint8_t>& detachedContent);
// ---------------------------------------------------------------------------
-// Platform abstraction.
-// ---------------------------------------------------------------------------
-
-// Returns the hardware-bound AES-128 key.
-const vector<uint8_t>& getHardwareBoundKey();
-
-// ---------------------------------------------------------------------------
// Utility functions specific to IdentityCredential.
// ---------------------------------------------------------------------------
-// Returns a reference to a Result with code OK and empty message.
-const Result& resultOK();
-
-// Returns a new Result with the given code and message.
-Result result(ResultCode code, const char* format, ...) __attribute__((format(printf, 2, 3)));
+// Returns the testing AES-128 key where all bits are set to 0.
+const vector<uint8_t>& getTestHardwareBoundKey();
// Splits the given bytestring into chunks. If the given vector is smaller or equal to
// |maxChunkSize| a vector with |content| as the only element is returned. Otherwise
@@ -280,21 +265,6 @@
// may be smaller than |maxChunkSize|.
vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize);
-// Calculates the MAC for |profile| using |storageKey|.
-optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
- const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey);
-
-// Checks authenticity of the MAC in |profile| using |storageKey|.
-bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
- const vector<uint8_t>& storageKey);
-
-// Returns the testing AES-128 key where all bits are set to 0.
-const vector<uint8_t>& getTestHardwareBoundKey();
-
-// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method.
-vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
- const vector<uint16_t> accessControlProfileIds);
-
} // namespace support
} // namespace identity
} // namespace hardware
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index 7d93a4b..e2828bf 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -1682,36 +1682,9 @@
}
// ---------------------------------------------------------------------------
-// Platform abstraction.
-// ---------------------------------------------------------------------------
-
-// This is not a very random HBK but that's OK because this is the SW
-// implementation where it can't be kept secret.
-vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
-
-const vector<uint8_t>& getHardwareBoundKey() {
- return hardwareBoundKey;
-}
-
-// ---------------------------------------------------------------------------
// Utility functions specific to IdentityCredential.
// ---------------------------------------------------------------------------
-Result okResult{ResultCode::OK, ""};
-
-const Result& resultOK() {
- return okResult;
-}
-
-Result result(ResultCode code, const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- string str;
- android::base::StringAppendV(&str, format, ap);
- va_end(ap);
- return Result{code, str};
-}
-
vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize) {
vector<vector<uint8_t>> ret;
@@ -1738,56 +1711,6 @@
return ret;
}
-vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) {
- cppbor::Map map;
- map.add("id", profile.id);
-
- if (profile.readerCertificate.size() > 0) {
- map.add("readerCertificate", cppbor::Bstr(profile.readerCertificate));
- }
-
- if (profile.userAuthenticationRequired) {
- map.add("userAuthenticationRequired", profile.userAuthenticationRequired);
- map.add("timeoutMillis", profile.timeoutMillis);
- map.add("secureUserId", profile.secureUserId);
- }
-
- return map.encode();
-}
-
-optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
- const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) {
- vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
-
- optional<vector<uint8_t>> nonce = getRandom(12);
- if (!nonce) {
- return {};
- }
- optional<vector<uint8_t>> macO = encryptAes128Gcm(storageKey, nonce.value(), {}, cborData);
- if (!macO) {
- return {};
- }
- return macO.value();
-}
-
-bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
- const vector<uint8_t>& storageKey) {
- vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
-
- if (profile.mac.size() < kAesGcmIvSize) {
- return false;
- }
- vector<uint8_t> nonce =
- vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + kAesGcmIvSize);
- optional<vector<uint8_t>> mac = encryptAes128Gcm(storageKey, nonce, {}, cborData);
- if (!mac) {
- return false;
- }
- if (mac.value() != vector<uint8_t>(profile.mac)) {
- return false;
- }
- return true;
-}
vector<uint8_t> testHardwareBoundKey = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
@@ -1795,20 +1718,6 @@
return testHardwareBoundKey;
}
-vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
- const vector<uint16_t> accessControlProfileIds) {
- cppbor::Map map;
- map.add("Namespace", nameSpace);
- map.add("Name", name);
-
- cppbor::Array acpIds;
- for (auto id : accessControlProfileIds) {
- acpIds.add(id);
- }
- map.add("AccessControlProfileIds", std::move(acpIds));
- return map.encode();
-}
-
} // namespace support
} // namespace identity
} // namespace hardware
diff --git a/keymaster/aidl/Android.bp b/keymaster/aidl/Android.bp
new file mode 100644
index 0000000..a2d73ead
--- /dev/null
+++ b/keymaster/aidl/Android.bp
@@ -0,0 +1,18 @@
+aidl_interface {
+ name: "android.hardware.keymaster",
+ vendor_available: true,
+ srcs: [
+ "android/hardware/keymaster/*.aidl",
+ ],
+ stability: "vintf",
+ backend: {
+ java: {
+ platform_apis: true,
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
diff --git a/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
new file mode 100644
index 0000000..58602aa
--- /dev/null
+++ b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
@@ -0,0 +1,89 @@
+/*
+ * 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.keymaster;
+
+import android.hardware.keymaster.Timestamp;
+import android.hardware.keymaster.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
+ * Fingerprint, 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 IKeymasterDevice 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; // Secure authenticator ID.
+
+ /**
+ * 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,
+ *
+ * Note that mac is a vec rather than an array, not because it's actually variable-length but
+ * because it could be empty. As documented in the IKeymasterDevice::begin,
+ * IKeymasterDevice::update and IKeymasterDevice::finish doc comments, an empty mac indicates
+ * that this auth token is empty.
+ */
+ byte[] mac;
+}
diff --git a/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl b/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl
new file mode 100644
index 0000000..3141858
--- /dev/null
+++ b/keymaster/aidl/android/hardware/keymaster/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.keymaster;
+
+/**
+ * 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/radio/config/1.3/IRadioConfigIndication.hal b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
similarity index 66%
rename from radio/config/1.3/IRadioConfigIndication.hal
rename to keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
index 9ef496c..4b2f108 100644
--- a/radio/config/1.3/IRadioConfigIndication.hal
+++ b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,13 +14,9 @@
* limitations under the License.
*/
-package android.hardware.radio.config@1.3;
+package android.hardware.keymaster;
-import @1.2::IRadioConfigIndication;
-
-/**
- * Interface declaring unsolicited radio config indications.
- */
-interface IRadioConfigIndication extends @1.2::IRadioConfigIndication {
-
-};
+@VintfStability
+parcelable Timestamp {
+ long milliSeconds;
+}
diff --git a/neuralnetworks/1.0/vts/functional/Android.bp b/neuralnetworks/1.0/vts/functional/Android.bp
index ba9fb45..03af671 100644
--- a/neuralnetworks/1.0/vts/functional/Android.bp
+++ b/neuralnetworks/1.0/vts/functional/Android.bp
@@ -14,13 +14,30 @@
// limitations under the License.
//
+cc_defaults {
+ name: "neuralnetworks_vts_functional_defaults",
+ defaults: ["VtsHalTargetTestDefaults"],
+ arch: {
+ x86: {
+ cflags: [ "-D_Float16=__fp16",
+ "-Xclang", "-fnative-half-type",
+ "-Xclang", "-fallow-half-arguments-and-returns" ],
+ },
+ x86_64: {
+ cflags: [ "-D_Float16=__fp16",
+ "-Xclang", "-fnative-half-type",
+ "-Xclang", "-fallow-half-arguments-and-returns" ],
+ },
+ },
+}
+
cc_library_static {
name: "VtsHalNeuralNetworksV1_0_utils",
srcs: [
"Callbacks.cpp",
"Utils.cpp",
],
- defaults: ["VtsHalTargetTestDefaults"],
+ defaults: ["neuralnetworks_vts_functional_defaults"],
export_include_dirs: ["include"],
shared_libs: [
"libfmq",
@@ -42,7 +59,7 @@
cc_test {
name: "VtsHalNeuralnetworksV1_0TargetTest",
- defaults: ["VtsHalTargetTestDefaults"],
+ defaults: ["neuralnetworks_vts_functional_defaults"],
srcs: [
"BasicTests.cpp",
"TestAssertions.cpp",
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
index 595ad85..e28605d 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
@@ -42,10 +42,11 @@
Model createModel(const TestModel& testModel) {
// Model operands.
- hidl_vec<Operand> operands(testModel.operands.size());
+ CHECK_EQ(testModel.referenced.size(), 0u); // Not supported in 1.0.
+ hidl_vec<Operand> operands(testModel.main.operands.size());
size_t constCopySize = 0, constRefSize = 0;
- for (uint32_t i = 0; i < testModel.operands.size(); i++) {
- const auto& op = testModel.operands[i];
+ for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+ const auto& op = testModel.main.operands[i];
DataLocation loc = {};
if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
@@ -70,9 +71,9 @@
}
// Model operations.
- hidl_vec<Operation> operations(testModel.operations.size());
- std::transform(testModel.operations.begin(), testModel.operations.end(), operations.begin(),
- [](const TestOperation& op) -> Operation {
+ hidl_vec<Operation> operations(testModel.main.operations.size());
+ std::transform(testModel.main.operations.begin(), testModel.main.operations.end(),
+ operations.begin(), [](const TestOperation& op) -> Operation {
return {.type = static_cast<OperationType>(op.type),
.inputs = op.inputs,
.outputs = op.outputs};
@@ -80,8 +81,8 @@
// Constant copies.
hidl_vec<uint8_t> operandValues(constCopySize);
- for (uint32_t i = 0; i < testModel.operands.size(); i++) {
- const auto& op = testModel.operands[i];
+ for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+ const auto& op = testModel.main.operands[i];
if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
const uint8_t* begin = op.data.get<uint8_t>();
const uint8_t* end = begin + op.data.size();
@@ -102,8 +103,8 @@
reinterpret_cast<uint8_t*>(static_cast<void*>(mappedMemory->getPointer()));
CHECK(mappedPtr != nullptr);
- for (uint32_t i = 0; i < testModel.operands.size(); i++) {
- const auto& op = testModel.operands[i];
+ for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+ const auto& op = testModel.main.operands[i];
if (op.lifetime == TestOperandLifeTime::CONSTANT_REFERENCE) {
const uint8_t* begin = op.data.get<uint8_t>();
const uint8_t* end = begin + op.data.size();
@@ -114,8 +115,8 @@
return {.operands = std::move(operands),
.operations = std::move(operations),
- .inputIndexes = testModel.inputIndexes,
- .outputIndexes = testModel.outputIndexes,
+ .inputIndexes = testModel.main.inputIndexes,
+ .outputIndexes = testModel.main.outputIndexes,
.operandValues = std::move(operandValues),
.pools = std::move(pools)};
}
diff --git a/neuralnetworks/1.0/vts/functional/Utils.cpp b/neuralnetworks/1.0/vts/functional/Utils.cpp
index 5b630fd..0dba85a 100644
--- a/neuralnetworks/1.0/vts/functional/Utils.cpp
+++ b/neuralnetworks/1.0/vts/functional/Utils.cpp
@@ -42,10 +42,10 @@
Request createRequest(const TestModel& testModel) {
// Model inputs.
- hidl_vec<RequestArgument> inputs(testModel.inputIndexes.size());
+ hidl_vec<RequestArgument> inputs(testModel.main.inputIndexes.size());
size_t inputSize = 0;
- for (uint32_t i = 0; i < testModel.inputIndexes.size(); i++) {
- const auto& op = testModel.operands[testModel.inputIndexes[i]];
+ for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
+ const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
if (op.data.size() == 0) {
// Omitted input.
inputs[i] = {.hasNoValue = true};
@@ -59,10 +59,10 @@
}
// Model outputs.
- hidl_vec<RequestArgument> outputs(testModel.outputIndexes.size());
+ hidl_vec<RequestArgument> outputs(testModel.main.outputIndexes.size());
size_t outputSize = 0;
- for (uint32_t i = 0; i < testModel.outputIndexes.size(); i++) {
- const auto& op = testModel.operands[testModel.outputIndexes[i]];
+ for (uint32_t i = 0; i < testModel.main.outputIndexes.size(); i++) {
+ const auto& op = testModel.main.operands[testModel.main.outputIndexes[i]];
// In the case of zero-sized output, we should at least provide a one-byte buffer.
// This is because zero-sized tensors are only supported internally to the driver, or
@@ -90,8 +90,8 @@
CHECK(inputPtr != nullptr);
// Copy input data to the memory pool.
- for (uint32_t i = 0; i < testModel.inputIndexes.size(); i++) {
- const auto& op = testModel.operands[testModel.inputIndexes[i]];
+ for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
+ const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
if (op.data.size() > 0) {
const uint8_t* begin = op.data.get<uint8_t>();
const uint8_t* end = begin + op.data.size();
diff --git a/neuralnetworks/1.1/vts/functional/Android.bp b/neuralnetworks/1.1/vts/functional/Android.bp
index 69e1761..9ba1925 100644
--- a/neuralnetworks/1.1/vts/functional/Android.bp
+++ b/neuralnetworks/1.1/vts/functional/Android.bp
@@ -16,7 +16,7 @@
cc_test {
name: "VtsHalNeuralnetworksV1_1TargetTest",
- defaults: ["VtsHalTargetTestDefaults"],
+ defaults: ["neuralnetworks_vts_functional_defaults"],
srcs: [
"BasicTests.cpp",
"TestAssertions.cpp",
diff --git a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp
index 7a929d6..cee15a3 100644
--- a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.cpp
@@ -49,10 +49,11 @@
Model createModel(const TestModel& testModel) {
// Model operands.
- hidl_vec<Operand> operands(testModel.operands.size());
+ CHECK_EQ(testModel.referenced.size(), 0u); // Not supported in 1.1.
+ hidl_vec<Operand> operands(testModel.main.operands.size());
size_t constCopySize = 0, constRefSize = 0;
- for (uint32_t i = 0; i < testModel.operands.size(); i++) {
- const auto& op = testModel.operands[i];
+ for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+ const auto& op = testModel.main.operands[i];
DataLocation loc = {};
if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
@@ -77,9 +78,9 @@
}
// Model operations.
- hidl_vec<Operation> operations(testModel.operations.size());
- std::transform(testModel.operations.begin(), testModel.operations.end(), operations.begin(),
- [](const TestOperation& op) -> Operation {
+ hidl_vec<Operation> operations(testModel.main.operations.size());
+ std::transform(testModel.main.operations.begin(), testModel.main.operations.end(),
+ operations.begin(), [](const TestOperation& op) -> Operation {
return {.type = static_cast<OperationType>(op.type),
.inputs = op.inputs,
.outputs = op.outputs};
@@ -87,8 +88,8 @@
// Constant copies.
hidl_vec<uint8_t> operandValues(constCopySize);
- for (uint32_t i = 0; i < testModel.operands.size(); i++) {
- const auto& op = testModel.operands[i];
+ for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+ const auto& op = testModel.main.operands[i];
if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
const uint8_t* begin = op.data.get<uint8_t>();
const uint8_t* end = begin + op.data.size();
@@ -109,8 +110,8 @@
reinterpret_cast<uint8_t*>(static_cast<void*>(mappedMemory->getPointer()));
CHECK(mappedPtr != nullptr);
- for (uint32_t i = 0; i < testModel.operands.size(); i++) {
- const auto& op = testModel.operands[i];
+ for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+ const auto& op = testModel.main.operands[i];
if (op.lifetime == TestOperandLifeTime::CONSTANT_REFERENCE) {
const uint8_t* begin = op.data.get<uint8_t>();
const uint8_t* end = begin + op.data.size();
@@ -121,8 +122,8 @@
return {.operands = std::move(operands),
.operations = std::move(operations),
- .inputIndexes = testModel.inputIndexes,
- .outputIndexes = testModel.outputIndexes,
+ .inputIndexes = testModel.main.inputIndexes,
+ .outputIndexes = testModel.main.outputIndexes,
.operandValues = std::move(operandValues),
.pools = std::move(pools),
.relaxComputationFloat32toFloat16 = testModel.isRelaxed};
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index e867120..f0fd769 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -2314,7 +2314,38 @@
AXIS_ALIGNED_BBOX_TRANSFORM = 41,
/**
- * Performs a forward LSTM on the input followed by a backward LSTM.
+ * A recurrent neural network layer that applies an LSTM cell to a
+ * sequence of inputs in forward and backward directions.
+ *
+ * The op supports cross-linking via an auxiliary input. Regular cell feeds
+ * one input into the two RNN cells in the following way:
+ *
+ * INPUT (INPUT_REVERSED)
+ * | |
+ * ---------------------
+ * | FW_LSTM BW_LSTM |
+ * ---------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
+ * An op with cross-linking takes two inputs and feeds them into the RNN
+ * cells in the following way:
+ *
+ * AUX_INPUT (AUX_INPUT_REVERSED)
+ * | |
+ * INPUT | (INPUT_R'D.)|
+ * | | | |
+ * -----------------------
+ * | \ / \ / |
+ * | FW_LSTM BW_LSTM |
+ * -----------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
+ * The cross-linking mode is enabled iff auxiliary input and auxiliary
+ * weights are present. While stacking this op on top of itself, this
+ * allows to connect both forward and backward outputs from previous cell
+ * to the next cell's input.
*
* Supported tensor {@link OperandType}:
* * {@link OperandType::TENSOR_FLOAT16}
@@ -2324,7 +2355,6 @@
*
* All input and output tensors must be of the same type.
*
- *
* Inputs:
* * 0: The input.
* A 3-D tensor of shape:
@@ -2533,8 +2563,8 @@
* * “activation” is the function passed as the “fused_activation_function”
* argument (if not “NONE”).
*
- * The op also supports an auxiliary input. Regular cell feeds one input
- * into the two RNN cells in the following way:
+ * The op supports cross-linking via an auxiliary input. Regular cell feeds
+ * one input into the two RNN cells in the following way:
*
* INPUT (INPUT_REVERSED)
* | |
@@ -2544,8 +2574,8 @@
* | |
* FW_OUT BW_OUT
*
- * An op with an auxiliary input takes two inputs and feeds them into the
- * RNN cells in the following way:
+ * An op with cross-linking takes two inputs and feeds them into the RNN
+ * cells in the following way:
*
* AUX_INPUT (AUX_INPUT_REVERSED)
* | |
@@ -2558,9 +2588,10 @@
* | |
* FW_OUT BW_OUT
*
- * While stacking this op on top of itself, this allows to connect both
- * forward and backward outputs from previous cell to the next cell's
- * inputs.
+ * The cross-linking mode is enabled iff auxiliary input and auxiliary
+ * weights are present. While stacking this op on top of itself, this
+ * allows to connect both forward and backward outputs from previous cell
+ * to the next cell's input.
*
* Supported tensor {@link OperandType}:
* * {@link OperandType::TENSOR_FLOAT16}
@@ -3165,7 +3196,7 @@
* * 8: An {@link OperandType::INT32} scalar, specifying the stride when
* walking through input in the ‘height’ dimension.
* * 9: An {@link OperandType::INT32} scalar, specifying the number of
- groups.
+ * groups.
* * 10: An {@link OperandType::INT32} scalar, and has to be one of the
* {@link FusedActivationFunc} values. Specifies the activation to
* invoke on the result.
diff --git a/neuralnetworks/1.2/vts/functional/Android.bp b/neuralnetworks/1.2/vts/functional/Android.bp
index fc727b7..31a1a81 100644
--- a/neuralnetworks/1.2/vts/functional/Android.bp
+++ b/neuralnetworks/1.2/vts/functional/Android.bp
@@ -16,7 +16,7 @@
cc_library_static {
name: "VtsHalNeuralNetworksV1_2Callbacks",
- defaults: ["VtsHalTargetTestDefaults"],
+ defaults: ["neuralnetworks_vts_functional_defaults"],
export_include_dirs: ["include"],
srcs: [
"Callbacks.cpp",
@@ -33,7 +33,7 @@
cc_test {
name: "VtsHalNeuralnetworksV1_2TargetTest",
- defaults: ["VtsHalTargetTestDefaults"],
+ defaults: ["neuralnetworks_vts_functional_defaults"],
srcs: [
"BasicTests.cpp",
"CompilationCachingTests.cpp",
@@ -71,5 +71,5 @@
header_libs: [
"libneuralnetworks_headers",
],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "vts-core"],
}
diff --git a/neuralnetworks/1.2/vts/functional/BasicTests.cpp b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
index 8e82c53..58d3c4a 100644
--- a/neuralnetworks/1.2/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
@@ -79,6 +79,18 @@
EXPECT_TRUE(ret.isOk());
}
+// device name test
+TEST_P(NeuralnetworksHidlTest, GetDeviceNameTest) {
+ const std::string deviceName = getName(GetParam());
+ auto pos = deviceName.find('-');
+ EXPECT_NE(pos, std::string::npos);
+ // The separator should not be the first or last character.
+ EXPECT_NE(pos, 0);
+ EXPECT_NE(pos, deviceName.length() - 1);
+ // There should only be 1 separator.
+ EXPECT_EQ(std::string::npos, deviceName.find('-', pos + 1));
+}
+
// device supported extensions test
TEST_P(NeuralnetworksHidlTest, GetDeviceSupportedExtensionsTest) {
Return<void> ret = kDevice->getSupportedExtensions(
diff --git a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
index 2130a76..10dec79 100644
--- a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
@@ -207,10 +207,10 @@
};
return {
- .operands = std::move(operands),
- .operations = std::move(operations),
- .inputIndexes = {1},
- .outputIndexes = {len * 2 + 1},
+ .main = {.operands = std::move(operands),
+ .operations = std::move(operations),
+ .inputIndexes = {1},
+ .outputIndexes = {len * 2 + 1}},
.isRelaxed = false,
};
}
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
index 599fd1d..4c8fede 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
@@ -75,10 +75,11 @@
Model createModel(const TestModel& testModel) {
// Model operands.
- hidl_vec<Operand> operands(testModel.operands.size());
+ CHECK_EQ(testModel.referenced.size(), 0u); // Not supported in 1.1.
+ hidl_vec<Operand> operands(testModel.main.operands.size());
size_t constCopySize = 0, constRefSize = 0;
- for (uint32_t i = 0; i < testModel.operands.size(); i++) {
- const auto& op = testModel.operands[i];
+ for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+ const auto& op = testModel.main.operands[i];
DataLocation loc = {};
if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
@@ -110,9 +111,9 @@
}
// Model operations.
- hidl_vec<Operation> operations(testModel.operations.size());
- std::transform(testModel.operations.begin(), testModel.operations.end(), operations.begin(),
- [](const TestOperation& op) -> Operation {
+ hidl_vec<Operation> operations(testModel.main.operations.size());
+ std::transform(testModel.main.operations.begin(), testModel.main.operations.end(),
+ operations.begin(), [](const TestOperation& op) -> Operation {
return {.type = static_cast<OperationType>(op.type),
.inputs = op.inputs,
.outputs = op.outputs};
@@ -120,8 +121,8 @@
// Constant copies.
hidl_vec<uint8_t> operandValues(constCopySize);
- for (uint32_t i = 0; i < testModel.operands.size(); i++) {
- const auto& op = testModel.operands[i];
+ for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+ const auto& op = testModel.main.operands[i];
if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
const uint8_t* begin = op.data.get<uint8_t>();
const uint8_t* end = begin + op.data.size();
@@ -142,8 +143,8 @@
reinterpret_cast<uint8_t*>(static_cast<void*>(mappedMemory->getPointer()));
CHECK(mappedPtr != nullptr);
- for (uint32_t i = 0; i < testModel.operands.size(); i++) {
- const auto& op = testModel.operands[i];
+ for (uint32_t i = 0; i < testModel.main.operands.size(); i++) {
+ const auto& op = testModel.main.operands[i];
if (op.lifetime == TestOperandLifeTime::CONSTANT_REFERENCE) {
const uint8_t* begin = op.data.get<uint8_t>();
const uint8_t* end = begin + op.data.size();
@@ -154,15 +155,15 @@
return {.operands = std::move(operands),
.operations = std::move(operations),
- .inputIndexes = testModel.inputIndexes,
- .outputIndexes = testModel.outputIndexes,
+ .inputIndexes = testModel.main.inputIndexes,
+ .outputIndexes = testModel.main.outputIndexes,
.operandValues = std::move(operandValues),
.pools = std::move(pools),
.relaxComputationFloat32toFloat16 = testModel.isRelaxed};
}
static bool isOutputSizeGreaterThanOne(const TestModel& testModel, uint32_t index) {
- const auto byteSize = testModel.operands[testModel.outputIndexes[index]].data.size();
+ const auto byteSize = testModel.main.operands[testModel.main.outputIndexes[index]].data.size();
return byteSize > 1u;
}
@@ -302,17 +303,17 @@
// either empty, or have the same number of elements as the number of outputs.
ASSERT_EQ(ErrorStatus::NONE, executionStatus);
ASSERT_TRUE(outputShapes.size() == 0 ||
- outputShapes.size() == testModel.outputIndexes.size());
+ outputShapes.size() == testModel.main.outputIndexes.size());
break;
case OutputType::UNSPECIFIED:
// If the model output operands are not fully specified, outputShapes must have
// the same number of elements as the number of outputs.
ASSERT_EQ(ErrorStatus::NONE, executionStatus);
- ASSERT_EQ(outputShapes.size(), testModel.outputIndexes.size());
+ ASSERT_EQ(outputShapes.size(), testModel.main.outputIndexes.size());
break;
case OutputType::INSUFFICIENT:
ASSERT_EQ(ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, executionStatus);
- ASSERT_EQ(outputShapes.size(), testModel.outputIndexes.size());
+ ASSERT_EQ(outputShapes.size(), testModel.main.outputIndexes.size());
ASSERT_FALSE(outputShapes[0].isSufficient);
return;
}
@@ -320,7 +321,7 @@
// Go through all outputs, check returned output shapes.
for (uint32_t i = 0; i < outputShapes.size(); i++) {
EXPECT_TRUE(outputShapes[i].isSufficient);
- const auto& expect = testModel.operands[testModel.outputIndexes[i]].dimensions;
+ const auto& expect = testModel.main.operands[testModel.main.outputIndexes[i]].dimensions;
const std::vector<uint32_t> actual = outputShapes[i].dimensions;
EXPECT_EQ(expect, actual);
}
diff --git a/neuralnetworks/1.3/IDevice.hal b/neuralnetworks/1.3/IDevice.hal
index 610db79..e0b04a8 100644
--- a/neuralnetworks/1.3/IDevice.hal
+++ b/neuralnetworks/1.3/IDevice.hal
@@ -48,19 +48,6 @@
getCapabilities_1_3() generates (ErrorStatus status, Capabilities capabilities);
/**
- * Returns whether the device is able to complete or abort a task within a
- * specified duration.
- *
- * @return prepareModelDeadline 'true' if the device supports completing or
- * aborting model preparation by the deadline when the deadline is supplied,
- * 'false' otherwise.
- * @return executionDeadline 'true' if the device supports completing or
- * aborting an execution by the deadline when the deadline is supplied,
- * 'false' otherwise.
- */
- supportsDeadlines() generates (bool prepareModelDeadline, bool executionDeadline);
-
- /**
* Gets the supported operations in a model.
*
* getSupportedOperations indicates which operations of the top-level
@@ -140,14 +127,10 @@
*
* prepareModel_1_3 can be called with an optional deadline. If the model
* is not able to be prepared before the provided deadline, the model
- * preparation must be aborted, and either {@link
+ * preparation may be aborted, and either {@link
* ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
- * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
- * If the service reports that it does not support preparation deadlines via
- * IDevice::supportsDeadlines, and prepareModel_1_3 is called with a
- * deadline, then the argument is invalid, and {@link
- * ErrorStatus::INVALID_ARGUMENT} must be returned.
*
* Optionally, the driver may save the prepared model to cache during the
* asynchronous preparation. Any error that occurs when saving to cache must
@@ -172,9 +155,9 @@
* model.
* @param priority The priority of the prepared model relative to other
* prepared models owned by the client.
- * @param deadline The time by which the model must be prepared. If the
- * model cannot be prepared by the deadline, the preparation must be
- * aborted.
+ * @param deadline The time by which the model is expected to be prepared.
+ * If the model cannot be prepared by the deadline, the preparation may
+ * be aborted.
* @param modelCache A vector of handles with each entry holding exactly one
* cache file descriptor for the security-sensitive cache. The length of
* the vector must either be 0 indicating that caching information is
@@ -209,8 +192,8 @@
* - GENERAL_FAILURE if there is an unspecified error
* - INVALID_ARGUMENT if one of the input arguments related to preparing
* the model is invalid
- * - MISSED_DEADLINE_* if the deadline for preparing a model cannot be
- * met
+ * - MISSED_DEADLINE_* if the preparation is aborted because the model
+ * cannot be prepared by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
prepareModel_1_3(Model model, ExecutionPreference preference,
@@ -260,21 +243,13 @@
* the model, the callback object must be invoked with the appropriate
* ErrorStatus value and nullptr for the IPreparedModel.
*
- * The model is prepared with a priority. This priority is relative to other
- * prepared models owned by the same client. Higher priority executions may
- * use more compute resources than lower priority executions, and may
- * preempt or starve lower priority executions.
- *
* prepareModelFromCache_1_3 can be called with an optional deadline. If the
* model is not able to prepared before the provided deadline, the model
- * preparation must be aborted, and either {@link
+ * preparation may be aborted, and either {@link
* ErrorStatus::MISSED_DEADLINE_TRANSIENT}
- * or {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The
+ * or {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The
* error due to an abort must be sent the same way as other errors,
- * described above. If the service reports that it does not support
- * preparation deadlines via IDevice::supportsDeadlines, and
- * prepareModelFromCache_1_3 is called with a deadline, then the argument is
- * invalid, and {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
+ * described above.
*
* The only information that may be unknown to the model at this stage is
* the shape of the tensors, which may only be known at execution time. As
@@ -284,11 +259,9 @@
* used with different shapes of inputs on different (possibly concurrent)
* executions.
*
- * @param priority The priority of the prepared model relative to other
- * prepared models owned by the client.
- * @param deadline The time by which the model must be prepared. If the
- * model cannot be prepared by the deadline, the preparation must be
- * aborted.
+ * @param deadline The time by which the model is expected to be prepared.
+ * If the model cannot be prepared by the deadline, the preparation may
+ * be aborted.
* @param modelCache A vector of handles with each entry holding exactly one
* cache file descriptor for the security-sensitive cache. The length of
* the vector must match the numModelCache returned from getNumberOfCacheFilesNeeded.
@@ -314,11 +287,11 @@
* - GENERAL_FAILURE if caching is not supported or if there is an
* unspecified error
* - INVALID_ARGUMENT if one of the input arguments is invalid
- * - MISSED_DEADLINE_* if the deadline for preparing a model cannot be
- * met
+ * - MISSED_DEADLINE_* if the preparation is aborted because the model
+ * cannot be prepared by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
- prepareModelFromCache_1_3(Priority priority, OptionalTimePoint deadline,
+ prepareModelFromCache_1_3(OptionalTimePoint deadline,
vec<handle> modelCache, vec<handle> dataCache,
uint8_t[Constant:BYTE_SIZE_OF_CACHE_TOKEN] token,
IPreparedModelCallback callback)
@@ -379,5 +352,5 @@
*/
allocate(BufferDesc desc, vec<IPreparedModel> preparedModels, vec<BufferRole> inputRoles,
vec<BufferRole> outputRoles)
- generates (ErrorStatus status, IBuffer buffer, int32_t token);
+ generates (ErrorStatus status, IBuffer buffer, uint32_t token);
};
diff --git a/neuralnetworks/1.3/IExecutionCallback.hal b/neuralnetworks/1.3/IExecutionCallback.hal
index 439428a..ea11b17 100644
--- a/neuralnetworks/1.3/IExecutionCallback.hal
+++ b/neuralnetworks/1.3/IExecutionCallback.hal
@@ -47,7 +47,8 @@
* corresponding output
* - INVALID_ARGUMENT if one of the input arguments to
* prepareModel is invalid
- * - MISSED_DEADLINE_* if the deadline could not be met
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
* @param outputShapes A list of shape information of model output operands.
* The index into "outputShapes" corresponds with to index
diff --git a/neuralnetworks/1.3/IFencedExecutionCallback.hal b/neuralnetworks/1.3/IFencedExecutionCallback.hal
index 39076b9..949438e 100644
--- a/neuralnetworks/1.3/IFencedExecutionCallback.hal
+++ b/neuralnetworks/1.3/IFencedExecutionCallback.hal
@@ -38,11 +38,24 @@
* - DEVICE_UNAVAILABLE if driver is offline or busy
* - GENERAL_FAILURE if the asynchronous task resulted in an
* unspecified error
- * @return timing Duration of execution. Unless MeasureTiming::YES was passed when
- * launching the execution and status is NONE, all times must
- * be reported as UINT64_MAX. A driver may choose to report
- * any time as UINT64_MAX, indicating that particular measurement is
- * not available.
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the
+ * driver
+ * @return timingLaunched The duration starts when executeFenced is called and ends when
+ * executeFenced signals the returned syncFence.
+ * Unless MeasureTiming::YES was passed when
+ * launching the execution and status is NONE, all times
+ * must be reported as UINT64_MAX. A driver may choose to
+ * report any time as UINT64_MAX, indicating that particular
+ * measurement is not available.
+ * @return timingFenced The duration starts when all waitFor sync fences have been signaled
+ * and ends when executeFenced signals the returned syncFence.
+ * Unless MeasureTiming::YES was passed when
+ * launching the execution and status is NONE, all times
+ * must be reported as UINT64_MAX. A driver may choose to
+ * report any time as UINT64_MAX, indicating that particular
+ * measurement is not available.
*/
- getExecutionInfo() generates (ErrorStatus status, Timing timing);
+ getExecutionInfo() generates (ErrorStatus status, Timing timingLaunched, Timing timingFenced);
};
diff --git a/neuralnetworks/1.3/IPreparedModel.hal b/neuralnetworks/1.3/IPreparedModel.hal
index f84bcf4..a1814b5 100644
--- a/neuralnetworks/1.3/IPreparedModel.hal
+++ b/neuralnetworks/1.3/IPreparedModel.hal
@@ -21,6 +21,7 @@
import @1.2::OutputShape;
import @1.2::Timing;
import ErrorStatus;
+import OptionalTimeoutDuration;
import OptionalTimePoint;
import Request;
import IExecutionCallback;
@@ -68,15 +69,11 @@
* There must be no failure unless the device itself is in a bad state.
*
* execute_1_3 can be called with an optional deadline. If the execution
- * is not able to completed before the provided deadline, the execution
- * must be aborted, and either {@link
+ * is not able to be completed before the provided deadline, the execution
+ * may be aborted, and either {@link
* ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
- * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
- * If the service reports that it does not support execution deadlines via
- * IDevice::supportsDeadlines, and execute_1_3 is called with a deadline,
- * then the argument is invalid, and {@link ErrorStatus::INVALID_ARGUMENT}
- * must be returned.
*
* Any number of calls to the execute* and executeSynchronously* functions,
* in any combination, may be made concurrently, even on the same
@@ -88,9 +85,20 @@
* The duration runs from the time the driver sees the call
* to the execute_1_3 function to the time the driver invokes
* the callback.
- * @param deadline The time by which execution must complete. If the
- * execution cannot be finished by the deadline, the
- * execution must be aborted.
+ * @param deadline The time by which the execution is expected to complete.
+ * If the execution cannot be completed by the deadline, the
+ * execution may be aborted.
+ * @param loopTimeoutDuration The maximum amount of time that should be spent
+ * executing a {@link OperationType::WHILE}
+ * operation. If a loop condition model does not
+ * output false within this duration, the
+ * execution must be aborted. If the model
+ * contains a {@link OperationType::WHILE}
+ * operation and no loop timeout duration is
+ * provided, the maximum amount of time is {@link
+ * LoopTimeoutDurationNs::DEFAULT}. When
+ * provided, the duration must not exceed {@link
+ * LoopTimeoutDurationNs::MAXIMUM}.
* @param callback A callback object used to return the error status of
* the execution, shape information of model output operands, and
* duration of execution. The callback object's notify function must
@@ -104,13 +112,13 @@
* not large enough to store the resultant values
* - INVALID_ARGUMENT if one of the input arguments is
* invalid
- * - MISSED_DEADLINE_* if the deadline for executing a model
- * cannot be met
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the
* driver
*/
execute_1_3(Request request, MeasureTiming measure, OptionalTimePoint deadline,
- IExecutionCallback callback)
+ OptionalTimeoutDuration loopTimeoutDuration, IExecutionCallback callback)
generates (ErrorStatus status);
/**
@@ -138,16 +146,12 @@
* (ErrorStatus::NONE): There must be no failure unless the device itself is
* in a bad state.
*
- * executeSynchronously_1_3 can be called with an optional deadline. If the
- * execution is not able to completed before the provided deadline, the
- * execution must be aborted, and either {@link
+ * executeSynchronously_1_3 may be called with an optional deadline. If the
+ * execution is not able to be completed before the provided deadline, the
+ * execution may be aborted, and either {@link
* ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
- * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
- * If the service reports that it does not support execution deadlines via
- * IDevice::supportsDeadlines, and executeSynchronously_1_3 is called with a
- * deadline, then the argument is invalid, and
- * {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
*
* Any number of calls to the execute* and executeSynchronously* functions,
* in any combination, may be made concurrently, even on the same
@@ -159,9 +163,20 @@
* The duration runs from the time the driver sees the call
* to the executeSynchronously_1_3 function to the time the driver
* returns from the function.
- * @param deadline The time by which execution must complete. If the
- * execution cannot be finished by the deadline, the
- * execution must be aborted.
+ * @param deadline The time by which the execution is expected to complete.
+ * If the execution cannot be finished by the deadline, the
+ * execution may be aborted.
+ * @param loopTimeoutDuration The maximum amount of time that should be spent
+ * executing a {@link OperationType::WHILE}
+ * operation. If a loop condition model does not
+ * output false within this duration, the
+ * execution must be aborted. If the model
+ * contains a {@link OperationType::WHILE}
+ * operation and no loop timeout duration is
+ * provided, the maximum amount of time is {@link
+ * LoopTimeoutDurationNs::DEFAULT}. When
+ * provided, the duration must not exceed {@link
+ * LoopTimeoutDurationNs::MAXIMUM}.
* @return status Error status of the execution, must be:
* - NONE if execution is performed successfully
* - DEVICE_UNAVAILABLE if driver is offline or busy
@@ -171,8 +186,8 @@
* corresponding output
* - INVALID_ARGUMENT if one of the input arguments is
* invalid
- * - MISSED_DEADLINE_* if the deadline for executing a model
- * cannot be met
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the
* driver
* @return outputShapes A list of shape information of model output operands.
@@ -186,7 +201,8 @@
* measurement is not available.
*/
executeSynchronously_1_3(Request request, MeasureTiming measure,
- OptionalTimePoint deadline)
+ OptionalTimePoint deadline,
+ OptionalTimeoutDuration loopTimeoutDuration)
generates (ErrorStatus status, vec<OutputShape> outputShapes,
Timing timing);
@@ -194,52 +210,83 @@
* Launch a fenced asynchronous execution on a prepared model.
*
* The execution is performed asynchronously with respect to the caller.
- * executeFenced must fully validate the request, and only accept one that is
- * guaranteed to be completed, unless a hardware failure or kernel panic happens on the device.
- * If there is an error during validation, executeFenced must immediately return with
- * the corresponding ErrorStatus. If the request is valid and there is no error launching,
- * executeFenced must dispatch an asynchronous task to perform the execution in the
- * background, and immediately return with ErrorStatus::NONE, a sync_fence that will be
- * signaled once the execution is completed, and a callback that can be used by the client
- * to query the duration and runtime error status. If the task has finished
- * before the call returns, empty handle may be returned for the sync fence. If the
- * asynchronous task fails to launch, executeFenced must immediately return with
- * ErrorStatus::GENERAL_FAILURE, and empty handle for the sync fence and nullptr
- * for callback. The execution must wait for all the sync fences (if any) in wait_for to be
- * signaled before starting the actual execution.
- *
- * If any of sync fences in wait_for changes to error status after the executeFenced
- * call succeeds, the driver must immediately set the returned sync fence to error status.
+ * executeFenced must verify the inputs to the function are correct, and the usages
+ * of memory pools allocated by IDevice::allocate are valid. If there is an error,
+ * executeFenced must immediately return with the corresponding ErrorStatus, an empty
+ * handle for syncFence, and nullptr for callback. If the inputs to the function
+ * are valid and there is no error, executeFenced must dispatch an asynchronous task
+ * to perform the execution in the background, and immediately return with
+ * ErrorStatus::NONE, a sync fence that will be signaled once the execution is completed,
+ * and a callback that can be used by the client to query the duration and runtime error
+ * status. If the task has finished before the call returns, an empty handle may be returned
+ * for syncFence. The execution must wait for all the sync fences (if any) in waitFor
+ * to be signaled before starting the actual execution.
*
* When the asynchronous task has finished its execution, it must
- * immediately signal the sync_fence created when dispatching. After
- * the sync_fence is signaled, the task must not modify the content of
+ * immediately signal the syncFence returned from the executeFenced call. After
+ * the syncFence is signaled, the task must not modify the content of
* any data object referenced by 'request' (described by the
* {@link @1.0::DataLocation} of a {@link @1.0::RequestArgument}).
*
+ * executeFenced may be called with an optional deadline and an optional duration.
+ * If the execution is not able to be completed before the provided deadline or
+ * within the timeout duration (measured from when all sync fences in waitFor are
+ * signaled), whichever comes earlier, the execution may be aborted, and either
+ * {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
+ * to an abort must be sent the same way as other errors, described above.
+ *
+ * If any of the sync fences in waitFor changes to error status after the executeFenced
+ * call succeeds, or the execution is aborted because it cannot finish before the deadline
+ * has been reached or the duration has elapsed, the driver must immediately set the returned
+ * syncFence to error status.
+ *
* Any number of calls to the executeFenced, execute* and executeSynchronously*
* functions, in any combination, may be made concurrently, even on the same
* IPreparedModel object.
*
* @param request The input and output information on which the prepared
- * model is to be executed.
+ * model is to be executed. The outputs in the request must have
+ * fully specified dimensions.
* @param waitFor A vector of sync fence file descriptors.
* Execution must not start until all sync fences have been signaled.
* @param measure Specifies whether or not to measure duration of the execution.
- * The duration runs from the time the driver sees the call
- * to the executeFenced function to the time sync_fence is triggered.
+ * @param deadline The time by which the execution is expected to complete.
+ * If the execution cannot be finished by the deadline, the
+ * execution may be aborted.
+ * @param loopTimeoutDuration The maximum amount of time that should be spent
+ * executing a {@link OperationType::WHILE}
+ * operation. If a loop condition model does not
+ * output false within this duration, the
+ * execution must be aborted. If the model
+ * contains a {@link OperationType::WHILE}
+ * operation and no loop timeout duration is
+ * provided, the maximum amount of time is {@link
+ * LoopTimeoutDurationNs::DEFAULT}. When
+ * provided, the duration must not exceed {@link
+ * LoopTimeoutDurationNs::MAXIMUM}.
+ * @param duration The length of time within which the execution is expected
+ * to complete after all sync fences in waitFor are signaled.
+ * If the execution cannot be finished within the duration,
+ * the execution may be aborted.
* @return status Error status of the call, must be:
* - NONE if task is successfully launched
* - DEVICE_UNAVAILABLE if driver is offline or busy
* - GENERAL_FAILURE if there is an unspecified error
* - INVALID_ARGUMENT if one of the input arguments is invalid, including
* fences in error states.
- * @return syncFence The sync fence that will be triggered when the task is completed.
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
+ * - RESOURCE_EXHAUSTED_* if the task was aborted by the
+ * driver
+ * @return syncFence The sync fence that will be signaled when the task is completed.
* The sync fence will be set to error if a critical error,
* e.g. hardware failure or kernel panic, occurs when doing execution.
* @return callback The IFencedExecutionCallback can be used to query information like duration
* and error status when the execution is completed.
*/
- executeFenced(Request request, vec<handle> waitFor, MeasureTiming measure)
+ executeFenced(Request request, vec<handle> waitFor, MeasureTiming measure,
+ OptionalTimePoint deadline, OptionalTimeoutDuration loopTimeoutDuration,
+ OptionalTimeoutDuration duration)
generates (ErrorStatus status, handle syncFence, IFencedExecutionCallback callback);
};
diff --git a/neuralnetworks/1.3/IPreparedModelCallback.hal b/neuralnetworks/1.3/IPreparedModelCallback.hal
index 11ebbf4..c0d3416 100644
--- a/neuralnetworks/1.3/IPreparedModelCallback.hal
+++ b/neuralnetworks/1.3/IPreparedModelCallback.hal
@@ -47,8 +47,8 @@
* unspecified error
* - INVALID_ARGUMENT if one of the input arguments to
* prepareModel is invalid
- * - MISSED_DEADLINE_* if the deadline for executing a model
- * cannot be met
+ * - MISSED_DEADLINE_* if the preparation is aborted because
+ * the model cannot be prepared by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the
* driver
* @param preparedModel A model that has been asynchronously prepared for
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index abc33e7..08d8e6b 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -22,9 +22,9 @@
import @1.0::RequestArgument;
import @1.2::Model.ExtensionNameAndPrefix;
import @1.2::Model.ExtensionTypeEncoding;
+import @1.2::Operand.ExtraParams;
import @1.2::OperandType;
import @1.2::OperationType;
-import @1.2::SymmPerChannelQuantParams;
import android.hidl.safe_union@1.0::Monostate;
@@ -2364,7 +2364,54 @@
AXIS_ALIGNED_BBOX_TRANSFORM = @1.2::OperationType:AXIS_ALIGNED_BBOX_TRANSFORM,
/**
- * Performs a forward LSTM on the input followed by a backward LSTM.
+ * A recurrent neural network layer that applies an LSTM cell to a
+ * sequence of inputs in forward and backward directions.
+ *
+ * The op supports cross-linking via an auxiliary input. Regular cell feeds
+ * one input into the two RNN cells in the following way:
+ *
+ * INPUT (INPUT_REVERSED)
+ * | |
+ * ---------------------
+ * | FW_LSTM BW_LSTM |
+ * ---------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
+ * An op with cross-linking takes two inputs and feeds them into the RNN
+ * cells in the following way:
+ *
+ * AUX_INPUT (AUX_INPUT_REVERSED)
+ * | |
+ * INPUT | (INPUT_R'D.)|
+ * | | | |
+ * -----------------------
+ * | \ / \ / |
+ * | FW_LSTM BW_LSTM |
+ * -----------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
+ * The cross-linking mode is enabled iff auxiliary input and auxiliary
+ * weights are present. While stacking this op on top of itself, this
+ * allows to connect both forward and backward outputs from previous cell
+ * to the next cell's input.
+ *
+ * Since HAL version 1.3 parallel linking mode is supported. The mode is
+ * enabled if auxiliary input is present but auxiliary weights are omitted.
+ * In this case, the cell feeds inputs into the RNN in the following way:
+ *
+ * INPUT (AUX_INPUT_REVERSED)
+ * | |
+ * ---------------------
+ * | FW_LSTM BW_LSTM |
+ * ---------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
+ * While stacking this op on top of itself, this allows to connect both
+ * forward and backward outputs from previous cell to the next cell's
+ * corresponding inputs.
*
* Supported tensor {@link OperandType}:
* * {@link OperandType::TENSOR_FLOAT16}
@@ -2374,7 +2421,6 @@
*
* All input and output tensors must be of the same type.
*
- *
* Inputs:
* * 0: The input.
* A 3-D tensor of shape:
@@ -2466,25 +2512,34 @@
* * 38: The backward input cell state.
* A 2-D tensor of shape [batch_size, bw_num_units].
* * 39: The auxiliary input. Optional.
- * A 3-D tensor of shape [max_time, batch_size, input_size], where “batch_size”
- * corresponds to the batching dimension, and “input_size” is the size
- * of the input.
- * * 40: The forward auxiliary input-to-input weights. Optional.
- * A 2-D tensor of shape [fw_num_units, input_size].
- * * 41: The forward auxiliary input-to-forget weights. Optional.
- * A 2-D tensor of shape [fw_num_units, input_size].
- * * 42: The forward auxiliary input-to-cell weights. Optional.
- * A 2-D tensor of shape [fw_num_units, input_size].
- * * 43: The forward auxiliary input-to-output weights. Optional.
- * A 2-D tensor of shape [fw_num_units, input_size].
- * * 44: The backward auxiliary input-to-input weights. Optional.
- * A 2-D tensor of shape [bw_num_units, input_size].
- * * 45: The backward auxiliary input-to-forget weights. Optional.
- * A 2-D tensor of shape [bw_num_units, input_size].
- * * 46: The backward auxiliary input-to-cell weights. Optional.
- * A 2-D tensor of shape [bw_num_units, input_size].
- * * 47: The backward auxiliary input-to-output weights. Optional.
- * A 2-D tensor of shape [bw_num_units, input_size].
+ * A 3-D tensor of shape [max_time, batch_size, aux_input_size],
+ * where “batch_size” corresponds to the batching dimension, and
+ * “aux_input_size” is the size of the auxiliary input. Optional. See
+ * the docs above for the usage modes explanation.
+ * * 40: The forward auxiliary input-to-input weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [fw_num_units, aux_input_size].
+ * * 41: The forward auxiliary input-to-forget weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [fw_num_units, aux_input_size].
+ * * 42: The forward auxiliary input-to-cell weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [fw_num_units, aux_input_size].
+ * * 43: The forward auxiliary input-to-output weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [fw_num_units, aux_input_size].
+ * * 44: The backward auxiliary input-to-input weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [bw_num_units, aux_input_size].
+ * * 45: The backward auxiliary input-to-forget weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [bw_num_units, aux_input_size].
+ * * 46: The backward auxiliary input-to-cell weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [bw_num_units, aux_input_size].
+ * * 47: The backward auxiliary input-to-output weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [bw_num_units, aux_input_size].
* * 48: The activation function.
* A value indicating the activation function:
* <ul>
@@ -2556,6 +2611,30 @@
* A 3-D tensor of shape:
* If time-major: [max_time, batch_size, bw_output_size]
* If batch-major: [batch_size, max_time, bw_output_size]
+ * * 2: The forward activation state output.
+ * A 2-D tensor of shape [batch_size, fw_output_size] containing an
+ * activation state from the last time step in the sequence. This
+ * output is optional and can be omitted. If this output is present
+ * then outputs 3-5 must be present as well.
+ * Available since HAL version 1.3.
+ * * 3: The forward cell state output.
+ * A tensor of shape [batch_size, fw_cell_size] containing a cell state
+ * from the last time step in the sequence. This output is optional
+ * and can be omitted. If this output is present
+ * then outputs 2, 4, 5 must be present as well.
+ * Available since HAL version 1.3.
+ * * 4: The backward activation state output.
+ * A 2-D tensor of shape [batch_size, bw_output_size] containing an
+ * activation state from the last time step in the sequence. This
+ * output is optional and can be omitted. If this output is present
+ * then outputs 2, 3, 5 must be present as well.
+ * Available since HAL version 1.3.
+ * * 5: The backward cell state output.
+ * A tensor of shape [batch_size, bw_cell_size] containing a cell state
+ * from the last time step in the sequence. This output is optional
+ * and can be omitted. If this output is present
+ * then outputs 2-4 must be present as well.
+ * Available since HAL version 1.3.
*/
BIDIRECTIONAL_SEQUENCE_LSTM = @1.2::OperationType:BIDIRECTIONAL_SEQUENCE_LSTM,
@@ -2583,8 +2662,8 @@
* * “activation” is the function passed as the “fused_activation_function”
* argument (if not “NONE”).
*
- * The op also supports an auxiliary input. Regular cell feeds one input
- * into the two RNN cells in the following way:
+ * The op supports cross-linking via an auxiliary input. Regular cell feeds
+ * one input into the two RNN cells in the following way:
*
* INPUT (INPUT_REVERSED)
* | |
@@ -2594,8 +2673,8 @@
* | |
* FW_OUT BW_OUT
*
- * An op with an auxiliary input takes two inputs and feeds them into the
- * RNN cells in the following way:
+ * An op with cross-linking takes two inputs and feeds them into the RNN
+ * cells in the following way:
*
* AUX_INPUT (AUX_INPUT_REVERSED)
* | |
@@ -2608,9 +2687,26 @@
* | |
* FW_OUT BW_OUT
*
+ * The cross-linking mode is enabled iff auxiliary input and auxiliary
+ * weights are present. While stacking this op on top of itself, this
+ * allows to connect both forward and backward outputs from previous cell
+ * to the next cell's input.
+ *
+ * Since HAL version 1.3 parallel linking mode is supported. The mode is
+ * enabled if auxiliary input is present but auxiliary weights are omitted.
+ * In this case, the cell feeds inputs into the RNN in the following way:
+ *
+ * INPUT (AUX_INPUT_REVERSED)
+ * | |
+ * ---------------------
+ * | FW_RNN BW_RNN |
+ * ---------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
* While stacking this op on top of itself, this allows to connect both
* forward and backward outputs from previous cell to the next cell's
- * inputs.
+ * corresponding inputs.
*
* Supported tensor {@link OperandType}:
* * {@link OperandType::TENSOR_FLOAT16}
@@ -2643,11 +2739,17 @@
* A 2-D tensor of shape [batchSize, bwNumUnits]. Specifies a hidden
* state input for the first time step of the computation.
* * 9: auxInput.
- * A 3-D tensor. The shape is the same as of the input 0.
+ * A 3-D tensor. The shape is defined by the input 6 (timeMajor). If
+ * it is set to true, then the input has a shape [maxTime, batchSize,
+ * auxInputSize], otherwise the input has a shape [batchSize, maxTime,
+ * auxInputSize]. Can be omitted. See the docs above for the usage
+ * modes explanation.
* * 10:fwAuxWeights.
- * A 2-D tensor of shape [fwNumUnits, inputSize].
+ * A 2-D tensor of shape [fwNumUnits, auxInputSize]. Can be omitted.
+ * See the docs above for the usage modes explanation.
* * 11:bwAuxWeights.
- * A 2-D tensor of shape [bwNumUnits, inputSize].
+ * A 2-D tensor of shape [bwNumUnits, auxInputSize]. Can be omitted.
+ * See the docs above for the usage modes explanation.
* * 12:fusedActivationFunction.
* A {@link FusedActivationFunc} value indicating the activation function. If
* “NONE” is specified then it results in a linear activation.
@@ -2673,6 +2775,18 @@
* (timeMajor). If it is set to true, then the shape is set to
* [maxTime, batchSize, bwNumUnits], otherwise the shape is set to
* [batchSize, maxTime, bwNumUnits].
+ * * 2: The forward hidden state output.
+ * A 2-D tensor of shape [batchSize, fwNumUnits] containing a hidden
+ * state from the last time step in the sequence. This output is
+ * optional and can be omitted. If this output is present then output
+ * 3 must be present as well.
+ * Available since HAL version 1.3.
+ * * 3: The backward hidden state output.
+ * A 2-D tensor of shape [batchSize, bwNumUnits] containing a hidden
+ * state from the last time step in the sequence. This output is
+ * optional and can be omitted. If this output is present then output
+ * 2 must be present as well.
+ * Available since HAL version 1.3.
*/
BIDIRECTIONAL_SEQUENCE_RNN = @1.2::OperationType:BIDIRECTIONAL_SEQUENCE_RNN,
@@ -3253,7 +3367,7 @@
* * 8: An {@link OperandType::INT32} scalar, specifying the stride when
* walking through input in the ‘height’ dimension.
* * 9: An {@link OperandType::INT32} scalar, specifying the number of
- groups.
+ * groups.
* * 10: An {@link OperandType::INT32} scalar, and has to be one of the
* {@link FusedActivationFunc} values. Specifies the activation to
* invoke on the result.
@@ -4656,6 +4770,15 @@
* A 3-D tensor of shape:
* If time-major: [max_time, batch_size, output_size]
* If batch-major: [batch_size, max_time, output_size]
+ * * 1: A tensor of shape [batch_size, output_size] containing a hidden
+ * state from the last time step in the sequence. This output is
+ * optional and can be omitted. If this output is present then
+ * output #2 must be present as well.
+ * Available since HAL version 1.3.
+ * * 2: A tensor of shape [batch_size, cell_size] containing a cell state
+ * from the last time step in the sequence. This output is optional
+ * and can be omitted.
+ * Available since HAL version 1.3.
*/
UNIDIRECTIONAL_SEQUENCE_LSTM = @1.2::OperationType:UNIDIRECTIONAL_SEQUENCE_LSTM,
@@ -4711,6 +4834,10 @@
* it is set to 1, then the output has a shape [maxTime, batchSize,
* numUnits], otherwise the output has a shape [batchSize, maxTime,
* numUnits].
+ * * 1: A tensor of shape [batchSize, numUnits] containing hidden state
+ * from the last time step in the sequence. This output is optional
+ * and can be omitted.
+ * Available since HAL version 1.3.
*/
UNIDIRECTIONAL_SEQUENCE_RNN = @1.2::OperationType:UNIDIRECTIONAL_SEQUENCE_RNN,
@@ -5127,8 +5254,10 @@
/**
* The capabilities of a driver.
*
- * Performance of an operation comes from the type of its first operand.
- * This represents performance for non extension operand types.
+ * This represents performance of non-extension operations.
+ *
+ * Performance of an operation other than {@link OperationType::IF} and
+ * {@link OperationType::WHILE} comes from the type of its first operand.
*/
struct Capabilities {
/**
@@ -5151,11 +5280,32 @@
/**
* Performance by operand type. Must be sorted by OperandType.
- * If a particular OperandType is not present in operandPerformance,
+ *
+ * If a particular {@link OperandType} is not present in operandPerformance,
* its performance is treated as
* { .execTime = FLT_MAX, .powerUsage = FLT_MAX }.
+ *
+ * Performance does not apply to {@link OperandType::SUBGRAPH}, and a driver
+ * must not report operand performance for {@link OperandType::SUBGRAPH}.
*/
vec<OperandPerformance> operandPerformance;
+
+ /**
+ * Performance of an {@link OperationType::IF} operation is the sum of
+ * {@link Capabilities::ifPerformance} and the mean of performance for the
+ * two branch subgraphs, where performance for a subgraph is the sum of the
+ * performance of all operations within the subgraph.
+ */
+ PerformanceInfo ifPerformance;
+
+ /**
+ * Performance of a {@link OperationType::WHILE} operation is the sum of
+ * {@link Capabilities::whilePerformance}, performance for the condition
+ * subgraph and performance for the body subgraph, where performance for a
+ * subgraph is the sum of the performance of all operations within the
+ * subgraph.
+ */
+ PerformanceInfo whilePerformance;
};
/**
@@ -5343,27 +5493,7 @@
/**
* Additional parameters specific to a particular operand type.
*/
- safe_union ExtraParams {
- /**
- * No additional parameters.
- */
- Monostate none;
-
- /**
- * Symmetric per-channel quantization parameters.
- *
- * Only applicable to operands of type TENSOR_QUANT8_SYMM_PER_CHANNEL.
- */
- SymmPerChannelQuantParams channelQuant;
-
- /**
- * Extension operand parameters.
- *
- * The framework treats this as an opaque data blob.
- * The format is up to individual extensions.
- */
- vec<uint8_t> extension;
- } extraParams;
+ @1.2::Operand.ExtraParams extraParams;
};
/**
@@ -5551,7 +5681,7 @@
* Specifies a driver-managed buffer. It is the token returned from IDevice::allocate,
* and is specific to the IDevice object.
*/
- int32_t token;
+ uint32_t token;
};
/**
@@ -5573,6 +5703,19 @@
* Time point of the steady clock (as from std::chrono::steady_clock)
* measured in nanoseconds.
*/
+ uint64_t nanosecondsSinceEpoch;
+};
+
+/**
+ * Optional timeout duration measured in nanoseconds.
+ */
+safe_union OptionalTimeoutDuration {
+ /** No time point provided. */
+ Monostate none;
+
+ /**
+ * Timeout duration measured in nanoseconds.
+ */
uint64_t nanoseconds;
};
@@ -5606,3 +5749,14 @@
*/
RESOURCE_EXHAUSTED_PERSISTENT,
};
+
+/**
+ * Each {@link OperationType::WHILE} operation in the model has an implicit
+ * execution timeout duration associated with it ("loop timeout duration").
+ * This duration is configurable on a per-execution basis and must not exceed
+ * 15 seconds. The default value is 2 seconds.
+ */
+enum LoopTimeoutDurationNs : uint64_t {
+ DEFAULT = 2000000000,
+ MAXIMUM = 15000000000,
+};
diff --git a/neuralnetworks/1.3/types.t b/neuralnetworks/1.3/types.t
index a973923..0a6e45e 100644
--- a/neuralnetworks/1.3/types.t
+++ b/neuralnetworks/1.3/types.t
@@ -24,9 +24,9 @@
import @1.0::RequestArgument;
import @1.2::Model.ExtensionNameAndPrefix;
import @1.2::Model.ExtensionTypeEncoding;
+import @1.2::Operand.ExtraParams;
import @1.2::OperandType;
import @1.2::OperationType;
-import @1.2::SymmPerChannelQuantParams;
import android.hidl.safe_union@1.0::Monostate;
@@ -103,8 +103,10 @@
/**
* The capabilities of a driver.
*
- * Performance of an operation comes from the type of its first operand.
- * This represents performance for non extension operand types.
+ * This represents performance of non-extension operations.
+ *
+ * Performance of an operation other than {@link OperationType::IF} and
+ * {@link OperationType::WHILE} comes from the type of its first operand.
*/
struct Capabilities {
/**
@@ -127,11 +129,32 @@
/**
* Performance by operand type. Must be sorted by OperandType.
- * If a particular OperandType is not present in operandPerformance,
+ *
+ * If a particular {@link OperandType} is not present in operandPerformance,
* its performance is treated as
* { .execTime = FLT_MAX, .powerUsage = FLT_MAX }.
+ *
+ * Performance does not apply to {@link OperandType::SUBGRAPH}, and a driver
+ * must not report operand performance for {@link OperandType::SUBGRAPH}.
*/
vec<OperandPerformance> operandPerformance;
+
+ /**
+ * Performance of an {@link OperationType::IF} operation is the sum of
+ * {@link Capabilities::ifPerformance} and the mean of performance for the
+ * two branch subgraphs, where performance for a subgraph is the sum of the
+ * performance of all operations within the subgraph.
+ */
+ PerformanceInfo ifPerformance;
+
+ /**
+ * Performance of a {@link OperationType::WHILE} operation is the sum of
+ * {@link Capabilities::whilePerformance}, performance for the condition
+ * subgraph and performance for the body subgraph, where performance for a
+ * subgraph is the sum of the performance of all operations within the
+ * subgraph.
+ */
+ PerformanceInfo whilePerformance;
};
/**
@@ -319,27 +342,7 @@
/**
* Additional parameters specific to a particular operand type.
*/
- safe_union ExtraParams {
- /**
- * No additional parameters.
- */
- Monostate none;
-
- /**
- * Symmetric per-channel quantization parameters.
- *
- * Only applicable to operands of type TENSOR_QUANT8_SYMM_PER_CHANNEL.
- */
- SymmPerChannelQuantParams channelQuant;
-
- /**
- * Extension operand parameters.
- *
- * The framework treats this as an opaque data blob.
- * The format is up to individual extensions.
- */
- vec<uint8_t> extension;
- } extraParams;
+ @1.2::Operand.ExtraParams extraParams;
};
/**
@@ -527,7 +530,7 @@
* Specifies a driver-managed buffer. It is the token returned from IDevice::allocate,
* and is specific to the IDevice object.
*/
- int32_t token;
+ uint32_t token;
};
/**
@@ -549,6 +552,19 @@
* Time point of the steady clock (as from std::chrono::steady_clock)
* measured in nanoseconds.
*/
+ uint64_t nanosecondsSinceEpoch;
+};
+
+/**
+ * Optional timeout duration measured in nanoseconds.
+ */
+safe_union OptionalTimeoutDuration {
+ /** No time point provided. */
+ Monostate none;
+
+ /**
+ * Timeout duration measured in nanoseconds.
+ */
uint64_t nanoseconds;
};
@@ -582,3 +598,14 @@
*/
RESOURCE_EXHAUSTED_PERSISTENT,
};
+
+/**
+ * Each {@link OperationType::WHILE} operation in the model has an implicit
+ * execution timeout duration associated with it ("loop timeout duration").
+ * This duration is configurable on a per-execution basis and must not exceed
+ * 15 seconds. The default value is 2 seconds.
+ */
+enum LoopTimeoutDurationNs : uint64_t {
+ DEFAULT = 2000000000,
+ MAXIMUM = 15000000000,
+};
diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index 8e7e9b9..f936267 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -16,7 +16,7 @@
cc_library_static {
name: "VtsHalNeuralNetworksV1_3_utils",
- defaults: ["VtsHalTargetTestDefaults"],
+ defaults: ["neuralnetworks_vts_functional_defaults"],
export_include_dirs: ["include"],
srcs: [
"Callbacks.cpp",
@@ -35,7 +35,7 @@
cc_test {
name: "VtsHalNeuralnetworksV1_3TargetTest",
- defaults: ["VtsHalTargetTestDefaults"],
+ defaults: ["neuralnetworks_vts_functional_defaults"],
srcs: [
"BasicTests.cpp",
"CompilationCachingTests.cpp",
diff --git a/neuralnetworks/1.3/vts/functional/BasicTests.cpp b/neuralnetworks/1.3/vts/functional/BasicTests.cpp
index 891850c..1c25369 100644
--- a/neuralnetworks/1.3/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/BasicTests.cpp
@@ -57,6 +57,11 @@
[](const OperandPerformance& a, const OperandPerformance& b) {
return a.type < b.type;
}));
+ EXPECT_TRUE(std::all_of(opPerf.begin(), opPerf.end(), [](const OperandPerformance& a) {
+ return a.type != OperandType::SUBGRAPH;
+ }));
+ EXPECT_TRUE(isPositive(capabilities.ifPerformance));
+ EXPECT_TRUE(isPositive(capabilities.whilePerformance));
});
EXPECT_TRUE(ret.isOk());
}
diff --git a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
index 576e524..ac18c8f 100644
--- a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
@@ -209,10 +209,10 @@
};
return {
- .operands = std::move(operands),
- .operations = std::move(operations),
- .inputIndexes = {1},
- .outputIndexes = {len * 2 + 1},
+ .main = {.operands = std::move(operands),
+ .operations = std::move(operations),
+ .inputIndexes = {1},
+ .outputIndexes = {len * 2 + 1}},
.isRelaxed = false,
};
}
@@ -370,7 +370,7 @@
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
Return<ErrorStatus> prepareLaunchStatus = kDevice->prepareModelFromCache_1_3(
- kDefaultPriority, {}, modelCache, dataCache, cacheToken, preparedModelCallback);
+ {}, modelCache, dataCache, cacheToken, preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
if (static_cast<ErrorStatus>(prepareLaunchStatus) != ErrorStatus::NONE) {
*preparedModel = nullptr;
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
index 88837db..8c9393b 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
@@ -74,12 +74,19 @@
enum class Executor { ASYNC, SYNC, BURST, FENCED };
-enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT };
+enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT, MISSED_DEADLINE };
enum class MemoryType { SHARED, DEVICE };
enum class IOType { INPUT, OUTPUT };
+static void waitForSyncFence(int syncFd) {
+ constexpr int kInfiniteTimeout = -1;
+ ASSERT_GT(syncFd, 0);
+ int r = sync_wait(syncFd, kInfiniteTimeout);
+ ASSERT_GE(r, 0);
+}
+
struct TestConfig {
Executor executor;
MeasureTiming measureTiming;
@@ -115,15 +122,15 @@
// Return {IBuffer object, token} if successful.
// Return {nullptr, 0} if device memory is not supported.
template <IOType ioType>
- std::pair<sp<IBuffer>, int32_t> allocate(uint32_t index) {
- std::pair<sp<IBuffer>, int32_t> buffer;
+ std::pair<sp<IBuffer>, uint32_t> allocate(uint32_t index) {
+ std::pair<sp<IBuffer>, uint32_t> buffer;
allocateInternal<ioType>(index, &buffer);
return buffer;
}
private:
template <IOType ioType>
- void allocateInternal(uint32_t index, std::pair<sp<IBuffer>, int32_t>* result) {
+ void allocateInternal(uint32_t index, std::pair<sp<IBuffer>, uint32_t>* result) {
ASSERT_NE(result, nullptr);
// Prepare arguments.
@@ -138,14 +145,14 @@
// Allocate device memory.
ErrorStatus status;
sp<IBuffer> buffer;
- int32_t token;
- const auto ret = kDevice->allocate(
- {}, {kPreparedModel}, inputRoles, outputRoles,
- [&status, &buffer, &token](ErrorStatus error, const sp<IBuffer>& buf, int32_t tok) {
- status = error;
- buffer = buf;
- token = tok;
- });
+ uint32_t token;
+ auto cb = [&status, &buffer, &token](ErrorStatus error, const sp<IBuffer>& buf,
+ uint32_t tok) {
+ status = error;
+ buffer = buf;
+ token = tok;
+ };
+ const auto ret = kDevice->allocate({}, {kPreparedModel}, inputRoles, outputRoles, cb);
// Check allocation results.
ASSERT_TRUE(ret.isOk());
@@ -162,7 +169,8 @@
if constexpr (ioType == IOType::INPUT) {
if (buffer != nullptr) {
// TestBuffer -> Shared memory.
- const auto& testBuffer = kTestModel.operands[kTestModel.inputIndexes[index]].data;
+ const auto& testBuffer =
+ kTestModel.main.operands[kTestModel.main.inputIndexes[index]].data;
ASSERT_GT(testBuffer.size(), 0);
hidl_memory tmp = nn::allocateSharedMemory(testBuffer.size());
sp<IMemory> inputMemory = mapMemory(tmp);
@@ -188,29 +196,45 @@
const TestModel& kTestModel;
};
-} // namespace
+Subgraph createSubgraph(const TestSubgraph& testSubgraph, uint32_t* constCopySize,
+ std::vector<const TestBuffer*>* constCopies, uint32_t* constRefSize,
+ std::vector<const TestBuffer*>* constReferences) {
+ CHECK(constCopySize != nullptr);
+ CHECK(constCopies != nullptr);
+ CHECK(constRefSize != nullptr);
+ CHECK(constReferences != nullptr);
-Model createModel(const TestModel& testModel) {
- // Model operands.
- hidl_vec<Operand> operands(testModel.operands.size());
- size_t constCopySize = 0, constRefSize = 0;
- for (uint32_t i = 0; i < testModel.operands.size(); i++) {
- const auto& op = testModel.operands[i];
+ // Operands.
+ hidl_vec<Operand> operands(testSubgraph.operands.size());
+ for (uint32_t i = 0; i < testSubgraph.operands.size(); i++) {
+ const auto& op = testSubgraph.operands[i];
DataLocation loc = {};
if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
- loc = {.poolIndex = 0,
- .offset = static_cast<uint32_t>(constCopySize),
- .length = static_cast<uint32_t>(op.data.size())};
- constCopySize += op.data.alignedSize();
+ loc = {
+ .poolIndex = 0,
+ .offset = *constCopySize,
+ .length = static_cast<uint32_t>(op.data.size()),
+ };
+ constCopies->push_back(&op.data);
+ *constCopySize += op.data.alignedSize();
} else if (op.lifetime == TestOperandLifeTime::CONSTANT_REFERENCE) {
- loc = {.poolIndex = 0,
- .offset = static_cast<uint32_t>(constRefSize),
- .length = static_cast<uint32_t>(op.data.size())};
- constRefSize += op.data.alignedSize();
+ loc = {
+ .poolIndex = 0,
+ .offset = *constRefSize,
+ .length = static_cast<uint32_t>(op.data.size()),
+ };
+ constReferences->push_back(&op.data);
+ *constRefSize += op.data.alignedSize();
+ } else if (op.lifetime == TestOperandLifeTime::SUBGRAPH) {
+ loc = {
+ .poolIndex = 0,
+ .offset = *op.data.get<uint32_t>(),
+ .length = 0,
+ };
}
- Operand::ExtraParams extraParams;
+ V1_2::Operand::ExtraParams extraParams;
if (op.type == TestOperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) {
extraParams.channelQuant(SymmPerChannelQuantParams{
.scales = op.channelQuant.scales, .channelDim = op.channelQuant.channelDim});
@@ -226,25 +250,52 @@
.extraParams = std::move(extraParams)};
}
- // Model operations.
- hidl_vec<Operation> operations(testModel.operations.size());
- std::transform(testModel.operations.begin(), testModel.operations.end(), operations.begin(),
- [](const TestOperation& op) -> Operation {
+ // Operations.
+ hidl_vec<Operation> operations(testSubgraph.operations.size());
+ std::transform(testSubgraph.operations.begin(), testSubgraph.operations.end(),
+ operations.begin(), [](const TestOperation& op) -> Operation {
return {.type = static_cast<OperationType>(op.type),
.inputs = op.inputs,
.outputs = op.outputs};
});
+ return {.operands = std::move(operands),
+ .operations = std::move(operations),
+ .inputIndexes = testSubgraph.inputIndexes,
+ .outputIndexes = testSubgraph.outputIndexes};
+}
+
+void copyTestBuffers(const std::vector<const TestBuffer*>& buffers, uint8_t* output) {
+ uint32_t offset = 0;
+ for (const TestBuffer* buffer : buffers) {
+ const uint8_t* begin = buffer->get<uint8_t>();
+ const uint8_t* end = begin + buffer->size();
+ std::copy(begin, end, output + offset);
+ offset += buffer->alignedSize();
+ }
+}
+
+} // namespace
+
+Model createModel(const TestModel& testModel) {
+ uint32_t constCopySize = 0;
+ uint32_t constRefSize = 0;
+ std::vector<const TestBuffer*> constCopies;
+ std::vector<const TestBuffer*> constReferences;
+
+ Subgraph mainSubgraph = createSubgraph(testModel.main, &constCopySize, &constCopies,
+ &constRefSize, &constReferences);
+ hidl_vec<Subgraph> refSubgraphs(testModel.referenced.size());
+ std::transform(testModel.referenced.begin(), testModel.referenced.end(), refSubgraphs.begin(),
+ [&constCopySize, &constCopies, &constRefSize,
+ &constReferences](const TestSubgraph& testSubgraph) {
+ return createSubgraph(testSubgraph, &constCopySize, &constCopies,
+ &constRefSize, &constReferences);
+ });
+
// Constant copies.
hidl_vec<uint8_t> operandValues(constCopySize);
- for (uint32_t i = 0; i < testModel.operands.size(); i++) {
- const auto& op = testModel.operands[i];
- if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
- const uint8_t* begin = op.data.get<uint8_t>();
- const uint8_t* end = begin + op.data.size();
- std::copy(begin, end, operandValues.data() + operands[i].location.offset);
- }
- }
+ copyTestBuffers(constCopies, operandValues.data());
// Shared memory.
hidl_vec<hidl_memory> pools = {};
@@ -259,27 +310,18 @@
reinterpret_cast<uint8_t*>(static_cast<void*>(mappedMemory->getPointer()));
CHECK(mappedPtr != nullptr);
- for (uint32_t i = 0; i < testModel.operands.size(); i++) {
- const auto& op = testModel.operands[i];
- if (op.lifetime == TestOperandLifeTime::CONSTANT_REFERENCE) {
- const uint8_t* begin = op.data.get<uint8_t>();
- const uint8_t* end = begin + op.data.size();
- std::copy(begin, end, mappedPtr + operands[i].location.offset);
- }
- }
+ copyTestBuffers(constReferences, mappedPtr);
}
- return {.main = {.operands = std::move(operands),
- .operations = std::move(operations),
- .inputIndexes = testModel.inputIndexes,
- .outputIndexes = testModel.outputIndexes},
+ return {.main = std::move(mainSubgraph),
+ .referenced = std::move(refSubgraphs),
.operandValues = std::move(operandValues),
.pools = std::move(pools),
.relaxComputationFloat32toFloat16 = testModel.isRelaxed};
}
static bool isOutputSizeGreaterThanOne(const TestModel& testModel, uint32_t index) {
- const auto byteSize = testModel.operands[testModel.outputIndexes[index]].data.size();
+ const auto byteSize = testModel.main.operands[testModel.main.outputIndexes[index]].data.size();
return byteSize > 1u;
}
@@ -310,13 +352,13 @@
// - [2+i, 2+i+o): Output device memories
DeviceMemoryAllocator allocator(device, preparedModel, testModel);
std::vector<sp<IBuffer>> buffers;
- std::vector<int32_t> tokens;
+ std::vector<uint32_t> tokens;
// Model inputs.
- hidl_vec<RequestArgument> inputs(testModel.inputIndexes.size());
+ hidl_vec<RequestArgument> inputs(testModel.main.inputIndexes.size());
size_t inputSize = 0;
- for (uint32_t i = 0; i < testModel.inputIndexes.size(); i++) {
- const auto& op = testModel.operands[testModel.inputIndexes[i]];
+ for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
+ const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
if (op.data.size() == 0) {
// Omitted input.
inputs[i] = {.hasNoValue = true};
@@ -343,10 +385,10 @@
}
// Model outputs.
- hidl_vec<RequestArgument> outputs(testModel.outputIndexes.size());
+ hidl_vec<RequestArgument> outputs(testModel.main.outputIndexes.size());
size_t outputSize = 0;
- for (uint32_t i = 0; i < testModel.outputIndexes.size(); i++) {
- const auto& op = testModel.operands[testModel.outputIndexes[i]];
+ for (uint32_t i = 0; i < testModel.main.outputIndexes.size(); i++) {
+ const auto& op = testModel.main.operands[testModel.main.outputIndexes[i]];
if (preferDeviceMemory) {
SCOPED_TRACE("Output index = " + std::to_string(i));
auto [buffer, token] = allocator.allocate<IOType::OUTPUT>(i);
@@ -391,9 +433,9 @@
CHECK(inputMemory.get() != nullptr);
uint8_t* inputPtr = static_cast<uint8_t*>(static_cast<void*>(inputMemory->getPointer()));
CHECK(inputPtr != nullptr);
- for (uint32_t i = 0; i < testModel.inputIndexes.size(); i++) {
+ for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
if (!inputs[i].hasNoValue && inputs[i].location.poolIndex == kInputPoolIndex) {
- const auto& op = testModel.operands[testModel.inputIndexes[i]];
+ const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
const uint8_t* begin = op.data.get<uint8_t>();
const uint8_t* end = begin + op.data.size();
std::copy(begin, end, inputPtr + inputs[i].location.offset);
@@ -436,7 +478,7 @@
if (outputLoc.poolIndex == kOutputPoolIndex) {
outputBuffers.emplace_back(outputLoc.length, outputPtr + outputLoc.offset);
} else {
- const auto& op = testModel.operands[testModel.outputIndexes[i]];
+ const auto& op = testModel.main.operands[testModel.main.outputIndexes[i]];
if (op.data.size() == 0) {
outputBuffers.emplace_back();
} else {
@@ -453,16 +495,18 @@
static Return<ErrorStatus> ExecutePreparedModel(const sp<IPreparedModel>& preparedModel,
const Request& request, MeasureTiming measure,
+ const OptionalTimeoutDuration& loopTimeoutDuration,
sp<ExecutionCallback>& callback) {
- return preparedModel->execute_1_3(request, measure, {}, callback);
+ return preparedModel->execute_1_3(request, measure, {}, loopTimeoutDuration, callback);
}
static Return<ErrorStatus> ExecutePreparedModel(const sp<IPreparedModel>& preparedModel,
const Request& request, MeasureTiming measure,
+ const OptionalTimeoutDuration& loopTimeoutDuration,
hidl_vec<OutputShape>* outputShapes,
Timing* timing) {
ErrorStatus result;
Return<void> ret = preparedModel->executeSynchronously_1_3(
- request, measure, {},
+ request, measure, {}, loopTimeoutDuration,
[&result, outputShapes, timing](ErrorStatus error, const hidl_vec<OutputShape>& shapes,
const Timing& time) {
result = error;
@@ -503,6 +547,17 @@
makeOutputInsufficientSize(/*outputIndex=*/0, &request);
}
+ OptionalTimeoutDuration loopTimeoutDuration;
+ // OutputType::MISSED_DEADLINE is only used by
+ // TestKind::INTINITE_LOOP_TIMEOUT tests to verify that an infinite loop is
+ // aborted after a timeout.
+ if (testConfig.outputType == OutputType::MISSED_DEADLINE) {
+ // Override the default loop timeout duration with a small value to
+ // speed up test execution.
+ constexpr uint64_t kMillisecond = 1'000'000;
+ loopTimeoutDuration.nanoseconds(1 * kMillisecond);
+ }
+
ErrorStatus executionStatus;
hidl_vec<OutputShape> outputShapes;
Timing timing;
@@ -512,8 +567,9 @@
// launch execution
sp<ExecutionCallback> executionCallback = new ExecutionCallback();
- Return<ErrorStatus> executionLaunchStatus = ExecutePreparedModel(
- preparedModel, request, testConfig.measureTiming, executionCallback);
+ Return<ErrorStatus> executionLaunchStatus =
+ ExecutePreparedModel(preparedModel, request, testConfig.measureTiming,
+ loopTimeoutDuration, executionCallback);
ASSERT_TRUE(executionLaunchStatus.isOk());
EXPECT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(executionLaunchStatus));
@@ -529,8 +585,9 @@
SCOPED_TRACE("synchronous");
// execute
- Return<ErrorStatus> executionReturnStatus = ExecutePreparedModel(
- preparedModel, request, testConfig.measureTiming, &outputShapes, &timing);
+ Return<ErrorStatus> executionReturnStatus =
+ ExecutePreparedModel(preparedModel, request, testConfig.measureTiming,
+ loopTimeoutDuration, &outputShapes, &timing);
ASSERT_TRUE(executionReturnStatus.isOk());
executionStatus = static_cast<ErrorStatus>(executionReturnStatus);
@@ -567,33 +624,29 @@
case Executor::FENCED: {
SCOPED_TRACE("fenced");
ErrorStatus result;
- hidl_handle sync_fence_handle;
- sp<IFencedExecutionCallback> fenced_callback;
+ hidl_handle syncFenceHandle;
+ sp<IFencedExecutionCallback> fencedCallback;
Return<void> ret = preparedModel->executeFenced(
- request, {}, testConfig.measureTiming,
- [&result, &sync_fence_handle, &fenced_callback](
+ request, {}, testConfig.measureTiming, {}, loopTimeoutDuration, {},
+ [&result, &syncFenceHandle, &fencedCallback](
ErrorStatus error, const hidl_handle& handle,
const sp<IFencedExecutionCallback>& callback) {
result = error;
- sync_fence_handle = handle;
- fenced_callback = callback;
+ syncFenceHandle = handle;
+ fencedCallback = callback;
});
ASSERT_TRUE(ret.isOk());
if (result != ErrorStatus::NONE) {
- ASSERT_EQ(sync_fence_handle.getNativeHandle(), nullptr);
- ASSERT_EQ(fenced_callback, nullptr);
+ ASSERT_EQ(syncFenceHandle.getNativeHandle(), nullptr);
+ ASSERT_EQ(fencedCallback, nullptr);
executionStatus = ErrorStatus::GENERAL_FAILURE;
- } else if (sync_fence_handle.getNativeHandle()) {
- constexpr int kInfiniteTimeout = -1;
- int sync_fd = sync_fence_handle.getNativeHandle()->data[0];
- ASSERT_GT(sync_fd, 0);
- int r = sync_wait(sync_fd, kInfiniteTimeout);
- ASSERT_GE(r, 0);
+ } else if (syncFenceHandle.getNativeHandle()) {
+ waitForSyncFence(syncFenceHandle.getNativeHandle()->data[0]);
}
if (result == ErrorStatus::NONE) {
- ASSERT_NE(fenced_callback, nullptr);
- Return<void> ret = fenced_callback->getExecutionInfo(
- [&executionStatus, &timing](ErrorStatus error, Timing t) {
+ ASSERT_NE(fencedCallback, nullptr);
+ Return<void> ret = fencedCallback->getExecutionInfo(
+ [&executionStatus, &timing](ErrorStatus error, Timing t, Timing) {
executionStatus = error;
timing = t;
});
@@ -635,25 +688,30 @@
// either empty, or have the same number of elements as the number of outputs.
ASSERT_EQ(ErrorStatus::NONE, executionStatus);
ASSERT_TRUE(outputShapes.size() == 0 ||
- outputShapes.size() == testModel.outputIndexes.size());
+ outputShapes.size() == testModel.main.outputIndexes.size());
break;
case OutputType::UNSPECIFIED:
// If the model output operands are not fully specified, outputShapes must have
// the same number of elements as the number of outputs.
ASSERT_EQ(ErrorStatus::NONE, executionStatus);
- ASSERT_EQ(outputShapes.size(), testModel.outputIndexes.size());
+ ASSERT_EQ(outputShapes.size(), testModel.main.outputIndexes.size());
break;
case OutputType::INSUFFICIENT:
ASSERT_EQ(ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, executionStatus);
- ASSERT_EQ(outputShapes.size(), testModel.outputIndexes.size());
+ ASSERT_EQ(outputShapes.size(), testModel.main.outputIndexes.size());
ASSERT_FALSE(outputShapes[0].isSufficient);
return;
+ case OutputType::MISSED_DEADLINE:
+ ASSERT_TRUE(executionStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+ executionStatus == ErrorStatus::MISSED_DEADLINE_PERSISTENT)
+ << "executionStatus = " << executionStatus;
+ return;
}
// Go through all outputs, check returned output shapes.
for (uint32_t i = 0; i < outputShapes.size(); i++) {
EXPECT_TRUE(outputShapes[i].isSufficient);
- const auto& expect = testModel.operands[testModel.outputIndexes[i]].dimensions;
+ const auto& expect = testModel.main.operands[testModel.main.outputIndexes[i]].dimensions;
const std::vector<uint32_t> actual = outputShapes[i].dimensions;
EXPECT_EQ(expect, actual);
}
@@ -698,6 +756,12 @@
LOG(FATAL) << "Wrong TestKind for EvaluatePreparedModel";
return;
} break;
+ case TestKind::INTINITE_LOOP_TIMEOUT: {
+ outputTypesList = {OutputType::MISSED_DEADLINE};
+ measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
+ // Burst does not support V1_3 loop timeout.
+ executorList = {Executor::ASYNC, Executor::SYNC, Executor::FENCED};
+ } break;
}
for (const OutputType outputType : outputTypesList) {
@@ -756,7 +820,8 @@
case TestKind::GENERAL:
case TestKind::DYNAMIC_SHAPE:
case TestKind::MEMORY_DOMAIN:
- case TestKind::FENCED_COMPUTE: {
+ case TestKind::FENCED_COMPUTE:
+ case TestKind::INTINITE_LOOP_TIMEOUT: {
createPreparedModel(device, model, &preparedModel);
if (preparedModel == nullptr) return;
EvaluatePreparedModel(device, preparedModel, testModel, testKind);
@@ -793,12 +858,6 @@
void GeneratedTestBase::SetUp() {
testing::TestWithParam<GeneratedTestParam>::SetUp();
ASSERT_NE(kDevice, nullptr);
-
- const Return<void> ret =
- kDevice->supportsDeadlines([this](bool prepareModelDeadline, bool executionDeadline) {
- mSupportsDeadlines = {prepareModelDeadline, executionDeadline};
- });
- ASSERT_TRUE(ret.isOk());
}
std::vector<NamedModel> getNamedModels(const FilterFn& filter) {
@@ -825,24 +884,31 @@
// Tag for the dynamic output shape tests
class QuantizationCouplingTest : public GeneratedTest {};
+// Tag for the loop timeout tests
+class InfiniteLoopTimeoutTest : public GeneratedTest {};
+
TEST_P(GeneratedTest, Test) {
- Execute(kDevice, kTestModel, /*testKind=*/TestKind::GENERAL);
+ Execute(kDevice, kTestModel, TestKind::GENERAL);
}
TEST_P(DynamicOutputShapeTest, Test) {
- Execute(kDevice, kTestModel, /*testKind=*/TestKind::DYNAMIC_SHAPE);
+ Execute(kDevice, kTestModel, TestKind::DYNAMIC_SHAPE);
}
TEST_P(MemoryDomainTest, Test) {
- Execute(kDevice, kTestModel, /*testKind=*/TestKind::MEMORY_DOMAIN);
+ Execute(kDevice, kTestModel, TestKind::MEMORY_DOMAIN);
}
TEST_P(FencedComputeTest, Test) {
- Execute(kDevice, kTestModel, /*testKind=*/TestKind::FENCED_COMPUTE);
+ Execute(kDevice, kTestModel, TestKind::FENCED_COMPUTE);
}
TEST_P(QuantizationCouplingTest, Test) {
- Execute(kDevice, kTestModel, /*testKind=*/TestKind::QUANTIZATION_COUPLING);
+ Execute(kDevice, kTestModel, TestKind::QUANTIZATION_COUPLING);
+}
+
+TEST_P(InfiniteLoopTimeoutTest, Test) {
+ Execute(kDevice, kTestModel, TestKind::INTINITE_LOOP_TIMEOUT);
}
INSTANTIATE_GENERATED_TEST(GeneratedTest,
@@ -859,7 +925,11 @@
[](const TestModel& testModel) { return !testModel.expectFailure; });
INSTANTIATE_GENERATED_TEST(QuantizationCouplingTest, [](const TestModel& testModel) {
- return testModel.hasQuant8CoupledOperands() && testModel.operations.size() == 1;
+ return testModel.hasQuant8CoupledOperands() && testModel.main.operations.size() == 1;
+});
+
+INSTANTIATE_GENERATED_TEST(InfiniteLoopTimeoutTest, [](const TestModel& testModel) {
+ return testModel.isInfiniteLoopTimeoutTest();
});
} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
index e597fac..834d335 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
@@ -36,7 +36,6 @@
void SetUp() override;
const sp<IDevice> kDevice = getData(std::get<NamedDevice>(GetParam()));
const test_helper::TestModel& kTestModel = *getData(std::get<NamedModel>(GetParam()));
- std::pair<bool, bool> mSupportsDeadlines;
};
using FilterFn = std::function<bool(const test_helper::TestModel&)>;
@@ -70,7 +69,9 @@
// Tests if quantized model with TENSOR_QUANT8_ASYMM produces the same result
// (OK/SKIPPED/FAILED) as the model with all such tensors converted to
// TENSOR_QUANT8_ASYMM_SIGNED.
- QUANTIZATION_COUPLING
+ QUANTIZATION_COUPLING,
+ // Runs a test model and verifies that MISSED_DEADLINE_* is returned.
+ INTINITE_LOOP_TIMEOUT
};
void EvaluatePreparedModel(const sp<IDevice>& device, const sp<IPreparedModel>& preparedModel,
diff --git a/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
index 62ffcda..879989e 100644
--- a/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
@@ -34,43 +34,52 @@
using HidlToken =
hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
-enum class DeadlineBoundType { NOW, UNLIMITED };
-constexpr std::array<DeadlineBoundType, 2> deadlineBounds = {DeadlineBoundType::NOW,
- DeadlineBoundType::UNLIMITED};
+enum class DeadlineBoundType { NOW, UNLIMITED, SHORT };
+constexpr std::array<DeadlineBoundType, 3> deadlineBounds = {
+ DeadlineBoundType::NOW, DeadlineBoundType::UNLIMITED, DeadlineBoundType::SHORT};
std::string toString(DeadlineBoundType type) {
switch (type) {
case DeadlineBoundType::NOW:
return "NOW";
case DeadlineBoundType::UNLIMITED:
return "UNLIMITED";
+ case DeadlineBoundType::SHORT:
+ return "SHORT";
}
LOG(FATAL) << "Unrecognized DeadlineBoundType: " << static_cast<int>(type);
return {};
}
+constexpr auto kShortDuration = std::chrono::milliseconds{5};
+
using Results = std::tuple<ErrorStatus, hidl_vec<OutputShape>, Timing>;
using MaybeResults = std::optional<Results>;
using ExecutionFunction =
std::function<MaybeResults(const sp<IPreparedModel>& preparedModel, const Request& request,
- DeadlineBoundType deadlineBound)>;
+ const OptionalTimePoint& deadline)>;
-static OptionalTimePoint makeOptionalTimePoint(DeadlineBoundType deadlineBoundType) {
- OptionalTimePoint deadline;
+static OptionalTimePoint makeDeadline(DeadlineBoundType deadlineBoundType) {
+ const auto getNanosecondsSinceEpoch = [](const auto& time) -> uint64_t {
+ const auto timeSinceEpoch = time.time_since_epoch();
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(timeSinceEpoch).count();
+ };
+
+ std::chrono::steady_clock::time_point timePoint;
switch (deadlineBoundType) {
- case DeadlineBoundType::NOW: {
- const auto currentTime = std::chrono::steady_clock::now();
- const auto currentTimeInNanoseconds =
- std::chrono::time_point_cast<std::chrono::nanoseconds>(currentTime);
- const uint64_t nanosecondsSinceEpoch =
- currentTimeInNanoseconds.time_since_epoch().count();
- deadline.nanoseconds(nanosecondsSinceEpoch);
- } break;
- case DeadlineBoundType::UNLIMITED: {
- uint64_t unlimited = std::numeric_limits<uint64_t>::max();
- deadline.nanoseconds(unlimited);
- } break;
+ case DeadlineBoundType::NOW:
+ timePoint = std::chrono::steady_clock::now();
+ break;
+ case DeadlineBoundType::UNLIMITED:
+ timePoint = std::chrono::steady_clock::time_point::max();
+ break;
+ case DeadlineBoundType::SHORT:
+ timePoint = std::chrono::steady_clock::now() + kShortDuration;
+ break;
}
+
+ OptionalTimePoint deadline;
+ deadline.nanosecondsSinceEpoch(getNanosecondsSinceEpoch(timePoint));
return deadline;
}
@@ -78,7 +87,7 @@
std::optional<DeadlineBoundType> deadlineBound) {
OptionalTimePoint deadline;
if (deadlineBound.has_value()) {
- deadline = makeOptionalTimePoint(deadlineBound.value());
+ deadline = makeDeadline(deadlineBound.value());
}
// see if service can handle model
@@ -125,11 +134,11 @@
} else {
switch (deadlineBound.value()) {
case DeadlineBoundType::NOW:
- // If the execution was launched with a deadline of NOW, the
- // deadline has already passed when the driver would launch the
- // execution. In this case, the driver must return
- // MISSED_DEADLINE_*.
- EXPECT_TRUE(prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+ case DeadlineBoundType::SHORT:
+ // Either the driver successfully completed the task or it
+ // aborted and returned MISSED_DEADLINE_*.
+ EXPECT_TRUE(prepareReturnStatus == ErrorStatus::NONE ||
+ prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
break;
case DeadlineBoundType::UNLIMITED:
@@ -143,8 +152,7 @@
ASSERT_EQ(prepareReturnStatus == ErrorStatus::NONE, preparedModel.get() != nullptr);
}
-void runPrepareModelTests(const sp<IDevice>& device, const Model& model,
- bool supportsPrepareModelDeadline) {
+void runPrepareModelTests(const sp<IDevice>& device, const Model& model) {
// test priority
for (auto priority : hidl_enum_range<Priority>{}) {
SCOPED_TRACE("priority: " + toString(priority));
@@ -153,23 +161,21 @@
}
// test deadline
- if (supportsPrepareModelDeadline) {
- for (auto deadlineBound : deadlineBounds) {
- SCOPED_TRACE("deadlineBound: " + toString(deadlineBound));
- runPrepareModelTest(device, model, kDefaultPriority, deadlineBound);
- }
+ for (auto deadlineBound : deadlineBounds) {
+ SCOPED_TRACE("deadlineBound: " + toString(deadlineBound));
+ runPrepareModelTest(device, model, kDefaultPriority, deadlineBound);
}
}
static MaybeResults executeAsynchronously(const sp<IPreparedModel>& preparedModel,
- const Request& request, DeadlineBoundType deadlineBound) {
+ const Request& request,
+ const OptionalTimePoint& deadline) {
SCOPED_TRACE("asynchronous");
const MeasureTiming measure = MeasureTiming::NO;
- const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
// launch execution
const sp<ExecutionCallback> callback = new ExecutionCallback();
- Return<ErrorStatus> ret = preparedModel->execute_1_3(request, measure, deadline, callback);
+ Return<ErrorStatus> ret = preparedModel->execute_1_3(request, measure, deadline, {}, callback);
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(ErrorStatus::NONE, ret.withDefault(ErrorStatus::GENERAL_FAILURE));
if (!ret.isOk() || ret != ErrorStatus::NONE) return std::nullopt;
@@ -185,18 +191,21 @@
}
static MaybeResults executeSynchronously(const sp<IPreparedModel>& preparedModel,
- const Request& request, DeadlineBoundType deadlineBound) {
+ const Request& request,
+ const OptionalTimePoint& deadline) {
SCOPED_TRACE("synchronous");
const MeasureTiming measure = MeasureTiming::NO;
- const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
// configure results callback
MaybeResults results;
- const auto cb = [&results](const auto&... args) { *results = {args...}; };
+ const auto cb = [&results](ErrorStatus status, const hidl_vec<OutputShape>& outputShapes,
+ const Timing& timing) {
+ results.emplace(status, outputShapes, timing);
+ };
// run execution
const Return<void> ret =
- preparedModel->executeSynchronously_1_3(request, measure, deadline, cb);
+ preparedModel->executeSynchronously_1_3(request, measure, deadline, {}, cb);
EXPECT_TRUE(ret.isOk());
if (!ret.isOk()) return std::nullopt;
@@ -207,9 +216,10 @@
void runExecutionTest(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
const Request& request, bool synchronous, DeadlineBoundType deadlineBound) {
const ExecutionFunction execute = synchronous ? executeSynchronously : executeAsynchronously;
+ const auto deadline = makeDeadline(deadlineBound);
// Perform execution and unpack results.
- const auto results = execute(preparedModel, request, deadlineBound);
+ const auto results = execute(preparedModel, request, deadline);
if (!results.has_value()) return;
const auto& [status, outputShapes, timing] = results.value();
@@ -220,13 +230,13 @@
// Validate deadline information if applicable.
switch (deadlineBound) {
case DeadlineBoundType::NOW:
- // If the execution was launched with a deadline of NOW, the
- // deadline has already passed when the driver would launch the
- // execution. In this case, the driver must return
- // MISSED_DEADLINE_*.
- ASSERT_TRUE(status == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+ case DeadlineBoundType::SHORT:
+ // Either the driver successfully completed the task or it
+ // aborted and returned MISSED_DEADLINE_*.
+ ASSERT_TRUE(status == ErrorStatus::NONE ||
+ status == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
status == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
- return;
+ break;
case DeadlineBoundType::UNLIMITED:
// If an unlimited deadline is supplied, we expect the execution to
// proceed normally. In this case, check it normally by breaking out
@@ -237,12 +247,13 @@
// If the model output operands are fully specified, outputShapes must be either
// either empty, or have the same number of elements as the number of outputs.
- ASSERT_TRUE(outputShapes.size() == 0 || outputShapes.size() == testModel.outputIndexes.size());
+ ASSERT_TRUE(outputShapes.size() == 0 ||
+ outputShapes.size() == testModel.main.outputIndexes.size());
// Go through all outputs, check returned output shapes.
for (uint32_t i = 0; i < outputShapes.size(); i++) {
EXPECT_TRUE(outputShapes[i].isSufficient);
- const auto& expect = testModel.operands[testModel.outputIndexes[i]].dimensions;
+ const auto& expect = testModel.main.operands[testModel.main.outputIndexes[i]].dimensions;
const std::vector<uint32_t> actual = outputShapes[i].dimensions;
EXPECT_EQ(expect, actual);
}
@@ -253,7 +264,9 @@
const std::vector<TestBuffer> outputs = getOutputBuffers(request10);
// We want "close-enough" results.
- checkResults(testModel, outputs);
+ if (status == ErrorStatus::NONE) {
+ checkResults(testModel, outputs);
+ }
}
void runExecutionTests(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
@@ -265,32 +278,27 @@
}
}
-void runTests(const sp<IDevice>& device, const TestModel& testModel,
- std::pair<bool, bool> supportsDeadlines) {
+void runTests(const sp<IDevice>& device, const TestModel& testModel) {
// setup
- const auto [supportsPrepareModelDeadline, supportsExecutionDeadline] = supportsDeadlines;
- if (!supportsPrepareModelDeadline && !supportsExecutionDeadline) return;
const Model model = createModel(testModel);
// run prepare model tests
- runPrepareModelTests(device, model, supportsPrepareModelDeadline);
+ runPrepareModelTests(device, model);
- if (supportsExecutionDeadline) {
- // prepare model
- sp<IPreparedModel> preparedModel;
- createPreparedModel(device, model, &preparedModel);
- if (preparedModel == nullptr) return;
+ // prepare model
+ sp<IPreparedModel> preparedModel;
+ createPreparedModel(device, model, &preparedModel);
+ if (preparedModel == nullptr) return;
- // run execution tests
- const Request request = nn::convertToV1_3(createRequest(testModel));
- runExecutionTests(preparedModel, testModel, request);
- }
+ // run execution tests
+ const Request request = nn::convertToV1_3(createRequest(testModel));
+ runExecutionTests(preparedModel, testModel, request);
}
class DeadlineTest : public GeneratedTestBase {};
TEST_P(DeadlineTest, Test) {
- runTests(kDevice, kTestModel, mSupportsDeadlines);
+ runTests(kDevice, kTestModel);
}
INSTANTIATE_GENERATED_TEST(DeadlineTest,
diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
index 1245432..8f2d4b7 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
@@ -44,18 +44,12 @@
}
static void validatePrepareModel(const sp<IDevice>& device, const std::string& message,
- const Model& model, ExecutionPreference preference,
- bool testDeadline) {
+ const Model& model, ExecutionPreference preference) {
SCOPED_TRACE(message + " [prepareModel_1_3]");
- OptionalTimePoint deadline;
- if (testDeadline) {
- deadline.nanoseconds(std::numeric_limits<uint64_t>::max());
- }
-
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
- model, preference, kDefaultPriority, deadline, hidl_vec<hidl_handle>(),
+ model, preference, kDefaultPriority, {}, hidl_vec<hidl_handle>(),
hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(prepareLaunchStatus));
@@ -79,13 +73,12 @@
// to the model does not leave this function.
static void validate(const sp<IDevice>& device, const std::string& message, Model model,
const std::function<void(Model*)>& mutation,
- ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER,
- bool testDeadline = false) {
+ ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER) {
mutation(&model);
- if (validExecutionPreference(preference) && !testDeadline) {
+ if (validExecutionPreference(preference)) {
validateGetSupportedOperations(device, message, model);
}
- validatePrepareModel(device, message, model, preference, testDeadline);
+ validatePrepareModel(device, message, model, preference);
}
static uint32_t addOperand(Model* model) {
@@ -182,6 +175,7 @@
case OperandType::TENSOR_FLOAT16:
case OperandType::TENSOR_FLOAT32:
case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
+ case OperandType::SUBGRAPH:
return 1.0f;
case OperandType::TENSOR_INT32:
return -1.0f;
@@ -220,6 +214,7 @@
case OperandType::TENSOR_FLOAT32:
case OperandType::TENSOR_INT32:
case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
+ case OperandType::SUBGRAPH:
return {1};
case OperandType::TENSOR_QUANT8_ASYMM:
return {-1, 256};
@@ -527,9 +522,15 @@
}
}
}
- // BIDIRECTIONAL_SEQUENCE_LSTM and BIDIRECTIONAL_SEQUENCE_RNN can have either one or two
- // outputs depending on their mergeOutputs parameter.
- if (operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_LSTM ||
+ // BIDIRECTIONAL_SEQUENCE_LSTM and BIDIRECTIONAL_SEQUENCE_RNN can have
+ // either one, two, three or four outputs depending on their
+ // mergeOutputs parameter and if state outputs are provided.
+ // UNIDIRECTIONAL_SEQUENCE_LSTM and UNIDIRECTIONAL_SEQUENCE_RNN can have
+ // either one or three outputs depending on whether state outputs are
+ // provided.
+ if (operation.type == OperationType::UNIDIRECTIONAL_SEQUENCE_LSTM ||
+ operation.type == OperationType::UNIDIRECTIONAL_SEQUENCE_RNN ||
+ operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_LSTM ||
operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_RNN) {
for (const size_t outOprand : operation.outputs) {
if (operand == outOprand) {
@@ -736,19 +737,9 @@
}
}
-///////////////////////// DEADLINE /////////////////////////
-
-static void deadlineTest(const sp<IDevice>& device, const Model& model) {
- const std::string message = "deadlineTest: deadline not supported";
- const auto noop = [](Model*) {};
- validate(device, message, model, noop, ExecutionPreference::FAST_SINGLE_ANSWER,
- /*testDeadline=*/true);
-}
-
////////////////////////// ENTRY POINT //////////////////////////////
-void validateModel(const sp<IDevice>& device, const Model& model,
- bool prepareModelDeadlineSupported) {
+void validateModel(const sp<IDevice>& device, const Model& model) {
mutateOperandTypeTest(device, model);
mutateOperandRankTest(device, model);
mutateOperandScaleTest(device, model);
@@ -764,9 +755,6 @@
addOperationInputTest(device, model);
addOperationOutputTest(device, model);
mutateExecutionPreferenceTest(device, model);
- if (!prepareModelDeadlineSupported) {
- deadlineTest(device, model);
- }
}
} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
index 1ddd09c..5e806e5 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
@@ -45,8 +45,7 @@
// that use the request. Note that the request here is passed by value, and any
// mutation to the request does not leave this function.
static void validate(const sp<IPreparedModel>& preparedModel, const std::string& message,
- Request request, const std::function<void(Request*)>& mutation,
- bool testDeadline = false) {
+ Request request, const std::function<void(Request*)>& mutation) {
mutation(&request);
// We'd like to test both with timing requested and without timing
@@ -59,18 +58,13 @@
};
MeasureTiming measure = (hash & 1) ? MeasureTiming::YES : MeasureTiming::NO;
- OptionalTimePoint deadline;
- if (testDeadline) {
- deadline.nanoseconds(std::numeric_limits<uint64_t>::max());
- }
-
// asynchronous
{
SCOPED_TRACE(message + " [execute_1_3]");
sp<ExecutionCallback> executionCallback = new ExecutionCallback();
Return<ErrorStatus> executeLaunchStatus =
- preparedModel->execute_1_3(request, measure, deadline, executionCallback);
+ preparedModel->execute_1_3(request, measure, {}, {}, executionCallback);
ASSERT_TRUE(executeLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeLaunchStatus));
@@ -88,7 +82,7 @@
SCOPED_TRACE(message + " [executeSynchronously_1_3]");
Return<void> executeStatus = preparedModel->executeSynchronously_1_3(
- request, measure, deadline,
+ request, measure, {}, {},
[](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes,
const Timing& timing) {
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
@@ -100,7 +94,7 @@
// burst
// TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2.
- if (!testDeadline) {
+ {
SCOPED_TRACE(message + " [burst]");
ASSERT_TRUE(nn::compliantWithV1_0(request));
@@ -142,16 +136,14 @@
// dispatch
{
SCOPED_TRACE(message + " [executeFenced]");
- Return<void> ret = preparedModel->executeFenced(
- request, {}, MeasureTiming::NO,
- [](ErrorStatus error, const hidl_handle& handle,
- const sp<IFencedExecutionCallback>& callback) {
- if (error != ErrorStatus::DEVICE_UNAVAILABLE) {
- ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
- }
- ASSERT_EQ(handle.getNativeHandle(), nullptr);
- ASSERT_EQ(callback, nullptr);
- });
+ Return<void> ret =
+ preparedModel->executeFenced(request, {}, MeasureTiming::NO, {}, {}, {},
+ [](ErrorStatus error, const hidl_handle& handle,
+ const sp<IFencedExecutionCallback>& callback) {
+ ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
+ ASSERT_EQ(handle.getNativeHandle(), nullptr);
+ ASSERT_EQ(callback, nullptr);
+ });
ASSERT_TRUE(ret.isOk());
}
}
@@ -176,29 +168,17 @@
}
}
-///////////////////////// DEADLINE ////////////////////////////////////
-
-static void deadlineTest(const sp<IPreparedModel>& preparedModel, const Request& request) {
- const std::string message = "deadlineTest: deadline not supported";
- const auto noop = [](Request*) {};
- validate(preparedModel, message, request, noop, /*testDeadline=*/true);
-}
-
///////////////////////////// ENTRY POINT //////////////////////////////////
-void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request,
- bool executionDeadlineSupported) {
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request) {
removeInputTest(preparedModel, request);
removeOutputTest(preparedModel, request);
- if (!executionDeadlineSupported) {
- deadlineTest(preparedModel, request);
- }
}
void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request) {
SCOPED_TRACE("Expecting request to fail [executeSynchronously_1_3]");
Return<void> executeStatus = preparedModel->executeSynchronously_1_3(
- request, MeasureTiming::NO, {},
+ request, MeasureTiming::NO, {}, {},
[](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
ASSERT_NE(ErrorStatus::NONE, error);
EXPECT_EQ(outputShapes.size(), 0);
diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
index c84f5b7..5b07034 100644
--- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
@@ -123,11 +123,9 @@
INSTANTIATE_DEVICE_TEST(NeuralnetworksHidlTest);
// Forward declaration from ValidateModel.cpp
-void validateModel(const sp<IDevice>& device, const Model& model,
- bool prepareModelDeadlineSupported);
+void validateModel(const sp<IDevice>& device, const Model& model);
// Forward declaration from ValidateRequest.cpp
-void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request,
- bool executionDeadlineSupported);
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request);
// Forward declaration from ValidateRequest.cpp
void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request);
// Forward declaration from ValidateBurst.cpp
@@ -136,31 +134,26 @@
// Validate sync_fence handles for dispatch with valid input
void validateExecuteFenced(const sp<IPreparedModel>& preparedModel, const Request& request) {
SCOPED_TRACE("Expecting request to fail [executeFenced]");
- Return<void> ret_null =
- preparedModel->executeFenced(request, {hidl_handle(nullptr)}, V1_2::MeasureTiming::NO,
- [](ErrorStatus error, const hidl_handle& handle,
- const sp<IFencedExecutionCallback>& callback) {
- // TODO: fix this once sample driver impl is merged.
- if (error != ErrorStatus::DEVICE_UNAVAILABLE) {
- ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
- }
- ASSERT_EQ(handle.getNativeHandle(), nullptr);
- ASSERT_EQ(callback, nullptr);
- });
+ Return<void> ret_null = preparedModel->executeFenced(
+ request, {hidl_handle(nullptr)}, V1_2::MeasureTiming::NO, {}, {}, {},
+ [](ErrorStatus error, const hidl_handle& handle,
+ const sp<IFencedExecutionCallback>& callback) {
+ ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
+ ASSERT_EQ(handle.getNativeHandle(), nullptr);
+ ASSERT_EQ(callback, nullptr);
+ });
ASSERT_TRUE(ret_null.isOk());
}
-void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request,
- std::pair<bool, bool> supportsDeadlines) {
- const auto [prepareModelDeadlineSupported, executionDeadlineSupported] = supportsDeadlines;
- validateModel(device, model, prepareModelDeadlineSupported);
+void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request) {
+ validateModel(device, model);
// Create IPreparedModel.
sp<IPreparedModel> preparedModel;
createPreparedModel(device, model, &preparedModel);
if (preparedModel == nullptr) return;
- validateRequest(preparedModel, request, executionDeadlineSupported);
+ validateRequest(preparedModel, request);
validateExecuteFenced(preparedModel, request);
// TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2.
@@ -169,12 +162,10 @@
validateBurst(preparedModel, request10);
}
-void validateFailure(const sp<IDevice>& device, const Model& model, const Request& request,
- std::pair<bool, bool> supportsDeadlines) {
- const bool prepareModelDeadlineSupported = supportsDeadlines.first;
+void validateFailure(const sp<IDevice>& device, const Model& model, const Request& request) {
// TODO: Should this always succeed?
// What if the invalid input is part of the model (i.e., a parameter).
- validateModel(device, model, prepareModelDeadlineSupported);
+ validateModel(device, model);
// Create IPreparedModel.
sp<IPreparedModel> preparedModel;
@@ -188,9 +179,9 @@
const Model model = createModel(kTestModel);
const Request request = nn::convertToV1_3(createRequest(kTestModel));
if (kTestModel.expectFailure) {
- validateFailure(kDevice, model, request, mSupportsDeadlines);
+ validateFailure(kDevice, model, request);
} else {
- validateEverything(kDevice, model, request, mSupportsDeadlines);
+ validateEverything(kDevice, model, request);
}
}
diff --git a/power/aidl/android/hardware/power/Boost.aidl b/power/aidl/android/hardware/power/Boost.aidl
index 162a36a..c992fd3 100644
--- a/power/aidl/android/hardware/power/Boost.aidl
+++ b/power/aidl/android/hardware/power/Boost.aidl
@@ -29,6 +29,13 @@
INTERACTION,
/**
+ * This boost indicates that the framework is likely to provide a new
+ * display frame soon. This implies that the device should ensure that the
+ * display processing path is powered up and ready to receive that update.
+ */
+ DISPLAY_UPDATE_IMMINENT,
+
+ /**
* Below hints are currently not sent in Android framework but OEM might choose to
* implement for power/perf optimizations.
*/
diff --git a/power/aidl/android/hardware/power/IPower.aidl b/power/aidl/android/hardware/power/IPower.aidl
index 9fb3fc0..2c4bd86 100644
--- a/power/aidl/android/hardware/power/IPower.aidl
+++ b/power/aidl/android/hardware/power/IPower.aidl
@@ -43,7 +43,7 @@
*/
boolean isModeSupported(in Mode type);
- /**
+ /**
* setBoost() indicates the device may need to boost some resources, as the
* the load is likely to increase before the kernel governors can react.
* Depending on the boost, it may be appropriate to raise the frequencies of
diff --git a/power/aidl/android/hardware/power/Mode.aidl b/power/aidl/android/hardware/power/Mode.aidl
index 9bb5b98..ae113e3 100644
--- a/power/aidl/android/hardware/power/Mode.aidl
+++ b/power/aidl/android/hardware/power/Mode.aidl
@@ -26,20 +26,64 @@
DOUBLE_TAP_TO_WAKE,
/**
- * This mode indidates Low power mode is activated or not. Low power
+ * This mode indicates Low power mode is activated or not. Low power
* mode is intended to save battery at the cost of performance.
*/
LOW_POWER,
/**
- * This mode indidates Sustained Performance mode is activated or not.
+ * This mode indicates Sustained Performance mode is activated or not.
* Sustained performance mode is intended to provide a consistent level of
* performance for a prolonged amount of time.
*/
SUSTAINED_PERFORMANCE,
/**
- * This mode indidates VR Mode is activated or not. VR mode is intended
+ * Sets the device to a fixed performance level which can be sustained under
+ * normal indoor conditions for at least 10 minutes.
+ *
+ * This is similar to sustained performance mode, except that whereas
+ * sustained performance mode puts an upper bound on performance in the
+ * interest of long-term stability, fixed performance mode puts both upper
+ * and lower bounds on performance such that any workload run while in a
+ * fixed performance mode should complete in a repeatable amount of time
+ * (except if the device is under thermal throttling).
+ *
+ * This mode is not intended for general purpose use, but rather to enable
+ * games and other performance-sensitive applications to reduce the number
+ * of variables during profiling and performance debugging. As such, while
+ * it is valid to set the device to minimum clocks for all subsystems in
+ * this mode, it is preferable to attempt to make the relative performance
+ * of the CPU, GPU, and other subsystems match typical usage, even if the
+ * frequencies have to be reduced to provide sustainability.
+ *
+ * To calibrate this mode, follow these steps:
+ *
+ * 1) Build and push the HWUI macrobench as described in
+ * //frameworks/base/libs/hwui/tests/macrobench/how_to_run.txt
+ * 2) Run the macrobench as follows:
+ * while true; do \
+ * adb shell /data/benchmarktest/hwuimacro/hwuimacro shadowgrid2 -c 200 -r 10; \
+ * done
+ * 3) Determine a fixed set of device clocks such that the loop in (2) can
+ * run for at least 10 minutes, starting from an idle device on a desk
+ * at room temperature (roughly 22 Celsius), without hitting thermal
+ * throttling.
+ * 4) After setting those clocks, set the system property
+ * ro.power.fixed_performance_scale_factor to a value N, where N is the
+ * number of times the loop from (2) runs during the 10 minute test
+ * cycle. It is expected that in FIXED_PERFORMANCE mode, unless there is
+ * thermal throttling, the loop will run N to N+1 times (inclusive).
+ *
+ * After calibrating this, while in FIXED_PERFORMANCE mode, the macrobench
+ * results obtained while running the loop in (2) should be consistent both
+ * within a given run and from the first run in the 10 minute window through
+ * the last run in the window.
+ */
+ FIXED_PERFORMANCE,
+
+ /**
+ * This mode indicates VR Mode is activated or not. VR mode is intended
* to provide minimum guarantee for performance for the amount of time the
* device can sustain it.
*/
@@ -76,6 +120,18 @@
*/
INTERACTIVE,
+ /**
+ * This mode indicates the device is in device idle, externally known as doze.
+ * More details on:
+ * https://developer.android.com/training/monitoring-device-state/doze-standby
+ */
+ DEVICE_IDLE,
+
+ /**
+ * This mode indicates that display is either off or still on but is optimized
+ * for low-power.
+ */
+ DISPLAY_INACTIVE,
/**
* Below hints are currently not sent in Android framework but OEM might choose to
diff --git a/power/aidl/vts/VtsHalPowerTargetTest.cpp b/power/aidl/vts/VtsHalPowerTargetTest.cpp
index c0e0858..25a385e 100644
--- a/power/aidl/vts/VtsHalPowerTargetTest.cpp
+++ b/power/aidl/vts/VtsHalPowerTargetTest.cpp
@@ -16,6 +16,7 @@
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
+#include <android-base/properties.h>
#include <android/hardware/power/Boost.h>
#include <android/hardware/power/IPower.h>
#include <android/hardware/power/Mode.h>
@@ -27,6 +28,7 @@
using android::ProcessState;
using android::sp;
using android::String16;
+using android::base::GetUintProperty;
using android::binder::Status;
using android::hardware::power::Boost;
using android::hardware::power::IPower;
@@ -77,7 +79,7 @@
for (const auto& mode : kInvalidModes) {
bool supported;
ASSERT_TRUE(power->isModeSupported(mode, &supported).isOk());
- // Should return false for values outsides enum
+ // Should return false for values outside enum
ASSERT_FALSE(supported);
}
}
@@ -103,11 +105,27 @@
for (const auto& boost : kInvalidBoosts) {
bool supported;
ASSERT_TRUE(power->isBoostSupported(boost, &supported).isOk());
- // Should return false for values outsides enum
+ // Should return false for values outside enum
ASSERT_FALSE(supported);
}
}
+// FIXED_PERFORMANCE mode is required for all devices which ship on Android 11
+// or later
+TEST_P(PowerAidl, hasFixedPerformance) {
+ auto apiLevel = GetUintProperty<uint64_t>("ro.product.first_api_level", 0);
+ if (apiLevel == 0) {
+ apiLevel = GetUintProperty<uint64_t>("ro.build.version.sdk", 0);
+ }
+ ASSERT_NE(apiLevel, 0);
+
+ if (apiLevel >= 30) {
+ bool supported;
+ ASSERT_TRUE(power->isModeSupported(Mode::FIXED_PERFORMANCE, &supported).isOk());
+ ASSERT_TRUE(supported);
+ }
+}
+
INSTANTIATE_TEST_SUITE_P(Power, PowerAidl,
testing::ValuesIn(android::getAidlHalInstanceNames(IPower::descriptor)),
android::PrintInstanceNameToString);
diff --git a/radio/1.1/vts/functional/radio_hidl_hal_api.cpp b/radio/1.1/vts/functional/radio_hidl_hal_api.cpp
index 75abbbf..02dcbab 100644
--- a/radio/1.1/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.1/vts/functional/radio_hidl_hal_api.cpp
@@ -24,6 +24,9 @@
/* Record the sim card state for the testing environment */
CardState cardStateForTest = cardStatus.cardState;
+#if 0
+ /* This test has to be removed for Japan Model.
+ * After "setSimCardPower power down", Japan model can not "setSimCardPower power up" */
/* Test setSimCardPower power down */
serial = GetRandomSerialNumber();
radio_v1_1->setSimCardPower_1_1(serial, CardPowerState::POWER_DOWN);
@@ -46,6 +49,7 @@
}
EXPECT_EQ(CardState::ABSENT, cardStatus.cardState);
}
+#endif
/* Test setSimCardPower power up */
serial = GetRandomSerialNumber();
diff --git a/radio/1.3/vts/functional/radio_hidl_hal_api.cpp b/radio/1.3/vts/functional/radio_hidl_hal_api.cpp
index 4e48141..ca64305 100644
--- a/radio/1.3/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.3/vts/functional/radio_hidl_hal_api.cpp
@@ -75,38 +75,3 @@
radioRsp_v1_3->rspInfo.error,
{RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
}
-
-/*
- * Test IRadio.setSystemSelectionChannels() for the response returned.
- *
- * This test is excluded from manifest, due to non-implementation in Q. Tracked by b/130254624.
- */
-TEST_P(RadioHidlTest_v1_3, setSystemSelectionChannels) {
- serial = GetRandomSerialNumber();
-
- RadioAccessSpecifier specifier = {.radioAccessNetwork = RadioAccessNetworks::GERAN,
- .geranBands = {GeranBands::BAND_450, GeranBands::BAND_480},
- .channels = {1, 2}};
-
- Return<void> res = radio_v1_3->setSystemSelectionChannels(serial, true, {specifier});
- ASSERT_OK(res);
- EXPECT_EQ(std::cv_status::no_timeout, wait());
- EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_3->rspInfo.type);
- EXPECT_EQ(serial, radioRsp_v1_3->rspInfo.serial);
- ALOGI("setSystemSelectionChannels, rspInfo.error = %s\n",
- toString(radioRsp_v1_3->rspInfo.error).c_str());
- ASSERT_TRUE(CheckAnyOfErrors(
- radioRsp_v1_3->rspInfo.error,
- {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::INTERNAL_ERR}));
-
- if (radioRsp_v1_3->rspInfo.error == RadioError::NONE) {
- Return<void> res = radio_v1_3->setSystemSelectionChannels(serial, false, {specifier});
- ASSERT_OK(res);
- EXPECT_EQ(std::cv_status::no_timeout, wait());
- EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_3->rspInfo.type);
- EXPECT_EQ(serial, radioRsp_v1_3->rspInfo.serial);
- ALOGI("setSystemSelectionChannels, rspInfo.error = %s\n",
- toString(radioRsp_v1_3->rspInfo.error).c_str());
- EXPECT_EQ(RadioError::NONE, radioRsp_v1_3->rspInfo.error);
- }
-}
\ No newline at end of file
diff --git a/radio/1.5/IRadio.hal b/radio/1.5/IRadio.hal
index bc40500..0b50436 100644
--- a/radio/1.5/IRadio.hal
+++ b/radio/1.5/IRadio.hal
@@ -18,10 +18,8 @@
import @1.0::CdmaSmsMessage;
import @1.2::DataRequestReason;
-import @1.4::DataProfileInfo;
import @1.4::IRadio;
import @1.5::AccessNetwork;
-import @1.5::BarringInfo;
import @1.5::DataProfileInfo;
import @1.5::IndicationFilter;
import @1.5::LinkAddress;
@@ -71,6 +69,35 @@
SignalThresholdInfo signalThresholdInfo, AccessNetwork accessNetwork);
/**
+ * Sets the link capacity reporting criteria.
+ *
+ * The resulting reporting criteria are the AND of all the supplied criteria.
+ *
+ * Note: Reporting criteria must be individually set for each RAN. If unset, reporting criteria
+ * for that RAN are implementation-defined.
+ *
+ * Response callback is IRadioResponse.setLinkCapacityReportingCriteriaResponse_1_5().
+ *
+ * @param serial Serial number of request.
+ * @param hysteresisMs A hysteresis time in milliseconds to prevent flapping. A value of 0
+ * disables hysteresis.
+ * @param hysteresisDlKbps An interval in kbps defining the required magnitude change between DL
+ * reports. hysteresisDlKbps must be smaller than the smallest threshold delta. A value of 0
+ * disables hysteresis.
+ * @param hysteresisUlKbps An interval in kbps defining the required magnitude change between UL
+ * reports. hysteresisUlKbps must be smaller than the smallest threshold delta. A value of 0
+ * disables hysteresis.
+ * @param thresholdsDownlinkKbps A vector of trigger thresholds in kbps for downlink reports. A
+ * vector size of 0 disables the use of DL thresholds for reporting.
+ * @param thresholdsUplinkKbps A vector of trigger thresholds in kbps for uplink reports. A
+ * vector size of 0 disables the use of UL thresholds for reporting.
+ * @param accessNetwork The type of network for which to apply these thresholds.
+ */
+ oneway setLinkCapacityReportingCriteria_1_5(int32_t serial, int32_t hysteresisMs,
+ int32_t hysteresisDlKbps, int32_t hysteresisUlKbps, vec<int32_t> thresholdsDownlinkKbps,
+ vec<int32_t> thresholdsUplinkKbps, AccessNetwork accessNetwork);
+
+ /**
* Enable or disable UiccApplications on the SIM. If disabled:
* - Modem will not register on any network.
* - SIM must be PRESENT, and the IccId of the SIM must still be accessible.
@@ -118,13 +145,13 @@
vec<RadioAccessSpecifier> specifiers);
/**
- * Starts a network scan
+ * Starts a network scan.
*
* @param serial Serial number of request.
* @param request Defines the radio networks/bands/channels which need to be scanned.
*
- * Same API as @1.4::IRadio.startNetworkScan_1_4, except using
- * 1.5 version of NetworkScanRequest
+ * Same API as @1.4::IRadio.startNetworkScan_1_4, except using the
+ * 1.5 NetworkScanRequest as the input param.
*/
oneway startNetworkScan_1_5(int32_t serial, NetworkScanRequest request);
@@ -166,14 +193,14 @@
* Response function is IRadioResponse.setupDataCallResponse_1_5()
*
* Note this API is the same as the 1.4 version except using the
- * 1.5 AccessNetwork, DataProfileInto, and link addresses as the input param.
+ * 1.5 AccessNetwork, DataProfileInto, and LinkAddress as the input param.
*/
oneway setupDataCall_1_5(int32_t serial, AccessNetwork accessNetwork,
DataProfileInfo dataProfileInfo, bool roamingAllowed,
DataRequestReason reason, vec<LinkAddress> addresses, vec<string> dnses);
/**
- * Set an apn to initial attach network
+ * Set an APN to initial attach network.
*
* @param serial Serial number of request.
* @param dataProfileInfo data profile containing APN settings
@@ -189,7 +216,7 @@
* Send data profiles of the current carrier to the modem.
*
* @param serial Serial number of request.
- * @param profiles Array of DataProfile to set.
+ * @param profiles Array of DataProfileInfo to set.
*
* Response callback is IRadioResponse.setDataProfileResponse_1_5()
*
@@ -230,7 +257,7 @@
*
* Prevents the reporting of specified unsolicited indications from the radio. This is used
* for power saving in instances when those indications are not needed. If unset, defaults to
- * @1.2::IndicationFilter:ALL.
+ * @1.5::IndicationFilter:ALL.
*
* @param serial Serial number of request.
* @param indicationFilter 32-bit bitmap of IndicationFilter. Bits set to 1 indicate the
@@ -250,7 +277,7 @@
oneway getBarringInfo(int32_t serial);
/**
- * Request current voice registration state
+ * Request current voice registration state.
*
* @param serial Serial number of request.
*
@@ -259,7 +286,7 @@
oneway getVoiceRegistrationState_1_5(int32_t serial);
/**
- * Request current data registration state
+ * Request current data registration state.
*
* @param serial Serial number of request.
*
diff --git a/radio/1.5/IRadioResponse.hal b/radio/1.5/IRadioResponse.hal
index 6a2187f..e87cad2 100644
--- a/radio/1.5/IRadioResponse.hal
+++ b/radio/1.5/IRadioResponse.hal
@@ -20,11 +20,11 @@
import @1.0::SendSmsResult;
import @1.4::IRadioResponse;
import @1.5::BarringInfo;
+import @1.5::CellIdentity;
import @1.5::CellInfo;
+import @1.5::PersoSubstate;
import @1.5::RegStateResult;
import @1.5::SetupDataCallResult;
-import @1.4::SetupDataCallResult;
-import @1.5::PersoSubstate;
/**
* Interface declaring response functions to solicited radio requests.
@@ -45,6 +45,17 @@
*
* Valid errors returned:
* RadioError:NONE
+ * RadioError:INVALID_ARGUMENTS
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:INTERNAL_ERR
+ */
+ oneway setLinkCapacityReportingCriteriaResponse_1_5(RadioResponseInfo info);
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ *
+ * Valid errors returned:
+ * RadioError:NONE
* RadioError:SIM_ABSENT
* RadioError:RADIO_NOT_AVAILABLE
* RadioError:INTERNAL_ERR
@@ -166,6 +177,7 @@
/**
* @param info Response info struct containing response type, serial no. and error
+ * @param cellIdentity CellIdentity for the barring infos.
* @param barringInfos a vector of barring info for all barring service types
*
* Valid errors returned:
@@ -174,9 +186,11 @@
* RadioError:INTERNAL_ERR
* RadioError:MODEM_ERR
*/
- oneway getBarringInfoResponse(RadioResponseInfo info, vec<BarringInfo> barringInfos);
+ oneway getBarringInfoResponse(RadioResponseInfo info, CellIdentity cellIdentity,
+ vec<BarringInfo> barringInfos);
/**
+ * @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
*
@@ -215,7 +229,6 @@
*/
oneway getCellInfoListResponse_1_5(RadioResponseInfo info, vec<CellInfo> cellInfo);
-
/**
* @param info Response info struct containing response type, serial no. and error
*
diff --git a/radio/1.5/types.hal b/radio/1.5/types.hal
index cf195cc..4d3c2d5 100644
--- a/radio/1.5/types.hal
+++ b/radio/1.5/types.hal
@@ -19,19 +19,15 @@
import @1.0::ApnAuthType;
import @1.0::DataProfileId;
import @1.0::DataProfileInfoType;
-import @1.0::CdmaSignalStrength;
-import @1.0::EvdoSignalStrength;
import @1.0::GsmSignalStrength;
import @1.0::LteSignalStrength;
import @1.0::PersoSubstate;
-import @1.0::RadioAccessFamily;
import @1.0::RadioError;
import @1.0::RegState;
import @1.0::TimeStampType;
import @1.1::EutranBands;
import @1.1::GeranBands;
import @1.1::RadioAccessNetworks;
-import @1.1::RadioAccessSpecifier;
import @1.1::ScanStatus;
import @1.1::ScanType;
import @1.1::UtranBands;
@@ -43,7 +39,6 @@
import @1.2::CellIdentityLte;
import @1.2::CellInfoCdma;
import @1.2::IndicationFilter;
-import @1.2::NetworkScanRequest;
import @1.2::TdscdmaSignalStrength;
import @1.2::WcdmaSignalStrength;
import @1.4::AccessNetwork;
@@ -51,11 +46,11 @@
import @1.4::CellIdentityNr;
import @1.4::DataCallFailCause;
import @1.4::DataConnActiveStatus;
-import @1.4::DataProfileInfo;
import @1.4::LteVopsInfo;
import @1.4::NrIndicators;
import @1.4::NrSignalStrength;
import @1.4::PdpProtocolType;
+import @1.4::RadioAccessFamily;
import @1.4::RadioTechnology;
import android.hidl.safe_union@1.0::Monostate;
@@ -131,7 +126,7 @@
/** Signal Measurement Type */
SignalMeasurementType signalMeasurement;
- /** A hysteresis time in milliseconds to prevent flapping. A value of 0 disables hysteresis */
+ /** A hysteresis time in milliseconds to prevent flapping. A value of 0 disables hysteresis. */
int32_t hysteresisMs;
/**
@@ -161,7 +156,8 @@
enum AccessNetwork : @1.4::AccessNetwork {
/**
- * Next-Generation Radio Access Network (NGRAN)
+ * Next-Generation Radio Access Network (NGRAN).
+ * Note NGRAN is only for standalone mode. Non-standalone mode uses AccessNetwork EUTRAN.
*/
NGRAN = 6,
};
@@ -175,7 +171,7 @@
};
/**
- * Overwritten from @1.1::RadioAccessSpecifier to add NGRAN and NgranBands
+ * Overwritten from @1.1::RadioAccessSpecifier to add NGRAN and NgranBands.
*/
struct RadioAccessSpecifier {
/**
@@ -264,8 +260,7 @@
};
/**
- * Overwritten from @1.2::NetworkScanRequest to update
- * RadioAccessSpecifier to 1.5 version
+ * Overwritten from @1.2::NetworkScanRequest to update RadioAccessSpecifier to 1.5 version.
*/
struct NetworkScanRequest {
ScanType type;
@@ -328,11 +323,10 @@
};
/**
- * Extended from @1.4::DataProfileInfo to update ApnTypes to 1.5 version and replace mtu with
+ * Overwritten from @1.4::DataProfileInfo to update ApnTypes to 1.5 version and replace mtu with
* mtuV4 and mtuV6. In the future, this must be extended instead of overwritten.
*/
struct DataProfileInfo {
-
/** ID of the data profile. */
DataProfileId profileId;
@@ -445,8 +439,8 @@
};
/**
- * Overwritten from @1.4::SetupDataCallResult in order to update the addresses to 1.5
- * version. In 1.5 the type of addresses changes to vector of LinkAddress, and mtu is replaced by
+ * Overwritten from @1.4::SetupDataCallResult in order to update the addresses to 1.5 version.
+ * In 1.5 the type of addresses changes to vector of LinkAddress, and mtu is replaced by
* mtuV4 and mtuV6.
*/
struct SetupDataCallResult {
@@ -687,7 +681,7 @@
} ratSpecificInfo;
};
-/** A union representing the CellIdentity of a single cell */
+/** A union representing the CellIdentity of a single cell. */
safe_union CellIdentity {
Monostate noinit;
@@ -699,125 +693,116 @@
CellIdentityNr nr;
};
-/**
- * Combined list of barring services for UTRAN, EUTRAN, and NGRAN.
- *
- * Barring information is defined in:
- * -UTRAN - 3gpp 25.331 Sec 10.2.48.8.6.
- * -EUTRAN - 3gpp 36.331 Sec 6.3.1 SystemInformationBlockType2
- * -NGRAN - 3gpp 38.331 Sec 6.3.2 UAC-BarringInfo and 22.261 Sec 6.22.2.[2-3]
- */
-enum BarringServiceType : int32_t {
- /** Applicabe to UTRAN */
- /** Barring for all CS services, including registration */
- CS_SERVICE,
- /** Barring for all PS services, including registration */
- PS_SERVICE,
- /** Barring for mobile-originated circuit-switched voice calls */
- CS_VOICE,
-
- /** Applicable to EUTRAN, NGRAN */
- /** Barring for mobile-originated signalling for any purpose */
- MO_SIGNALLING,
- /** Barring for mobile-originated internet or other interactive data */
- MO_DATA,
- /** Barring for circuit-switched fallback calling */
- CS_FALLBACK,
- /** Barring for IMS voice calling */
- MMTEL_VOICE,
- /** Barring for IMS video calling */
- MMTEL_VIDEO,
-
- /** Applicable to UTRAN, EUTRAN, NGRAN */
- /** Barring for emergency services, either CS or emergency MMTEL */
- EMERGENCY,
- /** Barring for short message services */
- SMS,
-
- /** Operator-specific barring codes; applicable to NGRAN */
- OPERATOR_1 = 1001,
- OPERATOR_2 = 1002,
- OPERATOR_3 = 1003,
- OPERATOR_4 = 1004,
- OPERATOR_5 = 1005,
- OPERATOR_6 = 1006,
- OPERATOR_7 = 1007,
- OPERATOR_8 = 1008,
- OPERATOR_9 = 1009,
- OPERATOR_10 = 1010,
- OPERATOR_11 = 1011,
- OPERATOR_12 = 1012,
- OPERATOR_13 = 1013,
- OPERATOR_14 = 1014,
- OPERATOR_15 = 1015,
- OPERATOR_16 = 1016,
- OPERATOR_17 = 1017,
- OPERATOR_18 = 1018,
- OPERATOR_19 = 1019,
- OPERATOR_20 = 1020,
- OPERATOR_21 = 1021,
- OPERATOR_22 = 1022,
- OPERATOR_23 = 1023,
- OPERATOR_24 = 1024,
- OPERATOR_25 = 1025,
- OPERATOR_26 = 1026,
- OPERATOR_27 = 1027,
- OPERATOR_28 = 1028,
- OPERATOR_29 = 1029,
- OPERATOR_30 = 1030,
- OPERATOR_31 = 1031,
- OPERATOR_32 = 1032,
-};
-
-enum BarringType : int32_t {
- /** Device is not barred for the given service */
- NONE,
- /** Device may be barred based on time and probability factors */
- CONDITIONAL,
- /* Device is unconditionally barred */
- UNCONDITIONAL,
-};
-
-struct ConditionalBarringInfo {
- /** The barring factor as a percentage 0-100 */
- int32_t barringFactor;
-
- /** The number of seconds between re-evaluations of barring */
- int32_t barringTimeSeconds;
-
- /**
- * Indicates whether barring is currently being applied.
- *
- * <p>True if the UE applies barring to a conditionally barred
- * service based on the conditional barring parameters.
- *
- * <p>False if the service is conditionally barred but barring
- * is not currently applied, which could be due to either the
- * barring criteria not having been evaluated (if the UE has not
- * attempted to use the service) or due to the criteria being
- * evaluated and the UE being permitted to use the service
- * despite conditional barring.
- */
- bool isBarred;
-};
-
-safe_union BarringTypeSpecificInfo {
- /** Barring type is either none or unconditional */
- Monostate noinit;
-
- /** Must be included if barring is conditional */
- ConditionalBarringInfo conditionalBarringInfo;
-};
-
struct BarringInfo {
- /** Barring service */
- BarringServiceType service;
+ /**
+ * Combined list of barring services for UTRAN, EUTRAN, and NGRAN.
+ *
+ * Barring information is defined in:
+ * -UTRAN - 3gpp 25.331 Sec 10.2.48.8.6.
+ * -EUTRAN - 3gpp 36.331 Sec 6.3.1 SystemInformationBlockType2
+ * -NGRAN - 3gpp 38.331 Sec 6.3.2 UAC-BarringInfo and 22.261 Sec 6.22.2.[2-3]
+ */
+ enum ServiceType : int32_t {
+ /** Applicable to UTRAN */
+ /** Barring for all CS services, including registration */
+ CS_SERVICE,
+ /** Barring for all PS services, including registration */
+ PS_SERVICE,
+ /** Barring for mobile-originated circuit-switched voice calls */
+ CS_VOICE,
+
+ /** Applicable to EUTRAN, NGRAN */
+ /** Barring for mobile-originated signalling for any purpose */
+ MO_SIGNALLING,
+ /** Barring for mobile-originated internet or other interactive data */
+ MO_DATA,
+ /** Barring for circuit-switched fallback calling */
+ CS_FALLBACK,
+ /** Barring for IMS voice calling */
+ MMTEL_VOICE,
+ /** Barring for IMS video calling */
+ MMTEL_VIDEO,
+
+ /** Applicable to UTRAN, EUTRAN, NGRAN */
+ /** Barring for emergency services, either CS or emergency MMTEL */
+ EMERGENCY,
+ /** Barring for short message services */
+ SMS,
+
+ /** Operator-specific barring codes; applicable to NGRAN */
+ OPERATOR_1 = 1001,
+ OPERATOR_2 = 1002,
+ OPERATOR_3 = 1003,
+ OPERATOR_4 = 1004,
+ OPERATOR_5 = 1005,
+ OPERATOR_6 = 1006,
+ OPERATOR_7 = 1007,
+ OPERATOR_8 = 1008,
+ OPERATOR_9 = 1009,
+ OPERATOR_10 = 1010,
+ OPERATOR_11 = 1011,
+ OPERATOR_12 = 1012,
+ OPERATOR_13 = 1013,
+ OPERATOR_14 = 1014,
+ OPERATOR_15 = 1015,
+ OPERATOR_16 = 1016,
+ OPERATOR_17 = 1017,
+ OPERATOR_18 = 1018,
+ OPERATOR_19 = 1019,
+ OPERATOR_20 = 1020,
+ OPERATOR_21 = 1021,
+ OPERATOR_22 = 1022,
+ OPERATOR_23 = 1023,
+ OPERATOR_24 = 1024,
+ OPERATOR_25 = 1025,
+ OPERATOR_26 = 1026,
+ OPERATOR_27 = 1027,
+ OPERATOR_28 = 1028,
+ OPERATOR_29 = 1029,
+ OPERATOR_30 = 1030,
+ OPERATOR_31 = 1031,
+ OPERATOR_32 = 1032,
+ } serviceType;
/** The type of barring applied to the service */
- BarringType type;
+ enum BarringType : int32_t {
+ /** Device is not barred for the given service */
+ NONE,
+ /** Device may be barred based on time and probability factors */
+ CONDITIONAL,
+ /* Device is unconditionally barred */
+ UNCONDITIONAL,
+ } barringType;
/** Type-specific barring info if applicable */
- BarringTypeSpecificInfo typeSpecificInfo;
+ safe_union BarringTypeSpecificInfo {
+ /** Barring type is either none or unconditional */
+ Monostate noinit;
+
+ /** Must be included if barring is conditional */
+ struct Conditional {
+ /** The barring factor as a percentage 0-100 */
+ int32_t factor;
+
+ /** The number of seconds between re-evaluations of barring */
+ int32_t timeSeconds;
+
+ /**
+ * Indicates whether barring is currently being applied.
+ *
+ * <p>True if the UE applies barring to a conditionally barred
+ * service based on the conditional barring parameters.
+ *
+ * <p>False if the service is conditionally barred but barring
+ * is not currently applied, which could be due to either the
+ * barring criteria not having been evaluated (if the UE has not
+ * attempted to use the service) or due to the criteria being
+ * evaluated and the UE being permitted to use the service
+ * despite conditional barring.
+ */
+ bool isBarred;
+ } conditional;
+ } barringTypeSpecificInfo;
};
enum IndicationFilter : @1.2::IndicationFilter {
@@ -963,14 +948,14 @@
string registeredPlmn;
/**
- * Access-technology-specific registration information, such as for Cdma2000.
+ * Access-technology-specific registration information, such as for CDMA2000.
*/
safe_union AccessTechnologySpecificInfo {
Monostate noinit;
struct Cdma2000RegistrationInfo {
/**
- * concurrent services support indicator. if registered on a CDMA system.
+ * Concurrent services support indicator. if registered on a CDMA system.
* false - Concurrent services not supported,
* true - Concurrent services supported
*/
@@ -999,10 +984,10 @@
struct EutranRegistrationInfo {
/**
* Network capabilities for voice over PS services. This info is valid only on LTE
- * network and must be present when device is camped on LTE. vopsInfo must be empty when
+ * network and must be present when device is camped on LTE. VopsInfo must be empty when
* device is camped only on 2G/3G.
*/
- LteVopsInfo lteVopsInfo; // LTE network capability
+ LteVopsInfo lteVopsInfo;
/**
* The parameters of NR 5G Non-Standalone. This value is only valid on E-UTRAN,
@@ -1032,17 +1017,20 @@
};
/**
- * Additional personalization categories in addition to those specified in 3GPP TS 22.022 and 3GPP2 C.S0068-0.
+ * Additional personalization categories in addition to those specified in 3GPP TS 22.022 and
+ * 3GPP2 C.S0068-0.
*/
enum PersoSubstate : @1.0::PersoSubstate {
SIM_SPN,
SIM_SPN_PUK,
- SIM_SP_EHPLMN, // Equivalent Home PLMN
+ /** Equivalent Home PLMN */
+ SIM_SP_EHPLMN,
SIM_SP_EHPLMN_PUK,
SIM_ICCID,
SIM_ICCID_PUK,
SIM_IMPI,
SIM_IMPI_PUK,
- SIM_NS_SP, // Network subset service provider
+ /** Network subset service provider */
+ SIM_NS_SP,
SIM_NS_SP_PUK,
};
diff --git a/radio/1.5/vts/functional/Android.bp b/radio/1.5/vts/functional/Android.bp
index 182985e..85c4f99 100644
--- a/radio/1.5/vts/functional/Android.bp
+++ b/radio/1.5/vts/functional/Android.bp
@@ -34,7 +34,6 @@
"android.hardware.radio@1.0",
"android.hardware.radio.config@1.0",
"android.hardware.radio.config@1.1",
- "android.hardware.radio.config@1.3",
],
header_libs: ["radio.util.header@1.0"],
test_suites: ["general-tests"]
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
index 09305de..435bd23 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
@@ -310,6 +310,99 @@
}
/*
+ * Test IRadio.setLinkCapacityReportingCriteria_1_5() invalid hysteresisDlKbps
+ */
+TEST_F(RadioHidlTest_v1_5, setLinkCapacityReportingCriteria_1_5_invalidHysteresisDlKbps) {
+ serial = GetRandomSerialNumber();
+
+ Return<void> res = radio_v1_5->setLinkCapacityReportingCriteria_1_5(
+ serial, 5000,
+ 5000, // hysteresisDlKbps too big for thresholds delta
+ 100, {1000, 5000, 10000, 20000}, {500, 1000, 5000, 10000},
+ ::android::hardware::radio::V1_5::AccessNetwork::GERAN);
+ ASSERT_OK(res);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_5->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial);
+
+ ALOGI("setLinkCapacityReportingCriteria_1_5_invalidHysteresisDlKbps, rspInfo.error = %s\n",
+ toString(radioRsp_v1_5->rspInfo.error).c_str());
+ // Allow REQUEST_NOT_SUPPORTED as setLinkCapacityReportingCriteria_1_5() may not be supported
+ // for GERAN
+ ASSERT_TRUE(
+ CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error,
+ {RadioError::INVALID_ARGUMENTS, RadioError::REQUEST_NOT_SUPPORTED}));
+}
+
+/*
+ * Test IRadio.setLinkCapacityReportingCriteria_1_5() invalid hysteresisUlKbps
+ */
+TEST_F(RadioHidlTest_v1_5, setLinkCapacityReportingCriteria_1_5_invalidHysteresisUlKbps) {
+ serial = GetRandomSerialNumber();
+
+ Return<void> res = radio_v1_5->setLinkCapacityReportingCriteria_1_5(
+ serial, 5000, 500,
+ 1000, // hysteresisUlKbps too big for thresholds delta
+ {1000, 5000, 10000, 20000}, {500, 1000, 5000, 10000},
+ ::android::hardware::radio::V1_5::AccessNetwork::GERAN);
+ ASSERT_OK(res);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_5->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial);
+
+ ALOGI("setLinkCapacityReportingCriteria_1_5_invalidHysteresisUlKbps, rspInfo.error = %s\n",
+ toString(radioRsp_v1_5->rspInfo.error).c_str());
+ // Allow REQUEST_NOT_SUPPORTED as setLinkCapacityReportingCriteria_1_5() may not be supported
+ // for GERAN
+ ASSERT_TRUE(
+ CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error,
+ {RadioError::INVALID_ARGUMENTS, RadioError::REQUEST_NOT_SUPPORTED}));
+}
+
+/*
+ * Test IRadio.setLinkCapacityReportingCriteria_1_5() empty params
+ */
+TEST_F(RadioHidlTest_v1_5, setLinkCapacityReportingCriteria_1_5_emptyParams) {
+ serial = GetRandomSerialNumber();
+
+ Return<void> res = radio_v1_5->setLinkCapacityReportingCriteria_1_5(
+ serial, 0, 0, 0, {}, {}, ::android::hardware::radio::V1_5::AccessNetwork::GERAN);
+ ASSERT_OK(res);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_5->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial);
+
+ ALOGI("setLinkCapacityReportingCriteria_1_5_emptyParams, rspInfo.error = %s\n",
+ toString(radioRsp_v1_5->rspInfo.error).c_str());
+ // Allow REQUEST_NOT_SUPPORTED as setLinkCapacityReportingCriteria_1_5() may not be supported
+ // for GERAN
+ ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error,
+ {RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED}));
+}
+
+/*
+ * Test IRadio.setLinkCapacityReportingCriteria_1_5() for GERAN
+ */
+TEST_F(RadioHidlTest_v1_5, setLinkCapacityReportingCriteria_1_5_Geran) {
+ serial = GetRandomSerialNumber();
+
+ Return<void> res = radio_v1_5->setLinkCapacityReportingCriteria_1_5(
+ serial, 5000, 500, 100, {1000, 5000, 10000, 20000}, {500, 1000, 5000, 10000},
+ ::android::hardware::radio::V1_5::AccessNetwork::GERAN);
+ ASSERT_OK(res);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_5->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial);
+
+ ALOGI("setLinkCapacityReportingCriteria_1_5_Geran, rspInfo.error = %s\n",
+ toString(radioRsp_v1_5->rspInfo.error).c_str());
+ // Allow REQUEST_NOT_SUPPORTED as setLinkCapacityReportingCriteria_1_5() may not be supported
+ // for GERAN
+ ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error,
+ {RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED}));
+}
+
+/*
* Test IRadio.enableUiccApplications() for the response returned.
* For SIM ABSENT case.
*/
@@ -969,7 +1062,10 @@
}
}
-TEST_F(RadioHidlTest_v1_5, setRadioPower_1_5_emergencyCall_cancalled) {
+/*
+ * Test IRadio.setRadioPower_1_5() for the response returned.
+ */
+TEST_F(RadioHidlTest_v1_5, setRadioPower_1_5_emergencyCall_cancelled) {
// Set radio power to off.
serial = GetRandomSerialNumber();
radio_v1_5->setRadioPower_1_5(serial, false, false, false);
@@ -1066,3 +1162,110 @@
CHECK_GENERAL_ERROR));
}
}
+
+/*
+ * Test IRadio.getBarringInfo() for the response returned.
+ */
+TEST_F(RadioHidlTest_v1_5, getBarringInfo) {
+ serial = GetRandomSerialNumber();
+
+ Return<void> res = radio_v1_5->getBarringInfo(serial);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_5->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial);
+
+ ASSERT_TRUE(radioRsp_v1_5->barringInfos.size() > 0);
+
+ std::set<BarringInfo::ServiceType> reportedServices;
+
+ // validate that the service types are in range
+ for (const auto& info : radioRsp_v1_5->barringInfos) {
+ ASSERT_TRUE((info.serviceType >= BarringInfo::ServiceType::CS_SERVICE &&
+ info.serviceType <= BarringInfo::ServiceType::SMS) ||
+ (info.serviceType >= BarringInfo::ServiceType::OPERATOR_1 &&
+ info.serviceType <= BarringInfo::ServiceType::OPERATOR_32));
+ reportedServices.insert(info.serviceType);
+
+ // Any type that is "conditional" must have sane values for conditional barring
+ // factor and time.
+ switch (info.barringType) {
+ case BarringInfo::BarringType::NONE: // fall through
+ case BarringInfo::BarringType::UNCONDITIONAL:
+ break;
+ case BarringInfo::BarringType::CONDITIONAL: {
+ const int32_t barringFactor = info.barringTypeSpecificInfo.conditional().factor;
+ ASSERT_TRUE(barringFactor >= 0 && barringFactor <= 100);
+ ASSERT_TRUE(info.barringTypeSpecificInfo.conditional().timeSeconds > 0);
+ break;
+ }
+ default:
+ FAIL();
+ }
+ }
+
+ // Certain types of barring are relevant for certain RANs. Ensure that only the right
+ // types are reported. Note that no types are required, simply that for a given technology
+ // only certain types are valid. This is one way to sanity check that implementations are
+ // not providing information that they don't have.
+ static const std::set<BarringInfo::ServiceType> UTRA_SERVICES{
+ BarringInfo::ServiceType::CS_SERVICE, BarringInfo::ServiceType::PS_SERVICE,
+ BarringInfo::ServiceType::CS_VOICE, BarringInfo::ServiceType::EMERGENCY,
+ BarringInfo::ServiceType::SMS,
+ };
+
+ static const std::set<BarringInfo::ServiceType> EUTRA_SERVICES{
+ BarringInfo::ServiceType::MO_SIGNALLING, BarringInfo::ServiceType::MO_DATA,
+ BarringInfo::ServiceType::CS_FALLBACK, BarringInfo::ServiceType::MMTEL_VOICE,
+ BarringInfo::ServiceType::MMTEL_VIDEO, BarringInfo::ServiceType::EMERGENCY,
+ BarringInfo::ServiceType::SMS,
+ };
+
+ static const std::set<BarringInfo::ServiceType> NGRA_SERVICES = {
+ BarringInfo::ServiceType::MO_SIGNALLING, BarringInfo::ServiceType::MO_DATA,
+ BarringInfo::ServiceType::CS_FALLBACK, BarringInfo::ServiceType::MMTEL_VOICE,
+ BarringInfo::ServiceType::MMTEL_VIDEO, BarringInfo::ServiceType::EMERGENCY,
+ BarringInfo::ServiceType::SMS, BarringInfo::ServiceType::OPERATOR_1,
+ BarringInfo::ServiceType::OPERATOR_2, BarringInfo::ServiceType::OPERATOR_3,
+ BarringInfo::ServiceType::OPERATOR_4, BarringInfo::ServiceType::OPERATOR_5,
+ BarringInfo::ServiceType::OPERATOR_6, BarringInfo::ServiceType::OPERATOR_7,
+ BarringInfo::ServiceType::OPERATOR_8, BarringInfo::ServiceType::OPERATOR_9,
+ BarringInfo::ServiceType::OPERATOR_10, BarringInfo::ServiceType::OPERATOR_11,
+ BarringInfo::ServiceType::OPERATOR_12, BarringInfo::ServiceType::OPERATOR_13,
+ BarringInfo::ServiceType::OPERATOR_14, BarringInfo::ServiceType::OPERATOR_15,
+ BarringInfo::ServiceType::OPERATOR_16, BarringInfo::ServiceType::OPERATOR_17,
+ BarringInfo::ServiceType::OPERATOR_18, BarringInfo::ServiceType::OPERATOR_19,
+ BarringInfo::ServiceType::OPERATOR_20, BarringInfo::ServiceType::OPERATOR_21,
+ BarringInfo::ServiceType::OPERATOR_22, BarringInfo::ServiceType::OPERATOR_23,
+ BarringInfo::ServiceType::OPERATOR_24, BarringInfo::ServiceType::OPERATOR_25,
+ BarringInfo::ServiceType::OPERATOR_26, BarringInfo::ServiceType::OPERATOR_27,
+ BarringInfo::ServiceType::OPERATOR_28, BarringInfo::ServiceType::OPERATOR_29,
+ BarringInfo::ServiceType::OPERATOR_30, BarringInfo::ServiceType::OPERATOR_31,
+ };
+
+ const std::set<BarringInfo::ServiceType>* compareTo = nullptr;
+
+ switch (radioRsp_v1_5->barringCellIdentity.getDiscriminator()) {
+ case android::hardware::radio::V1_5::CellIdentity::hidl_discriminator::wcdma:
+ // fall through
+ case android::hardware::radio::V1_5::CellIdentity::hidl_discriminator::tdscdma:
+ compareTo = &UTRA_SERVICES;
+ break;
+ case android::hardware::radio::V1_5::CellIdentity::hidl_discriminator::lte:
+ compareTo = &EUTRA_SERVICES;
+ break;
+ case android::hardware::radio::V1_5::CellIdentity::hidl_discriminator::nr:
+ compareTo = &NGRA_SERVICES;
+ break;
+
+ case android::hardware::radio::V1_5::CellIdentity::hidl_discriminator::cdma:
+ // fall through
+ default:
+ FAIL();
+ break;
+ }
+
+ std::set<BarringInfo::ServiceType> diff;
+
+ std::set_difference(reportedServices.begin(), reportedServices.end(), compareTo->begin(),
+ compareTo->end(), std::inserter(diff, diff.begin()));
+}
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_test.cpp b/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
index c29ebf9..a5d236d 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
+++ b/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
@@ -47,9 +47,9 @@
EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial);
EXPECT_EQ(RadioError::NONE, radioRsp_v1_5->rspInfo.error);
- sp<::android::hardware::radio::config::V1_3::IRadioConfig> radioConfig =
+ sp<::android::hardware::radio::config::V1_1::IRadioConfig> radioConfig =
::testing::VtsHalHidlTargetTestBase::getService<
- ::android::hardware::radio::config::V1_3::IRadioConfig>();
+ ::android::hardware::radio::config::V1_1::IRadioConfig>();
/* Enforce Vts tesing with RadioConfig is existed. */
ASSERT_NE(nullptr, radioConfig.get());
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
index d1c17e6..a7c1cdc 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
+++ b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
@@ -22,7 +22,7 @@
#include <condition_variable>
#include <mutex>
-#include <android/hardware/radio/config/1.3/IRadioConfig.h>
+#include <android/hardware/radio/config/1.1/IRadioConfig.h>
#include <android/hardware/radio/1.5/IRadio.h>
#include <android/hardware/radio/1.5/IRadioIndication.h>
@@ -86,6 +86,10 @@
// Whether Uicc applications are enabled or not.
bool areUiccApplicationsEnabled;
+ // Barring Info Response
+ ::android::hardware::radio::V1_5::CellIdentity barringCellIdentity;
+ ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::BarringInfo> barringInfos;
+
RadioResponse_v1_5(RadioHidlTest_v1_5& parent_v1_5);
virtual ~RadioResponse_v1_5() = default;
@@ -114,9 +118,6 @@
Return<void> supplyNetworkDepersonalizationResponse(const RadioResponseInfo& info,
int32_t remainingRetries);
- Return<void> supplySimDepersonalizationResponse(const RadioResponseInfo& info,
- ::android::hardware::radio::V1_5::PersoSubstate persoType, int32_t remainingRetries);
-
Return<void> getCurrentCallsResponse(
const RadioResponseInfo& info,
const ::android::hardware::hidl_vec<::android::hardware::radio::V1_0::Call>& calls);
@@ -534,6 +535,8 @@
/* 1.5 Api */
Return<void> setSignalStrengthReportingCriteriaResponse_1_5(const RadioResponseInfo& info);
+ Return<void> setLinkCapacityReportingCriteriaResponse_1_5(const RadioResponseInfo& info);
+
Return<void> enableUiccApplicationsResponse(const RadioResponseInfo& info);
Return<void> areUiccApplicationsEnabledResponse(const RadioResponseInfo& info, bool enabled);
@@ -559,6 +562,7 @@
Return<void> getBarringInfoResponse(
const RadioResponseInfo& info,
+ const ::android::hardware::radio::V1_5::CellIdentity& cellIdentity,
const ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::BarringInfo>&
barringInfos);
@@ -579,6 +583,10 @@
Return<void> sendCdmaSmsExpectMoreResponse(const RadioResponseInfo& info,
const SendSmsResult& sms);
+
+ Return<void> supplySimDepersonalizationResponse(
+ const RadioResponseInfo& info,
+ ::android::hardware::radio::V1_5::PersoSubstate persoType, int32_t remainingRetries);
};
/* Callback class for radio indication */
diff --git a/radio/1.5/vts/functional/radio_response.cpp b/radio/1.5/vts/functional/radio_response.cpp
index a62d086..e4f9ce8 100644
--- a/radio/1.5/vts/functional/radio_response.cpp
+++ b/radio/1.5/vts/functional/radio_response.cpp
@@ -62,13 +62,6 @@
return Void();
}
-Return<void> RadioResponse_v1_5::supplySimDepersonalizationResponse(
- const RadioResponseInfo& /*info*/,
- ::android::hardware::radio::V1_5::PersoSubstate /*persoType*/,
- int32_t /*remainingRetries*/) {
- return Void();
-}
-
Return<void> RadioResponse_v1_5::getCurrentCallsResponse(
const RadioResponseInfo& /*info*/,
const ::android::hardware::hidl_vec<::android::hardware::radio::V1_0::Call>& /*calls*/) {
@@ -901,6 +894,13 @@
return Void();
}
+Return<void> RadioResponse_v1_5::setLinkCapacityReportingCriteriaResponse_1_5(
+ const RadioResponseInfo& info) {
+ rspInfo = info;
+ parent_v1_5.notify(info.serial);
+ return Void();
+}
+
Return<void> RadioResponse_v1_5::enableUiccApplicationsResponse(const RadioResponseInfo& info) {
rspInfo = info;
parent_v1_5.notify(info.serial);
@@ -970,8 +970,11 @@
Return<void> RadioResponse_v1_5::getBarringInfoResponse(
const RadioResponseInfo& info,
+ const ::android::hardware::radio::V1_5::CellIdentity& cellIdentity,
const ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::BarringInfo>&
- /*barringInfos*/) {
+ barringInfos) {
+ this->barringCellIdentity = cellIdentity;
+ this->barringInfos = barringInfos;
rspInfo = info;
parent_v1_5.notify(info.serial);
return Void();
@@ -1011,3 +1014,10 @@
const SendSmsResult& /*sms*/) {
return Void();
}
+
+Return<void> RadioResponse_v1_5::supplySimDepersonalizationResponse(
+ const RadioResponseInfo& /*info*/,
+ ::android::hardware::radio::V1_5::PersoSubstate /*persoType*/,
+ int32_t /*remainingRetries*/) {
+ return Void();
+}
diff --git a/radio/config/1.3/Android.bp b/radio/config/1.3/Android.bp
deleted file mode 100644
index 7360270..0000000
--- a/radio/config/1.3/Android.bp
+++ /dev/null
@@ -1,26 +0,0 @@
-// This file is autogenerated by hidl-gen -Landroidbp.
-
-hidl_interface {
- name: "android.hardware.radio.config@1.3",
- root: "android.hardware",
- vndk: {
- enabled: true,
- },
- srcs: [
- "types.hal",
- "IRadioConfig.hal",
- "IRadioConfigIndication.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.1",
- "android.hardware.radio@1.4",
- "android.hardware.radio@1.5",
- "android.hidl.base@1.0",
- ],
- gen_java: true,
-}
diff --git a/radio/config/1.3/IRadioConfig.hal b/radio/config/1.3/IRadioConfig.hal
deleted file mode 100644
index d01f54b..0000000
--- a/radio/config/1.3/IRadioConfig.hal
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.
- */
-
-package android.hardware.radio.config@1.3;
-
-import @1.1::IRadioConfig;
-
-/**
- * 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.
- */
-interface IRadioConfig extends @1.1::IRadioConfig {
- /**
- * Request current phone capability.
- *
- * @param serial Serial number of request.
- *
- * Response callback is IRadioResponse.getPhoneCapabilityResponse_1_3() which
- * will return <@1.3::PhoneCapability>.
- */
- oneway getPhoneCapability_1_3(int32_t serial);
-};
diff --git a/radio/config/1.3/IRadioConfigResponse.hal b/radio/config/1.3/IRadioConfigResponse.hal
deleted file mode 100644
index e13aa1e..0000000
--- a/radio/config/1.3/IRadioConfigResponse.hal
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-package android.hardware.radio.config@1.3;
-
-import android.hardware.radio@1.0::RadioResponseInfo;
-import @1.2::IRadioConfigResponse;
-import @1.3::PhoneCapability;
-
-/**
- * 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 phoneCapability <@1.3::PhoneCapability> it defines modem's capability for example
- * how many logical modems it has, how many data connections it supports.
- *
- * Valid errors returned:
- * RadioError:NONE
- * RadioError:RADIO_NOT_AVAILABLE
- */
- oneway getPhoneCapabilityResponse_1_3(RadioResponseInfo info, PhoneCapability phoneCapability);
-};
diff --git a/radio/config/1.3/default/Android.bp b/radio/config/1.3/default/Android.bp
deleted file mode 100644
index 163c5c5..0000000
--- a/radio/config/1.3/default/Android.bp
+++ /dev/null
@@ -1,28 +0,0 @@
-cc_binary {
- name: "android.hardware.radio.config@1.3-service",
- init_rc: ["android.hardware.radio.config@1.3-service.rc"],
- relative_install_path: "hw",
- vintf_fragments: ["radio-config-default.xml"],
- vendor: true,
- srcs: [
- "RadioConfig.cpp",
- "RadioConfigIndication.cpp",
- "RadioConfigResponse.cpp",
- "service.cpp",
- ],
- shared_libs: [
- "libhidlbase",
- "liblog",
- "libutils",
- "android.hardware.radio.config@1.0",
- "android.hardware.radio.config@1.1",
- "android.hardware.radio.config@1.2",
- "android.hardware.radio.config@1.3",
- "android.hardware.radio@1.0",
- "android.hardware.radio@1.1",
- "android.hardware.radio@1.2",
- "android.hardware.radio@1.3",
- "android.hardware.radio@1.4",
- "android.hardware.radio@1.5",
- ],
-}
diff --git a/radio/config/1.3/default/RadioConfig.cpp b/radio/config/1.3/default/RadioConfig.cpp
deleted file mode 100644
index 01e98f1..0000000
--- a/radio/config/1.3/default/RadioConfig.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * 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 "RadioConfig.h"
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-using namespace ::android::hardware::radio::V1_0;
-
-// Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
-Return<void> RadioConfig::setResponseFunctions(
- const sp<V1_0::IRadioConfigResponse>& radioConfigResponse,
- const sp<V1_0::IRadioConfigIndication>& radioConfigIndication) {
- mRadioConfigResponse = radioConfigResponse;
- mRadioConfigIndication = radioConfigIndication;
-
- mRadioConfigResponseV1_3 =
- V1_3::IRadioConfigResponse::castFrom(mRadioConfigResponse).withDefault(nullptr);
- mRadioConfigIndicationV1_3 =
- V1_3::IRadioConfigIndication::castFrom(mRadioConfigIndication).withDefault(nullptr);
- if (mRadioConfigResponseV1_3 == nullptr || mRadioConfigIndicationV1_3 == nullptr) {
- mRadioConfigResponseV1_3 = nullptr;
- mRadioConfigIndicationV1_3 = nullptr;
- }
-
- mRadioConfigResponseV1_2 =
- V1_2::IRadioConfigResponse::castFrom(mRadioConfigResponse).withDefault(nullptr);
- mRadioConfigIndicationV1_2 =
- V1_2::IRadioConfigIndication::castFrom(mRadioConfigIndication).withDefault(nullptr);
- if (mRadioConfigResponseV1_2 == nullptr || mRadioConfigIndicationV1_2 == nullptr) {
- mRadioConfigResponseV1_2 = nullptr;
- mRadioConfigIndicationV1_2 = nullptr;
- }
-
- mRadioConfigResponseV1_1 =
- V1_1::IRadioConfigResponse::castFrom(mRadioConfigResponse).withDefault(nullptr);
- mRadioConfigIndicationV1_1 =
- V1_1::IRadioConfigIndication::castFrom(mRadioConfigIndication).withDefault(nullptr);
- if (mRadioConfigResponseV1_1 == nullptr || mRadioConfigIndicationV1_1 == nullptr) {
- mRadioConfigResponseV1_1 = nullptr;
- mRadioConfigIndicationV1_1 = nullptr;
- }
-
- return Void();
-}
-
-Return<void> RadioConfig::getSimSlotsStatus(int32_t /* serial */) {
- hidl_vec<V1_0::SimSlotStatus> slotStatus;
- RadioResponseInfo info;
- mRadioConfigResponse->getSimSlotsStatusResponse(info, slotStatus);
- return Void();
-}
-
-Return<void> RadioConfig::setSimSlotsMapping(int32_t /* serial */,
- const hidl_vec<uint32_t>& /* slotMap */) {
- RadioResponseInfo info;
- mRadioConfigResponse->setSimSlotsMappingResponse(info);
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_1::IRadioConfig follow.
-Return<void> RadioConfig::getPhoneCapability(int32_t /* serial */) {
- V1_1::PhoneCapability phoneCapability;
- RadioResponseInfo info;
- mRadioConfigResponseV1_1->getPhoneCapabilityResponse(info, phoneCapability);
- return Void();
-}
-
-Return<void> RadioConfig::setPreferredDataModem(int32_t /* serial */, uint8_t /* modemId */) {
- RadioResponseInfo info;
- mRadioConfigResponseV1_1->setPreferredDataModemResponse(info);
- return Void();
-}
-
-Return<void> RadioConfig::setModemsConfig(int32_t /* serial */,
- const V1_1::ModemsConfig& /* modemsConfig */) {
- RadioResponseInfo info;
- mRadioConfigResponseV1_1->setModemsConfigResponse(info);
- return Void();
-}
-
-Return<void> RadioConfig::getModemsConfig(int32_t /* serial */) {
- V1_1::ModemsConfig modemsConfig;
- RadioResponseInfo info;
- mRadioConfigResponseV1_1->getModemsConfigResponse(info, modemsConfig);
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_3::IRadioConfig follow.
-Return<void> RadioConfig::getPhoneCapability_1_3(int32_t /* serial */) {
- V1_3::PhoneCapability phoneCapability;
- RadioResponseInfo info;
- mRadioConfigResponseV1_3->getPhoneCapabilityResponse_1_3(info, phoneCapability);
- return Void();
-}
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
diff --git a/radio/config/1.3/default/RadioConfig.h b/radio/config/1.3/default/RadioConfig.h
deleted file mode 100644
index 57ff368..0000000
--- a/radio/config/1.3/default/RadioConfig.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * 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_RADIO_CONFIG_V1_3_RADIOCONFIG_H
-#define ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIG_H
-
-#include <android/hardware/radio/config/1.3/IRadioConfig.h>
-#include <android/hardware/radio/config/1.3/IRadioConfigIndication.h>
-#include <android/hardware/radio/config/1.3/IRadioConfigResponse.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-using namespace ::android::hardware::radio::config;
-
-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 RadioConfig : public V1_3::IRadioConfig {
- sp<V1_0::IRadioConfigResponse> mRadioConfigResponse;
- sp<V1_0::IRadioConfigIndication> mRadioConfigIndication;
- sp<V1_1::IRadioConfigResponse> mRadioConfigResponseV1_1;
- sp<V1_1::IRadioConfigIndication> mRadioConfigIndicationV1_1;
- sp<V1_2::IRadioConfigResponse> mRadioConfigResponseV1_2;
- sp<V1_2::IRadioConfigIndication> mRadioConfigIndicationV1_2;
- sp<V1_3::IRadioConfigResponse> mRadioConfigResponseV1_3;
- sp<V1_3::IRadioConfigIndication> mRadioConfigIndicationV1_3;
-
- // Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
- Return<void> setResponseFunctions(
- const sp<V1_0::IRadioConfigResponse>& radioConfigResponse,
- const sp<V1_0::IRadioConfigIndication>& radioConfigIndication);
- Return<void> getSimSlotsStatus(int32_t serial);
- Return<void> setSimSlotsMapping(int32_t serial, const hidl_vec<uint32_t>& slotMap);
-
- // Methods from ::android::hardware::radio::config::V1_1::IRadioConfig follow.
- Return<void> getPhoneCapability(int32_t serial);
- Return<void> setPreferredDataModem(int32_t serial, uint8_t modemId);
- Return<void> setModemsConfig(int32_t serial, const V1_1::ModemsConfig& modemsConfig);
- Return<void> getModemsConfig(int32_t serial);
-
- // Methods from ::android::hardware::radio::config::V1_3::IRadioConfig follow.
- Return<void> getPhoneCapability_1_3(int32_t serial);
-};
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIG_H
diff --git a/radio/config/1.3/default/RadioConfigIndication.cpp b/radio/config/1.3/default/RadioConfigIndication.cpp
deleted file mode 100644
index 608fa1c..0000000
--- a/radio/config/1.3/default/RadioConfigIndication.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * 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 "RadioConfigIndication.h"
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-// Methods from ::android::hardware::radio::config::V1_0::IRadioConfigIndication follow.
-Return<void> RadioConfigIndication::simSlotsStatusChanged(
- RadioIndicationType /* type */, const hidl_vec<V1_0::SimSlotStatus>& /* slotStatus */) {
- // TODO implement
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_2::IRadioConfigIndication follow.
-Return<void> RadioConfigIndication::simSlotsStatusChanged_1_2(
- RadioIndicationType /* type */, const hidl_vec<V1_2::SimSlotStatus>& /* slotStatus */) {
- // TODO implement
- return Void();
-}
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
diff --git a/radio/config/1.3/default/RadioConfigIndication.h b/radio/config/1.3/default/RadioConfigIndication.h
deleted file mode 100644
index c92446c..0000000
--- a/radio/config/1.3/default/RadioConfigIndication.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * 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_RADIO_CONFIG_V1_3_RADIOCONFIGINDICATION_H
-#define ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGINDICATION_H
-
-#include <android/hardware/radio/config/1.3/IRadioConfigIndication.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-using namespace ::android::hardware::radio::V1_0;
-using namespace ::android::hardware::radio::config;
-
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
-struct RadioConfigIndication : public IRadioConfigIndication {
- // Methods from ::android::hardware::radio::config::V1_0::IRadioConfigIndication follow.
- Return<void> simSlotsStatusChanged(RadioIndicationType type,
- const hidl_vec<V1_0::SimSlotStatus>& slotStatus) override;
-
- // Methods from ::android::hardware::radio::config::V1_2::IRadioConfigIndication follow.
- Return<void> simSlotsStatusChanged_1_2(
- RadioIndicationType type, const hidl_vec<V1_2::SimSlotStatus>& slotStatus) override;
-};
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGINDICATION_H
diff --git a/radio/config/1.3/default/RadioConfigResponse.cpp b/radio/config/1.3/default/RadioConfigResponse.cpp
deleted file mode 100644
index 1d48a13..0000000
--- a/radio/config/1.3/default/RadioConfigResponse.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * 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 "RadioConfigResponse.h"
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-// Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
-Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
- const RadioResponseInfo& /* info */,
- const hidl_vec<V1_0::SimSlotStatus>& /* slotStatus */) {
- // TODO implement
- return Void();
-}
-
-Return<void> RadioConfigResponse::setSimSlotsMappingResponse(const RadioResponseInfo& /* info */) {
- // TODO implement
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_1::IRadioConfigResponse follow.
-Return<void> RadioConfigResponse::getPhoneCapabilityResponse(
- const RadioResponseInfo& /* info */, const V1_1::PhoneCapability& /* phoneCapability */) {
- // TODO implement
- return Void();
-}
-
-Return<void> RadioConfigResponse::setPreferredDataModemResponse(
- const RadioResponseInfo& /* info */) {
- // TODO implement
- return Void();
-}
-
-Return<void> RadioConfigResponse::setModemsConfigResponse(const RadioResponseInfo& /* info */) {
- // TODO implement
- return Void();
-}
-
-Return<void> RadioConfigResponse::getModemsConfigResponse(
- const RadioResponseInfo& /* info */, const V1_1::ModemsConfig& /* modemsConfig */) {
- // TODO implement
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_2::IRadioConfigResponse follow.
-Return<void> RadioConfigResponse::getSimSlotsStatusResponse_1_2(
- const RadioResponseInfo& /* info */,
- const hidl_vec<V1_2::SimSlotStatus>& /* slotStatus */) {
- // TODO implement
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_3::IRadioConfigResponse follow.
-Return<void> RadioConfigResponse::getPhoneCapabilityResponse_1_3(
- const RadioResponseInfo& /* info */, const V1_3::PhoneCapability& /* phoneCapability */) {
- // TODO implement
- return Void();
-}
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
diff --git a/radio/config/1.3/default/RadioConfigResponse.h b/radio/config/1.3/default/RadioConfigResponse.h
deleted file mode 100644
index dc169bb..0000000
--- a/radio/config/1.3/default/RadioConfigResponse.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * 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_RADIO_CONFIG_V1_3_RADIOCONFIGRESPONSE_H
-#define ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGRESPONSE_H
-
-#include <android/hardware/radio/config/1.3/IRadioConfigResponse.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-using namespace ::android::hardware::radio::config;
-
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-
-using ::android::hardware::radio::V1_0::RadioResponseInfo;
-
-struct RadioConfigResponse : public IRadioConfigResponse {
- // Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
- Return<void> getSimSlotsStatusResponse(
- const RadioResponseInfo& info,
- const hidl_vec<V1_0::SimSlotStatus>& slotStatus) override;
- Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info) override;
-
- // Methods from ::android::hardware::radio::config::V1_1::IRadioConfigResponse follow.
- Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
- const V1_1::PhoneCapability& phoneCapability) override;
- Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info) override;
- Return<void> setModemsConfigResponse(const RadioResponseInfo& info) override;
- Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
- const V1_1::ModemsConfig& modemsConfig) override;
-
- // Methods from ::android::hardware::radio::config::V1_2::IRadioConfigResponse follow.
- Return<void> getSimSlotsStatusResponse_1_2(
- const RadioResponseInfo& info,
- const hidl_vec<V1_2::SimSlotStatus>& slotStatus) override;
-
- // Methods from ::android::hardware::radio::config::V1_3::IRadioConfigResponse follow.
- Return<void> getPhoneCapabilityResponse_1_3(
- const RadioResponseInfo& info, const V1_3::PhoneCapability& phoneCapability) override;
-};
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGRESPONSE_H
diff --git a/radio/config/1.3/default/android.hardware.radio.config@1.3-service.rc b/radio/config/1.3/default/android.hardware.radio.config@1.3-service.rc
deleted file mode 100644
index 6df9b52..0000000
--- a/radio/config/1.3/default/android.hardware.radio.config@1.3-service.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service vendor.radio-config-hal-1-3 /vendor/bin/hw/android.hardware.radio.config@1.3-service
- interface android.hardware.radio.config@1.0::IRadioConfig default
- interface android.hardware.radio.config@1.1::IRadioConfig default
- interface android.hardware.radio.config@1.3::IRadioConfig default
- class hal
- user system
- group system
diff --git a/radio/config/1.3/default/radio-config-default.xml b/radio/config/1.3/default/radio-config-default.xml
deleted file mode 100644
index 72f363e..0000000
--- a/radio/config/1.3/default/radio-config-default.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** 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.
-*/
--->
-<manifest version="1.0" type="device">
- <hal format="hidl">
- <name>android.hardware.radio.config</name>
- <transport>hwbinder</transport>
- <version>1.3</version>
- <interface>
- <name>IRadioConfig</name>
- <instance>default</instance>
- </interface>
- </hal>
-</manifest>
diff --git a/radio/config/1.3/default/service.cpp b/radio/config/1.3/default/service.cpp
deleted file mode 100644
index b1e6736..0000000
--- a/radio/config/1.3/default/service.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * 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.radio.config@1.3-service"
-
-#include <android/hardware/radio/config/1.3/IRadioConfig.h>
-#include <hidl/HidlTransportSupport.h>
-
-#include "RadioConfig.h"
-
-using android::OK;
-using android::sp;
-using android::status_t;
-using android::hardware::configureRpcThreadpool;
-using android::hardware::joinRpcThreadpool;
-using android::hardware::radio::config::V1_3::IRadioConfig;
-using android::hardware::radio::config::V1_3::implementation::RadioConfig;
-
-int main() {
- configureRpcThreadpool(1, true);
- sp<IRadioConfig> radioConfig = new RadioConfig;
- const status_t status = radioConfig->registerAsService();
- ALOGW_IF(status != OK, "Could not register IRadioConfig 1.3");
- ALOGD("Default service is ready.");
-
- joinRpcThreadpool();
- return 1;
-}
diff --git a/radio/config/1.3/types.hal b/radio/config/1.3/types.hal
deleted file mode 100644
index 7860006..0000000
--- a/radio/config/1.3/types.hal
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * 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.
- */
-
-package android.hardware.radio.config@1.3;
-
-import android.hardware.radio@1.1::GeranBands;
-import android.hardware.radio@1.1::EutranBands;
-import android.hardware.radio@1.4::RadioAccessFamily;
-import android.hardware.radio@1.5::NgranBands;
-import android.hardware.radio@1.5::UtranBands;
-
-/** Type for the SIM slot. */
-enum SlotType : int32_t {
- /** Slot type for UICC/pSIM (physical SIM). */
- UICC = 1,
- /** Slot type for iUICC/iSIM (integrated SIM). */
- IUICC = 2,
- /** Slot type for eUICC/eSIM (embedded SIM). */
- EUICC = 3,
- /** Slot type for soft SIM (no physical SIM). */
- SOFT_SIM = 4,
-};
-
-/** A field in PhoneCapability that holds information about the SIM slot. */
-struct SimSlotCapability {
- /** Corresponds to physicalSlotId in Radio@1.2::CardStatus. */
- uint32_t physicalSlotId;
-
- /** Type of slot. */
- SlotType slotType;
-};
-
-/** Bitmask of features that can be supported by a single modem. */
-enum ModemFeatures : int32_t {
- /** 3GPP2 capability. */
- THREE_GPP2_REG = 1 << 0,
- /** 3GPP capability. */
- THREE_GPP_REG = 1 << 1,
- /** CDMA 2000 with EHRPD capability. */
- CDMA2000_EHRPD_REG = 1 << 2,
- /** GSM capability. */
- GERAN_REG = 1 << 3,
- /** UMTS capability. */
- UTRAN_REG = 1 << 4,
- /** LTE capability. */
- EUTRAN_REG = 1 << 5,
- /** 5G capability. */
- NGRAN_REG = 1 << 6,
- /** Dual Connectivity capability. */
- EN_DC_REG = 1 << 7,
- /** VoLTE capability (IMS registered). */
- PS_VOICE_REG = 1 << 8,
- /** CS voice call capability. */
- CS_VOICE_SESSION = 1 << 9,
- /** Internet connection capability. */
- INTERACTIVE_DATA_SESSION = 1 << 10,
- /** Dedicated bearer capability. */
- DEDICATED_BEARER = 1 << 11,
- /** Network scanning capability. */
- NETWORK_SCAN = 1 << 12,
- /** CDMA capability for SIM associated with modem. */
- CSIM = 1 << 13,
-};
-
-struct ConcurrentModemFeatures {
- /**
- * A vector of concurrently supportable modem features across all modems.
- * Each entry in the vector is a bitfield of ModemFeatures that can be used
- * concurrently with the other ModemFeatures in that list.
- * Each bitfield must be the full set of features for a single modem.
- *
- * On a Dual-SIM device, each entry will be a vector of length 2.
- * The examples below depict the modemFeatures for four Dual-SIM setups:
- * 1. Only one modem can PS attach (IMS registered).
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
- * (GERAN_REG | UTRAN_REG)
- * }
- * or
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * INTERACTIVE_DATA_SESSION),
- * (GERAN_REG | UTRAN_REG | CS_VOICE_SESSION)
- * }
- * 2. Both modems can PS attach (dual VoLTE).
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG)
- * }
- * 3. Both modems can maintain an Internet connection, but they share
- * one RF hardware.
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * INTERACTIVE_DATA_SESSION)
- * }
- * 4. Both modems can maintain an Internet connection, and they have
- * their own RF hardware.
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * INTERACTIVE_DATA_SESSION | DEDICATED_BEARER)
- * }
- */
- vec<bitfield<ModemFeatures>> modemFeatures;
-};
-
-/**
- * Overwritten from @1.1::PhoneCapability to add new capabilities and deprecate
- * maxActiveData, maxActiveInternetData, isInternetLingeringSupported, logicalModemList.
- * Replaces RadioConfig@1.1::ModemInfo and should replace Radio@1.4::RadioCapabilities
- * in the next major version upgrade. In the future, this should be extended instead of overwritten.
- */
-struct PhoneCapability {
- /**
- * 3GPP UE category for UTRAN downlink direction.
- * 25.306 Table 5.1a
- */
- uint8_t utranUeCategoryDl;
- /**
- * 3GPP UE category for UTRAN uplink direction.
- * 25.306 Table 5.1g
- */
- uint8_t utranUeCategoryUl;
- /**
- * 3GPP UE category for EUTRAN downlink direction.
- * 25.306 Table 4.1a
- */
- uint8_t eutranUeCategoryDl;
- /**
- * 3GPP UE category for EUTRAN uplink direction.
- * 25.306 Table 4.1a-2
- */
- uint8_t eutranUeCategoryUl;
-
- /**
- * Length of grace period for switching between logical modems, in milliseconds.
- * Used only when the number of logical modems is greater than the number of
- * Internet connections the device can support, otherwise must be 0.
- */
- uint64_t psDataConnectionLingerTimeMillis;
-
- vec<GeranBands> geranBands;
- vec<UtranBands> utranBands;
- vec<EutranBands> eutranBands;
- vec<NgranBands> ngranBands;
-
- /**
- * 32-bit bitmap of supported Radio@1.4::RadioAccessFamily types.
- * Note that RadioAccessFamily is actually the radio access technologies, so it should be
- * renamed in the next major version upgrade.
- */
- bitfield<RadioAccessFamily> supportedRats;
-
- /**
- * List of unique logical modem UUIDs from Radio@1.4::RadioCapabilities.
- * A UUID is typically "com.xxxx.lmX" where X is the logical modem ID.
- * Must be equal to the number of logical modems in the device.
- * Radio@1.2::RadioConst::MAX_UUID_LENGTH is the max length of each UUID.
- */
- vec<string> logicalModemUuids;
-
- /**
- * List of SIM slot capabilities. The order of physical slot IDs must correspond to
- * the order of modems in logicalModemUuids.
- */
- vec<SimSlotCapability> simSlotCapabilities;
-
- /**
- * A vector of all sets of concurrently supportable modem feature sets. The order of modems
- * in modemFeatures must correspond to the order of modems in logicalModemUuids.
- * Each entry in concurrentFeatureSupport is independent of others in the list
- * and represents a set of concurrently supportable features across all modems.
- * Each entry in ConcurrentModemFeatures::modemFeatures is a bitfield of
- * concurrently supported ModemFeatures for one modem.
- */
- vec<ConcurrentModemFeatures> concurrentFeatureSupport;
-};
diff --git a/radio/config/1.3/vts/functional/Android.bp b/radio/config/1.3/vts/functional/Android.bp
deleted file mode 100644
index 6b28faf..0000000
--- a/radio/config/1.3/vts/functional/Android.bp
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-// 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",
- "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-core"],
-}
diff --git a/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp b/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
deleted file mode 100644
index 3bacacf..0000000
--- a/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.
- */
-
-#include <radio_config_hidl_hal_utils.h>
-
-INSTANTIATE_TEST_SUITE_P(
- PerInstance, RadioConfigHidlTest,
- testing::ValuesIn(android::hardware::getAllHalInstanceNames(
- ::android::hardware::radio::config::V1_3::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
deleted file mode 100644
index 7f90023..0000000
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.
- */
-
-#include <radio_config_hidl_hal_utils.h>
-
-#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
-
-/*
- * Test IRadioConfig.getPhoneCapability_1_3()
- */
-TEST_P(RadioConfigHidlTest, getPhoneCapability_1_3) {
- serial = GetRandomSerialNumber();
- Return<void> res = radioConfig->getPhoneCapability_1_3(serial);
- ASSERT_OK(res);
- EXPECT_EQ(std::cv_status::no_timeout, wait());
- EXPECT_EQ(RadioResponseType::SOLICITED, radioConfigRsp->rspInfo.type);
- EXPECT_EQ(serial, radioConfigRsp->rspInfo.serial);
- ALOGI("getPhoneCapability_1_3, rspInfo.error = %s\n",
- toString(radioConfigRsp->rspInfo.error).c_str());
-
- ASSERT_TRUE(CheckAnyOfErrors(
- radioConfigRsp->rspInfo.error,
- {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::INTERNAL_ERR}));
-
- if (radioConfigRsp->rspInfo.error == RadioError ::NONE) {
- int numModems = radioConfigRsp->phoneCap_1_3.logicalModemUuids.size();
- EXPECT_GE(numModems, 0);
- // length of simSlotCapabilities should be equal to length of logicalModemUuids.
- EXPECT_EQ(numModems, radioConfigRsp->phoneCap_1_3.simSlotCapabilities.size());
- // length of modemFeatures in each ConcurrentModemFeatures should be
- // equal to length of logicalModemUuids.
- for (V1_3::ConcurrentModemFeatures cmf :
- radioConfigRsp->phoneCap_1_3.concurrentFeatureSupport) {
- EXPECT_EQ(numModems, cmf.modemFeatures.size());
- }
- }
-}
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
deleted file mode 100644
index cd48b25..0000000
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.
- */
-
-#include <radio_config_hidl_hal_utils.h>
-
-void RadioConfigHidlTest::SetUp() {
- radioConfig = V1_3::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
deleted file mode 100644
index b21c7c0..0000000
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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.
- */
-
-#include <android-base/logging.h>
-
-#include <chrono>
-#include <condition_variable>
-#include <mutex>
-
-#include <android/hardware/radio/config/1.3/IRadioConfig.h>
-#include <android/hardware/radio/config/1.3/IRadioConfigIndication.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;
-
-using ::android::sp;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
-using ::android::hardware::radio::V1_0::RadioIndicationType;
-using ::android::hardware::radio::V1_0::RadioResponseInfo;
-using ::android::hardware::radio::V1_0::RadioResponseType;
-
-#define TIMEOUT_PERIOD 75
-
-class RadioConfigHidlTest;
-
-/* Callback class for radio config response */
-class RadioConfigResponse : public V1_3::IRadioConfigResponse {
- protected:
- RadioConfigHidlTest& parent;
-
- public:
- RadioResponseInfo rspInfo;
- V1_1::PhoneCapability phoneCap_1_1;
- V1_3::PhoneCapability phoneCap_1_3;
-
- RadioConfigResponse(RadioConfigHidlTest& parent);
- virtual ~RadioConfigResponse() = default;
-
- /* 1.0 Api */
- Return<void> getSimSlotsStatusResponse(const RadioResponseInfo& info,
- const hidl_vec<V1_0::SimSlotStatus>& slotStatus);
-
- Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info);
-
- /* 1.1 Api */
- Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
- const V1_1::PhoneCapability& phoneCapability);
-
- Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info);
-
- Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
- const V1_1::ModemsConfig& mConfig);
-
- Return<void> setModemsConfigResponse(const RadioResponseInfo& info);
-
- /* 1.2 Api */
- Return<void> getSimSlotsStatusResponse_1_2(const RadioResponseInfo& info,
- const hidl_vec<V1_2::SimSlotStatus>& slotStatus);
-
- /* 1.3 Api */
- Return<void> getPhoneCapabilityResponse_1_3(const RadioResponseInfo& info,
- const V1_3::PhoneCapability& phoneCapability);
-};
-
-/* Callback class for radio config indication */
-class RadioConfigIndication : public V1_3::IRadioConfigIndication {
- protected:
- RadioConfigHidlTest& parent;
-
- public:
- RadioConfigIndication(RadioConfigHidlTest& parent);
- virtual ~RadioConfigIndication() = default;
-
- /* 1.2 Api */
- Return<void> simSlotsStatusChanged_1_2(RadioIndicationType type,
- const hidl_vec<V1_2::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<V1_3::IRadioConfig> radioConfig;
-
- /* radio config response handle */
- sp<RadioConfigResponse> radioConfigRsp;
-};
diff --git a/radio/config/1.3/vts/functional/radio_config_response.cpp b/radio/config/1.3/vts/functional/radio_config_response.cpp
deleted file mode 100644
index 22098d3..0000000
--- a/radio/config/1.3/vts/functional/radio_config_response.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.
- */
-
-#include <radio_config_hidl_hal_utils.h>
-
-using namespace ::android::hardware::radio::config;
-
-using ::android::hardware::hidl_vec;
-
-using ::android::hardware::radio::V1_0::RadioResponseInfo;
-
-RadioConfigResponse::RadioConfigResponse(RadioConfigHidlTest& parent) : parent(parent) {}
-
-/* 1.0 Apis */
-Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
- const RadioResponseInfo& /* info */,
- const hidl_vec<V1_0::SimSlotStatus>& /* slotStatus */) {
- return Void();
-}
-
-Return<void> RadioConfigResponse::setSimSlotsMappingResponse(const RadioResponseInfo& /* info */) {
- return Void();
-}
-
-/* 1.1 Apis */
-Return<void> RadioConfigResponse::getPhoneCapabilityResponse(
- const RadioResponseInfo& info, const V1_1::PhoneCapability& phoneCapability) {
- rspInfo = info;
- phoneCap_1_1 = phoneCapability;
- parent.notify(info.serial);
- return Void();
-}
-
-Return<void> RadioConfigResponse::setPreferredDataModemResponse(
- const RadioResponseInfo& /* info */) {
- return Void();
-}
-
-Return<void> RadioConfigResponse::getModemsConfigResponse(const RadioResponseInfo& /* info */,
- const V1_1::ModemsConfig& /* mConfig */) {
- return Void();
-}
-
-Return<void> RadioConfigResponse::setModemsConfigResponse(const RadioResponseInfo& /* info */) {
- return Void();
-}
-
-/* 1.2 Apis */
-Return<void> RadioConfigResponse::getSimSlotsStatusResponse_1_2(
- const RadioResponseInfo& /* info */,
- const hidl_vec<V1_2::SimSlotStatus>& /* slotStatus */) {
- return Void();
-}
-
-/* 1.3 Apis */
-Return<void> RadioConfigResponse::getPhoneCapabilityResponse_1_3(
- const RadioResponseInfo& info, const V1_3::PhoneCapability& phoneCapability) {
- rspInfo = info;
- phoneCap_1_3 = phoneCapability;
- parent.notify(info.serial);
- return Void();
-}
diff --git a/rebootescrow/aidl/default/RebootEscrow.cpp b/rebootescrow/aidl/default/RebootEscrow.cpp
index 94d0901..dbc0921 100644
--- a/rebootescrow/aidl/default/RebootEscrow.cpp
+++ b/rebootescrow/aidl/default/RebootEscrow.cpp
@@ -29,7 +29,7 @@
using ::android::base::unique_fd;
ndk::ScopedAStatus RebootEscrow::storeKey(const std::vector<int8_t>& kek) {
- int rawFd = TEMP_FAILURE_RETRY(::open(REBOOT_ESCROW_DEVICE, O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
+ int rawFd = TEMP_FAILURE_RETRY(::open(devicePath_.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
unique_fd fd(rawFd);
if (fd.get() < 0) {
LOG(WARNING) << "Could not open reboot escrow device";
@@ -48,20 +48,19 @@
}
ndk::ScopedAStatus RebootEscrow::retrieveKey(std::vector<int8_t>* _aidl_return) {
- int rawFd = TEMP_FAILURE_RETRY(::open(REBOOT_ESCROW_DEVICE, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
+ int rawFd = TEMP_FAILURE_RETRY(::open(devicePath_.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
unique_fd fd(rawFd);
if (fd.get() < 0) {
LOG(WARNING) << "Could not open reboot escrow device";
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
}
- std::string encodedString;
- if (!::android::base::ReadFdToString(fd, &encodedString)) {
- LOG(WARNING) << "Could not read device to string";
+ std::vector<uint8_t> encodedBytes(hadamard::OUTPUT_SIZE_BYTES);
+ if (!::android::base::ReadFully(fd, &encodedBytes[0], encodedBytes.size())) {
+ LOG(WARNING) << "Could not read device";
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
}
- std::vector<uint8_t> encodedBytes(encodedString.begin(), encodedString.end());
auto keyBytes = hadamard::DecodeKey(encodedBytes);
std::vector<int8_t> signedKeyBytes(keyBytes.begin(), keyBytes.end());
diff --git a/rebootescrow/aidl/default/include/rebootescrow-impl/RebootEscrow.h b/rebootescrow/aidl/default/include/rebootescrow-impl/RebootEscrow.h
index 1ed7397..00ff16b 100644
--- a/rebootescrow/aidl/default/include/rebootescrow-impl/RebootEscrow.h
+++ b/rebootescrow/aidl/default/include/rebootescrow-impl/RebootEscrow.h
@@ -23,11 +23,14 @@
namespace hardware {
namespace rebootescrow {
-static const char* REBOOT_ESCROW_DEVICE = "/dev/access-kregistry";
-
class RebootEscrow : public BnRebootEscrow {
+ public:
+ explicit RebootEscrow(const std::string& devicePath) : devicePath_(devicePath) {}
ndk::ScopedAStatus storeKey(const std::vector<int8_t>& kek) override;
ndk::ScopedAStatus retrieveKey(std::vector<int8_t>* _aidl_return) override;
+
+ private:
+ const std::string devicePath_;
};
} // namespace rebootescrow
diff --git a/rebootescrow/aidl/default/service.cpp b/rebootescrow/aidl/default/service.cpp
index bd2378e..8a8086b 100644
--- a/rebootescrow/aidl/default/service.cpp
+++ b/rebootescrow/aidl/default/service.cpp
@@ -17,15 +17,21 @@
#include "rebootescrow-impl/RebootEscrow.h"
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
using aidl::android::hardware::rebootescrow::RebootEscrow;
+constexpr auto kRebootEscrowDeviceProperty = "ro.rebootescrow.device";
+constexpr auto kRebootEscrowDeviceDefault = "/dev/access-kregistry";
+
int main() {
ABinderProcess_setThreadPoolMaxThreadCount(0);
- auto re = ndk::SharedRefBase::make<RebootEscrow>();
+ auto rebootEscrowDevicePath =
+ android::base::GetProperty(kRebootEscrowDeviceProperty, kRebootEscrowDeviceDefault);
+ auto re = ndk::SharedRefBase::make<RebootEscrow>(rebootEscrowDevicePath);
const std::string instance = std::string() + RebootEscrow::descriptor + "/default";
binder_status_t status = AServiceManager_addService(re->asBinder().get(), instance.c_str());
CHECK(status == STATUS_OK);
diff --git a/sensors/1.0/vts/functional/Android.bp b/sensors/1.0/vts/functional/Android.bp
index 1167fd4..aaefccb 100644
--- a/sensors/1.0/vts/functional/Android.bp
+++ b/sensors/1.0/vts/functional/Android.bp
@@ -20,7 +20,7 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"SensorsHidlEnvironmentV1_0.cpp",
- "VtsHalSensorsV1_0TargetTest.cpp"
+ "VtsHalSensorsV1_0TargetTest.cpp",
],
static_libs: [
"android.hardware.graphics.allocator@2.0",
@@ -31,6 +31,8 @@
"android.hardware.sensors@1.0",
"VtsHalSensorsTargetTestUtils",
],
- test_suites: ["general-tests", "vts-core"],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
}
-
diff --git a/sensors/1.0/vts/functional/SensorsHidlEnvironmentV1_0.h b/sensors/1.0/vts/functional/SensorsHidlEnvironmentV1_0.h
index 29bfa50..485ed1e 100644
--- a/sensors/1.0/vts/functional/SensorsHidlEnvironmentV1_0.h
+++ b/sensors/1.0/vts/functional/SensorsHidlEnvironmentV1_0.h
@@ -29,8 +29,9 @@
using ::android::sp;
class SensorsHidlTest;
-class SensorsHidlEnvironmentV1_0 : public SensorsHidlEnvironmentBase {
- public:
+class SensorsHidlEnvironmentV1_0
+ : public SensorsHidlEnvironmentBase<::android::hardware::sensors::V1_0::Event> {
+ public:
using Event = ::android::hardware::sensors::V1_0::Event;
SensorsHidlEnvironmentV1_0(const std::string& service_name)
: SensorsHidlEnvironmentBase(service_name) {}
diff --git a/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp b/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
index 2cad54d..e298651 100644
--- a/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
+++ b/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
@@ -33,8 +33,7 @@
using namespace ::android::hardware::sensors::V1_0;
// The main test class for SENSORS HIDL HAL.
-
-class SensorsHidlTest : public SensorsHidlTestBase {
+class SensorsHidlTest : public SensorsHidlTestBase<SensorType, Event, SensorInfo> {
public:
virtual void SetUp() override {
mEnvironment = new SensorsHidlEnvironmentV1_0(GetParam());
@@ -80,7 +79,7 @@
inline sp<ISensors>& S() { return mEnvironment->sensors; }
- SensorsHidlEnvironmentBase* getEnvironment() override { return mEnvironment; }
+ SensorsHidlEnvironmentBase<Event>* getEnvironment() override { return mEnvironment; }
private:
// Test environment for sensors HAL.
@@ -257,55 +256,55 @@
// Test if sensor hal can do UI speed accelerometer streaming properly
TEST_P(SensorsHidlTest, AccelerometerStreamingOperationSlow) {
testStreamingOperation(SensorType::ACCELEROMETER, std::chrono::milliseconds(200),
- std::chrono::seconds(5), sAccelNormChecker);
+ std::chrono::seconds(5), mAccelNormChecker);
}
// Test if sensor hal can do normal speed accelerometer streaming properly
TEST_P(SensorsHidlTest, AccelerometerStreamingOperationNormal) {
testStreamingOperation(SensorType::ACCELEROMETER, std::chrono::milliseconds(20),
- std::chrono::seconds(5), sAccelNormChecker);
+ std::chrono::seconds(5), mAccelNormChecker);
}
// Test if sensor hal can do game speed accelerometer streaming properly
TEST_P(SensorsHidlTest, AccelerometerStreamingOperationFast) {
testStreamingOperation(SensorType::ACCELEROMETER, std::chrono::milliseconds(5),
- std::chrono::seconds(5), sAccelNormChecker);
+ std::chrono::seconds(5), mAccelNormChecker);
}
// Test if sensor hal can do UI speed gyroscope streaming properly
TEST_P(SensorsHidlTest, GyroscopeStreamingOperationSlow) {
testStreamingOperation(SensorType::GYROSCOPE, std::chrono::milliseconds(200),
- std::chrono::seconds(5), sGyroNormChecker);
+ std::chrono::seconds(5), mGyroNormChecker);
}
// Test if sensor hal can do normal speed gyroscope streaming properly
TEST_P(SensorsHidlTest, GyroscopeStreamingOperationNormal) {
testStreamingOperation(SensorType::GYROSCOPE, std::chrono::milliseconds(20),
- std::chrono::seconds(5), sGyroNormChecker);
+ std::chrono::seconds(5), mGyroNormChecker);
}
// Test if sensor hal can do game speed gyroscope streaming properly
TEST_P(SensorsHidlTest, GyroscopeStreamingOperationFast) {
testStreamingOperation(SensorType::GYROSCOPE, std::chrono::milliseconds(5),
- std::chrono::seconds(5), sGyroNormChecker);
+ std::chrono::seconds(5), mGyroNormChecker);
}
// Test if sensor hal can do UI speed magnetometer streaming properly
TEST_P(SensorsHidlTest, MagnetometerStreamingOperationSlow) {
testStreamingOperation(SensorType::MAGNETIC_FIELD, std::chrono::milliseconds(200),
- std::chrono::seconds(5), NullChecker());
+ std::chrono::seconds(5), NullChecker<Event>());
}
// Test if sensor hal can do normal speed magnetometer streaming properly
TEST_P(SensorsHidlTest, MagnetometerStreamingOperationNormal) {
testStreamingOperation(SensorType::MAGNETIC_FIELD, std::chrono::milliseconds(20),
- std::chrono::seconds(5), NullChecker());
+ std::chrono::seconds(5), NullChecker<Event>());
}
// Test if sensor hal can do game speed magnetometer streaming properly
TEST_P(SensorsHidlTest, MagnetometerStreamingOperationFast) {
testStreamingOperation(SensorType::MAGNETIC_FIELD, std::chrono::milliseconds(5),
- std::chrono::seconds(5), NullChecker());
+ std::chrono::seconds(5), NullChecker<Event>());
}
// Test if sensor hal can do accelerometer sampling rate switch properly when sensor is active
@@ -344,109 +343,109 @@
// Test sensor event direct report with ashmem for accel sensor at normal rate
TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationNormal) {
testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::ASHMEM, RateLevel::NORMAL,
- sAccelNormChecker);
+ mAccelNormChecker);
}
// Test sensor event direct report with ashmem for accel sensor at fast rate
TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationFast) {
testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::ASHMEM, RateLevel::FAST,
- sAccelNormChecker);
+ mAccelNormChecker);
}
// Test sensor event direct report with ashmem for accel sensor at very fast rate
TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationVeryFast) {
testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::ASHMEM,
- RateLevel::VERY_FAST, sAccelNormChecker);
+ RateLevel::VERY_FAST, mAccelNormChecker);
}
// Test sensor event direct report with ashmem for gyro sensor at normal rate
TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationNormal) {
testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::NORMAL,
- sGyroNormChecker);
+ mGyroNormChecker);
}
// Test sensor event direct report with ashmem for gyro sensor at fast rate
TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationFast) {
testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::FAST,
- sGyroNormChecker);
+ mGyroNormChecker);
}
// Test sensor event direct report with ashmem for gyro sensor at very fast rate
TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationVeryFast) {
testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::VERY_FAST,
- sGyroNormChecker);
+ mGyroNormChecker);
}
// Test sensor event direct report with ashmem for mag sensor at normal rate
TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationNormal) {
testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::ASHMEM, RateLevel::NORMAL,
- NullChecker());
+ NullChecker<Event>());
}
// Test sensor event direct report with ashmem for mag sensor at fast rate
TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationFast) {
testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::ASHMEM, RateLevel::FAST,
- NullChecker());
+ NullChecker<Event>());
}
// Test sensor event direct report with ashmem for mag sensor at very fast rate
TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationVeryFast) {
testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::ASHMEM,
- RateLevel::VERY_FAST, NullChecker());
+ RateLevel::VERY_FAST, NullChecker<Event>());
}
// Test sensor event direct report with gralloc for accel sensor at normal rate
TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationNormal) {
testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::GRALLOC, RateLevel::NORMAL,
- sAccelNormChecker);
+ mAccelNormChecker);
}
// Test sensor event direct report with gralloc for accel sensor at fast rate
TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationFast) {
testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::GRALLOC, RateLevel::FAST,
- sAccelNormChecker);
+ mAccelNormChecker);
}
// Test sensor event direct report with gralloc for accel sensor at very fast rate
TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationVeryFast) {
testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::GRALLOC,
- RateLevel::VERY_FAST, sAccelNormChecker);
+ RateLevel::VERY_FAST, mAccelNormChecker);
}
// Test sensor event direct report with gralloc for gyro sensor at normal rate
TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationNormal) {
testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::NORMAL,
- sGyroNormChecker);
+ mGyroNormChecker);
}
// Test sensor event direct report with gralloc for gyro sensor at fast rate
TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationFast) {
testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::FAST,
- sGyroNormChecker);
+ mGyroNormChecker);
}
// Test sensor event direct report with gralloc for gyro sensor at very fast rate
TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationVeryFast) {
testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::VERY_FAST,
- sGyroNormChecker);
+ mGyroNormChecker);
}
// Test sensor event direct report with gralloc for mag sensor at normal rate
TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationNormal) {
testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::GRALLOC, RateLevel::NORMAL,
- NullChecker());
+ NullChecker<Event>());
}
// Test sensor event direct report with gralloc for mag sensor at fast rate
TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationFast) {
testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::GRALLOC, RateLevel::FAST,
- NullChecker());
+ NullChecker<Event>());
}
// Test sensor event direct report with gralloc for mag sensor at very fast rate
TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationVeryFast) {
testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::GRALLOC,
- RateLevel::VERY_FAST, NullChecker());
+ RateLevel::VERY_FAST, NullChecker<Event>());
}
INSTANTIATE_TEST_SUITE_P(
diff --git a/sensors/2.0/default/Android.bp b/sensors/2.0/default/Android.bp
index 62c9487..bb38327 100644
--- a/sensors/2.0/default/Android.bp
+++ b/sensors/2.0/default/Android.bp
@@ -20,13 +20,17 @@
relative_install_path: "hw",
srcs: [
"service.cpp",
- "Sensor.cpp",
- "Sensors.cpp",
],
init_rc: ["android.hardware.sensors@2.0-service-mock.rc"],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
+ ],
shared_libs: [
"android.hardware.sensors@1.0",
"android.hardware.sensors@2.0",
+ // Needed to compile some shared utilities for both 2.0/2.1 impls, but
+ // isn't normally needed for a HAL that only supports 2.0.
+ "android.hardware.sensors@2.1",
"libcutils",
"libfmq",
"libhidlbase",
@@ -34,5 +38,9 @@
"libpower",
"libutils",
],
+ static_libs: [
+ "android.hardware.sensors@1.0-convert",
+ "android.hardware.sensors@2.X-shared-impl",
+ ],
vintf_fragments: ["android.hardware.sensors@2.0.xml"],
}
diff --git a/sensors/2.0/default/Sensors.cpp b/sensors/2.0/default/Sensors.cpp
deleted file mode 100644
index 23dd26b..0000000
--- a/sensors/2.0/default/Sensors.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * 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 "Sensors.h"
-
-#include <android/hardware/sensors/2.0/types.h>
-#include <log/log.h>
-
-namespace android {
-namespace hardware {
-namespace sensors {
-namespace V2_0 {
-namespace implementation {
-
-using ::android::hardware::sensors::V1_0::Event;
-using ::android::hardware::sensors::V1_0::OperationMode;
-using ::android::hardware::sensors::V1_0::RateLevel;
-using ::android::hardware::sensors::V1_0::Result;
-using ::android::hardware::sensors::V1_0::SharedMemInfo;
-using ::android::hardware::sensors::V2_0::SensorTimeout;
-using ::android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
-
-constexpr const char* kWakeLockName = "SensorsHAL_WAKEUP";
-
-Sensors::Sensors()
- : mEventQueueFlag(nullptr),
- mNextHandle(1),
- mOutstandingWakeUpEvents(0),
- mReadWakeLockQueueRun(false),
- mAutoReleaseWakeLockTime(0),
- mHasWakeLock(false) {
- AddSensor<AccelSensor>();
- AddSensor<GyroSensor>();
- AddSensor<AmbientTempSensor>();
- AddSensor<DeviceTempSensor>();
- AddSensor<PressureSensor>();
- AddSensor<MagnetometerSensor>();
- AddSensor<LightSensor>();
- AddSensor<ProximitySensor>();
- AddSensor<RelativeHumiditySensor>();
-}
-
-Sensors::~Sensors() {
- deleteEventFlag();
- mReadWakeLockQueueRun = false;
- mWakeLockThread.join();
-}
-
-// Methods from ::android::hardware::sensors::V2_0::ISensors follow.
-Return<void> Sensors::getSensorsList(getSensorsList_cb _hidl_cb) {
- std::vector<SensorInfo> sensors;
- for (const auto& sensor : mSensors) {
- sensors.push_back(sensor.second->getSensorInfo());
- }
-
- // Call the HIDL callback with the SensorInfo
- _hidl_cb(sensors);
-
- return Void();
-}
-
-Return<Result> Sensors::setOperationMode(OperationMode mode) {
- for (auto sensor : mSensors) {
- sensor.second->setOperationMode(mode);
- }
- return Result::OK;
-}
-
-Return<Result> Sensors::activate(int32_t sensorHandle, bool enabled) {
- auto sensor = mSensors.find(sensorHandle);
- if (sensor != mSensors.end()) {
- sensor->second->activate(enabled);
- return Result::OK;
- }
- return Result::BAD_VALUE;
-}
-
-Return<Result> Sensors::initialize(
- const ::android::hardware::MQDescriptorSync<Event>& eventQueueDescriptor,
- const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
- const sp<ISensorsCallback>& sensorsCallback) {
- Result result = Result::OK;
-
- // Ensure that all sensors are disabled
- for (auto sensor : mSensors) {
- sensor.second->activate(false /* enable */);
- }
-
- // Stop the Wake Lock thread if it is currently running
- if (mReadWakeLockQueueRun.load()) {
- mReadWakeLockQueueRun = false;
- mWakeLockThread.join();
- }
-
- // Save a reference to the callback
- mCallback = sensorsCallback;
-
- // Create the Event FMQ from the eventQueueDescriptor. Reset the read/write positions.
- mEventQueue =
- std::make_unique<EventMessageQueue>(eventQueueDescriptor, true /* resetPointers */);
-
- // Ensure that any existing EventFlag is properly deleted
- deleteEventFlag();
-
- // Create the EventFlag that is used to signal to the framework that sensor events have been
- // written to the Event FMQ
- if (EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag) != OK) {
- result = Result::BAD_VALUE;
- }
-
- // Create the Wake Lock FMQ that is used by the framework to communicate whenever WAKE_UP
- // events have been successfully read and handled by the framework.
- mWakeLockQueue =
- std::make_unique<WakeLockMessageQueue>(wakeLockDescriptor, true /* resetPointers */);
-
- if (!mCallback || !mEventQueue || !mWakeLockQueue || mEventQueueFlag == nullptr) {
- result = Result::BAD_VALUE;
- }
-
- // Start the thread to read events from the Wake Lock FMQ
- mReadWakeLockQueueRun = true;
- mWakeLockThread = std::thread(startReadWakeLockThread, this);
-
- return result;
-}
-
-Return<Result> Sensors::batch(int32_t sensorHandle, int64_t samplingPeriodNs,
- int64_t /* maxReportLatencyNs */) {
- auto sensor = mSensors.find(sensorHandle);
- if (sensor != mSensors.end()) {
- sensor->second->batch(samplingPeriodNs);
- return Result::OK;
- }
- return Result::BAD_VALUE;
-}
-
-Return<Result> Sensors::flush(int32_t sensorHandle) {
- auto sensor = mSensors.find(sensorHandle);
- if (sensor != mSensors.end()) {
- return sensor->second->flush();
- }
- return Result::BAD_VALUE;
-}
-
-Return<Result> Sensors::injectSensorData(const Event& event) {
- auto sensor = mSensors.find(event.sensorHandle);
- if (sensor != mSensors.end()) {
- return sensor->second->injectEvent(event);
- }
-
- return Result::BAD_VALUE;
-}
-
-Return<void> Sensors::registerDirectChannel(const SharedMemInfo& /* mem */,
- registerDirectChannel_cb _hidl_cb) {
- _hidl_cb(Result::INVALID_OPERATION, -1 /* channelHandle */);
- return Return<void>();
-}
-
-Return<Result> Sensors::unregisterDirectChannel(int32_t /* channelHandle */) {
- return Result::INVALID_OPERATION;
-}
-
-Return<void> Sensors::configDirectReport(int32_t /* sensorHandle */, int32_t /* channelHandle */,
- RateLevel /* rate */, configDirectReport_cb _hidl_cb) {
- _hidl_cb(Result::INVALID_OPERATION, 0 /* reportToken */);
- return Return<void>();
-}
-
-void Sensors::postEvents(const std::vector<Event>& events, bool wakeup) {
- std::lock_guard<std::mutex> lock(mWriteLock);
- if (mEventQueue->write(events.data(), events.size())) {
- mEventQueueFlag->wake(static_cast<uint32_t>(EventQueueFlagBits::READ_AND_PROCESS));
-
- if (wakeup) {
- // Keep track of the number of outstanding WAKE_UP events in order to properly hold
- // a wake lock until the framework has secured a wake lock
- updateWakeLock(events.size(), 0 /* eventsHandled */);
- }
- }
-}
-
-void Sensors::updateWakeLock(int32_t eventsWritten, int32_t eventsHandled) {
- std::lock_guard<std::mutex> lock(mWakeLockLock);
- int32_t newVal = mOutstandingWakeUpEvents + eventsWritten - eventsHandled;
- if (newVal < 0) {
- mOutstandingWakeUpEvents = 0;
- } else {
- mOutstandingWakeUpEvents = newVal;
- }
-
- if (eventsWritten > 0) {
- // Update the time at which the last WAKE_UP event was sent
- mAutoReleaseWakeLockTime = ::android::uptimeMillis() +
- static_cast<uint32_t>(SensorTimeout::WAKE_LOCK_SECONDS) * 1000;
- }
-
- if (!mHasWakeLock && mOutstandingWakeUpEvents > 0 &&
- acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLockName) == 0) {
- mHasWakeLock = true;
- } else if (mHasWakeLock) {
- // Check if the wake lock should be released automatically if
- // SensorTimeout::WAKE_LOCK_SECONDS has elapsed since the last WAKE_UP event was written to
- // the Wake Lock FMQ.
- if (::android::uptimeMillis() > mAutoReleaseWakeLockTime) {
- ALOGD("No events read from wake lock FMQ for %d seconds, auto releasing wake lock",
- SensorTimeout::WAKE_LOCK_SECONDS);
- mOutstandingWakeUpEvents = 0;
- }
-
- if (mOutstandingWakeUpEvents == 0 && release_wake_lock(kWakeLockName) == 0) {
- mHasWakeLock = false;
- }
- }
-}
-
-void Sensors::readWakeLockFMQ() {
- while (mReadWakeLockQueueRun.load()) {
- constexpr int64_t kReadTimeoutNs = 500 * 1000 * 1000; // 500 ms
- uint32_t eventsHandled = 0;
-
- // Read events from the Wake Lock FMQ. Timeout after a reasonable amount of time to ensure
- // that any held wake lock is able to be released if it is held for too long.
- mWakeLockQueue->readBlocking(&eventsHandled, 1 /* count */, 0 /* readNotification */,
- static_cast<uint32_t>(WakeLockQueueFlagBits::DATA_WRITTEN),
- kReadTimeoutNs);
- updateWakeLock(0 /* eventsWritten */, eventsHandled);
- }
-}
-
-void Sensors::startReadWakeLockThread(Sensors* sensors) {
- sensors->readWakeLockFMQ();
-}
-
-void Sensors::deleteEventFlag() {
- status_t status = EventFlag::deleteEventFlag(&mEventQueueFlag);
- if (status != OK) {
- ALOGI("Failed to delete event flag: %d", status);
- }
-}
-
-} // namespace implementation
-} // namespace V2_0
-} // namespace sensors
-} // namespace hardware
-} // namespace android
diff --git a/sensors/2.0/default/Sensors.h b/sensors/2.0/default/Sensors.h
deleted file mode 100644
index d06dd78..0000000
--- a/sensors/2.0/default/Sensors.h
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_HARDWARE_SENSORS_V2_0_SENSORS_H
-#define ANDROID_HARDWARE_SENSORS_V2_0_SENSORS_H
-
-#include "Sensor.h"
-
-#include <android/hardware/sensors/2.0/ISensors.h>
-#include <fmq/MessageQueue.h>
-#include <hardware_legacy/power.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-
-#include <atomic>
-#include <memory>
-#include <thread>
-
-namespace android {
-namespace hardware {
-namespace sensors {
-namespace V2_0 {
-namespace implementation {
-
-using ::android::sp;
-using ::android::hardware::EventFlag;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::MessageQueue;
-using ::android::hardware::MQDescriptor;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
-struct Sensors : public ISensors, public ISensorsEventCallback {
- using Event = ::android::hardware::sensors::V1_0::Event;
- using OperationMode = ::android::hardware::sensors::V1_0::OperationMode;
- using RateLevel = ::android::hardware::sensors::V1_0::RateLevel;
- using Result = ::android::hardware::sensors::V1_0::Result;
- using SharedMemInfo = ::android::hardware::sensors::V1_0::SharedMemInfo;
-
- Sensors();
- virtual ~Sensors();
-
- // Methods from ::android::hardware::sensors::V2_0::ISensors follow.
- Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
-
- Return<Result> setOperationMode(OperationMode mode) override;
-
- Return<Result> activate(int32_t sensorHandle, bool enabled) override;
-
- Return<Result> initialize(
- const ::android::hardware::MQDescriptorSync<Event>& eventQueueDescriptor,
- const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
- const sp<ISensorsCallback>& sensorsCallback) override;
-
- Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
- int64_t maxReportLatencyNs) override;
-
- Return<Result> flush(int32_t sensorHandle) override;
-
- Return<Result> injectSensorData(const Event& event) override;
-
- Return<void> registerDirectChannel(const SharedMemInfo& mem,
- registerDirectChannel_cb _hidl_cb) override;
-
- Return<Result> unregisterDirectChannel(int32_t channelHandle) override;
-
- Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
- configDirectReport_cb _hidl_cb) override;
-
- void postEvents(const std::vector<Event>& events, bool wakeup) override;
-
- private:
- /**
- * Add a new sensor
- */
- template <class SensorType>
- void AddSensor() {
- std::shared_ptr<SensorType> sensor =
- std::make_shared<SensorType>(mNextHandle++ /* sensorHandle */, this /* callback */);
- mSensors[sensor->getSensorInfo().sensorHandle] = sensor;
- }
-
- /**
- * Utility function to delete the Event Flag
- */
- void deleteEventFlag();
-
- /**
- * Function to read the Wake Lock FMQ and release the wake lock when appropriate
- */
- void readWakeLockFMQ();
-
- static void startReadWakeLockThread(Sensors* sensors);
-
- /**
- * Responsible for acquiring and releasing a wake lock when there are unhandled WAKE_UP events
- */
- void updateWakeLock(int32_t eventsWritten, int32_t eventsHandled);
-
- using EventMessageQueue = MessageQueue<Event, kSynchronizedReadWrite>;
- using WakeLockMessageQueue = MessageQueue<uint32_t, kSynchronizedReadWrite>;
-
- /**
- * The Event FMQ where sensor events are written
- */
- std::unique_ptr<EventMessageQueue> mEventQueue;
-
- /**
- * The Wake Lock FMQ that is read to determine when the framework has handled WAKE_UP events
- */
- std::unique_ptr<WakeLockMessageQueue> mWakeLockQueue;
-
- /**
- * Event Flag to signal to the framework when sensor events are available to be read
- */
- EventFlag* mEventQueueFlag;
-
- /**
- * Callback for asynchronous events, such as dynamic sensor connections.
- */
- sp<ISensorsCallback> mCallback;
-
- /**
- * A map of the available sensors
- */
- std::map<int32_t, std::shared_ptr<Sensor>> mSensors;
-
- /**
- * The next available sensor handle
- */
- int32_t mNextHandle;
-
- /**
- * Lock to protect writes to the FMQs
- */
- std::mutex mWriteLock;
-
- /**
- * Lock to protect acquiring and releasing the wake lock
- */
- std::mutex mWakeLockLock;
-
- /**
- * Track the number of WAKE_UP events that have not been handled by the framework
- */
- uint32_t mOutstandingWakeUpEvents;
-
- /**
- * A thread to read the Wake Lock FMQ
- */
- std::thread mWakeLockThread;
-
- /**
- * Flag to indicate that the Wake Lock Thread should continue to run
- */
- std::atomic_bool mReadWakeLockQueueRun;
-
- /**
- * Track the time when the wake lock should automatically be released
- */
- int64_t mAutoReleaseWakeLockTime;
-
- /**
- * Flag to indicate if a wake lock has been acquired
- */
- bool mHasWakeLock;
-};
-
-} // namespace implementation
-} // namespace V2_0
-} // namespace sensors
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_SENSORS_V2_0_SENSORS_H
diff --git a/sensors/2.0/default/SensorsV2_0.h b/sensors/2.0/default/SensorsV2_0.h
new file mode 100644
index 0000000..345835a
--- /dev/null
+++ b/sensors/2.0/default/SensorsV2_0.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.
+ */
+
+#ifndef ANDROID_HARDWARE_SENSORS_V2_0_H
+#define ANDROID_HARDWARE_SENSORS_V2_0_H
+
+#include "Sensors.h"
+
+#include <android/hardware/sensors/2.0/ISensors.h>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_0 {
+namespace implementation {
+
+struct SensorsV2_0 : public ::android::hardware::sensors::V2_X::implementation::Sensors<ISensors> {
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace sensors
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SENSORS_V2_0_H
\ No newline at end of file
diff --git a/sensors/2.0/default/service.cpp b/sensors/2.0/default/service.cpp
index 5c13e33..e20bf85 100644
--- a/sensors/2.0/default/service.cpp
+++ b/sensors/2.0/default/service.cpp
@@ -20,17 +20,17 @@
#include <hidl/HidlTransportSupport.h>
#include <log/log.h>
#include <utils/StrongPointer.h>
-#include "Sensors.h"
+#include "SensorsV2_0.h"
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::hardware::sensors::V2_0::ISensors;
-using android::hardware::sensors::V2_0::implementation::Sensors;
+using android::hardware::sensors::V2_0::implementation::SensorsV2_0;
int main(int /* argc */, char** /* argv */) {
configureRpcThreadpool(1, true);
- android::sp<ISensors> sensors = new Sensors();
+ android::sp<ISensors> sensors = new SensorsV2_0();
if (sensors->registerAsService() != ::android::OK) {
ALOGE("Failed to register Sensors HAL instance");
return -1;
diff --git a/sensors/2.0/multihal/Android.bp b/sensors/2.0/multihal/Android.bp
index 811c455..24c475c 100644
--- a/sensors/2.0/multihal/Android.bp
+++ b/sensors/2.0/multihal/Android.bp
@@ -46,7 +46,7 @@
],
init_rc: ["android.hardware.sensors@2.0-service-multihal.rc"],
vintf_fragments: ["android.hardware.sensors@2.0-multihal.xml"],
- shared_libs: ["android.hardware.sensors@2.0-ScopedWakelock"]
+ shared_libs: ["android.hardware.sensors@2.0-ScopedWakelock"],
}
cc_library_headers {
@@ -66,8 +66,8 @@
],
vendor_available: true,
export_header_lib_headers: [
- "android.hardware.sensors@2.0-multihal.header"
- ]
+ "android.hardware.sensors@2.0-multihal.header",
+ ],
}
// The below targets should only be used for testing.
@@ -85,7 +85,7 @@
"android.hardware.sensors@2.0-multihal.header",
],
export_shared_lib_headers: [
- "android.hardware.sensors@2.0-ScopedWakelock",
+ "android.hardware.sensors@2.0-ScopedWakelock",
],
shared_libs: [
"libutils",
diff --git a/sensors/2.0/multihal/HalProxy.cpp b/sensors/2.0/multihal/HalProxy.cpp
index 7c52661..ac6f17a 100644
--- a/sensors/2.0/multihal/HalProxy.cpp
+++ b/sensors/2.0/multihal/HalProxy.cpp
@@ -290,6 +290,8 @@
stream << " Wakelock ref count: " << mWakelockRefCount << std::endl;
stream << " # of events on pending write writes queue: " << mSizePendingWriteEventsQueue
<< std::endl;
+ stream << " Most events seen on pending write events queue: "
+ << mMostEventsObservedPendingWriteEventsQueue << std::endl;
if (!mPendingWriteEventsQueue.empty()) {
stream << " Size of events list on front of pending writes queue: "
<< mPendingWriteEventsQueue.front().first.size() << std::endl;
@@ -571,6 +573,8 @@
std::vector<Event> eventsLeft(events.begin() + numToWrite, events.end());
mPendingWriteEventsQueue.push({eventsLeft, numWakeupEvents});
mSizePendingWriteEventsQueue += numLeft;
+ mMostEventsObservedPendingWriteEventsQueue =
+ std::max(mMostEventsObservedPendingWriteEventsQueue, mSizePendingWriteEventsQueue);
mEventQueueWriteCV.notify_one();
}
}
diff --git a/sensors/2.0/multihal/include/HalProxy.h b/sensors/2.0/multihal/include/HalProxy.h
index ce28e67..978f7cf 100644
--- a/sensors/2.0/multihal/include/HalProxy.h
+++ b/sensors/2.0/multihal/include/HalProxy.h
@@ -200,6 +200,9 @@
*/
std::queue<std::pair<std::vector<Event>, size_t>> mPendingWriteEventsQueue;
+ //! The most events observed on the pending write events queue for debug purposes.
+ size_t mMostEventsObservedPendingWriteEventsQueue = 0;
+
//! The max number of events allowed in the pending write events queue
static constexpr size_t kMaxSizePendingWriteEventsQueue = 100000;
diff --git a/sensors/2.0/multihal/tests/Android.bp b/sensors/2.0/multihal/tests/Android.bp
index 1637312..a9feaf7 100644
--- a/sensors/2.0/multihal/tests/Android.bp
+++ b/sensors/2.0/multihal/tests/Android.bp
@@ -25,7 +25,7 @@
shared_libs: [
"android.hardware.sensors@1.0",
"android.hardware.sensors@2.0",
- "android.hardware.sensors@2.0-ScopedWakelock",
+ "android.hardware.sensors@2.0-ScopedWakelock",
"libcutils",
"libfmq",
"libhardware",
@@ -38,7 +38,7 @@
"android.hardware.sensors@2.0-HalProxy",
],
cflags: [
- "-DLOG_TAG=\"FakeSubHal\""
+ "-DLOG_TAG=\"FakeSubHal\"",
],
}
diff --git a/sensors/2.0/vts/functional/Android.bp b/sensors/2.0/vts/functional/Android.bp
index 4765fa2..08c59b6 100644
--- a/sensors/2.0/vts/functional/Android.bp
+++ b/sensors/2.0/vts/functional/Android.bp
@@ -19,8 +19,10 @@
cflags: ["-DLOG_TAG=\"sensors_hidl_hal_test\""],
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
- "SensorsHidlEnvironmentV2_0.cpp",
- "VtsHalSensorsV2_0TargetTest.cpp"
+ "VtsHalSensorsV2_0TargetTest.cpp",
+ ],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
],
static_libs: [
"android.hardware.graphics.allocator@2.0",
@@ -29,9 +31,15 @@
"android.hardware.graphics.mapper@2.1",
"android.hardware.graphics.mapper@3.0",
"android.hardware.sensors@1.0",
+ "android.hardware.sensors@1.0-convert",
"android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
"libfmq",
"VtsHalSensorsTargetTestUtils",
+ "VtsHalSensorsV2_0TargetTest-lib",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core",
],
}
-
diff --git a/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp b/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp
index c5eb442..8895350 100644
--- a/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp
+++ b/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp
@@ -14,1130 +14,19 @@
* limitations under the License.
*/
-#include "SensorsHidlEnvironmentV2_0.h"
-#include "sensors-vts-utils/SensorsHidlTestBase.h"
-#include "sensors-vts-utils/SensorsTestSharedMemory.h"
+#include "VtsHalSensorsV2_XTargetTest.h"
-#include <android/hardware/sensors/2.0/ISensors.h>
-#include <android/hardware/sensors/2.0/types.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-#include <log/log.h>
-#include <utils/SystemClock.h>
-
-#include <cinttypes>
-#include <condition_variable>
-#include <cstring>
-#include <map>
-#include <vector>
-
-using ::android::sp;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::sensors::V1_0::MetaDataEventType;
-using ::android::hardware::sensors::V1_0::OperationMode;
-using ::android::hardware::sensors::V1_0::SensorsEventFormatOffset;
-using ::android::hardware::sensors::V1_0::SensorStatus;
-using ::android::hardware::sensors::V1_0::SharedMemType;
-using ::android::hardware::sensors::V1_0::Vec3;
-using std::chrono::duration_cast;
-using std::chrono::microseconds;
-using std::chrono::milliseconds;
-using std::chrono::nanoseconds;
-
-constexpr size_t kEventSize = static_cast<size_t>(SensorsEventFormatOffset::TOTAL_LENGTH);
-
-class EventCallback : public IEventCallback {
- public:
- void reset() {
- mFlushMap.clear();
- mEventMap.clear();
- }
-
- void onEvent(const ::android::hardware::sensors::V1_0::Event& event) override {
- if (event.sensorType == SensorType::META_DATA &&
- event.u.meta.what == MetaDataEventType::META_DATA_FLUSH_COMPLETE) {
- std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
- mFlushMap[event.sensorHandle]++;
- mFlushCV.notify_all();
- } else if (event.sensorType != SensorType::ADDITIONAL_INFO) {
- std::unique_lock<std::recursive_mutex> lock(mEventMutex);
- mEventMap[event.sensorHandle].push_back(event);
- mEventCV.notify_all();
- }
- }
-
- int32_t getFlushCount(int32_t sensorHandle) {
- std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
- return mFlushMap[sensorHandle];
- }
-
- void waitForFlushEvents(const std::vector<SensorInfo>& sensorsToWaitFor,
- int32_t numCallsToFlush, milliseconds timeout) {
- std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
- mFlushCV.wait_for(lock, timeout,
- [&] { return flushesReceived(sensorsToWaitFor, numCallsToFlush); });
- }
-
- const std::vector<Event> getEvents(int32_t sensorHandle) {
- std::unique_lock<std::recursive_mutex> lock(mEventMutex);
- return mEventMap[sensorHandle];
- }
-
- void waitForEvents(const std::vector<SensorInfo>& sensorsToWaitFor, milliseconds timeout) {
- std::unique_lock<std::recursive_mutex> lock(mEventMutex);
- mEventCV.wait_for(lock, timeout, [&] { return eventsReceived(sensorsToWaitFor); });
- }
-
- protected:
- bool flushesReceived(const std::vector<SensorInfo>& sensorsToWaitFor, int32_t numCallsToFlush) {
- for (const SensorInfo& sensor : sensorsToWaitFor) {
- if (getFlushCount(sensor.sensorHandle) < numCallsToFlush) {
- return false;
- }
- }
- return true;
- }
-
- bool eventsReceived(const std::vector<SensorInfo>& sensorsToWaitFor) {
- for (const SensorInfo& sensor : sensorsToWaitFor) {
- if (getEvents(sensor.sensorHandle).size() == 0) {
- return false;
- }
- }
- return true;
- }
-
- std::map<int32_t, int32_t> mFlushMap;
- std::recursive_mutex mFlushMutex;
- std::condition_variable_any mFlushCV;
-
- std::map<int32_t, std::vector<Event>> mEventMap;
- std::recursive_mutex mEventMutex;
- std::condition_variable_any mEventCV;
-};
-
-// The main test class for SENSORS HIDL HAL.
-
-class SensorsHidlTest : public SensorsHidlTestBase {
- public:
- virtual void SetUp() override {
- mEnvironment = new SensorsHidlEnvironmentV2_0(GetParam());
- mEnvironment->HidlSetUp();
- // Ensure that we have a valid environment before performing tests
- ASSERT_NE(getSensors(), nullptr);
- }
-
- virtual void TearDown() override { mEnvironment->HidlTearDown(); }
-
- protected:
- SensorInfo defaultSensorByType(SensorType type) override;
- std::vector<SensorInfo> getSensorsList();
- // implementation wrapper
- Return<void> getSensorsList(ISensors::getSensorsList_cb _hidl_cb) override {
- return getSensors()->getSensorsList(_hidl_cb);
- }
-
- Return<Result> activate(int32_t sensorHandle, bool enabled) override;
-
- Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
- int64_t maxReportLatencyNs) override {
- return getSensors()->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs);
- }
-
- Return<Result> flush(int32_t sensorHandle) override {
- return getSensors()->flush(sensorHandle);
- }
-
- Return<Result> injectSensorData(const Event& event) override {
- return getSensors()->injectSensorData(event);
- }
-
- Return<void> registerDirectChannel(const SharedMemInfo& mem,
- ISensors::registerDirectChannel_cb _hidl_cb) override;
-
- Return<Result> unregisterDirectChannel(int32_t channelHandle) override {
- return getSensors()->unregisterDirectChannel(channelHandle);
- }
-
- Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
- ISensors::configDirectReport_cb _hidl_cb) override {
- return getSensors()->configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb);
- }
-
- inline sp<::android::hardware::sensors::V2_0::ISensors>& getSensors() {
- return mEnvironment->mSensors;
- }
-
- SensorsHidlEnvironmentBase* getEnvironment() override { return mEnvironment; }
-
- // Test helpers
- void runSingleFlushTest(const std::vector<SensorInfo>& sensors, bool activateSensor,
- int32_t expectedFlushCount, Result expectedResponse);
- void runFlushTest(const std::vector<SensorInfo>& sensors, bool activateSensor,
- int32_t flushCalls, int32_t expectedFlushCount, Result expectedResponse);
-
- // Helper functions
- void activateAllSensors(bool enable);
- std::vector<SensorInfo> getNonOneShotSensors();
- std::vector<SensorInfo> getNonOneShotAndNonSpecialSensors();
- std::vector<SensorInfo> getOneShotSensors();
- std::vector<SensorInfo> getInjectEventSensors();
- int32_t getInvalidSensorHandle();
- bool getDirectChannelSensor(SensorInfo* sensor, SharedMemType* memType, RateLevel* rate);
- void verifyDirectChannel(SharedMemType memType);
- void verifyRegisterDirectChannel(std::shared_ptr<SensorsTestSharedMemory> mem,
- int32_t* directChannelHandle, bool supportsSharedMemType,
- bool supportsAnyDirectChannel);
- void verifyConfigure(const SensorInfo& sensor, SharedMemType memType,
- int32_t directChannelHandle, bool directChannelSupported);
- void verifyUnregisterDirectChannel(int32_t directChannelHandle, bool directChannelSupported);
- void checkRateLevel(const SensorInfo& sensor, int32_t directChannelHandle, RateLevel rateLevel);
- void queryDirectChannelSupport(SharedMemType memType, bool* supportsSharedMemType,
- bool* supportsAnyDirectChannel);
-
- private:
- // Test environment for sensors HAL.
- SensorsHidlEnvironmentV2_0* mEnvironment;
-};
-
-Return<Result> SensorsHidlTest::activate(int32_t sensorHandle, bool enabled) {
- // If activating a sensor, add the handle in a set so that when test fails it can be turned off.
- // The handle is not removed when it is deactivating on purpose so that it is not necessary to
- // check the return value of deactivation. Deactivating a sensor more than once does not have
- // negative effect.
- if (enabled) {
- mSensorHandles.insert(sensorHandle);
- }
- return getSensors()->activate(sensorHandle, enabled);
-}
-
-Return<void> SensorsHidlTest::registerDirectChannel(const SharedMemInfo& mem,
- ISensors::registerDirectChannel_cb cb) {
- // If registeration of a channel succeeds, add the handle of channel to a set so that it can be
- // unregistered when test fails. Unregister a channel does not remove the handle on purpose.
- // Unregistering a channel more than once should not have negative effect.
- getSensors()->registerDirectChannel(mem, [&](auto result, auto channelHandle) {
- if (result == Result::OK) {
- mDirectChannelHandles.insert(channelHandle);
- }
- cb(result, channelHandle);
- });
- return Void();
-}
-
-SensorInfo SensorsHidlTest::defaultSensorByType(SensorType type) {
- SensorInfo ret;
-
- ret.type = (SensorType)-1;
- getSensors()->getSensorsList([&](const auto& list) {
- const size_t count = list.size();
- for (size_t i = 0; i < count; ++i) {
- if (list[i].type == type) {
- ret = list[i];
- return;
- }
- }
- });
-
- return ret;
-}
-
-std::vector<SensorInfo> SensorsHidlTest::getSensorsList() {
- std::vector<SensorInfo> ret;
-
- getSensors()->getSensorsList([&](const auto& list) {
- const size_t count = list.size();
- ret.reserve(list.size());
- for (size_t i = 0; i < count; ++i) {
- ret.push_back(list[i]);
- }
- });
-
- return ret;
-}
-
-std::vector<SensorInfo> SensorsHidlTest::getNonOneShotSensors() {
- std::vector<SensorInfo> sensors;
- for (const SensorInfo& info : getSensorsList()) {
- if (extractReportMode(info.flags) != SensorFlagBits::ONE_SHOT_MODE) {
- sensors.push_back(info);
- }
- }
- return sensors;
-}
-
-std::vector<SensorInfo> SensorsHidlTest::getNonOneShotAndNonSpecialSensors() {
- std::vector<SensorInfo> sensors;
- for (const SensorInfo& info : getSensorsList()) {
- SensorFlagBits reportMode = extractReportMode(info.flags);
- if (reportMode != SensorFlagBits::ONE_SHOT_MODE &&
- reportMode != SensorFlagBits::SPECIAL_REPORTING_MODE) {
- sensors.push_back(info);
- }
- }
- return sensors;
-}
-
-std::vector<SensorInfo> SensorsHidlTest::getOneShotSensors() {
- std::vector<SensorInfo> sensors;
- for (const SensorInfo& info : getSensorsList()) {
- if (extractReportMode(info.flags) == SensorFlagBits::ONE_SHOT_MODE) {
- sensors.push_back(info);
- }
- }
- return sensors;
-}
-
-std::vector<SensorInfo> SensorsHidlTest::getInjectEventSensors() {
- std::vector<SensorInfo> sensors;
- for (const SensorInfo& info : getSensorsList()) {
- if (info.flags & static_cast<uint32_t>(SensorFlagBits::DATA_INJECTION)) {
- sensors.push_back(info);
- }
- }
- return sensors;
-}
-
-int32_t SensorsHidlTest::getInvalidSensorHandle() {
- // Find a sensor handle that does not exist in the sensor list
- int32_t maxHandle = 0;
- for (const SensorInfo& sensor : getSensorsList()) {
- maxHandle = max(maxHandle, sensor.sensorHandle);
- }
- return maxHandle + 1;
-}
-
-// Test if sensor list returned is valid
-TEST_P(SensorsHidlTest, SensorListValid) {
+TEST_P(SensorsHidlTest, SensorListDoesntContainInvalidType) {
getSensors()->getSensorsList([&](const auto& list) {
const size_t count = list.size();
for (size_t i = 0; i < count; ++i) {
const auto& s = list[i];
- SCOPED_TRACE(::testing::Message()
- << i << "/" << count << ": "
- << " handle=0x" << std::hex << std::setw(8) << std::setfill('0')
- << s.sensorHandle << std::dec << " type=" << static_cast<int>(s.type)
- << " name=" << s.name);
-
- // Test non-empty type string
- EXPECT_FALSE(s.typeAsString.empty());
-
- // Test defined type matches defined string type
- EXPECT_NO_FATAL_FAILURE(assertTypeMatchStringType(s.type, s.typeAsString));
-
- // Test if all sensor has name and vendor
- EXPECT_FALSE(s.name.empty());
- EXPECT_FALSE(s.vendor.empty());
-
- // Test power > 0, maxRange > 0
- EXPECT_LE(0, s.power);
- EXPECT_LT(0, s.maxRange);
-
- // Info type, should have no sensor
- EXPECT_FALSE(s.type == SensorType::ADDITIONAL_INFO || s.type == SensorType::META_DATA);
-
- // Test fifoMax >= fifoReserved
- EXPECT_GE(s.fifoMaxEventCount, s.fifoReservedEventCount)
- << "max=" << s.fifoMaxEventCount << " reserved=" << s.fifoReservedEventCount;
-
- // Test Reporting mode valid
- EXPECT_NO_FATAL_FAILURE(assertTypeMatchReportMode(s.type, extractReportMode(s.flags)));
-
- // Test min max are in the right order
- EXPECT_LE(s.minDelay, s.maxDelay);
- // Test min/max delay matches reporting mode
- EXPECT_NO_FATAL_FAILURE(
- assertDelayMatchReportMode(s.minDelay, s.maxDelay, extractReportMode(s.flags)));
+ EXPECT_FALSE(s.type == ::android::hardware::sensors::V2_1::SensorType::HINGE_ANGLE);
}
});
}
-// Test that SetOperationMode returns the expected value
-TEST_P(SensorsHidlTest, SetOperationMode) {
- std::vector<SensorInfo> sensors = getInjectEventSensors();
- if (getInjectEventSensors().size() > 0) {
- ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
- ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::DATA_INJECTION));
- ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
- } else {
- ASSERT_EQ(Result::BAD_VALUE, getSensors()->setOperationMode(OperationMode::DATA_INJECTION));
- }
-}
-
-// Test that an injected event is written back to the Event FMQ
-TEST_P(SensorsHidlTest, InjectSensorEventData) {
- std::vector<SensorInfo> sensors = getInjectEventSensors();
- if (sensors.size() == 0) {
- return;
- }
-
- ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::DATA_INJECTION));
-
- EventCallback callback;
- getEnvironment()->registerCallback(&callback);
-
- // AdditionalInfo event should not be sent to Event FMQ
- Event additionalInfoEvent;
- additionalInfoEvent.sensorType = SensorType::ADDITIONAL_INFO;
- additionalInfoEvent.timestamp = android::elapsedRealtimeNano();
-
- Event injectedEvent;
- injectedEvent.timestamp = android::elapsedRealtimeNano();
- Vec3 data = {1, 2, 3, SensorStatus::ACCURACY_HIGH};
- injectedEvent.u.vec3 = data;
-
- for (const auto& s : sensors) {
- additionalInfoEvent.sensorHandle = s.sensorHandle;
- EXPECT_EQ(Result::OK, getSensors()->injectSensorData(additionalInfoEvent));
-
- injectedEvent.sensorType = s.type;
- injectedEvent.sensorHandle = s.sensorHandle;
- EXPECT_EQ(Result::OK, getSensors()->injectSensorData(injectedEvent));
- }
-
- // Wait for events to be written back to the Event FMQ
- callback.waitForEvents(sensors, milliseconds(1000) /* timeout */);
-
- for (const auto& s : sensors) {
- auto events = callback.getEvents(s.sensorHandle);
- auto lastEvent = events.back();
-
- // Verify that only a single event has been received
- ASSERT_EQ(events.size(), 1);
-
- // Verify that the event received matches the event injected and is not the additional
- // info event
- ASSERT_EQ(lastEvent.sensorType, s.type);
- ASSERT_EQ(lastEvent.sensorType, s.type);
- ASSERT_EQ(lastEvent.timestamp, injectedEvent.timestamp);
- ASSERT_EQ(lastEvent.u.vec3.x, injectedEvent.u.vec3.x);
- ASSERT_EQ(lastEvent.u.vec3.y, injectedEvent.u.vec3.y);
- ASSERT_EQ(lastEvent.u.vec3.z, injectedEvent.u.vec3.z);
- ASSERT_EQ(lastEvent.u.vec3.status, injectedEvent.u.vec3.status);
- }
-
- getEnvironment()->unregisterCallback();
- ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
-}
-
-// Test if sensor hal can do UI speed accelerometer streaming properly
-TEST_P(SensorsHidlTest, AccelerometerStreamingOperationSlow) {
- testStreamingOperation(SensorType::ACCELEROMETER, std::chrono::milliseconds(200),
- std::chrono::seconds(5), sAccelNormChecker);
-}
-
-// Test if sensor hal can do normal speed accelerometer streaming properly
-TEST_P(SensorsHidlTest, AccelerometerStreamingOperationNormal) {
- testStreamingOperation(SensorType::ACCELEROMETER, std::chrono::milliseconds(20),
- std::chrono::seconds(5), sAccelNormChecker);
-}
-
-// Test if sensor hal can do game speed accelerometer streaming properly
-TEST_P(SensorsHidlTest, AccelerometerStreamingOperationFast) {
- testStreamingOperation(SensorType::ACCELEROMETER, std::chrono::milliseconds(5),
- std::chrono::seconds(5), sAccelNormChecker);
-}
-
-// Test if sensor hal can do UI speed gyroscope streaming properly
-TEST_P(SensorsHidlTest, GyroscopeStreamingOperationSlow) {
- testStreamingOperation(SensorType::GYROSCOPE, std::chrono::milliseconds(200),
- std::chrono::seconds(5), sGyroNormChecker);
-}
-
-// Test if sensor hal can do normal speed gyroscope streaming properly
-TEST_P(SensorsHidlTest, GyroscopeStreamingOperationNormal) {
- testStreamingOperation(SensorType::GYROSCOPE, std::chrono::milliseconds(20),
- std::chrono::seconds(5), sGyroNormChecker);
-}
-
-// Test if sensor hal can do game speed gyroscope streaming properly
-TEST_P(SensorsHidlTest, GyroscopeStreamingOperationFast) {
- testStreamingOperation(SensorType::GYROSCOPE, std::chrono::milliseconds(5),
- std::chrono::seconds(5), sGyroNormChecker);
-}
-
-// Test if sensor hal can do UI speed magnetometer streaming properly
-TEST_P(SensorsHidlTest, MagnetometerStreamingOperationSlow) {
- testStreamingOperation(SensorType::MAGNETIC_FIELD, std::chrono::milliseconds(200),
- std::chrono::seconds(5), NullChecker());
-}
-
-// Test if sensor hal can do normal speed magnetometer streaming properly
-TEST_P(SensorsHidlTest, MagnetometerStreamingOperationNormal) {
- testStreamingOperation(SensorType::MAGNETIC_FIELD, std::chrono::milliseconds(20),
- std::chrono::seconds(5), NullChecker());
-}
-
-// Test if sensor hal can do game speed magnetometer streaming properly
-TEST_P(SensorsHidlTest, MagnetometerStreamingOperationFast) {
- testStreamingOperation(SensorType::MAGNETIC_FIELD, std::chrono::milliseconds(5),
- std::chrono::seconds(5), NullChecker());
-}
-
-// Test if sensor hal can do accelerometer sampling rate switch properly when sensor is active
-TEST_P(SensorsHidlTest, AccelerometerSamplingPeriodHotSwitchOperation) {
- testSamplingRateHotSwitchOperation(SensorType::ACCELEROMETER);
- testSamplingRateHotSwitchOperation(SensorType::ACCELEROMETER, false /*fastToSlow*/);
-}
-
-// Test if sensor hal can do gyroscope sampling rate switch properly when sensor is active
-TEST_P(SensorsHidlTest, GyroscopeSamplingPeriodHotSwitchOperation) {
- testSamplingRateHotSwitchOperation(SensorType::GYROSCOPE);
- testSamplingRateHotSwitchOperation(SensorType::GYROSCOPE, false /*fastToSlow*/);
-}
-
-// Test if sensor hal can do magnetometer sampling rate switch properly when sensor is active
-TEST_P(SensorsHidlTest, MagnetometerSamplingPeriodHotSwitchOperation) {
- testSamplingRateHotSwitchOperation(SensorType::MAGNETIC_FIELD);
- testSamplingRateHotSwitchOperation(SensorType::MAGNETIC_FIELD, false /*fastToSlow*/);
-}
-
-// Test if sensor hal can do accelerometer batching properly
-TEST_P(SensorsHidlTest, AccelerometerBatchingOperation) {
- testBatchingOperation(SensorType::ACCELEROMETER);
-}
-
-// Test if sensor hal can do gyroscope batching properly
-TEST_P(SensorsHidlTest, GyroscopeBatchingOperation) {
- testBatchingOperation(SensorType::GYROSCOPE);
-}
-
-// Test if sensor hal can do magnetometer batching properly
-TEST_P(SensorsHidlTest, MagnetometerBatchingOperation) {
- testBatchingOperation(SensorType::MAGNETIC_FIELD);
-}
-
-// Test sensor event direct report with ashmem for accel sensor at normal rate
-TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationNormal) {
- testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::ASHMEM, RateLevel::NORMAL,
- sAccelNormChecker);
-}
-
-// Test sensor event direct report with ashmem for accel sensor at fast rate
-TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationFast) {
- testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::ASHMEM, RateLevel::FAST,
- sAccelNormChecker);
-}
-
-// Test sensor event direct report with ashmem for accel sensor at very fast rate
-TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationVeryFast) {
- testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::ASHMEM,
- RateLevel::VERY_FAST, sAccelNormChecker);
-}
-
-// Test sensor event direct report with ashmem for gyro sensor at normal rate
-TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationNormal) {
- testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::NORMAL,
- sGyroNormChecker);
-}
-
-// Test sensor event direct report with ashmem for gyro sensor at fast rate
-TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationFast) {
- testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::FAST,
- sGyroNormChecker);
-}
-
-// Test sensor event direct report with ashmem for gyro sensor at very fast rate
-TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationVeryFast) {
- testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::VERY_FAST,
- sGyroNormChecker);
-}
-
-// Test sensor event direct report with ashmem for mag sensor at normal rate
-TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationNormal) {
- testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::ASHMEM, RateLevel::NORMAL,
- NullChecker());
-}
-
-// Test sensor event direct report with ashmem for mag sensor at fast rate
-TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationFast) {
- testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::ASHMEM, RateLevel::FAST,
- NullChecker());
-}
-
-// Test sensor event direct report with ashmem for mag sensor at very fast rate
-TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationVeryFast) {
- testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::ASHMEM,
- RateLevel::VERY_FAST, NullChecker());
-}
-
-// Test sensor event direct report with gralloc for accel sensor at normal rate
-TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationNormal) {
- testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::GRALLOC, RateLevel::NORMAL,
- sAccelNormChecker);
-}
-
-// Test sensor event direct report with gralloc for accel sensor at fast rate
-TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationFast) {
- testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::GRALLOC, RateLevel::FAST,
- sAccelNormChecker);
-}
-
-// Test sensor event direct report with gralloc for accel sensor at very fast rate
-TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationVeryFast) {
- testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::GRALLOC,
- RateLevel::VERY_FAST, sAccelNormChecker);
-}
-
-// Test sensor event direct report with gralloc for gyro sensor at normal rate
-TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationNormal) {
- testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::NORMAL,
- sGyroNormChecker);
-}
-
-// Test sensor event direct report with gralloc for gyro sensor at fast rate
-TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationFast) {
- testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::FAST,
- sGyroNormChecker);
-}
-
-// Test sensor event direct report with gralloc for gyro sensor at very fast rate
-TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationVeryFast) {
- testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::VERY_FAST,
- sGyroNormChecker);
-}
-
-// Test sensor event direct report with gralloc for mag sensor at normal rate
-TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationNormal) {
- testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::GRALLOC, RateLevel::NORMAL,
- NullChecker());
-}
-
-// Test sensor event direct report with gralloc for mag sensor at fast rate
-TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationFast) {
- testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::GRALLOC, RateLevel::FAST,
- NullChecker());
-}
-
-// Test sensor event direct report with gralloc for mag sensor at very fast rate
-TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationVeryFast) {
- testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::GRALLOC,
- RateLevel::VERY_FAST, NullChecker());
-}
-
-void SensorsHidlTest::activateAllSensors(bool enable) {
- for (const SensorInfo& sensorInfo : getSensorsList()) {
- if (isValidType(sensorInfo.type)) {
- batch(sensorInfo.sensorHandle, sensorInfo.minDelay, 0 /* maxReportLatencyNs */);
- activate(sensorInfo.sensorHandle, enable);
- }
- }
-}
-
-// Test that if initialize is called twice, then the HAL writes events to the FMQs from the second
-// call to the function.
-TEST_P(SensorsHidlTest, CallInitializeTwice) {
- // Create a helper class so that a second environment is able to be instantiated
- class SensorsHidlEnvironmentTest : public SensorsHidlEnvironmentV2_0 {
- public:
- SensorsHidlEnvironmentTest(const std::string& service_name)
- : SensorsHidlEnvironmentV2_0(service_name) {}
- };
-
- if (getSensorsList().size() == 0) {
- // No sensors
- return;
- }
-
- constexpr useconds_t kCollectionTimeoutUs = 1000 * 1000; // 1s
- constexpr int32_t kNumEvents = 1;
-
- // Create a new environment that calls initialize()
- std::unique_ptr<SensorsHidlEnvironmentTest> newEnv =
- std::make_unique<SensorsHidlEnvironmentTest>(GetParam());
- newEnv->HidlSetUp();
- if (HasFatalFailure()) {
- return; // Exit early if setting up the new environment failed
- }
-
- activateAllSensors(true);
- // Verify that the old environment does not receive any events
- ASSERT_EQ(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), 0);
- // Verify that the new event queue receives sensor events
- ASSERT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents, newEnv.get()).size(), kNumEvents);
- activateAllSensors(false);
-
- // Cleanup the test environment
- newEnv->HidlTearDown();
-
- // Restore the test environment for future tests
- getEnvironment()->HidlTearDown();
- getEnvironment()->HidlSetUp();
- if (HasFatalFailure()) {
- return; // Exit early if resetting the environment failed
- }
-
- // Ensure that the original environment is receiving events
- activateAllSensors(true);
- ASSERT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents).size(), kNumEvents);
- activateAllSensors(false);
-}
-
-TEST_P(SensorsHidlTest, CleanupConnectionsOnInitialize) {
- activateAllSensors(true);
-
- // Verify that events are received
- constexpr useconds_t kCollectionTimeoutUs = 1000 * 1000; // 1s
- constexpr int32_t kNumEvents = 1;
- ASSERT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), kNumEvents);
-
- // Clear the active sensor handles so they are not disabled during TearDown
- auto handles = mSensorHandles;
- mSensorHandles.clear();
- getEnvironment()->HidlTearDown();
- getEnvironment()->HidlSetUp();
- if (HasFatalFailure()) {
- return; // Exit early if resetting the environment failed
- }
-
- // Verify no events are received until sensors are re-activated
- ASSERT_EQ(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), 0);
- activateAllSensors(true);
- ASSERT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), kNumEvents);
-
- // Disable sensors
- activateAllSensors(false);
-
- // Restore active sensors prior to clearing the environment
- mSensorHandles = handles;
-}
-
-void SensorsHidlTest::runSingleFlushTest(const std::vector<SensorInfo>& sensors,
- bool activateSensor, int32_t expectedFlushCount,
- Result expectedResponse) {
- runFlushTest(sensors, activateSensor, 1 /* flushCalls */, expectedFlushCount, expectedResponse);
-}
-
-void SensorsHidlTest::runFlushTest(const std::vector<SensorInfo>& sensors, bool activateSensor,
- int32_t flushCalls, int32_t expectedFlushCount,
- Result expectedResponse) {
- EventCallback callback;
- getEnvironment()->registerCallback(&callback);
-
- for (const SensorInfo& sensor : sensors) {
- // Configure and activate the sensor
- batch(sensor.sensorHandle, sensor.maxDelay, 0 /* maxReportLatencyNs */);
- activate(sensor.sensorHandle, activateSensor);
-
- // Flush the sensor
- for (int32_t i = 0; i < flushCalls; i++) {
- Result flushResult = flush(sensor.sensorHandle);
- ASSERT_EQ(flushResult, expectedResponse);
- }
- }
-
- // Wait up to one second for the flush events
- callback.waitForFlushEvents(sensors, flushCalls, milliseconds(1000) /* timeout */);
-
- // Deactivate all sensors after waiting for flush events so pending flush events are not
- // abandoned by the HAL.
- for (const SensorInfo& sensor : sensors) {
- activate(sensor.sensorHandle, false);
- }
- getEnvironment()->unregisterCallback();
-
- // Check that the correct number of flushes are present for each sensor
- for (const SensorInfo& sensor : sensors) {
- ASSERT_EQ(callback.getFlushCount(sensor.sensorHandle), expectedFlushCount);
- }
-}
-
-TEST_P(SensorsHidlTest, FlushSensor) {
- // Find a sensor that is not a one-shot sensor
- std::vector<SensorInfo> sensors = getNonOneShotSensors();
- if (sensors.size() == 0) {
- return;
- }
-
- constexpr int32_t kFlushes = 5;
- runSingleFlushTest(sensors, true /* activateSensor */, 1 /* expectedFlushCount */, Result::OK);
- runFlushTest(sensors, true /* activateSensor */, kFlushes, kFlushes, Result::OK);
-}
-
-TEST_P(SensorsHidlTest, FlushOneShotSensor) {
- // Find a sensor that is a one-shot sensor
- std::vector<SensorInfo> sensors = getOneShotSensors();
- if (sensors.size() == 0) {
- return;
- }
-
- runSingleFlushTest(sensors, true /* activateSensor */, 0 /* expectedFlushCount */,
- Result::BAD_VALUE);
-}
-
-TEST_P(SensorsHidlTest, FlushInactiveSensor) {
- // Attempt to find a non-one shot sensor, then a one-shot sensor if necessary
- std::vector<SensorInfo> sensors = getNonOneShotSensors();
- if (sensors.size() == 0) {
- sensors = getOneShotSensors();
- if (sensors.size() == 0) {
- return;
- }
- }
-
- runSingleFlushTest(sensors, false /* activateSensor */, 0 /* expectedFlushCount */,
- Result::BAD_VALUE);
-}
-
-TEST_P(SensorsHidlTest, FlushNonexistentSensor) {
- SensorInfo sensor;
- std::vector<SensorInfo> sensors = getNonOneShotSensors();
- if (sensors.size() == 0) {
- sensors = getOneShotSensors();
- if (sensors.size() == 0) {
- return;
- }
- }
- sensor = sensors.front();
- sensor.sensorHandle = getInvalidSensorHandle();
- runSingleFlushTest(std::vector<SensorInfo>{sensor}, false /* activateSensor */,
- 0 /* expectedFlushCount */, Result::BAD_VALUE);
-}
-
-TEST_P(SensorsHidlTest, Batch) {
- if (getSensorsList().size() == 0) {
- return;
- }
-
- activateAllSensors(false /* enable */);
- for (const SensorInfo& sensor : getSensorsList()) {
- // Call batch on inactive sensor
- // One shot sensors have minDelay set to -1 which is an invalid
- // parameter. Use 0 instead to avoid errors.
- int64_t samplingPeriodNs = extractReportMode(sensor.flags) == SensorFlagBits::ONE_SHOT_MODE
- ? 0
- : sensor.minDelay;
- ASSERT_EQ(batch(sensor.sensorHandle, samplingPeriodNs, 0 /* maxReportLatencyNs */),
- Result::OK);
-
- // Activate the sensor
- activate(sensor.sensorHandle, true /* enabled */);
-
- // Call batch on an active sensor
- ASSERT_EQ(batch(sensor.sensorHandle, sensor.maxDelay, 0 /* maxReportLatencyNs */),
- Result::OK);
- }
- activateAllSensors(false /* enable */);
-
- // Call batch on an invalid sensor
- SensorInfo sensor = getSensorsList().front();
- sensor.sensorHandle = getInvalidSensorHandle();
- ASSERT_EQ(batch(sensor.sensorHandle, sensor.minDelay, 0 /* maxReportLatencyNs */),
- Result::BAD_VALUE);
-}
-
-TEST_P(SensorsHidlTest, Activate) {
- if (getSensorsList().size() == 0) {
- return;
- }
-
- // Verify that sensor events are generated when activate is called
- for (const SensorInfo& sensor : getSensorsList()) {
- batch(sensor.sensorHandle, sensor.minDelay, 0 /* maxReportLatencyNs */);
- ASSERT_EQ(activate(sensor.sensorHandle, true), Result::OK);
-
- // Call activate on a sensor that is already activated
- ASSERT_EQ(activate(sensor.sensorHandle, true), Result::OK);
-
- // Deactivate the sensor
- ASSERT_EQ(activate(sensor.sensorHandle, false), Result::OK);
-
- // Call deactivate on a sensor that is already deactivated
- ASSERT_EQ(activate(sensor.sensorHandle, false), Result::OK);
- }
-
- // Attempt to activate an invalid sensor
- int32_t invalidHandle = getInvalidSensorHandle();
- ASSERT_EQ(activate(invalidHandle, true), Result::BAD_VALUE);
- ASSERT_EQ(activate(invalidHandle, false), Result::BAD_VALUE);
-}
-
-TEST_P(SensorsHidlTest, NoStaleEvents) {
- constexpr milliseconds kFiveHundredMs(500);
- constexpr milliseconds kOneSecond(1000);
-
- // Register the callback to receive sensor events
- EventCallback callback;
- getEnvironment()->registerCallback(&callback);
-
- // This test is not valid for one-shot or special-report-mode sensors
- const std::vector<SensorInfo> sensors = getNonOneShotAndNonSpecialSensors();
- milliseconds maxMinDelay(0);
- for (const SensorInfo& sensor : sensors) {
- milliseconds minDelay = duration_cast<milliseconds>(microseconds(sensor.minDelay));
- maxMinDelay = milliseconds(std::max(maxMinDelay.count(), minDelay.count()));
- }
-
- // Activate the sensors so that they start generating events
- activateAllSensors(true);
-
- // According to the CDD, the first sample must be generated within 400ms + 2 * sample_time
- // and the maximum reporting latency is 100ms + 2 * sample_time. Wait a sufficient amount
- // of time to guarantee that a sample has arrived.
- callback.waitForEvents(sensors, kFiveHundredMs + (5 * maxMinDelay));
- activateAllSensors(false);
-
- // Save the last received event for each sensor
- std::map<int32_t, int64_t> lastEventTimestampMap;
- for (const SensorInfo& sensor : sensors) {
- // Some on-change sensors may not report an event without stimulus
- if (extractReportMode(sensor.flags) != SensorFlagBits::ON_CHANGE_MODE) {
- ASSERT_GE(callback.getEvents(sensor.sensorHandle).size(), 1);
- }
- if (callback.getEvents(sensor.sensorHandle).size() >= 1) {
- lastEventTimestampMap[sensor.sensorHandle] =
- callback.getEvents(sensor.sensorHandle).back().timestamp;
- }
- }
-
- // Allow some time to pass, reset the callback, then reactivate the sensors
- usleep(duration_cast<microseconds>(kOneSecond + (5 * maxMinDelay)).count());
- callback.reset();
- activateAllSensors(true);
- callback.waitForEvents(sensors, kFiveHundredMs + (5 * maxMinDelay));
- activateAllSensors(false);
-
- for (const SensorInfo& sensor : sensors) {
- // Skip sensors that did not previously report an event
- if (lastEventTimestampMap.find(sensor.sensorHandle) == lastEventTimestampMap.end()) {
- continue;
- }
- // Skip on-change sensors that do not consistently report an initial event
- if (callback.getEvents(sensor.sensorHandle).size() < 1) {
- continue;
- }
- // Ensure that the first event received is not stale by ensuring that its timestamp is
- // sufficiently different from the previous event
- const Event newEvent = callback.getEvents(sensor.sensorHandle).front();
- milliseconds delta = duration_cast<milliseconds>(
- nanoseconds(newEvent.timestamp - lastEventTimestampMap[sensor.sensorHandle]));
- milliseconds sensorMinDelay = duration_cast<milliseconds>(microseconds(sensor.minDelay));
- ASSERT_GE(delta, kFiveHundredMs + (3 * sensorMinDelay));
- }
-}
-
-void SensorsHidlTest::checkRateLevel(const SensorInfo& sensor, int32_t directChannelHandle,
- RateLevel rateLevel) {
- configDirectReport(sensor.sensorHandle, directChannelHandle, rateLevel,
- [&](Result result, int32_t reportToken) {
- if (isDirectReportRateSupported(sensor, rateLevel)) {
- ASSERT_EQ(result, Result::OK);
- if (rateLevel != RateLevel::STOP) {
- ASSERT_GT(reportToken, 0);
- }
- } else {
- ASSERT_EQ(result, Result::BAD_VALUE);
- }
- });
-}
-
-void SensorsHidlTest::queryDirectChannelSupport(SharedMemType memType, bool* supportsSharedMemType,
- bool* supportsAnyDirectChannel) {
- *supportsSharedMemType = false;
- *supportsAnyDirectChannel = false;
- for (const SensorInfo& curSensor : getSensorsList()) {
- if (isDirectChannelTypeSupported(curSensor, memType)) {
- *supportsSharedMemType = true;
- }
- if (isDirectChannelTypeSupported(curSensor, SharedMemType::ASHMEM) ||
- isDirectChannelTypeSupported(curSensor, SharedMemType::GRALLOC)) {
- *supportsAnyDirectChannel = true;
- }
-
- if (*supportsSharedMemType && *supportsAnyDirectChannel) {
- break;
- }
- }
-}
-
-void SensorsHidlTest::verifyRegisterDirectChannel(std::shared_ptr<SensorsTestSharedMemory> mem,
- int32_t* directChannelHandle,
- bool supportsSharedMemType,
- bool supportsAnyDirectChannel) {
- char* buffer = mem->getBuffer();
- memset(buffer, 0xff, mem->getSize());
-
- registerDirectChannel(mem->getSharedMemInfo(), [&](Result result, int32_t channelHandle) {
- if (supportsSharedMemType) {
- ASSERT_EQ(result, Result::OK);
- ASSERT_GT(channelHandle, 0);
-
- // Verify that the memory has been zeroed
- for (size_t i = 0; i < mem->getSize(); i++) {
- ASSERT_EQ(buffer[i], 0x00);
- }
- } else {
- Result expectedResult =
- supportsAnyDirectChannel ? Result::BAD_VALUE : Result::INVALID_OPERATION;
- ASSERT_EQ(result, expectedResult);
- ASSERT_EQ(channelHandle, -1);
- }
- *directChannelHandle = channelHandle;
- });
-}
-
-void SensorsHidlTest::verifyConfigure(const SensorInfo& sensor, SharedMemType memType,
- int32_t directChannelHandle, bool supportsAnyDirectChannel) {
- if (isDirectChannelTypeSupported(sensor, memType)) {
- // Verify that each rate level is properly supported
- checkRateLevel(sensor, directChannelHandle, RateLevel::NORMAL);
- checkRateLevel(sensor, directChannelHandle, RateLevel::FAST);
- checkRateLevel(sensor, directChannelHandle, RateLevel::VERY_FAST);
- checkRateLevel(sensor, directChannelHandle, RateLevel::STOP);
-
- // Verify that a sensor handle of -1 is only acceptable when using RateLevel::STOP
- configDirectReport(
- -1 /* sensorHandle */, directChannelHandle, RateLevel::NORMAL,
- [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::BAD_VALUE); });
- configDirectReport(
- -1 /* sensorHandle */, directChannelHandle, RateLevel::STOP,
- [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::OK); });
- } else {
- // directChannelHandle will be -1 here, HAL should either reject it as a bad value if there
- // is some level of direct channel report, otherwise return INVALID_OPERATION if direct
- // channel is not supported at all
- Result expectedResult =
- supportsAnyDirectChannel ? Result::BAD_VALUE : Result::INVALID_OPERATION;
- configDirectReport(sensor.sensorHandle, directChannelHandle, RateLevel::NORMAL,
- [expectedResult](Result result, int32_t /* reportToken */) {
- ASSERT_EQ(result, expectedResult);
- });
- }
-}
-
-void SensorsHidlTest::verifyUnregisterDirectChannel(int32_t directChannelHandle,
- bool supportsAnyDirectChannel) {
- Result expectedResult = supportsAnyDirectChannel ? Result::OK : Result::INVALID_OPERATION;
- ASSERT_EQ(unregisterDirectChannel(directChannelHandle), expectedResult);
-}
-
-void SensorsHidlTest::verifyDirectChannel(SharedMemType memType) {
- constexpr size_t kNumEvents = 1;
- constexpr size_t kMemSize = kNumEvents * kEventSize;
-
- std::shared_ptr<SensorsTestSharedMemory> mem(
- SensorsTestSharedMemory::create(memType, kMemSize));
- ASSERT_NE(mem, nullptr);
-
- bool supportsSharedMemType;
- bool supportsAnyDirectChannel;
- queryDirectChannelSupport(memType, &supportsSharedMemType, &supportsAnyDirectChannel);
-
- for (const SensorInfo& sensor : getSensorsList()) {
- int32_t directChannelHandle = 0;
- verifyRegisterDirectChannel(mem, &directChannelHandle, supportsSharedMemType,
- supportsAnyDirectChannel);
- verifyConfigure(sensor, memType, directChannelHandle, supportsAnyDirectChannel);
- verifyUnregisterDirectChannel(directChannelHandle, supportsAnyDirectChannel);
- }
-}
-
-TEST_P(SensorsHidlTest, DirectChannelAshmem) {
- verifyDirectChannel(SharedMemType::ASHMEM);
-}
-
-TEST_P(SensorsHidlTest, DirectChannelGralloc) {
- verifyDirectChannel(SharedMemType::GRALLOC);
-}
-
-bool SensorsHidlTest::getDirectChannelSensor(SensorInfo* sensor, SharedMemType* memType,
- RateLevel* rate) {
- bool found = false;
- for (const SensorInfo& curSensor : getSensorsList()) {
- if (isDirectChannelTypeSupported(curSensor, SharedMemType::ASHMEM)) {
- *memType = SharedMemType::ASHMEM;
- *sensor = curSensor;
- found = true;
- break;
- } else if (isDirectChannelTypeSupported(curSensor, SharedMemType::GRALLOC)) {
- *memType = SharedMemType::GRALLOC;
- *sensor = curSensor;
- found = true;
- break;
- }
- }
-
- if (found) {
- // Find a supported rate level
- constexpr int kNumRateLevels = 3;
- RateLevel rates[kNumRateLevels] = {RateLevel::NORMAL, RateLevel::FAST,
- RateLevel::VERY_FAST};
- *rate = RateLevel::STOP;
- for (int i = 0; i < kNumRateLevels; i++) {
- if (isDirectReportRateSupported(*sensor, rates[i])) {
- *rate = rates[i];
- }
- }
-
- // At least one rate level must be supported
- EXPECT_NE(*rate, RateLevel::STOP);
- }
- return found;
-}
-
-TEST_P(SensorsHidlTest, ConfigureDirectChannelWithInvalidHandle) {
- SensorInfo sensor;
- SharedMemType memType;
- RateLevel rate;
- if (!getDirectChannelSensor(&sensor, &memType, &rate)) {
- return;
- }
-
- // Verify that an invalid channel handle produces a BAD_VALUE result
- configDirectReport(sensor.sensorHandle, -1, rate, [](Result result, int32_t /* reportToken */) {
- ASSERT_EQ(result, Result::BAD_VALUE);
- });
-}
-
-TEST_P(SensorsHidlTest, CleanupDirectConnectionOnInitialize) {
- constexpr size_t kNumEvents = 1;
- constexpr size_t kMemSize = kNumEvents * kEventSize;
-
- SensorInfo sensor;
- SharedMemType memType;
- RateLevel rate;
-
- if (!getDirectChannelSensor(&sensor, &memType, &rate)) {
- return;
- }
-
- std::shared_ptr<SensorsTestSharedMemory> mem(
- SensorsTestSharedMemory::create(memType, kMemSize));
- ASSERT_NE(mem, nullptr);
-
- int32_t directChannelHandle = 0;
- registerDirectChannel(mem->getSharedMemInfo(), [&](Result result, int32_t channelHandle) {
- ASSERT_EQ(result, Result::OK);
- directChannelHandle = channelHandle;
- });
-
- // Configure the channel and expect success
- configDirectReport(
- sensor.sensorHandle, directChannelHandle, rate,
- [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::OK); });
-
- // Call initialize() via the environment setup to cause the HAL to re-initialize
- // Clear the active direct connections so they are not stopped during TearDown
- auto handles = mDirectChannelHandles;
- mDirectChannelHandles.clear();
- getEnvironment()->HidlTearDown();
- getEnvironment()->HidlSetUp();
- if (HasFatalFailure()) {
- return; // Exit early if resetting the environment failed
- }
-
- // Attempt to configure the direct channel and expect it to fail
- configDirectReport(
- sensor.sensorHandle, directChannelHandle, rate,
- [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::BAD_VALUE); });
-
- // Restore original handles, though they should already be deactivated
- mDirectChannelHandles = handles;
-}
-
INSTANTIATE_TEST_SUITE_P(PerInstance, SensorsHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(
android::hardware::sensors::V2_0::ISensors::descriptor)),
- android::hardware::PrintInstanceNameToString);
-// vim: set ts=2 sw=2
+ android::hardware::PrintInstanceNameToString);
\ No newline at end of file
diff --git a/sensors/2.1/Android.bp b/sensors/2.1/Android.bp
new file mode 100644
index 0000000..8e80e1f
--- /dev/null
+++ b/sensors/2.1/Android.bp
@@ -0,0 +1,21 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.sensors@2.1",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "ISensors.hal",
+ "ISensorsCallback.hal",
+ ],
+ interfaces: [
+ "android.hardware.sensors@1.0",
+ "android.hardware.sensors@2.0",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: false,
+ gen_java_constants: true,
+}
diff --git a/sensors/2.1/ISensors.hal b/sensors/2.1/ISensors.hal
new file mode 100644
index 0000000..d401fa5
--- /dev/null
+++ b/sensors/2.1/ISensors.hal
@@ -0,0 +1,148 @@
+/*
+ * 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.sensors@2.1;
+
+import @1.0::Result;
+import @2.0::ISensors;
+import @2.1::ISensorsCallback;
+
+interface ISensors extends @2.0::ISensors {
+ /**
+ * Enumerate all available (static) sensors.
+ *
+ * The SensorInfo for each sensor returned by getSensorsList must be stable
+ * from the initial call to getSensorsList after a device boot until the
+ * entire system restarts. The SensorInfo for each sensor must not change
+ * between subsequent calls to getSensorsList, even across restarts of the
+ * HAL and its dependencies (for example, the sensor handle for a given
+ * sensor must not change across HAL restarts).
+ */
+ getSensorsList_2_1() generates (vec<SensorInfo> list);
+
+ /**
+ * Initialize the Sensors HAL's Fast Message Queues (FMQ) and callback.
+ *
+ * The Fast Message Queues (FMQ) that are used to send data between the
+ * framework and the HAL. The callback is used by the HAL to notify the
+ * framework of asynchronous events, such as a dynamic sensor connection.
+ *
+ * The Event FMQ is used to transport sensor events from the HAL to the
+ * framework. The Event FMQ is created using the eventQueueDescriptor.
+ * Data may only be written to the Event FMQ. Data must not be read from
+ * the Event FMQ since the framework is the only reader. Upon receiving
+ * sensor events, the HAL writes the sensor events to the Event FMQ.
+ *
+ * Once the HAL is finished writing sensor events to the Event FMQ, the HAL
+ * must notify the framework that sensor events are available to be read and
+ * processed. This is accomplished by either:
+ * 1) Calling the Event FMQ’s EventFlag::wake() function with
+ EventQueueFlagBits::READ_AND_PROCESS
+ * 2) Setting the write notification in the Event FMQ’s writeBlocking()
+ * function to EventQueueFlagBits::READ_AND_PROCESS.
+ *
+ * If the Event FMQ’s writeBlocking() function is used, the read
+ * notification must be set to EventQueueFlagBits::EVENTS_READ in order to
+ * be notified and unblocked when the framework has successfully read events
+ * from the Event FMQ.
+ *
+ * The Wake Lock FMQ is used by the framework to notify the HAL when it is
+ * safe to release its wake_lock. When the framework receives WAKE_UP events
+ * from the Event FMQ and the framework has acquired a wake_lock, the
+ * framework must write the number of WAKE_UP events processed to the Wake
+ * Lock FMQ. When the HAL reads the data from the Wake Lock FMQ, the HAL
+ * decrements its current count of unprocessed WAKE_UP events and releases
+ * its wake_lock if the current count of unprocessed WAKE_UP events is
+ * zero. It is important to note that the HAL must acquire the wake lock and
+ * update its internal state regarding the number of outstanding WAKE_UP
+ * events _before_ posting the event to the Wake Lock FMQ, in order to avoid
+ * a race condition that can lead to loss of wake lock synchronization with
+ * the framework.
+ *
+ * The framework must use the WakeLockQueueFlagBits::DATA_WRITTEN value to
+ * notify the HAL that data has been written to the Wake Lock FMQ and must
+ * be read by HAL.
+ *
+ * The ISensorsCallback is used by the HAL to notify the framework of
+ * asynchronous events, such as a dynamic sensor connection.
+ *
+ * The name of any wake_lock acquired by the Sensors HAL for WAKE_UP events
+ * must begin with "SensorsHAL_WAKEUP".
+ *
+ * If WAKE_LOCK_TIMEOUT_SECONDS has elapsed since the most recent WAKE_UP
+ * event was written to the Event FMQ without receiving a message on the
+ * Wake Lock FMQ, then any held wake_lock for WAKE_UP events must be
+ * released.
+ *
+ * If either the Event FMQ or the Wake Lock FMQ is already initialized when
+ * initialize is invoked, then both existing FMQs must be discarded and the
+ * new descriptors must be used to create new FMQs within the HAL. The
+ * number of outstanding WAKE_UP events should also be reset to zero, and
+ * any outstanding wake_locks held as a result of WAKE_UP events should be
+ * released.
+ *
+ * All active sensor requests and direct channels must be closed and
+ * properly cleaned up when initialize is called in order to ensure that the
+ * HAL and framework's state is consistent (e.g. after a runtime restart).
+ *
+ * initialize must be thread safe and prevent concurrent calls
+ * to initialize from simultaneously modifying state.
+ *
+ * @param eventQueueDescriptor Fast Message Queue descriptor that is used to
+ * create the Event FMQ which is where sensor events are written. The
+ * descriptor is obtained from the framework's FMQ that is used to read
+ * sensor events.
+ * @param wakeLockDescriptor Fast Message Queue descriptor that is used to
+ * create the Wake Lock FMQ which is where wake_lock events are read
+ * from. The descriptor is obtained from the framework's FMQ that is
+ * used to write wake_lock events.
+ * @param sensorsCallback sensors callback that receives asynchronous data
+ * from the Sensors HAL.
+ * @return result OK on success; BAD_VALUE if descriptor is invalid (such
+ * as null)
+ */
+ @entry
+ @callflow(next = {"getSensorsList"})
+ initialize_2_1(fmq_sync<Event> eventQueueDescriptor,
+ fmq_sync<uint32_t> wakeLockDescriptor,
+ ISensorsCallback sensorsCallback)
+ generates
+ (Result result);
+
+ /**
+ * Inject a single sensor event or push operation environment parameters to
+ * device.
+ *
+ * When device is in NORMAL mode, this function is called to push operation
+ * environment data to device. In this operation, Event is always of
+ * SensorType::AdditionalInfo type. See operation evironment parameters
+ * section in AdditionalInfoType.
+ *
+ * When device is in DATA_INJECTION mode, this function is also used for
+ * injecting sensor events.
+ *
+ * Regardless of OperationMode, injected SensorType::ADDITIONAL_INFO
+ * type events should not be routed back to the sensor event queue.
+ *
+ * @see AdditionalInfoType
+ * @see OperationMode
+ * @param event sensor event to be injected
+ * @return result OK on success; PERMISSION_DENIED if operation is not
+ * allowed; INVALID_OPERATION, if this functionality is unsupported;
+ * BAD_VALUE if sensor event cannot be injected.
+ */
+ injectSensorData_2_1(Event event) generates (Result result);
+};
diff --git a/sensors/2.1/ISensorsCallback.hal b/sensors/2.1/ISensorsCallback.hal
new file mode 100644
index 0000000..de521d5
--- /dev/null
+++ b/sensors/2.1/ISensorsCallback.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.sensors@2.1;
+
+import @2.0::ISensorsCallback;
+import @2.1::SensorInfo;
+
+interface ISensorsCallback extends @2.0::ISensorsCallback {
+ /**
+ * Notify the framework that new dynamic sensors have been connected.
+ *
+ * If a dynamic sensor was previously connected and has not been
+ * disconnected, then that sensor must not be included in sensorInfos.
+ *
+ * @param sensorInfos vector of SensorInfo for each dynamic sensor that
+ * was connected.
+ */
+ oneway onDynamicSensorsConnected_2_1(vec<SensorInfo> sensorInfos);
+};
diff --git a/sensors/2.1/default/Android.bp b/sensors/2.1/default/Android.bp
new file mode 100644
index 0000000..27b439d
--- /dev/null
+++ b/sensors/2.1/default/Android.bp
@@ -0,0 +1,45 @@
+//
+// 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.
+
+cc_binary {
+ name: "android.hardware.sensors@2.1-service.mock",
+ defaults: ["hidl_defaults"],
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "SensorsV2_1.cpp",
+ "service.cpp",
+ ],
+ init_rc: ["android.hardware.sensors@2.1-service-mock.rc"],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
+ ],
+ shared_libs: [
+ "android.hardware.sensors@1.0",
+ "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "liblog",
+ "libpower",
+ "libutils",
+ ],
+ static_libs: [
+ "android.hardware.sensors@1.0-convert",
+ "android.hardware.sensors@2.X-shared-impl",
+ ],
+ vintf_fragments: ["android.hardware.sensors@2.1.xml"],
+}
diff --git a/sensors/2.1/default/OWNERS b/sensors/2.1/default/OWNERS
new file mode 100644
index 0000000..90c2330
--- /dev/null
+++ b/sensors/2.1/default/OWNERS
@@ -0,0 +1,3 @@
+arthuri@google.com
+bduddie@google.com
+stange@google.com
diff --git a/sensors/2.1/default/SensorsV2_1.cpp b/sensors/2.1/default/SensorsV2_1.cpp
new file mode 100644
index 0000000..2e3d315
--- /dev/null
+++ b/sensors/2.1/default/SensorsV2_1.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 "SensorsV2_1.h"
+
+#include "Sensor.h"
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_1 {
+namespace implementation {
+
+using V2_X::implementation::ISensorsEventCallback;
+using V2_X::implementation::OnChangeSensor;
+
+class HingeAngleSensor : public OnChangeSensor {
+ public:
+ HingeAngleSensor(int32_t sensorHandle, ISensorsEventCallback* callback)
+ : OnChangeSensor(callback) {
+ mSensorInfo.sensorHandle = sensorHandle;
+ mSensorInfo.name = "Hinge Angle Sensor";
+ mSensorInfo.vendor = "Vendor String";
+ mSensorInfo.version = 1;
+ mSensorInfo.type = SensorType::HINGE_ANGLE;
+ mSensorInfo.typeAsString = "";
+ mSensorInfo.maxRange = 360.0f;
+ mSensorInfo.resolution = 1.0f;
+ mSensorInfo.power = 0.001f;
+ mSensorInfo.minDelay = 40 * 1000; // microseconds
+ mSensorInfo.maxDelay = V2_X::implementation::kDefaultMaxDelayUs;
+ mSensorInfo.fifoReservedEventCount = 0;
+ mSensorInfo.fifoMaxEventCount = 0;
+ mSensorInfo.requiredPermission = "";
+ mSensorInfo.flags = static_cast<uint32_t>(V1_0::SensorFlagBits::ON_CHANGE_MODE);
+ }
+};
+
+SensorsV2_1::SensorsV2_1() {
+ AddSensor<HingeAngleSensor>();
+}
+
+// Methods from ::android::hardware::sensors::V2_1::ISensors follow.
+Return<void> SensorsV2_1::getSensorsList_2_1(ISensors::getSensorsList_2_1_cb _hidl_cb) {
+ std::vector<SensorInfo> sensors;
+ for (const auto& sensor : mSensors) {
+ sensors.push_back(sensor.second->getSensorInfo());
+ }
+
+ // Call the HIDL callback with the SensorInfo
+ _hidl_cb(sensors);
+
+ return Void();
+}
+
+Return<Result> SensorsV2_1::initialize_2_1(
+ const ::android::hardware::MQDescriptorSync<V2_1::Event>& eventQueueDescriptor,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<V2_1::ISensorsCallback>& sensorsCallback) {
+ auto eventQueue = std::make_unique<MessageQueue<V2_1::Event, kSynchronizedReadWrite>>(
+ eventQueueDescriptor, true /* resetPointers */);
+ std::unique_ptr<EventMessageQueueWrapperBase> wrapper =
+ std::make_unique<EventMessageQueueWrapperV2_1>(eventQueue);
+ mCallbackWrapper = new ISensorsCallbackWrapper(sensorsCallback);
+ return initializeBase(wrapper, wakeLockDescriptor, mCallbackWrapper);
+}
+
+Return<Result> SensorsV2_1::injectSensorData_2_1(const V2_1::Event& event) {
+ return injectSensorData(convertToOldEvent(event));
+}
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace sensors
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/sensors/2.1/default/SensorsV2_1.h b/sensors/2.1/default/SensorsV2_1.h
new file mode 100644
index 0000000..9f7fe04
--- /dev/null
+++ b/sensors/2.1/default/SensorsV2_1.h
@@ -0,0 +1,74 @@
+/*
+ * 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_SENSORS_V2_1_H
+#define ANDROID_HARDWARE_SENSORS_V2_1_H
+
+#include "Sensors.h"
+
+#include "EventMessageQueueWrapper.h"
+
+#include <android/hardware/sensors/2.1/ISensors.h>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_1 {
+namespace implementation {
+
+using Result = ::android::hardware::sensors::V1_0::Result;
+using Sensors = ::android::hardware::sensors::V2_X::implementation::Sensors<ISensors>;
+
+class ISensorsCallbackWrapper : public V2_0::ISensorsCallback {
+ public:
+ ISensorsCallbackWrapper(const sp<V2_1::ISensorsCallback>& callback) : mCallback(callback) {}
+
+ Return<void> onDynamicSensorsConnected(const hidl_vec<V1_0::SensorInfo>& sensorInfos) override {
+ return mCallback->onDynamicSensorsConnected_2_1(convertToNewSensorInfos(sensorInfos));
+ }
+
+ Return<void> onDynamicSensorsDisconnected(const hidl_vec<int32_t>& sensorHandles) override {
+ return mCallback->onDynamicSensorsDisconnected(sensorHandles);
+ }
+
+ private:
+ sp<V2_1::ISensorsCallback> mCallback;
+};
+
+struct SensorsV2_1 : public Sensors {
+ SensorsV2_1();
+
+ // Methods from ::android::hardware::sensors::V2_1::ISensors follow.
+ Return<void> getSensorsList_2_1(ISensors::getSensorsList_2_1_cb _hidl_cb) override;
+
+ Return<Result> initialize_2_1(
+ const ::android::hardware::MQDescriptorSync<V2_1::Event>& eventQueueDescriptor,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<V2_1::ISensorsCallback>& sensorsCallback) override;
+
+ Return<Result> injectSensorData_2_1(const V2_1::Event& event) override;
+
+ private:
+ sp<ISensorsCallbackWrapper> mCallbackWrapper;
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace sensors
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SENSORS_V2_1_H
\ No newline at end of file
diff --git a/sensors/2.1/default/android.hardware.sensors@2.1-service-mock.rc b/sensors/2.1/default/android.hardware.sensors@2.1-service-mock.rc
new file mode 100644
index 0000000..d4147e7
--- /dev/null
+++ b/sensors/2.1/default/android.hardware.sensors@2.1-service-mock.rc
@@ -0,0 +1,7 @@
+service vendor.sensors-hal-2-1-mock /vendor/bin/hw/android.hardware.sensors@2.1-service.mock
+ interface android.hardware.sensors@2.0::ISensors default
+ interface android.hardware.sensors@2.1::ISensors default
+ class hal
+ user system
+ group system
+ rlimit rtprio 10 10
diff --git a/sensors/2.1/default/android.hardware.sensors@2.1.xml b/sensors/2.1/default/android.hardware.sensors@2.1.xml
new file mode 100644
index 0000000..18bd3ae
--- /dev/null
+++ b/sensors/2.1/default/android.hardware.sensors@2.1.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.sensors</name>
+ <transport>hwbinder</transport>
+ <version>2.1</version>
+ <interface>
+ <name>ISensors</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/sensors/2.1/default/service.cpp b/sensors/2.1/default/service.cpp
new file mode 100644
index 0000000..1f3087c
--- /dev/null
+++ b/sensors/2.1/default/service.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.sensors@2.1-service"
+
+#include <android/hardware/sensors/2.1/ISensors.h>
+#include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+#include "SensorsV2_1.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::sensors::V2_1::ISensors;
+using android::hardware::sensors::V2_1::implementation::SensorsV2_1;
+
+int main(int /* argc */, char** /* argv */) {
+ configureRpcThreadpool(1, true);
+
+ android::sp<ISensors> sensors = new SensorsV2_1();
+ if (sensors->registerAsService() != ::android::OK) {
+ ALOGE("Failed to register Sensors HAL instance");
+ return -1;
+ }
+
+ joinRpcThreadpool();
+ return 1; // joinRpcThreadpool shouldn't exit
+}
diff --git a/sensors/2.1/types.hal b/sensors/2.1/types.hal
new file mode 100644
index 0000000..503bece
--- /dev/null
+++ b/sensors/2.1/types.hal
@@ -0,0 +1,160 @@
+/*
+ * 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.sensors@2.1;
+
+import @1.0::EventPayload;
+import @1.0::SensorType;
+import @1.0::SensorFlagBits;
+
+@export(name="", value_prefix="SENSOR_TYPE_")
+enum SensorType : @1.0::SensorType {
+ /**
+ * HINGE_ANGLE
+ * reporting-mode: on-change
+ * wake-up sensor: yes
+ *
+ * A sensor of this type measures the angle, in degrees, between two
+ * integral parts of the device. Movement of a hinge measured by this sensor
+ * type is expected to alter the ways in which the user may interact with
+ * the device, for example by unfolding or revealing a display.
+ *
+ * Sensor data is output using @1.0::EventPayload.scalar.
+ *
+ * Implement wake-up proximity sensor before implementing a non wake-up
+ * proximity sensor.
+ */
+ HINGE_ANGLE = 36,
+};
+
+struct Event {
+ /** Time measured in nanoseconds, in "elapsedRealtimeNano()'s" timebase. */
+ int64_t timestamp;
+
+ /** sensor identifier */
+ int32_t sensorHandle;
+
+ @2.1::SensorType sensorType;
+
+ /** Union discriminated on sensorType */
+ EventPayload u;
+};
+
+struct SensorInfo {
+ /**
+ * handle that identifies this sensors. This handle is used to reference
+ * this sensor throughout the HAL API.
+ */
+ int32_t sensorHandle;
+
+ /**
+ * Name of this sensor.
+ * All sensors of the same "type" must have a different "name".
+ */
+ string name;
+
+ /** vendor of the hardware part */
+ string vendor;
+
+ /**
+ * version of the hardware part + driver. The value of this field
+ * must increase when the driver is updated in a way that changes the
+ * output of this sensor. This is important for fused sensors when the
+ * fusion algorithm is updated.
+ */
+ int32_t version;
+
+ /** this sensor's type. */
+ @2.1::SensorType type;
+
+ /**
+ * type of this sensor as a string.
+ *
+ * When defining an OEM specific sensor or sensor manufacturer specific
+ * sensor, use your reserve domain name as a prefix.
+ * e.g. com.google.glass.onheaddetector
+ *
+ * For sensors of known type defined in SensorType (value <
+ * SensorType::DEVICE_PRIVATE_BASE), this can be an empty string.
+ */
+ string typeAsString;
+
+ /** maximum range of this sensor's value in SI units */
+ float maxRange;
+
+ /** smallest difference between two values reported by this sensor */
+ float resolution;
+
+ /** rough estimate of this sensor's power consumption in mA */
+ float power;
+
+ /**
+ * this value depends on the reporting mode:
+ *
+ * continuous: minimum sample period allowed in microseconds
+ * on-change : 0
+ * one-shot :-1
+ * special : 0, unless otherwise noted
+ */
+ int32_t minDelay;
+
+ /**
+ * number of events reserved for this sensor in the batch mode FIFO.
+ * If there is a dedicated FIFO for this sensor, then this is the
+ * size of this FIFO. If the FIFO is shared with other sensors,
+ * this is the size reserved for that sensor and it can be zero.
+ */
+ uint32_t fifoReservedEventCount;
+
+ /**
+ * maximum number of events of this sensor that could be batched.
+ * This is especially relevant when the FIFO is shared between
+ * several sensors; this value is then set to the size of that FIFO.
+ */
+ uint32_t fifoMaxEventCount;
+
+ /**
+ * permission required to see this sensor, register to it and receive data.
+ * Set to "" if no permission is required. Some sensor types like the
+ * heart rate monitor have a mandatory require_permission.
+ * For sensors that always require a specific permission, like the heart
+ * rate monitor, the android framework might overwrite this string
+ * automatically.
+ */
+ string requiredPermission;
+
+ /**
+ * This value is defined only for continuous mode and on-change sensors.
+ * It is the delay between two sensor events corresponding to the lowest
+ * frequency that this sensor supports. When lower frequencies are requested
+ * through batch()/setDelay() the events will be generated at this frequency
+ * instead.
+ * It can be used by the framework or applications to estimate when the
+ * batch FIFO may be full.
+ *
+ * NOTE: periodNs is in nanoseconds where as maxDelay/minDelay are in
+ * microseconds.
+ *
+ * continuous, on-change: maximum sampling period allowed in
+ * microseconds.
+ *
+ * one-shot, special : 0
+ */
+ int32_t maxDelay;
+
+ /** Bitmask of SensorFlagBits */
+ bitfield<SensorFlagBits> flags;
+};
\ No newline at end of file
diff --git a/sensors/2.1/vts/functional/Android.bp b/sensors/2.1/vts/functional/Android.bp
new file mode 100644
index 0000000..c4f5e9d
--- /dev/null
+++ b/sensors/2.1/vts/functional/Android.bp
@@ -0,0 +1,47 @@
+//
+// 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.
+//
+
+cc_test {
+ name: "VtsHalSensorsV2_1TargetTest",
+ cflags: [
+ "-DLOG_TAG=\"sensors_hidl_hal_test\"",
+ ],
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "VtsHalSensorsV2_1TargetTest.cpp",
+ ],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
+ ],
+ static_libs: [
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@2.1",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.sensors@1.0",
+ "android.hardware.sensors@1.0-convert",
+ "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
+ "libfmq",
+ "VtsHalSensorsTargetTestUtils",
+ "VtsHalSensorsV2_1TargetTest-lib",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
+}
diff --git a/sensors/2.1/vts/functional/AndroidTest.xml b/sensors/2.1/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..0d8593e
--- /dev/null
+++ b/sensors/2.1/vts/functional/AndroidTest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs VtsHalSensorsV2_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.RunCommandTargetPreparer">
+ <option name="run-command" value="stop"/>
+ <option name="teardown-command" value="start"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="VtsHalSensorsV2_1TargetTest->/data/local/tmp/VtsHalSensorsV2_1TargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-timeout" value="900000" />
+ <option name="runtime-hint" value="300000"/>
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalSensorsV2_1TargetTest" />
+ </test>
+</configuration>
diff --git a/sensors/2.1/vts/functional/OWNERS b/sensors/2.1/vts/functional/OWNERS
new file mode 100644
index 0000000..892da15
--- /dev/null
+++ b/sensors/2.1/vts/functional/OWNERS
@@ -0,0 +1,8 @@
+# Sensors team
+arthuri@google.com
+bduddie@google.com
+stange@google.com
+
+# VTS team
+trong@google.com
+yim@google.com
diff --git a/sensors/2.1/vts/functional/VtsHalSensorsV2_1TargetTest.cpp b/sensors/2.1/vts/functional/VtsHalSensorsV2_1TargetTest.cpp
new file mode 100644
index 0000000..230bb6c
--- /dev/null
+++ b/sensors/2.1/vts/functional/VtsHalSensorsV2_1TargetTest.cpp
@@ -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.
+ */
+
+#include "VtsHalSensorsV2_XTargetTest.h"
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, SensorsHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+ android::hardware::sensors::V2_1::ISensors::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/sensors/common/default/2.X/Android.bp b/sensors/common/default/2.X/Android.bp
new file mode 100644
index 0000000..8b0d52f
--- /dev/null
+++ b/sensors/common/default/2.X/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: "android.hardware.sensors@2.X-shared-impl",
+ vendor: true,
+ export_include_dirs: ["."],
+ srcs: [
+ "Sensor.cpp",
+ ],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
+ ],
+ shared_libs: [
+ "android.hardware.sensors@1.0",
+ "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "liblog",
+ "libpower",
+ "libutils",
+ ],
+}
diff --git a/sensors/common/default/2.X/OWNERS b/sensors/common/default/2.X/OWNERS
new file mode 100644
index 0000000..90c2330
--- /dev/null
+++ b/sensors/common/default/2.X/OWNERS
@@ -0,0 +1,3 @@
+arthuri@google.com
+bduddie@google.com
+stange@google.com
diff --git a/sensors/2.0/default/Sensor.cpp b/sensors/common/default/2.X/Sensor.cpp
similarity index 94%
rename from sensors/2.0/default/Sensor.cpp
rename to sensors/common/default/2.X/Sensor.cpp
index c09173f..1841dff 100644
--- a/sensors/2.0/default/Sensor.cpp
+++ b/sensors/common/default/2.X/Sensor.cpp
@@ -23,14 +23,17 @@
namespace android {
namespace hardware {
namespace sensors {
-namespace V2_0 {
+namespace V2_X {
namespace implementation {
using ::android::hardware::sensors::V1_0::MetaDataEventType;
+using ::android::hardware::sensors::V1_0::OperationMode;
+using ::android::hardware::sensors::V1_0::Result;
using ::android::hardware::sensors::V1_0::SensorFlagBits;
using ::android::hardware::sensors::V1_0::SensorStatus;
-
-static constexpr float kDefaultMaxDelayUs = 10 * 1000 * 1000;
+using ::android::hardware::sensors::V2_1::Event;
+using ::android::hardware::sensors::V2_1::SensorInfo;
+using ::android::hardware::sensors::V2_1::SensorType;
Sensor::Sensor(ISensorsEventCallback* callback)
: mIsEnabled(false),
@@ -204,8 +207,8 @@
mSensorInfo.typeAsString = "";
mSensorInfo.maxRange = 78.4f; // +/- 8g
mSensorInfo.resolution = 1.52e-5;
- mSensorInfo.power = 0.001f; // mA
- mSensorInfo.minDelay = 20 * 1000; // microseconds
+ mSensorInfo.power = 0.001f; // mA
+ mSensorInfo.minDelay = 20 * 1000; // microseconds
mSensorInfo.maxDelay = kDefaultMaxDelayUs;
mSensorInfo.fifoReservedEventCount = 0;
mSensorInfo.fifoMaxEventCount = 0;
@@ -221,9 +224,9 @@
mSensorInfo.version = 1;
mSensorInfo.type = SensorType::PRESSURE;
mSensorInfo.typeAsString = "";
- mSensorInfo.maxRange = 1100.0f; // hPa
- mSensorInfo.resolution = 0.005f; // hPa
- mSensorInfo.power = 0.001f; // mA
+ mSensorInfo.maxRange = 1100.0f; // hPa
+ mSensorInfo.resolution = 0.005f; // hPa
+ mSensorInfo.power = 0.001f; // mA
mSensorInfo.minDelay = 100 * 1000; // microseconds
mSensorInfo.maxDelay = kDefaultMaxDelayUs;
mSensorInfo.fifoReservedEventCount = 0;
@@ -242,7 +245,7 @@
mSensorInfo.typeAsString = "";
mSensorInfo.maxRange = 1300.0f;
mSensorInfo.resolution = 0.01f;
- mSensorInfo.power = 0.001f; // mA
+ mSensorInfo.power = 0.001f; // mA
mSensorInfo.minDelay = 20 * 1000; // microseconds
mSensorInfo.maxDelay = kDefaultMaxDelayUs;
mSensorInfo.fifoReservedEventCount = 0;
@@ -261,8 +264,8 @@
mSensorInfo.typeAsString = "";
mSensorInfo.maxRange = 43000.0f;
mSensorInfo.resolution = 10.0f;
- mSensorInfo.power = 0.001f; // mA
- mSensorInfo.minDelay = 200 * 1000; // microseconds
+ mSensorInfo.power = 0.001f; // mA
+ mSensorInfo.minDelay = 200 * 1000; // microseconds
mSensorInfo.maxDelay = kDefaultMaxDelayUs;
mSensorInfo.fifoReservedEventCount = 0;
mSensorInfo.fifoMaxEventCount = 0;
@@ -280,7 +283,7 @@
mSensorInfo.typeAsString = "";
mSensorInfo.maxRange = 5.0f;
mSensorInfo.resolution = 1.0f;
- mSensorInfo.power = 0.012f; // mA
+ mSensorInfo.power = 0.012f; // mA
mSensorInfo.minDelay = 200 * 1000; // microseconds
mSensorInfo.maxDelay = kDefaultMaxDelayUs;
mSensorInfo.fifoReservedEventCount = 0;
@@ -367,7 +370,7 @@
}
} // namespace implementation
-} // namespace V2_0
+} // namespace V2_X
} // namespace sensors
} // namespace hardware
} // namespace android
diff --git a/sensors/2.0/default/Sensor.h b/sensors/common/default/2.X/Sensor.h
similarity index 79%
rename from sensors/2.0/default/Sensor.h
rename to sensors/common/default/2.X/Sensor.h
index 61900fa..2f8a143 100644
--- a/sensors/2.0/default/Sensor.h
+++ b/sensors/common/default/2.X/Sensor.h
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_SENSORS_V2_0_SENSOR_H
-#define ANDROID_HARDWARE_SENSORS_V2_0_SENSOR_H
+#ifndef ANDROID_HARDWARE_SENSORS_V2_X_SENSOR_H
+#define ANDROID_HARDWARE_SENSORS_V2_X_SENSOR_H
#include <android/hardware/sensors/1.0/types.h>
+#include <android/hardware/sensors/2.1/types.h>
#include <condition_variable>
#include <memory>
@@ -25,26 +26,30 @@
#include <thread>
#include <vector>
-using ::android::hardware::sensors::V1_0::Event;
-using ::android::hardware::sensors::V1_0::OperationMode;
-using ::android::hardware::sensors::V1_0::Result;
-using ::android::hardware::sensors::V1_0::SensorInfo;
-using ::android::hardware::sensors::V1_0::SensorType;
-
namespace android {
namespace hardware {
namespace sensors {
-namespace V2_0 {
+namespace V2_X {
namespace implementation {
+static constexpr float kDefaultMaxDelayUs = 10 * 1000 * 1000;
+
class ISensorsEventCallback {
- public:
+ public:
+ using Event = ::android::hardware::sensors::V2_1::Event;
+
virtual ~ISensorsEventCallback(){};
virtual void postEvents(const std::vector<Event>& events, bool wakeup) = 0;
};
class Sensor {
- public:
+ public:
+ 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 SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
+ using SensorType = ::android::hardware::sensors::V2_1::SensorType;
+
Sensor(ISensorsEventCallback* callback);
virtual ~Sensor();
@@ -57,7 +62,7 @@
bool supportsDataInjection() const;
Result injectEvent(const Event& event);
- protected:
+ protected:
void run();
virtual std::vector<Event> readEvents();
static void startThread(Sensor* sensor);
@@ -80,68 +85,68 @@
};
class OnChangeSensor : public Sensor {
- public:
+ public:
OnChangeSensor(ISensorsEventCallback* callback);
virtual void activate(bool enable) override;
- protected:
+ protected:
virtual std::vector<Event> readEvents() override;
- protected:
+ protected:
Event mPreviousEvent;
bool mPreviousEventSet;
};
class AccelSensor : public Sensor {
- public:
+ public:
AccelSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class GyroSensor : public Sensor {
- public:
+ public:
GyroSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class AmbientTempSensor : public OnChangeSensor {
- public:
+ public:
AmbientTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class DeviceTempSensor : public OnChangeSensor {
- public:
+ public:
DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class PressureSensor : public Sensor {
- public:
+ public:
PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class MagnetometerSensor : public Sensor {
- public:
+ public:
MagnetometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class LightSensor : public OnChangeSensor {
- public:
+ public:
LightSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class ProximitySensor : public OnChangeSensor {
- public:
+ public:
ProximitySensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class RelativeHumiditySensor : public OnChangeSensor {
- public:
+ public:
RelativeHumiditySensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
} // namespace implementation
-} // namespace V2_0
+} // namespace V2_X
} // namespace sensors
} // namespace hardware
} // namespace android
-#endif // ANDROID_HARDWARE_SENSORS_V2_0_SENSOR_H
+#endif // ANDROID_HARDWARE_SENSORS_V2_X_SENSOR_H
diff --git a/sensors/common/default/2.X/Sensors.h b/sensors/common/default/2.X/Sensors.h
new file mode 100644
index 0000000..ee8240d
--- /dev/null
+++ b/sensors/common/default/2.X/Sensors.h
@@ -0,0 +1,377 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_SENSORS_V2_X_SENSORS_H
+#define ANDROID_HARDWARE_SENSORS_V2_X_SENSORS_H
+
+#include "EventMessageQueueWrapper.h"
+#include "Sensor.h"
+
+#include <android/hardware/sensors/2.0/ISensors.h>
+#include <android/hardware/sensors/2.0/types.h>
+#include <fmq/MessageQueue.h>
+#include <hardware_legacy/power.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <log/log.h>
+
+#include <atomic>
+#include <memory>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_X {
+namespace implementation {
+
+template <class ISensorsInterface>
+struct Sensors : public ISensorsInterface, public ISensorsEventCallback {
+ using Event = ::android::hardware::sensors::V1_0::Event;
+ using OperationMode = ::android::hardware::sensors::V1_0::OperationMode;
+ using RateLevel = ::android::hardware::sensors::V1_0::RateLevel;
+ using Result = ::android::hardware::sensors::V1_0::Result;
+ using SharedMemInfo = ::android::hardware::sensors::V1_0::SharedMemInfo;
+ using EventQueueFlagBits = ::android::hardware::sensors::V2_0::EventQueueFlagBits;
+ using SensorTimeout = ::android::hardware::sensors::V2_0::SensorTimeout;
+ using WakeLockQueueFlagBits = ::android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
+ using ISensorsCallback = ::android::hardware::sensors::V2_0::ISensorsCallback;
+ using EventMessageQueue = MessageQueue<Event, kSynchronizedReadWrite>;
+ using WakeLockMessageQueue = MessageQueue<uint32_t, kSynchronizedReadWrite>;
+
+ static constexpr const char* kWakeLockName = "SensorsHAL_WAKEUP";
+
+ Sensors()
+ : mEventQueueFlag(nullptr),
+ mNextHandle(1),
+ mOutstandingWakeUpEvents(0),
+ mReadWakeLockQueueRun(false),
+ mAutoReleaseWakeLockTime(0),
+ mHasWakeLock(false) {
+ AddSensor<AccelSensor>();
+ AddSensor<GyroSensor>();
+ AddSensor<AmbientTempSensor>();
+ AddSensor<DeviceTempSensor>();
+ AddSensor<PressureSensor>();
+ AddSensor<MagnetometerSensor>();
+ AddSensor<LightSensor>();
+ AddSensor<ProximitySensor>();
+ AddSensor<RelativeHumiditySensor>();
+ }
+
+ virtual ~Sensors() {
+ deleteEventFlag();
+ mReadWakeLockQueueRun = false;
+ mWakeLockThread.join();
+ }
+
+ // 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);
+ }
+ return Result::OK;
+ }
+
+ Return<Result> activate(int32_t sensorHandle, bool enabled) override {
+ auto sensor = mSensors.find(sensorHandle);
+ if (sensor != mSensors.end()) {
+ sensor->second->activate(enabled);
+ return Result::OK;
+ }
+ return Result::BAD_VALUE;
+ }
+
+ Return<Result> initialize(
+ const ::android::hardware::MQDescriptorSync<Event>& eventQueueDescriptor,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<ISensorsCallback>& sensorsCallback) override {
+ auto eventQueue =
+ std::make_unique<EventMessageQueue>(eventQueueDescriptor, true /* resetPointers */);
+ std::unique_ptr<V2_1::implementation::EventMessageQueueWrapperBase> wrapper =
+ std::make_unique<V2_1::implementation::EventMessageQueueWrapperV1_0>(eventQueue);
+ return initializeBase(wrapper, wakeLockDescriptor, sensorsCallback);
+ }
+
+ Return<Result> initializeBase(
+ std::unique_ptr<V2_1::implementation::EventMessageQueueWrapperBase>& eventQueue,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<ISensorsCallback>& sensorsCallback) {
+ Result result = Result::OK;
+
+ // Ensure that all sensors are disabled
+ for (auto sensor : mSensors) {
+ sensor.second->activate(false /* enable */);
+ }
+
+ // Stop the Wake Lock thread if it is currently running
+ if (mReadWakeLockQueueRun.load()) {
+ mReadWakeLockQueueRun = false;
+ mWakeLockThread.join();
+ }
+
+ // Save a reference to the callback
+ mCallback = sensorsCallback;
+
+ // Save the event queue.
+ mEventQueue = std::move(eventQueue);
+
+ // Ensure that any existing EventFlag is properly deleted
+ deleteEventFlag();
+
+ // Create the EventFlag that is used to signal to the framework that sensor events have been
+ // written to the Event FMQ
+ if (EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag) != OK) {
+ result = Result::BAD_VALUE;
+ }
+
+ // Create the Wake Lock FMQ that is used by the framework to communicate whenever WAKE_UP
+ // events have been successfully read and handled by the framework.
+ mWakeLockQueue = std::make_unique<WakeLockMessageQueue>(wakeLockDescriptor,
+ true /* resetPointers */);
+
+ if (!mCallback || !mEventQueue || !mWakeLockQueue || mEventQueueFlag == nullptr) {
+ result = Result::BAD_VALUE;
+ }
+
+ // Start the thread to read events from the Wake Lock FMQ
+ mReadWakeLockQueueRun = true;
+ mWakeLockThread = std::thread(startReadWakeLockThread, this);
+
+ return result;
+ }
+
+ Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t /* maxReportLatencyNs */) override {
+ auto sensor = mSensors.find(sensorHandle);
+ if (sensor != mSensors.end()) {
+ sensor->second->batch(samplingPeriodNs);
+ return Result::OK;
+ }
+ return Result::BAD_VALUE;
+ }
+
+ Return<Result> flush(int32_t sensorHandle) override {
+ auto sensor = mSensors.find(sensorHandle);
+ if (sensor != mSensors.end()) {
+ return sensor->second->flush();
+ }
+ return Result::BAD_VALUE;
+ }
+
+ Return<Result> injectSensorData(const Event& event) override {
+ auto sensor = mSensors.find(event.sensorHandle);
+ if (sensor != mSensors.end()) {
+ return sensor->second->injectEvent(V2_1::implementation::convertToNewEvent(event));
+ }
+
+ return Result::BAD_VALUE;
+ }
+
+ Return<void> registerDirectChannel(const SharedMemInfo& /* mem */,
+ V2_0::ISensors::registerDirectChannel_cb _hidl_cb) override {
+ _hidl_cb(Result::INVALID_OPERATION, -1 /* channelHandle */);
+ return Return<void>();
+ }
+
+ Return<Result> unregisterDirectChannel(int32_t /* channelHandle */) override {
+ return Result::INVALID_OPERATION;
+ }
+
+ Return<void> configDirectReport(int32_t /* sensorHandle */, int32_t /* channelHandle */,
+ RateLevel /* rate */,
+ V2_0::ISensors::configDirectReport_cb _hidl_cb) override {
+ _hidl_cb(Result::INVALID_OPERATION, 0 /* reportToken */);
+ return Return<void>();
+ }
+
+ void postEvents(const std::vector<V2_1::Event>& events, bool wakeup) override {
+ std::lock_guard<std::mutex> lock(mWriteLock);
+ if (mEventQueue->write(events)) {
+ mEventQueueFlag->wake(static_cast<uint32_t>(EventQueueFlagBits::READ_AND_PROCESS));
+
+ if (wakeup) {
+ // Keep track of the number of outstanding WAKE_UP events in order to properly hold
+ // a wake lock until the framework has secured a wake lock
+ updateWakeLock(events.size(), 0 /* eventsHandled */);
+ }
+ }
+ }
+
+ protected:
+ /**
+ * Add a new sensor
+ */
+ template <class SensorType>
+ void AddSensor() {
+ std::shared_ptr<SensorType> sensor =
+ std::make_shared<SensorType>(mNextHandle++ /* sensorHandle */, this /* callback */);
+ mSensors[sensor->getSensorInfo().sensorHandle] = sensor;
+ }
+
+ /**
+ * Utility function to delete the Event Flag
+ */
+ void deleteEventFlag() {
+ status_t status = EventFlag::deleteEventFlag(&mEventQueueFlag);
+ if (status != OK) {
+ ALOGI("Failed to delete event flag: %d", status);
+ }
+ }
+
+ static void startReadWakeLockThread(Sensors* sensors) { sensors->readWakeLockFMQ(); }
+
+ /**
+ * Function to read the Wake Lock FMQ and release the wake lock when appropriate
+ */
+ void readWakeLockFMQ() {
+ while (mReadWakeLockQueueRun.load()) {
+ constexpr int64_t kReadTimeoutNs = 500 * 1000 * 1000; // 500 ms
+ uint32_t eventsHandled = 0;
+
+ // Read events from the Wake Lock FMQ. Timeout after a reasonable amount of time to
+ // ensure that any held wake lock is able to be released if it is held for too long.
+ mWakeLockQueue->readBlocking(&eventsHandled, 1 /* count */, 0 /* readNotification */,
+ static_cast<uint32_t>(WakeLockQueueFlagBits::DATA_WRITTEN),
+ kReadTimeoutNs);
+ updateWakeLock(0 /* eventsWritten */, eventsHandled);
+ }
+ }
+
+ /**
+ * Responsible for acquiring and releasing a wake lock when there are unhandled WAKE_UP events
+ */
+ void updateWakeLock(int32_t eventsWritten, int32_t eventsHandled) {
+ std::lock_guard<std::mutex> lock(mWakeLockLock);
+ int32_t newVal = mOutstandingWakeUpEvents + eventsWritten - eventsHandled;
+ if (newVal < 0) {
+ mOutstandingWakeUpEvents = 0;
+ } else {
+ mOutstandingWakeUpEvents = newVal;
+ }
+
+ if (eventsWritten > 0) {
+ // Update the time at which the last WAKE_UP event was sent
+ mAutoReleaseWakeLockTime =
+ ::android::uptimeMillis() +
+ static_cast<uint32_t>(SensorTimeout::WAKE_LOCK_SECONDS) * 1000;
+ }
+
+ if (!mHasWakeLock && mOutstandingWakeUpEvents > 0 &&
+ acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLockName) == 0) {
+ mHasWakeLock = true;
+ } else if (mHasWakeLock) {
+ // Check if the wake lock should be released automatically if
+ // SensorTimeout::WAKE_LOCK_SECONDS has elapsed since the last WAKE_UP event was written
+ // to the Wake Lock FMQ.
+ if (::android::uptimeMillis() > mAutoReleaseWakeLockTime) {
+ ALOGD("No events read from wake lock FMQ for %d seconds, auto releasing wake lock",
+ SensorTimeout::WAKE_LOCK_SECONDS);
+ mOutstandingWakeUpEvents = 0;
+ }
+
+ if (mOutstandingWakeUpEvents == 0 && release_wake_lock(kWakeLockName) == 0) {
+ mHasWakeLock = false;
+ }
+ }
+ }
+
+ /**
+ * The Event FMQ where sensor events are written
+ */
+ std::unique_ptr<V2_1::implementation::EventMessageQueueWrapperBase> mEventQueue;
+
+ /**
+ * The Wake Lock FMQ that is read to determine when the framework has handled WAKE_UP events
+ */
+ std::unique_ptr<WakeLockMessageQueue> mWakeLockQueue;
+
+ /**
+ * Event Flag to signal to the framework when sensor events are available to be read
+ */
+ EventFlag* mEventQueueFlag;
+
+ /**
+ * Callback for asynchronous events, such as dynamic sensor connections.
+ */
+ sp<ISensorsCallback> mCallback;
+
+ /**
+ * A map of the available sensors
+ */
+ std::map<int32_t, std::shared_ptr<Sensor>> mSensors;
+
+ /**
+ * The next available sensor handle
+ */
+ int32_t mNextHandle;
+
+ /**
+ * Lock to protect writes to the FMQs
+ */
+ std::mutex mWriteLock;
+
+ /**
+ * Lock to protect acquiring and releasing the wake lock
+ */
+ std::mutex mWakeLockLock;
+
+ /**
+ * Track the number of WAKE_UP events that have not been handled by the framework
+ */
+ uint32_t mOutstandingWakeUpEvents;
+
+ /**
+ * A thread to read the Wake Lock FMQ
+ */
+ std::thread mWakeLockThread;
+
+ /**
+ * Flag to indicate that the Wake Lock Thread should continue to run
+ */
+ std::atomic_bool mReadWakeLockQueueRun;
+
+ /**
+ * Track the time when the wake lock should automatically be released
+ */
+ int64_t mAutoReleaseWakeLockTime;
+
+ /**
+ * Flag to indicate if a wake lock has been acquired
+ */
+ bool mHasWakeLock;
+};
+
+} // namespace implementation
+} // namespace V2_X
+} // namespace sensors
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SENSORS_V2_X_SENSORS_H
diff --git a/sensors/common/utils/Android.bp b/sensors/common/utils/Android.bp
new file mode 100644
index 0000000..aec6c4b
--- /dev/null
+++ b/sensors/common/utils/Android.bp
@@ -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.
+
+cc_library_headers {
+ name: "android.hardware.sensors@2.X-shared-utils",
+ vendor_available: true,
+ defaults: ["hidl_defaults"],
+ export_include_dirs: ["."],
+ shared_libs: [
+ "android.hardware.sensors@1.0",
+ "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
+ "libbinder",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "liblog",
+ "libpower",
+ "libutils",
+ ],
+ static_libs: [
+ "android.hardware.sensors@1.0-convert",
+ ],
+}
diff --git a/sensors/common/utils/EventMessageQueueWrapper.h b/sensors/common/utils/EventMessageQueueWrapper.h
new file mode 100644
index 0000000..bf3261f
--- /dev/null
+++ b/sensors/common/utils/EventMessageQueueWrapper.h
@@ -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.
+ */
+
+#ifndef ANDROID_HARDWARE_SENSORS_V2_1_EVENTMESSAGEQUEUEWRAPPER_H
+#define ANDROID_HARDWARE_SENSORS_V2_1_EVENTMESSAGEQUEUEWRAPPER_H
+
+#include "convertV2_1.h"
+
+#include <android/hardware/sensors/2.1/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <log/log.h>
+
+#include <atomic>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_1 {
+namespace implementation {
+
+class EventMessageQueueWrapperBase : public RefBase {
+ public:
+ virtual ~EventMessageQueueWrapperBase() {}
+
+ virtual std::atomic<uint32_t>* getEventFlagWord() = 0;
+ virtual size_t availableToRead() = 0;
+ virtual bool read(V2_1::Event* events, size_t numToRead) = 0;
+ virtual bool write(const std::vector<V2_1::Event>& events) = 0;
+};
+
+class EventMessageQueueWrapperV1_0 : public EventMessageQueueWrapperBase {
+ public:
+ using EventMessageQueue = MessageQueue<V1_0::Event, kSynchronizedReadWrite>;
+
+ EventMessageQueueWrapperV1_0(std::unique_ptr<EventMessageQueue>& queue)
+ : mQueue(std::move(queue)) {}
+
+ const ::android::hardware::MQDescriptorSync<V1_0::Event>* getDesc() {
+ return mQueue->getDesc();
+ }
+
+ virtual std::atomic<uint32_t>* getEventFlagWord() override {
+ return mQueue->getEventFlagWord();
+ }
+
+ virtual size_t availableToRead() override { return mQueue->availableToRead(); }
+
+ virtual bool read(V2_1::Event* events, size_t numToRead) override {
+ return mQueue->read(reinterpret_cast<V1_0::Event*>(events), numToRead);
+ }
+
+ virtual bool write(const std::vector<V2_1::Event>& events) override {
+ const std::vector<V1_0::Event>& oldEvents = convertToOldEvents(events);
+ return mQueue->write(oldEvents.data(), oldEvents.size());
+ }
+
+ private:
+ std::unique_ptr<EventMessageQueue> mQueue;
+};
+
+class EventMessageQueueWrapperV2_1 : public EventMessageQueueWrapperBase {
+ public:
+ using EventMessageQueue = MessageQueue<V2_1::Event, kSynchronizedReadWrite>;
+
+ EventMessageQueueWrapperV2_1(std::unique_ptr<EventMessageQueue>& queue)
+ : mQueue(std::move(queue)) {}
+
+ const ::android::hardware::MQDescriptorSync<V2_1::Event>* getDesc() {
+ return mQueue->getDesc();
+ }
+
+ std::atomic<uint32_t>* getEventFlagWord() override { return mQueue->getEventFlagWord(); }
+
+ virtual size_t availableToRead() override { return mQueue->availableToRead(); }
+
+ virtual bool read(V2_1::Event* events, size_t numToRead) override {
+ return mQueue->read(events, numToRead);
+ }
+
+ bool write(const std::vector<V2_1::Event>& events) override {
+ return mQueue->write(events.data(), events.size());
+ }
+
+ private:
+ std::unique_ptr<EventMessageQueue> mQueue;
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace sensors
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SENSORS_V2_1_EVENTMESSAGEQUEUEWRAPPER_H
\ No newline at end of file
diff --git a/sensors/common/utils/ISensorsWrapper.h b/sensors/common/utils/ISensorsWrapper.h
new file mode 100644
index 0000000..e9c22b1
--- /dev/null
+++ b/sensors/common/utils/ISensorsWrapper.h
@@ -0,0 +1,282 @@
+/*
+ * 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_SENSORS_V2_1_ISENSORSWRAPPER_H
+#define ANDROID_HARDWARE_SENSORS_V2_1_ISENSORSWRAPPER_H
+
+#include "EventMessageQueueWrapper.h"
+#include "ISensorsWrapper.h"
+
+#include "android/hardware/sensors/1.0/ISensors.h"
+#include "android/hardware/sensors/1.0/types.h"
+#include "android/hardware/sensors/2.0/ISensors.h"
+#include "android/hardware/sensors/2.0/ISensorsCallback.h"
+#include "android/hardware/sensors/2.1/ISensors.h"
+#include "android/hardware/sensors/2.1/ISensorsCallback.h"
+#include "android/hardware/sensors/2.1/types.h"
+
+#include <utils/LightRefBase.h>
+
+#include <cassert>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::hardware::MessageQueue;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::Return;
+using ::android::hardware::sensors::V1_0::ISensors;
+using ::android::hardware::sensors::V1_0::OperationMode;
+using ::android::hardware::sensors::V1_0::RateLevel;
+using ::android::hardware::sensors::V1_0::Result;
+using ::android::hardware::sensors::V1_0::SharedMemInfo;
+using ::android::hardware::sensors::V2_1::Event;
+using ::android::hardware::sensors::V2_1::ISensorsCallback;
+
+// TODO: Look into providing this as a param if it needs to be a different value
+// than the framework.
+static constexpr size_t MAX_RECEIVE_BUFFER_EVENT_COUNT = 256;
+
+/*
+ * The ISensorsWrapper interface includes all function from supported Sensors HAL versions. This
+ * allows for the SensorDevice to use the ISensorsWrapper interface to interact with the Sensors
+ * HAL regardless of the current version of the Sensors HAL that is loaded. Each concrete
+ * instantiation of ISensorsWrapper must correspond to a specific Sensors HAL version. This design
+ * is beneficial because only the functions that change between Sensors HAL versions must be newly
+ * implemented, any previously implemented function that does not change may remain the same.
+ *
+ * Functions that exist across all versions of the Sensors HAL should be implemented as pure
+ * virtual functions which forces the concrete instantiations to implement the functions.
+ *
+ * Functions that do not exist across all versions of the Sensors HAL should include a default
+ * implementation that generates an error if called. The default implementation should never
+ * be called and must be overridden by Sensors HAL versions that support the function.
+ */
+class ISensorsWrapperBase : public VirtualLightRefBase {
+ public:
+ virtual bool supportsPolling() const = 0;
+
+ virtual bool supportsMessageQueues() const = 0;
+
+ virtual void linkToDeath(android::sp<android::hardware::hidl_death_recipient> deathRecipient,
+ uint64_t cookie) = 0;
+
+ virtual Return<void> getSensorsList(
+ ::android::hardware::sensors::V2_1::ISensors::getSensorsList_2_1_cb _hidl_cb) = 0;
+
+ virtual Return<Result> setOperationMode(OperationMode mode) = 0;
+
+ virtual Return<Result> activate(int32_t sensorHandle, bool enabled) = 0;
+
+ virtual Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) = 0;
+
+ virtual Return<Result> flush(int32_t sensorHandle) = 0;
+
+ virtual Return<Result> injectSensorData(const Event& event) = 0;
+
+ virtual Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ ISensors::registerDirectChannel_cb _hidl_cb) = 0;
+
+ virtual Return<Result> unregisterDirectChannel(int32_t channelHandle) = 0;
+
+ virtual Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle,
+ RateLevel rate,
+ ISensors::configDirectReport_cb _hidl_cb) = 0;
+
+ virtual Return<void> poll(int32_t /* maxCount */, ISensors::poll_cb /* _hidl_cb */) {
+ // Enforce this method is never invoked as it should be overridden if it's meant to be used.
+ assert(false);
+ return Return<void>();
+ }
+
+ virtual EventMessageQueueWrapperBase* getEventQueue() { return nullptr; }
+
+ virtual Return<Result> initialize(const MQDescriptorSync<uint32_t>& /* wakeLockDesc */,
+ const ::android::sp<ISensorsCallback>& /* callback */) {
+ // Enforce this method is never invoked as it should be overridden if it's meant to be used.
+ assert(false);
+ return Result::INVALID_OPERATION;
+ }
+};
+
+template <typename T>
+class SensorsWrapperBase : public ISensorsWrapperBase {
+ public:
+ SensorsWrapperBase(sp<T> sensors) : mSensors(sensors){};
+
+ void linkToDeath(android::sp<android::hardware::hidl_death_recipient> deathRecipient,
+ uint64_t cookie) override {
+ mSensors->linkToDeath(deathRecipient, cookie);
+ }
+
+ virtual Return<void> getSensorsList(
+ ::android::hardware::sensors::V2_1::ISensors::getSensorsList_2_1_cb _hidl_cb) override {
+ return mSensors->getSensorsList(
+ [&](const auto& list) { _hidl_cb(convertToNewSensorInfos(list)); });
+ }
+
+ Return<Result> setOperationMode(OperationMode mode) override {
+ return mSensors->setOperationMode(mode);
+ }
+
+ Return<Result> activate(int32_t sensorHandle, bool enabled) override {
+ return mSensors->activate(sensorHandle, enabled);
+ }
+
+ Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) override {
+ return mSensors->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs);
+ }
+
+ Return<Result> flush(int32_t sensorHandle) override { return mSensors->flush(sensorHandle); }
+
+ virtual Return<Result> injectSensorData(const Event& event) override {
+ return mSensors->injectSensorData(convertToOldEvent(event));
+ }
+
+ Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ ISensors::registerDirectChannel_cb _hidl_cb) override {
+ return mSensors->registerDirectChannel(mem, _hidl_cb);
+ }
+
+ Return<Result> unregisterDirectChannel(int32_t channelHandle) override {
+ return mSensors->unregisterDirectChannel(channelHandle);
+ }
+
+ Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
+ ISensors::configDirectReport_cb _hidl_cb) override {
+ return mSensors->configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb);
+ }
+
+ protected:
+ sp<T> mSensors;
+};
+
+class ISensorsWrapperV1_0 : public SensorsWrapperBase<hardware::sensors::V1_0::ISensors> {
+ public:
+ ISensorsWrapperV1_0(sp<hardware::sensors::V1_0::ISensors> sensors)
+ : SensorsWrapperBase(sensors){};
+
+ bool supportsPolling() const override { return true; }
+
+ bool supportsMessageQueues() const override { return false; }
+
+ Return<void> poll(int32_t maxCount,
+ hardware::sensors::V1_0::ISensors::poll_cb _hidl_cb) override {
+ return mSensors->poll(maxCount, _hidl_cb);
+ }
+};
+
+class ISensorsWrapperV2_0 : public SensorsWrapperBase<hardware::sensors::V2_0::ISensors> {
+ public:
+ typedef MessageQueue<::android::hardware::sensors::V1_0::Event,
+ ::android::hardware::kSynchronizedReadWrite>
+ EventMessageQueue;
+
+ ISensorsWrapperV2_0(sp<hardware::sensors::V2_0::ISensors> sensors)
+ : SensorsWrapperBase(sensors) {
+ auto eventQueue = std::make_unique<EventMessageQueue>(MAX_RECEIVE_BUFFER_EVENT_COUNT,
+ true /* configureEventFlagWord */);
+ mEventQueue = std::make_unique<EventMessageQueueWrapperV1_0>(eventQueue);
+ };
+
+ bool supportsPolling() const override { return false; }
+
+ bool supportsMessageQueues() const override { return true; }
+
+ EventMessageQueueWrapperBase* getEventQueue() override { return mEventQueue.get(); }
+
+ Return<Result> initialize(const MQDescriptorSync<uint32_t>& wakeLockDesc,
+ const ::android::sp<ISensorsCallback>& callback) override {
+ return mSensors->initialize(*mEventQueue->getDesc(), wakeLockDesc, callback);
+ }
+
+ private:
+ std::unique_ptr<EventMessageQueueWrapperV1_0> mEventQueue;
+};
+
+class ISensorsWrapperV2_1 : public SensorsWrapperBase<hardware::sensors::V2_1::ISensors> {
+ public:
+ typedef MessageQueue<Event, ::android::hardware::kSynchronizedReadWrite> EventMessageQueueV2_1;
+
+ ISensorsWrapperV2_1(sp<hardware::sensors::V2_1::ISensors> sensors)
+ : SensorsWrapperBase(sensors) {
+ auto eventQueue = std::make_unique<EventMessageQueueV2_1>(
+ MAX_RECEIVE_BUFFER_EVENT_COUNT, true /* configureEventFlagWord */);
+ mEventQueue = std::make_unique<EventMessageQueueWrapperV2_1>(eventQueue);
+ };
+
+ bool supportsPolling() const override { return false; }
+
+ bool supportsMessageQueues() const override { return true; }
+
+ EventMessageQueueWrapperBase* getEventQueue() override { return mEventQueue.get(); }
+
+ Return<void> getSensorsList(
+ ::android::hardware::sensors::V2_1::ISensors::getSensorsList_2_1_cb _hidl_cb) override {
+ return mSensors->getSensorsList_2_1(_hidl_cb);
+ }
+
+ Return<Result> injectSensorData(const Event& event) override {
+ return mSensors->injectSensorData_2_1(event);
+ }
+
+ Return<Result> initialize(const MQDescriptorSync<uint32_t>& wakeLockDesc,
+ const ::android::sp<ISensorsCallback>& callback) override {
+ return mSensors->initialize_2_1(*mEventQueue->getDesc(), wakeLockDesc, callback);
+ }
+
+ private:
+ std::unique_ptr<EventMessageQueueWrapperV2_1> mEventQueue;
+};
+
+inline sp<ISensorsWrapperV2_0> wrapISensors(sp<V2_0::ISensors> sensors) {
+ return new ISensorsWrapperV2_0(sensors);
+}
+
+inline sp<ISensorsWrapperV2_1> wrapISensors(sp<V2_1::ISensors> sensors) {
+ return new ISensorsWrapperV2_1(sensors);
+}
+
+class NoOpSensorsCallback : public ISensorsCallback {
+ public:
+ Return<void> onDynamicSensorsConnected(
+ const hidl_vec<V1_0::SensorInfo>& /* sensorInfos */) override {
+ return Return<void>();
+ }
+
+ Return<void> onDynamicSensorsDisconnected(
+ const hidl_vec<int32_t>& /* sensorHandles */) override {
+ return Return<void>();
+ }
+
+ Return<void> onDynamicSensorsConnected_2_1(
+ const hidl_vec<SensorInfo>& /* sensorInfos */) override {
+ return Return<void>();
+ }
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace sensors
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SENSORS_V2_1_ISENSORSWRAPPER_H
\ No newline at end of file
diff --git a/sensors/common/utils/OWNERS b/sensors/common/utils/OWNERS
new file mode 100644
index 0000000..90c2330
--- /dev/null
+++ b/sensors/common/utils/OWNERS
@@ -0,0 +1,3 @@
+arthuri@google.com
+bduddie@google.com
+stange@google.com
diff --git a/sensors/common/utils/convertV2_1.h b/sensors/common/utils/convertV2_1.h
new file mode 100644
index 0000000..9231011
--- /dev/null
+++ b/sensors/common/utils/convertV2_1.h
@@ -0,0 +1,122 @@
+/*
+ * 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_SENSORS_V2_1_CONVERT_H
+#define ANDROID_HARDWARE_SENSORS_V2_1_CONVERT_H
+
+#include <android/hardware/sensors/2.1/types.h>
+#include <hardware/sensors.h>
+#include <sensors/convert.h>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_1 {
+namespace implementation {
+
+static_assert(sizeof(V1_0::Event) == sizeof(V2_1::Event),
+ "New and old Event types must have the same size");
+static_assert(sizeof(V1_0::SensorInfo) == sizeof(V2_1::SensorInfo),
+ "New and old SensorInfo types must have the same size");
+
+// The following conversion methods are safe as the only difference between
+// V1_0 and V2_1 for these types is an added enum value to SensorType which doesn't
+// change the memory layout of the types.
+inline const V1_0::Event& convertToOldEvent(const V2_1::Event& event) {
+ return reinterpret_cast<const V1_0::Event&>(event);
+}
+
+inline const std::vector<V1_0::Event>& convertToOldEvents(const std::vector<V2_1::Event>& events) {
+ return reinterpret_cast<const std::vector<V1_0::Event>&>(events);
+}
+
+inline V1_0::Event* convertToOldEvent(V2_1::Event* event) {
+ return reinterpret_cast<V1_0::Event*>(event);
+}
+
+inline const V2_1::SensorInfo& convertToNewSensorInfo(const V1_0::SensorInfo& info) {
+ return reinterpret_cast<const V2_1::SensorInfo&>(info);
+}
+
+inline const V1_0::SensorInfo& convertToOldSensorInfo(const V2_1::SensorInfo& info) {
+ return reinterpret_cast<const V1_0::SensorInfo&>(info);
+}
+
+inline const V2_1::Event& convertToNewEvent(const V1_0::Event& event) {
+ return reinterpret_cast<const V2_1::Event&>(event);
+}
+
+inline const std::vector<V2_1::Event>& convertToNewEvents(const std::vector<V1_0::Event>& events) {
+ return reinterpret_cast<const std::vector<V2_1::Event>&>(events);
+}
+
+inline const hidl_vec<V2_1::Event>& convertToNewEvents(const hidl_vec<V1_0::Event>& events) {
+ return reinterpret_cast<const hidl_vec<V2_1::Event>&>(events);
+}
+
+inline const hidl_vec<V2_1::SensorInfo>& convertToNewSensorInfos(
+ const hidl_vec<V1_0::SensorInfo>& infos) {
+ return reinterpret_cast<const hidl_vec<V2_1::SensorInfo>&>(infos);
+}
+
+inline const hidl_vec<V1_0::SensorInfo>& convertToOldSensorInfos(
+ const hidl_vec<V2_1::SensorInfo>& infos) {
+ return reinterpret_cast<const hidl_vec<V1_0::SensorInfo>&>(infos);
+}
+
+inline void convertFromSensorEvent(const sensors_event_t& src, V2_1::Event* dst) {
+ switch ((SensorType)src.type) {
+ case SensorType::HINGE_ANGLE:
+ // Only fill in values for hinge angle as other sensors
+ // will have it filled in by legacy code.
+ *dst = {
+ .timestamp = src.timestamp,
+ .sensorHandle = src.sensor,
+ .sensorType = (SensorType)src.type,
+ };
+ dst->u.scalar = src.data[0];
+ break;
+ default:
+ V1_0::implementation::convertFromSensorEvent(src, convertToOldEvent(dst));
+ break;
+ }
+}
+
+inline void convertToSensorEvent(const V2_1::Event& src, sensors_event_t* dst) {
+ switch (src.sensorType) {
+ case SensorType::HINGE_ANGLE:
+ // Only fill in values for hinge angle as other sensors
+ // will have it filled in by legacy code.
+ *dst = {.version = sizeof(sensors_event_t),
+ .sensor = src.sensorHandle,
+ .type = (int32_t)src.sensorType,
+ .reserved0 = 0,
+ .timestamp = src.timestamp};
+ dst->data[0] = src.u.scalar;
+ break;
+ default:
+ V1_0::implementation::convertToSensorEvent(convertToOldEvent(src), dst);
+ break;
+ }
+}
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace sensors
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SENSORS_V2_1_CONVERT_H
diff --git a/sensors/common/vts/2_X/Android.bp b/sensors/common/vts/2_X/Android.bp
new file mode 100644
index 0000000..8cdb5d1
--- /dev/null
+++ b/sensors/common/vts/2_X/Android.bp
@@ -0,0 +1,55 @@
+//
+// 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.
+//
+
+cc_defaults {
+ name: "VtsHalSensorsV2_XTargetTest-defaults",
+ cflags: ["-DLOG_TAG=\"sensors_hidl_hal_test\""],
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "SensorsHidlEnvironmentV2_X.cpp",
+ ],
+ export_include_dirs: ["."],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
+ ],
+ shared_libs: [
+ "libbinder",
+ ],
+ static_libs: [
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@2.1",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.sensors@1.0",
+ "android.hardware.sensors@1.0-convert",
+ "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
+ "libfmq",
+ "VtsHalSensorsTargetTestUtils",
+ ],
+}
+
+cc_test_library {
+ name: "VtsHalSensorsV2_0TargetTest-lib",
+ defaults: ["VtsHalSensorsV2_XTargetTest-defaults"],
+}
+
+cc_test_library {
+ name: "VtsHalSensorsV2_1TargetTest-lib",
+ cflags: ["-DSENSORS_HAL_2_1"],
+ defaults: ["VtsHalSensorsV2_XTargetTest-defaults"],
+}
diff --git a/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp b/sensors/common/vts/2_X/SensorsHidlEnvironmentV2_X.cpp
similarity index 69%
rename from sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp
rename to sensors/common/vts/2_X/SensorsHidlEnvironmentV2_X.cpp
index 81db5a0..a8c2513 100644
--- a/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp
+++ b/sensors/common/vts/2_X/SensorsHidlEnvironmentV2_X.cpp
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-#include "SensorsHidlEnvironmentV2_0.h"
+#include "SensorsHidlEnvironmentV2_X.h"
#include <android/hardware/sensors/2.0/types.h>
+#include <android/hardware/sensors/2.1/types.h>
+
#include <log/log.h>
#include <algorithm>
@@ -26,18 +28,20 @@
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::sensors::V1_0::Result;
-using ::android::hardware::sensors::V1_0::SensorInfo;
using ::android::hardware::sensors::V2_0::EventQueueFlagBits;
+using ::android::hardware::sensors::V2_1::SensorInfo;
+#ifdef SENSORS_HAL_2_1
+using ::android::hardware::sensors::V2_1::ISensors;
+#else
using ::android::hardware::sensors::V2_0::ISensors;
-using ::android::hardware::sensors::V2_0::ISensorsCallback;
+#endif
+using ::android::hardware::sensors::V2_1::ISensorsCallback;
template <typename EnumType>
constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) {
return static_cast<typename std::underlying_type<EnumType>::type>(value);
}
-constexpr size_t SensorsHidlEnvironmentV2_0::MAX_RECEIVE_BUFFER_EVENT_COUNT;
-
void SensorsHalDeathRecipient::serviceDied(
uint64_t /* cookie */,
const ::android::wp<::android::hidl::base::V1_0::IBase>& /* service */) {
@@ -45,48 +49,34 @@
FAIL() << "Sensors HAL died during test";
}
-struct SensorsCallback : ISensorsCallback {
- Return<void> onDynamicSensorsConnected(const hidl_vec<SensorInfo>& /* sensorInfos */) {
- return Return<void>();
- }
-
- Return<void> onDynamicSensorsDisconnected(const hidl_vec<int32_t>& /* sensorHandles */) {
- return Return<void>();
- }
-};
-
-bool SensorsHidlEnvironmentV2_0::resetHal() {
+bool SensorsHidlEnvironmentV2_X::resetHal() {
bool succeed = false;
do {
- mSensors = ISensors::getService(mServiceName);
+ mSensors = wrapISensors(ISensors::getService(mServiceName));
if (mSensors == nullptr) {
break;
}
mSensors->linkToDeath(mDeathRecipient, 0 /* cookie */);
// Initialize FMQs
- mEventQueue = std::make_unique<EventMessageQueue>(MAX_RECEIVE_BUFFER_EVENT_COUNT,
- true /* configureEventFlagWord */);
-
mWakeLockQueue = std::make_unique<WakeLockQueue>(MAX_RECEIVE_BUFFER_EVENT_COUNT,
true /* configureEventFlagWord */);
- if (mEventQueue == nullptr || mWakeLockQueue == nullptr) {
+ if (mWakeLockQueue == nullptr) {
break;
}
EventFlag::deleteEventFlag(&mEventQueueFlag);
- EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag);
+ EventFlag::createEventFlag(mSensors->getEventQueue()->getEventFlagWord(), &mEventQueueFlag);
if (mEventQueueFlag == nullptr) {
break;
}
- mSensors->initialize(*mEventQueue->getDesc(), *mWakeLockQueue->getDesc(),
- new SensorsCallback());
+ mSensors->initialize(*mWakeLockQueue->getDesc(), new NoOpSensorsCallback());
std::vector<SensorInfo> sensorList;
if (!mSensors->getSensorsList([&](const hidl_vec<SensorInfo>& list) { sensorList = list; })
- .isOk()) {
+ .isOk()) {
break;
}
@@ -113,7 +103,7 @@
return succeed;
}
-void SensorsHidlEnvironmentV2_0::HidlTearDown() {
+void SensorsHidlEnvironmentV2_X::HidlTearDown() {
mStopThread = true;
if (mEventQueueFlag != nullptr) {
@@ -127,25 +117,25 @@
}
}
-void SensorsHidlEnvironmentV2_0::startPollingThread() {
+void SensorsHidlEnvironmentV2_X::startPollingThread() {
mStopThread = false;
mEvents.reserve(MAX_RECEIVE_BUFFER_EVENT_COUNT);
mPollThread = std::thread(pollingThread, this);
}
-void SensorsHidlEnvironmentV2_0::readEvents() {
- size_t availableEvents = mEventQueue->availableToRead();
+void SensorsHidlEnvironmentV2_X::readEvents() {
+ size_t availableEvents = mSensors->getEventQueue()->availableToRead();
if (availableEvents == 0) {
uint32_t eventFlagState = 0;
mEventQueueFlag->wait(asBaseType(EventQueueFlagBits::READ_AND_PROCESS), &eventFlagState);
- availableEvents = mEventQueue->availableToRead();
+ availableEvents = mSensors->getEventQueue()->availableToRead();
}
size_t eventsToRead = std::min(availableEvents, mEventBuffer.size());
if (eventsToRead > 0) {
- if (mEventQueue->read(mEventBuffer.data(), eventsToRead)) {
+ if (mSensors->getEventQueue()->read(mEventBuffer.data(), eventsToRead)) {
mEventQueueFlag->wake(asBaseType(EventQueueFlagBits::EVENTS_READ));
for (size_t i = 0; i < eventsToRead; i++) {
addEvent(mEventBuffer[i]);
@@ -154,7 +144,7 @@
}
}
-void SensorsHidlEnvironmentV2_0::pollingThread(SensorsHidlEnvironmentV2_0* env) {
+void SensorsHidlEnvironmentV2_X::pollingThread(SensorsHidlEnvironmentV2_X* env) {
ALOGD("polling thread start");
while (!env->mStopThread.load()) {
diff --git a/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.h b/sensors/common/vts/2_X/SensorsHidlEnvironmentV2_X.h
similarity index 68%
rename from sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.h
rename to sensors/common/vts/2_X/SensorsHidlEnvironmentV2_X.h
index 819cdd4..01f451f 100644
--- a/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.h
+++ b/sensors/common/vts/2_X/SensorsHidlEnvironmentV2_X.h
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-#ifndef ANDROID_SENSORS_HIDL_ENVIRONMENT_V2_0_H
-#define ANDROID_SENSORS_HIDL_ENVIRONMENT_V2_0_H
+#ifndef ANDROID_SENSORS_HIDL_ENVIRONMENT_V2_X_H
+#define ANDROID_SENSORS_HIDL_ENVIRONMENT_V2_X_H
+#include "ISensorsWrapper.h"
#include "sensors-vts-utils/SensorsHidlEnvironmentBase.h"
-#include <android/hardware/sensors/1.0/types.h>
-#include <android/hardware/sensors/2.0/ISensors.h>
+#include <android/hardware/sensors/2.1/ISensors.h>
+#include <android/hardware/sensors/2.1/types.h>
+
#include <fmq/MessageQueue.h>
#include <utils/StrongPointer.h>
@@ -30,6 +32,10 @@
using ::android::sp;
using ::android::hardware::MessageQueue;
+using ::android::hardware::sensors::V2_1::implementation::ISensorsWrapperBase;
+using ::android::hardware::sensors::V2_1::implementation::MAX_RECEIVE_BUFFER_EVENT_COUNT;
+using ::android::hardware::sensors::V2_1::implementation::NoOpSensorsCallback;
+using ::android::hardware::sensors::V2_1::implementation::wrapISensors;
class SensorsHidlTest;
@@ -39,14 +45,14 @@
const ::android::wp<::android::hidl::base::V1_0::IBase>& service) override;
};
-class SensorsHidlEnvironmentV2_0 : public SensorsHidlEnvironmentBase {
- public:
- using Event = ::android::hardware::sensors::V1_0::Event;
+class SensorsHidlEnvironmentV2_X
+ : public SensorsHidlEnvironmentBase<::android::hardware::sensors::V2_1::Event> {
+ public:
virtual void HidlTearDown() override;
- protected:
+ protected:
friend SensorsHidlTest;
- SensorsHidlEnvironmentV2_0(const std::string& service_name)
+ SensorsHidlEnvironmentV2_X(const std::string& service_name)
: SensorsHidlEnvironmentBase(service_name), mEventQueueFlag(nullptr) {}
/**
@@ -66,19 +72,19 @@
*
* @param env SensorEnvironment to being polling for events on
*/
- static void pollingThread(SensorsHidlEnvironmentV2_0* env);
+ static void pollingThread(SensorsHidlEnvironmentV2_X* env);
/**
* Reads and saves sensor events from the Event FMQ
*/
void readEvents();
- GTEST_DISALLOW_COPY_AND_ASSIGN_(SensorsHidlEnvironmentV2_0);
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(SensorsHidlEnvironmentV2_X);
/**
* Pointer to the Sensors HAL Interface that allows the test to call HAL functions.
*/
- sp<android::hardware::sensors::V2_0::ISensors> mSensors;
+ sp<ISensorsWrapperBase> mSensors;
/**
* Monitors the HAL for crashes, triggering test failure if seen
@@ -86,22 +92,11 @@
sp<SensorsHalDeathRecipient> mDeathRecipient = new SensorsHalDeathRecipient();
/**
- * Type used to simplify the creation of the Event FMQ
- */
- typedef MessageQueue<Event, ::android::hardware::kSynchronizedReadWrite> EventMessageQueue;
-
- /**
* Type used to simplify the creation of the Wake Lock FMQ
*/
typedef MessageQueue<uint32_t, ::android::hardware::kSynchronizedReadWrite> WakeLockQueue;
/**
- * The Event FMQ where the test framework is able to read sensor events that the Sensors HAL
- * has written.
- */
- std::unique_ptr<EventMessageQueue> mEventQueue;
-
- /**
* The Wake Lock FMQ is used by the test to notify the Sensors HAL whenever it has processed
* WAKE_UP sensor events.
*/
@@ -114,14 +109,10 @@
::android::hardware::EventFlag* mEventQueueFlag;
/**
- * The maximum number of sensor events that can be read from the Event FMQ at one time.
- */
- static constexpr size_t MAX_RECEIVE_BUFFER_EVENT_COUNT = 128;
-
- /**
* An array that is used to store sensor events read from the Event FMQ
*/
- std::array<Event, MAX_RECEIVE_BUFFER_EVENT_COUNT> mEventBuffer;
+ std::array<::android::hardware::sensors::V2_1::Event, MAX_RECEIVE_BUFFER_EVENT_COUNT>
+ mEventBuffer;
};
-#endif // ANDROID_SENSORS_HIDL_ENVIRONMENT_V2_0_H
+#endif // ANDROID_SENSORS_HIDL_ENVIRONMENT_V2_X_H
diff --git a/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
new file mode 100644
index 0000000..53ed259
--- /dev/null
+++ b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
@@ -0,0 +1,1186 @@
+/*
+ * 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 "SensorsHidlEnvironmentV2_X.h"
+#include "convertV2_1.h"
+#include "sensors-vts-utils/SensorsHidlTestBase.h"
+#include "sensors-vts-utils/SensorsTestSharedMemory.h"
+
+#include <android/hardware/sensors/2.1/ISensors.h>
+#include <android/hardware/sensors/2.1/types.h>
+
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+#include <utils/SystemClock.h>
+
+#include <cinttypes>
+#include <condition_variable>
+#include <cstring>
+#include <map>
+#include <vector>
+
+/**
+ * This file contains the core tests and test logic for both sensors HAL 2.0
+ * and 2.1. To make it easier to share the code between both VTS test suites,
+ * this is defined as a header so they can both include and use all pieces of
+ * code.
+ */
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::sensors::V1_0::MetaDataEventType;
+using ::android::hardware::sensors::V1_0::OperationMode;
+using ::android::hardware::sensors::V1_0::SensorsEventFormatOffset;
+using ::android::hardware::sensors::V1_0::SensorStatus;
+using ::android::hardware::sensors::V1_0::SharedMemType;
+using ::android::hardware::sensors::V1_0::Vec3;
+using ::android::hardware::sensors::V2_1::implementation::convertToOldSensorInfos;
+using std::chrono::duration_cast;
+using std::chrono::microseconds;
+using std::chrono::milliseconds;
+using std::chrono::nanoseconds;
+
+using EventV1_0 = ::android::hardware::sensors::V1_0::Event;
+using ISensorsType = ::android::hardware::sensors::V2_1::ISensors;
+using SensorTypeVersion = ::android::hardware::sensors::V2_1::SensorType;
+using EventType = ::android::hardware::sensors::V2_1::Event;
+using SensorInfoType = ::android::hardware::sensors::V2_1::SensorInfo;
+using SensorsHidlTestBaseV2_X = SensorsHidlTestBase<SensorTypeVersion, EventType, SensorInfoType>;
+
+constexpr size_t kEventSize = static_cast<size_t>(SensorsEventFormatOffset::TOTAL_LENGTH);
+
+class EventCallback : public IEventCallback<EventType> {
+ public:
+ void reset() {
+ mFlushMap.clear();
+ mEventMap.clear();
+ }
+
+ void onEvent(const EventType& event) override {
+ if (event.sensorType == SensorTypeVersion::META_DATA &&
+ event.u.meta.what == MetaDataEventType::META_DATA_FLUSH_COMPLETE) {
+ std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
+ mFlushMap[event.sensorHandle]++;
+ mFlushCV.notify_all();
+ } else if (event.sensorType != SensorTypeVersion::ADDITIONAL_INFO) {
+ std::unique_lock<std::recursive_mutex> lock(mEventMutex);
+ mEventMap[event.sensorHandle].push_back(event);
+ mEventCV.notify_all();
+ }
+ }
+
+ int32_t getFlushCount(int32_t sensorHandle) {
+ std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
+ return mFlushMap[sensorHandle];
+ }
+
+ void waitForFlushEvents(const std::vector<SensorInfoType>& sensorsToWaitFor,
+ int32_t numCallsToFlush, milliseconds timeout) {
+ std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
+ mFlushCV.wait_for(lock, timeout,
+ [&] { return flushesReceived(sensorsToWaitFor, numCallsToFlush); });
+ }
+
+ const std::vector<EventType> getEvents(int32_t sensorHandle) {
+ std::unique_lock<std::recursive_mutex> lock(mEventMutex);
+ return mEventMap[sensorHandle];
+ }
+
+ void waitForEvents(const std::vector<SensorInfoType>& sensorsToWaitFor, milliseconds timeout) {
+ std::unique_lock<std::recursive_mutex> lock(mEventMutex);
+ mEventCV.wait_for(lock, timeout, [&] { return eventsReceived(sensorsToWaitFor); });
+ }
+
+ protected:
+ bool flushesReceived(const std::vector<SensorInfoType>& sensorsToWaitFor,
+ int32_t numCallsToFlush) {
+ for (const SensorInfoType& sensor : sensorsToWaitFor) {
+ if (getFlushCount(sensor.sensorHandle) < numCallsToFlush) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool eventsReceived(const std::vector<SensorInfoType>& sensorsToWaitFor) {
+ for (const SensorInfoType& sensor : sensorsToWaitFor) {
+ if (getEvents(sensor.sensorHandle).size() == 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ std::map<int32_t, int32_t> mFlushMap;
+ std::recursive_mutex mFlushMutex;
+ std::condition_variable_any mFlushCV;
+
+ std::map<int32_t, std::vector<EventType>> mEventMap;
+ std::recursive_mutex mEventMutex;
+ std::condition_variable_any mEventCV;
+};
+
+/**
+ * Define the template specific versions of the static helper methods in
+ * SensorsHidlTestBase used to test that hinge angle is exposed properly.
+ */
+template <>
+SensorFlagBits expectedReportModeForType(::android::hardware::sensors::V2_1::SensorType type) {
+ switch (type) {
+ case ::android::hardware::sensors::V2_1::SensorType::HINGE_ANGLE:
+ return SensorFlagBits::ON_CHANGE_MODE;
+ default:
+ return expectedReportModeForType(
+ static_cast<::android::hardware::sensors::V1_0::SensorType>(type));
+ }
+}
+
+template <>
+void assertTypeMatchStringType(::android::hardware::sensors::V2_1::SensorType type,
+ const hidl_string& stringType) {
+ switch (type) {
+ case (::android::hardware::sensors::V2_1::SensorType::HINGE_ANGLE):
+ ASSERT_STREQ(SENSOR_STRING_TYPE_HINGE_ANGLE, stringType.c_str());
+ break;
+ default:
+ assertTypeMatchStringType(
+ static_cast<::android::hardware::sensors::V1_0::SensorType>(type), stringType);
+ break;
+ }
+}
+
+// The main test class for SENSORS HIDL HAL.
+class SensorsHidlTest : public SensorsHidlTestBaseV2_X {
+ public:
+ virtual void SetUp() override {
+ mEnvironment = new SensorsHidlEnvironmentV2_X(GetParam());
+ mEnvironment->HidlSetUp();
+ // Ensure that we have a valid environment before performing tests
+ ASSERT_NE(getSensors(), nullptr);
+ }
+
+ virtual void TearDown() override { mEnvironment->HidlTearDown(); }
+
+ protected:
+ SensorInfoType defaultSensorByType(SensorTypeVersion type) override;
+ std::vector<SensorInfoType> getSensorsList();
+ // implementation wrapper
+
+ Return<void> getSensorsList(ISensorsType::getSensorsList_cb _hidl_cb) override {
+ return getSensors()->getSensorsList(
+ [&](const auto& list) { _hidl_cb(convertToOldSensorInfos(list)); });
+ }
+
+ Return<Result> activate(int32_t sensorHandle, bool enabled) override;
+
+ Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) override {
+ return getSensors()->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs);
+ }
+
+ Return<Result> flush(int32_t sensorHandle) override {
+ return getSensors()->flush(sensorHandle);
+ }
+
+ Return<Result> injectSensorData(const EventType& event) override {
+ return getSensors()->injectSensorData(event);
+ }
+
+ Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ ISensorsType::registerDirectChannel_cb _hidl_cb) override;
+
+ Return<Result> unregisterDirectChannel(int32_t channelHandle) override {
+ return getSensors()->unregisterDirectChannel(channelHandle);
+ }
+
+ Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
+ ISensorsType::configDirectReport_cb _hidl_cb) override {
+ return getSensors()->configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb);
+ }
+
+ inline sp<ISensorsWrapperBase>& getSensors() { return mEnvironment->mSensors; }
+
+ SensorsHidlEnvironmentBase<EventType>* getEnvironment() override { return mEnvironment; }
+
+ // Test helpers
+ void runSingleFlushTest(const std::vector<SensorInfoType>& sensors, bool activateSensor,
+ int32_t expectedFlushCount, Result expectedResponse);
+ void runFlushTest(const std::vector<SensorInfoType>& sensors, bool activateSensor,
+ int32_t flushCalls, int32_t expectedFlushCount, Result expectedResponse);
+
+ // Helper functions
+ void activateAllSensors(bool enable);
+ std::vector<SensorInfoType> getNonOneShotSensors();
+ std::vector<SensorInfoType> getNonOneShotAndNonSpecialSensors();
+ std::vector<SensorInfoType> getOneShotSensors();
+ std::vector<SensorInfoType> getInjectEventSensors();
+ int32_t getInvalidSensorHandle();
+ bool getDirectChannelSensor(SensorInfoType* sensor, SharedMemType* memType, RateLevel* rate);
+ void verifyDirectChannel(SharedMemType memType);
+ void verifyRegisterDirectChannel(
+ std::shared_ptr<SensorsTestSharedMemory<SensorTypeVersion, EventType>> mem,
+ int32_t* directChannelHandle, bool supportsSharedMemType,
+ bool supportsAnyDirectChannel);
+ void verifyConfigure(const SensorInfoType& sensor, SharedMemType memType,
+ int32_t directChannelHandle, bool directChannelSupported);
+ void verifyUnregisterDirectChannel(int32_t directChannelHandle, bool directChannelSupported);
+ void checkRateLevel(const SensorInfoType& sensor, int32_t directChannelHandle,
+ RateLevel rateLevel);
+ void queryDirectChannelSupport(SharedMemType memType, bool* supportsSharedMemType,
+ bool* supportsAnyDirectChannel);
+
+ private:
+ // Test environment for sensors HAL.
+ SensorsHidlEnvironmentV2_X* mEnvironment;
+};
+
+Return<Result> SensorsHidlTest::activate(int32_t sensorHandle, bool enabled) {
+ // If activating a sensor, add the handle in a set so that when test fails it can be turned off.
+ // The handle is not removed when it is deactivating on purpose so that it is not necessary to
+ // check the return value of deactivation. Deactivating a sensor more than once does not have
+ // negative effect.
+ if (enabled) {
+ mSensorHandles.insert(sensorHandle);
+ }
+ return getSensors()->activate(sensorHandle, enabled);
+}
+
+Return<void> SensorsHidlTest::registerDirectChannel(const SharedMemInfo& mem,
+ ISensors::registerDirectChannel_cb cb) {
+ // If registeration of a channel succeeds, add the handle of channel to a set so that it can be
+ // unregistered when test fails. Unregister a channel does not remove the handle on purpose.
+ // Unregistering a channel more than once should not have negative effect.
+ getSensors()->registerDirectChannel(mem, [&](auto result, auto channelHandle) {
+ if (result == Result::OK) {
+ mDirectChannelHandles.insert(channelHandle);
+ }
+ cb(result, channelHandle);
+ });
+ return Void();
+}
+
+SensorInfoType SensorsHidlTest::defaultSensorByType(SensorTypeVersion type) {
+ SensorInfoType ret;
+
+ ret.type = (SensorTypeVersion)-1;
+ getSensors()->getSensorsList([&](const auto& list) {
+ const size_t count = list.size();
+ for (size_t i = 0; i < count; ++i) {
+ if (list[i].type == type) {
+ ret = list[i];
+ return;
+ }
+ }
+ });
+
+ return ret;
+}
+
+std::vector<SensorInfoType> SensorsHidlTest::getSensorsList() {
+ std::vector<SensorInfoType> ret;
+
+ getSensors()->getSensorsList([&](const auto& list) {
+ const size_t count = list.size();
+ ret.reserve(list.size());
+ for (size_t i = 0; i < count; ++i) {
+ ret.push_back(list[i]);
+ }
+ });
+
+ return ret;
+}
+
+std::vector<SensorInfoType> SensorsHidlTest::getNonOneShotSensors() {
+ std::vector<SensorInfoType> sensors;
+ for (const SensorInfoType& info : getSensorsList()) {
+ if (extractReportMode(info.flags) != SensorFlagBits::ONE_SHOT_MODE) {
+ sensors.push_back(info);
+ }
+ }
+ return sensors;
+}
+
+std::vector<SensorInfoType> SensorsHidlTest::getNonOneShotAndNonSpecialSensors() {
+ std::vector<SensorInfoType> sensors;
+ for (const SensorInfoType& info : getSensorsList()) {
+ SensorFlagBits reportMode = extractReportMode(info.flags);
+ if (reportMode != SensorFlagBits::ONE_SHOT_MODE &&
+ reportMode != SensorFlagBits::SPECIAL_REPORTING_MODE) {
+ sensors.push_back(info);
+ }
+ }
+ return sensors;
+}
+
+std::vector<SensorInfoType> SensorsHidlTest::getOneShotSensors() {
+ std::vector<SensorInfoType> sensors;
+ for (const SensorInfoType& info : getSensorsList()) {
+ if (extractReportMode(info.flags) == SensorFlagBits::ONE_SHOT_MODE) {
+ sensors.push_back(info);
+ }
+ }
+ return sensors;
+}
+
+std::vector<SensorInfoType> SensorsHidlTest::getInjectEventSensors() {
+ std::vector<SensorInfoType> sensors;
+ for (const SensorInfoType& info : getSensorsList()) {
+ if (info.flags & static_cast<uint32_t>(SensorFlagBits::DATA_INJECTION)) {
+ sensors.push_back(info);
+ }
+ }
+ return sensors;
+}
+
+int32_t SensorsHidlTest::getInvalidSensorHandle() {
+ // Find a sensor handle that does not exist in the sensor list
+ int32_t maxHandle = 0;
+ for (const SensorInfoType& sensor : getSensorsList()) {
+ maxHandle = std::max(maxHandle, sensor.sensorHandle);
+ }
+ return maxHandle + 1;
+}
+
+// Test if sensor list returned is valid
+TEST_P(SensorsHidlTest, SensorListValid) {
+ getSensors()->getSensorsList([&](const auto& list) {
+ const size_t count = list.size();
+ for (size_t i = 0; i < count; ++i) {
+ const auto& s = list[i];
+ SCOPED_TRACE(::testing::Message()
+ << i << "/" << count << ": "
+ << " handle=0x" << std::hex << std::setw(8) << std::setfill('0')
+ << s.sensorHandle << std::dec << " type=" << static_cast<int>(s.type)
+ << " name=" << s.name);
+
+ // Test non-empty type string
+ EXPECT_FALSE(s.typeAsString.empty());
+
+ // Test defined type matches defined string type
+ EXPECT_NO_FATAL_FAILURE(assertTypeMatchStringType(s.type, s.typeAsString));
+
+ // Test if all sensor has name and vendor
+ EXPECT_FALSE(s.name.empty());
+ EXPECT_FALSE(s.vendor.empty());
+
+ // Test power > 0, maxRange > 0
+ EXPECT_LE(0, s.power);
+ EXPECT_LT(0, s.maxRange);
+
+ // Info type, should have no sensor
+ EXPECT_FALSE(s.type == SensorTypeVersion::ADDITIONAL_INFO ||
+ s.type == SensorTypeVersion::META_DATA);
+
+ // Test fifoMax >= fifoReserved
+ EXPECT_GE(s.fifoMaxEventCount, s.fifoReservedEventCount)
+ << "max=" << s.fifoMaxEventCount << " reserved=" << s.fifoReservedEventCount;
+
+ // Test Reporting mode valid
+ EXPECT_NO_FATAL_FAILURE(assertTypeMatchReportMode(s.type, extractReportMode(s.flags)));
+
+ // Test min max are in the right order
+ EXPECT_LE(s.minDelay, s.maxDelay);
+ // Test min/max delay matches reporting mode
+ EXPECT_NO_FATAL_FAILURE(
+ assertDelayMatchReportMode(s.minDelay, s.maxDelay, extractReportMode(s.flags)));
+ }
+ });
+}
+
+// Test that SetOperationMode returns the expected value
+TEST_P(SensorsHidlTest, SetOperationMode) {
+ std::vector<SensorInfoType> sensors = getInjectEventSensors();
+ if (getInjectEventSensors().size() > 0) {
+ ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
+ ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::DATA_INJECTION));
+ ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
+ } else {
+ ASSERT_EQ(Result::BAD_VALUE, getSensors()->setOperationMode(OperationMode::DATA_INJECTION));
+ }
+}
+
+// Test that an injected event is written back to the Event FMQ
+TEST_P(SensorsHidlTest, InjectSensorEventData) {
+ std::vector<SensorInfoType> sensors = getInjectEventSensors();
+ if (sensors.size() == 0) {
+ return;
+ }
+
+ ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::DATA_INJECTION));
+
+ EventCallback callback;
+ getEnvironment()->registerCallback(&callback);
+
+ // AdditionalInfo event should not be sent to Event FMQ
+ EventType additionalInfoEvent;
+ additionalInfoEvent.sensorType = SensorTypeVersion::ADDITIONAL_INFO;
+ additionalInfoEvent.timestamp = android::elapsedRealtimeNano();
+
+ EventType injectedEvent;
+ injectedEvent.timestamp = android::elapsedRealtimeNano();
+ Vec3 data = {1, 2, 3, SensorStatus::ACCURACY_HIGH};
+ injectedEvent.u.vec3 = data;
+
+ for (const auto& s : sensors) {
+ additionalInfoEvent.sensorHandle = s.sensorHandle;
+ EXPECT_EQ(Result::OK, getSensors()->injectSensorData(additionalInfoEvent));
+
+ injectedEvent.sensorType = s.type;
+ injectedEvent.sensorHandle = s.sensorHandle;
+ EXPECT_EQ(Result::OK, getSensors()->injectSensorData(injectedEvent));
+ }
+
+ // Wait for events to be written back to the Event FMQ
+ callback.waitForEvents(sensors, milliseconds(1000) /* timeout */);
+
+ for (const auto& s : sensors) {
+ auto events = callback.getEvents(s.sensorHandle);
+ auto lastEvent = events.back();
+
+ // Verify that only a single event has been received
+ ASSERT_EQ(events.size(), 1);
+
+ // Verify that the event received matches the event injected and is not the additional
+ // info event
+ ASSERT_EQ(lastEvent.sensorType, s.type);
+ ASSERT_EQ(lastEvent.sensorType, s.type);
+ ASSERT_EQ(lastEvent.timestamp, injectedEvent.timestamp);
+ ASSERT_EQ(lastEvent.u.vec3.x, injectedEvent.u.vec3.x);
+ ASSERT_EQ(lastEvent.u.vec3.y, injectedEvent.u.vec3.y);
+ ASSERT_EQ(lastEvent.u.vec3.z, injectedEvent.u.vec3.z);
+ ASSERT_EQ(lastEvent.u.vec3.status, injectedEvent.u.vec3.status);
+ }
+
+ getEnvironment()->unregisterCallback();
+ ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
+}
+
+// Test if sensor hal can do UI speed accelerometer streaming properly
+TEST_P(SensorsHidlTest, AccelerometerStreamingOperationSlow) {
+ testStreamingOperation(SensorTypeVersion::ACCELEROMETER, std::chrono::milliseconds(200),
+ std::chrono::seconds(5), mAccelNormChecker);
+}
+
+// Test if sensor hal can do normal speed accelerometer streaming properly
+TEST_P(SensorsHidlTest, AccelerometerStreamingOperationNormal) {
+ testStreamingOperation(SensorTypeVersion::ACCELEROMETER, std::chrono::milliseconds(20),
+ std::chrono::seconds(5), mAccelNormChecker);
+}
+
+// Test if sensor hal can do game speed accelerometer streaming properly
+TEST_P(SensorsHidlTest, AccelerometerStreamingOperationFast) {
+ testStreamingOperation(SensorTypeVersion::ACCELEROMETER, std::chrono::milliseconds(5),
+ std::chrono::seconds(5), mAccelNormChecker);
+}
+
+// Test if sensor hal can do UI speed gyroscope streaming properly
+TEST_P(SensorsHidlTest, GyroscopeStreamingOperationSlow) {
+ testStreamingOperation(SensorTypeVersion::GYROSCOPE, std::chrono::milliseconds(200),
+ std::chrono::seconds(5), mGyroNormChecker);
+}
+
+// Test if sensor hal can do normal speed gyroscope streaming properly
+TEST_P(SensorsHidlTest, GyroscopeStreamingOperationNormal) {
+ testStreamingOperation(SensorTypeVersion::GYROSCOPE, std::chrono::milliseconds(20),
+ std::chrono::seconds(5), mGyroNormChecker);
+}
+
+// Test if sensor hal can do game speed gyroscope streaming properly
+TEST_P(SensorsHidlTest, GyroscopeStreamingOperationFast) {
+ testStreamingOperation(SensorTypeVersion::GYROSCOPE, std::chrono::milliseconds(5),
+ std::chrono::seconds(5), mGyroNormChecker);
+}
+
+// Test if sensor hal can do UI speed magnetometer streaming properly
+TEST_P(SensorsHidlTest, MagnetometerStreamingOperationSlow) {
+ testStreamingOperation(SensorTypeVersion::MAGNETIC_FIELD, std::chrono::milliseconds(200),
+ std::chrono::seconds(5), NullChecker<EventType>());
+}
+
+// Test if sensor hal can do normal speed magnetometer streaming properly
+TEST_P(SensorsHidlTest, MagnetometerStreamingOperationNormal) {
+ testStreamingOperation(SensorTypeVersion::MAGNETIC_FIELD, std::chrono::milliseconds(20),
+ std::chrono::seconds(5), NullChecker<EventType>());
+}
+
+// Test if sensor hal can do game speed magnetometer streaming properly
+TEST_P(SensorsHidlTest, MagnetometerStreamingOperationFast) {
+ testStreamingOperation(SensorTypeVersion::MAGNETIC_FIELD, std::chrono::milliseconds(5),
+ std::chrono::seconds(5), NullChecker<EventType>());
+}
+
+// Test if sensor hal can do accelerometer sampling rate switch properly when sensor is active
+TEST_P(SensorsHidlTest, AccelerometerSamplingPeriodHotSwitchOperation) {
+ testSamplingRateHotSwitchOperation(SensorTypeVersion::ACCELEROMETER);
+ testSamplingRateHotSwitchOperation(SensorTypeVersion::ACCELEROMETER, false /*fastToSlow*/);
+}
+
+// Test if sensor hal can do gyroscope sampling rate switch properly when sensor is active
+TEST_P(SensorsHidlTest, GyroscopeSamplingPeriodHotSwitchOperation) {
+ testSamplingRateHotSwitchOperation(SensorTypeVersion::GYROSCOPE);
+ testSamplingRateHotSwitchOperation(SensorTypeVersion::GYROSCOPE, false /*fastToSlow*/);
+}
+
+// Test if sensor hal can do magnetometer sampling rate switch properly when sensor is active
+TEST_P(SensorsHidlTest, MagnetometerSamplingPeriodHotSwitchOperation) {
+ testSamplingRateHotSwitchOperation(SensorTypeVersion::MAGNETIC_FIELD);
+ testSamplingRateHotSwitchOperation(SensorTypeVersion::MAGNETIC_FIELD, false /*fastToSlow*/);
+}
+
+// Test if sensor hal can do accelerometer batching properly
+TEST_P(SensorsHidlTest, AccelerometerBatchingOperation) {
+ testBatchingOperation(SensorTypeVersion::ACCELEROMETER);
+}
+
+// Test if sensor hal can do gyroscope batching properly
+TEST_P(SensorsHidlTest, GyroscopeBatchingOperation) {
+ testBatchingOperation(SensorTypeVersion::GYROSCOPE);
+}
+
+// Test if sensor hal can do magnetometer batching properly
+TEST_P(SensorsHidlTest, MagnetometerBatchingOperation) {
+ testBatchingOperation(SensorTypeVersion::MAGNETIC_FIELD);
+}
+
+// Test sensor event direct report with ashmem for accel sensor at normal rate
+TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationNormal) {
+ testDirectReportOperation(SensorTypeVersion::ACCELEROMETER, SharedMemType::ASHMEM,
+ RateLevel::NORMAL, mAccelNormChecker);
+}
+
+// Test sensor event direct report with ashmem for accel sensor at fast rate
+TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationFast) {
+ testDirectReportOperation(SensorTypeVersion::ACCELEROMETER, SharedMemType::ASHMEM,
+ RateLevel::FAST, mAccelNormChecker);
+}
+
+// Test sensor event direct report with ashmem for accel sensor at very fast rate
+TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationVeryFast) {
+ testDirectReportOperation(SensorTypeVersion::ACCELEROMETER, SharedMemType::ASHMEM,
+ RateLevel::VERY_FAST, mAccelNormChecker);
+}
+
+// Test sensor event direct report with ashmem for gyro sensor at normal rate
+TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationNormal) {
+ testDirectReportOperation(SensorTypeVersion::GYROSCOPE, SharedMemType::ASHMEM,
+ RateLevel::NORMAL, mGyroNormChecker);
+}
+
+// Test sensor event direct report with ashmem for gyro sensor at fast rate
+TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationFast) {
+ testDirectReportOperation(SensorTypeVersion::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::FAST,
+ mGyroNormChecker);
+}
+
+// Test sensor event direct report with ashmem for gyro sensor at very fast rate
+TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationVeryFast) {
+ testDirectReportOperation(SensorTypeVersion::GYROSCOPE, SharedMemType::ASHMEM,
+ RateLevel::VERY_FAST, mGyroNormChecker);
+}
+
+// Test sensor event direct report with ashmem for mag sensor at normal rate
+TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationNormal) {
+ testDirectReportOperation(SensorTypeVersion::MAGNETIC_FIELD, SharedMemType::ASHMEM,
+ RateLevel::NORMAL, NullChecker<EventType>());
+}
+
+// Test sensor event direct report with ashmem for mag sensor at fast rate
+TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationFast) {
+ testDirectReportOperation(SensorTypeVersion::MAGNETIC_FIELD, SharedMemType::ASHMEM,
+ RateLevel::FAST, NullChecker<EventType>());
+}
+
+// Test sensor event direct report with ashmem for mag sensor at very fast rate
+TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationVeryFast) {
+ testDirectReportOperation(SensorTypeVersion::MAGNETIC_FIELD, SharedMemType::ASHMEM,
+ RateLevel::VERY_FAST, NullChecker<EventType>());
+}
+
+// Test sensor event direct report with gralloc for accel sensor at normal rate
+TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationNormal) {
+ testDirectReportOperation(SensorTypeVersion::ACCELEROMETER, SharedMemType::GRALLOC,
+ RateLevel::NORMAL, mAccelNormChecker);
+}
+
+// Test sensor event direct report with gralloc for accel sensor at fast rate
+TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationFast) {
+ testDirectReportOperation(SensorTypeVersion::ACCELEROMETER, SharedMemType::GRALLOC,
+ RateLevel::FAST, mAccelNormChecker);
+}
+
+// Test sensor event direct report with gralloc for accel sensor at very fast rate
+TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationVeryFast) {
+ testDirectReportOperation(SensorTypeVersion::ACCELEROMETER, SharedMemType::GRALLOC,
+ RateLevel::VERY_FAST, mAccelNormChecker);
+}
+
+// Test sensor event direct report with gralloc for gyro sensor at normal rate
+TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationNormal) {
+ testDirectReportOperation(SensorTypeVersion::GYROSCOPE, SharedMemType::GRALLOC,
+ RateLevel::NORMAL, mGyroNormChecker);
+}
+
+// Test sensor event direct report with gralloc for gyro sensor at fast rate
+TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationFast) {
+ testDirectReportOperation(SensorTypeVersion::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::FAST,
+ mGyroNormChecker);
+}
+
+// Test sensor event direct report with gralloc for gyro sensor at very fast rate
+TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationVeryFast) {
+ testDirectReportOperation(SensorTypeVersion::GYROSCOPE, SharedMemType::GRALLOC,
+ RateLevel::VERY_FAST, mGyroNormChecker);
+}
+
+// Test sensor event direct report with gralloc for mag sensor at normal rate
+TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationNormal) {
+ testDirectReportOperation(SensorTypeVersion::MAGNETIC_FIELD, SharedMemType::GRALLOC,
+ RateLevel::NORMAL, NullChecker<EventType>());
+}
+
+// Test sensor event direct report with gralloc for mag sensor at fast rate
+TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationFast) {
+ testDirectReportOperation(SensorTypeVersion::MAGNETIC_FIELD, SharedMemType::GRALLOC,
+ RateLevel::FAST, NullChecker<EventType>());
+}
+
+// Test sensor event direct report with gralloc for mag sensor at very fast rate
+TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationVeryFast) {
+ testDirectReportOperation(SensorTypeVersion::MAGNETIC_FIELD, SharedMemType::GRALLOC,
+ RateLevel::VERY_FAST, NullChecker<EventType>());
+}
+
+void SensorsHidlTest::activateAllSensors(bool enable) {
+ for (const SensorInfoType& sensorInfo : getSensorsList()) {
+ if (isValidType(sensorInfo.type)) {
+ batch(sensorInfo.sensorHandle, sensorInfo.minDelay, 0 /* maxReportLatencyNs */);
+ activate(sensorInfo.sensorHandle, enable);
+ }
+ }
+}
+
+// Test that if initialize is called twice, then the HAL writes events to the FMQs from the second
+// call to the function.
+TEST_P(SensorsHidlTest, CallInitializeTwice) {
+ // Create a helper class so that a second environment is able to be instantiated
+ class SensorsHidlEnvironmentTest : public SensorsHidlEnvironmentV2_X {
+ public:
+ SensorsHidlEnvironmentTest(const std::string& service_name)
+ : SensorsHidlEnvironmentV2_X(service_name) {}
+ };
+
+ if (getSensorsList().size() == 0) {
+ // No sensors
+ return;
+ }
+
+ constexpr useconds_t kCollectionTimeoutUs = 1000 * 1000; // 1s
+ constexpr int32_t kNumEvents = 1;
+
+ // Create a new environment that calls initialize()
+ std::unique_ptr<SensorsHidlEnvironmentTest> newEnv =
+ std::make_unique<SensorsHidlEnvironmentTest>(GetParam());
+ newEnv->HidlSetUp();
+ if (HasFatalFailure()) {
+ return; // Exit early if setting up the new environment failed
+ }
+
+ activateAllSensors(true);
+ // Verify that the old environment does not receive any events
+ EXPECT_EQ(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), 0);
+ // Verify that the new event queue receives sensor events
+ EXPECT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents, newEnv.get(), newEnv.get()).size(),
+ kNumEvents);
+ activateAllSensors(false);
+
+ // Cleanup the test environment
+ newEnv->HidlTearDown();
+
+ // Restore the test environment for future tests
+ getEnvironment()->HidlTearDown();
+ getEnvironment()->HidlSetUp();
+ if (HasFatalFailure()) {
+ return; // Exit early if resetting the environment failed
+ }
+
+ // Ensure that the original environment is receiving events
+ activateAllSensors(true);
+ EXPECT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents).size(), kNumEvents);
+ activateAllSensors(false);
+}
+
+TEST_P(SensorsHidlTest, CleanupConnectionsOnInitialize) {
+ activateAllSensors(true);
+
+ // Verify that events are received
+ constexpr useconds_t kCollectionTimeoutUs = 1000 * 1000; // 1s
+ constexpr int32_t kNumEvents = 1;
+ ASSERT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), kNumEvents);
+
+ // Clear the active sensor handles so they are not disabled during TearDown
+ auto handles = mSensorHandles;
+ mSensorHandles.clear();
+ getEnvironment()->HidlTearDown();
+ getEnvironment()->HidlSetUp();
+ if (HasFatalFailure()) {
+ return; // Exit early if resetting the environment failed
+ }
+
+ // Verify no events are received until sensors are re-activated
+ ASSERT_EQ(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), 0);
+ activateAllSensors(true);
+ ASSERT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), kNumEvents);
+
+ // Disable sensors
+ activateAllSensors(false);
+
+ // Restore active sensors prior to clearing the environment
+ mSensorHandles = handles;
+}
+
+void SensorsHidlTest::runSingleFlushTest(const std::vector<SensorInfoType>& sensors,
+ bool activateSensor, int32_t expectedFlushCount,
+ Result expectedResponse) {
+ runFlushTest(sensors, activateSensor, 1 /* flushCalls */, expectedFlushCount, expectedResponse);
+}
+
+void SensorsHidlTest::runFlushTest(const std::vector<SensorInfoType>& sensors, bool activateSensor,
+ int32_t flushCalls, int32_t expectedFlushCount,
+ Result expectedResponse) {
+ EventCallback callback;
+ getEnvironment()->registerCallback(&callback);
+
+ for (const SensorInfoType& sensor : sensors) {
+ // Configure and activate the sensor
+ batch(sensor.sensorHandle, sensor.maxDelay, 0 /* maxReportLatencyNs */);
+ activate(sensor.sensorHandle, activateSensor);
+
+ // Flush the sensor
+ for (int32_t i = 0; i < flushCalls; i++) {
+ Result flushResult = flush(sensor.sensorHandle);
+ ASSERT_EQ(flushResult, expectedResponse);
+ }
+ }
+
+ // Wait up to one second for the flush events
+ callback.waitForFlushEvents(sensors, flushCalls, milliseconds(1000) /* timeout */);
+
+ // Deactivate all sensors after waiting for flush events so pending flush events are not
+ // abandoned by the HAL.
+ for (const SensorInfoType& sensor : sensors) {
+ activate(sensor.sensorHandle, false);
+ }
+ getEnvironment()->unregisterCallback();
+
+ // Check that the correct number of flushes are present for each sensor
+ for (const SensorInfoType& sensor : sensors) {
+ ASSERT_EQ(callback.getFlushCount(sensor.sensorHandle), expectedFlushCount);
+ }
+}
+
+TEST_P(SensorsHidlTest, FlushSensor) {
+ // Find a sensor that is not a one-shot sensor
+ std::vector<SensorInfoType> sensors = getNonOneShotSensors();
+ if (sensors.size() == 0) {
+ return;
+ }
+
+ constexpr int32_t kFlushes = 5;
+ runSingleFlushTest(sensors, true /* activateSensor */, 1 /* expectedFlushCount */, Result::OK);
+ runFlushTest(sensors, true /* activateSensor */, kFlushes, kFlushes, Result::OK);
+}
+
+TEST_P(SensorsHidlTest, FlushOneShotSensor) {
+ // Find a sensor that is a one-shot sensor
+ std::vector<SensorInfoType> sensors = getOneShotSensors();
+ if (sensors.size() == 0) {
+ return;
+ }
+
+ runSingleFlushTest(sensors, true /* activateSensor */, 0 /* expectedFlushCount */,
+ Result::BAD_VALUE);
+}
+
+TEST_P(SensorsHidlTest, FlushInactiveSensor) {
+ // Attempt to find a non-one shot sensor, then a one-shot sensor if necessary
+ std::vector<SensorInfoType> sensors = getNonOneShotSensors();
+ if (sensors.size() == 0) {
+ sensors = getOneShotSensors();
+ if (sensors.size() == 0) {
+ return;
+ }
+ }
+
+ runSingleFlushTest(sensors, false /* activateSensor */, 0 /* expectedFlushCount */,
+ Result::BAD_VALUE);
+}
+
+TEST_P(SensorsHidlTest, FlushNonexistentSensor) {
+ SensorInfoType sensor;
+ std::vector<SensorInfoType> sensors = getNonOneShotSensors();
+ if (sensors.size() == 0) {
+ sensors = getOneShotSensors();
+ if (sensors.size() == 0) {
+ return;
+ }
+ }
+ sensor = sensors.front();
+ sensor.sensorHandle = getInvalidSensorHandle();
+ runSingleFlushTest(std::vector<SensorInfoType>{sensor}, false /* activateSensor */,
+ 0 /* expectedFlushCount */, Result::BAD_VALUE);
+}
+
+TEST_P(SensorsHidlTest, Batch) {
+ if (getSensorsList().size() == 0) {
+ return;
+ }
+
+ activateAllSensors(false /* enable */);
+ for (const SensorInfoType& sensor : getSensorsList()) {
+ // Call batch on inactive sensor
+ // One shot sensors have minDelay set to -1 which is an invalid
+ // parameter. Use 0 instead to avoid errors.
+ int64_t samplingPeriodNs = extractReportMode(sensor.flags) == SensorFlagBits::ONE_SHOT_MODE
+ ? 0
+ : sensor.minDelay;
+ ASSERT_EQ(batch(sensor.sensorHandle, samplingPeriodNs, 0 /* maxReportLatencyNs */),
+ Result::OK);
+
+ // Activate the sensor
+ activate(sensor.sensorHandle, true /* enabled */);
+
+ // Call batch on an active sensor
+ ASSERT_EQ(batch(sensor.sensorHandle, sensor.maxDelay, 0 /* maxReportLatencyNs */),
+ Result::OK);
+ }
+ activateAllSensors(false /* enable */);
+
+ // Call batch on an invalid sensor
+ SensorInfoType sensor = getSensorsList().front();
+ sensor.sensorHandle = getInvalidSensorHandle();
+ ASSERT_EQ(batch(sensor.sensorHandle, sensor.minDelay, 0 /* maxReportLatencyNs */),
+ Result::BAD_VALUE);
+}
+
+TEST_P(SensorsHidlTest, Activate) {
+ if (getSensorsList().size() == 0) {
+ return;
+ }
+
+ // Verify that sensor events are generated when activate is called
+ for (const SensorInfoType& sensor : getSensorsList()) {
+ batch(sensor.sensorHandle, sensor.minDelay, 0 /* maxReportLatencyNs */);
+ ASSERT_EQ(activate(sensor.sensorHandle, true), Result::OK);
+
+ // Call activate on a sensor that is already activated
+ ASSERT_EQ(activate(sensor.sensorHandle, true), Result::OK);
+
+ // Deactivate the sensor
+ ASSERT_EQ(activate(sensor.sensorHandle, false), Result::OK);
+
+ // Call deactivate on a sensor that is already deactivated
+ ASSERT_EQ(activate(sensor.sensorHandle, false), Result::OK);
+ }
+
+ // Attempt to activate an invalid sensor
+ int32_t invalidHandle = getInvalidSensorHandle();
+ ASSERT_EQ(activate(invalidHandle, true), Result::BAD_VALUE);
+ ASSERT_EQ(activate(invalidHandle, false), Result::BAD_VALUE);
+}
+
+TEST_P(SensorsHidlTest, NoStaleEvents) {
+ constexpr milliseconds kFiveHundredMs(500);
+ constexpr milliseconds kOneSecond(1000);
+
+ // Register the callback to receive sensor events
+ EventCallback callback;
+ getEnvironment()->registerCallback(&callback);
+
+ // This test is not valid for one-shot or special-report-mode sensors
+ const std::vector<SensorInfoType> sensors = getNonOneShotAndNonSpecialSensors();
+ milliseconds maxMinDelay(0);
+ for (const SensorInfoType& sensor : sensors) {
+ milliseconds minDelay = duration_cast<milliseconds>(microseconds(sensor.minDelay));
+ maxMinDelay = milliseconds(std::max(maxMinDelay.count(), minDelay.count()));
+ }
+
+ // Activate the sensors so that they start generating events
+ activateAllSensors(true);
+
+ // According to the CDD, the first sample must be generated within 400ms + 2 * sample_time
+ // and the maximum reporting latency is 100ms + 2 * sample_time. Wait a sufficient amount
+ // of time to guarantee that a sample has arrived.
+ callback.waitForEvents(sensors, kFiveHundredMs + (5 * maxMinDelay));
+ activateAllSensors(false);
+
+ // Save the last received event for each sensor
+ std::map<int32_t, int64_t> lastEventTimestampMap;
+ for (const SensorInfoType& sensor : sensors) {
+ // Some on-change sensors may not report an event without stimulus
+ if (extractReportMode(sensor.flags) != SensorFlagBits::ON_CHANGE_MODE) {
+ ASSERT_GE(callback.getEvents(sensor.sensorHandle).size(), 1);
+ }
+ if (callback.getEvents(sensor.sensorHandle).size() >= 1) {
+ lastEventTimestampMap[sensor.sensorHandle] =
+ callback.getEvents(sensor.sensorHandle).back().timestamp;
+ }
+ }
+
+ // Allow some time to pass, reset the callback, then reactivate the sensors
+ usleep(duration_cast<microseconds>(kOneSecond + (5 * maxMinDelay)).count());
+ callback.reset();
+ activateAllSensors(true);
+ callback.waitForEvents(sensors, kFiveHundredMs + (5 * maxMinDelay));
+ activateAllSensors(false);
+
+ for (const SensorInfoType& sensor : sensors) {
+ // Skip sensors that did not previously report an event
+ if (lastEventTimestampMap.find(sensor.sensorHandle) == lastEventTimestampMap.end()) {
+ continue;
+ }
+ // Skip on-change sensors that do not consistently report an initial event
+ if (callback.getEvents(sensor.sensorHandle).size() < 1) {
+ continue;
+ }
+ // Ensure that the first event received is not stale by ensuring that its timestamp is
+ // sufficiently different from the previous event
+ const EventType newEvent = callback.getEvents(sensor.sensorHandle).front();
+ milliseconds delta = duration_cast<milliseconds>(
+ nanoseconds(newEvent.timestamp - lastEventTimestampMap[sensor.sensorHandle]));
+ milliseconds sensorMinDelay = duration_cast<milliseconds>(microseconds(sensor.minDelay));
+ ASSERT_GE(delta, kFiveHundredMs + (3 * sensorMinDelay));
+ }
+}
+
+void SensorsHidlTest::checkRateLevel(const SensorInfoType& sensor, int32_t directChannelHandle,
+ RateLevel rateLevel) {
+ configDirectReport(sensor.sensorHandle, directChannelHandle, rateLevel,
+ [&](Result result, int32_t reportToken) {
+ if (isDirectReportRateSupported(sensor, rateLevel)) {
+ ASSERT_EQ(result, Result::OK);
+ if (rateLevel != RateLevel::STOP) {
+ ASSERT_GT(reportToken, 0);
+ }
+ } else {
+ ASSERT_EQ(result, Result::BAD_VALUE);
+ }
+ });
+}
+
+void SensorsHidlTest::queryDirectChannelSupport(SharedMemType memType, bool* supportsSharedMemType,
+ bool* supportsAnyDirectChannel) {
+ *supportsSharedMemType = false;
+ *supportsAnyDirectChannel = false;
+ for (const SensorInfoType& curSensor : getSensorsList()) {
+ if (isDirectChannelTypeSupported(curSensor, memType)) {
+ *supportsSharedMemType = true;
+ }
+ if (isDirectChannelTypeSupported(curSensor, SharedMemType::ASHMEM) ||
+ isDirectChannelTypeSupported(curSensor, SharedMemType::GRALLOC)) {
+ *supportsAnyDirectChannel = true;
+ }
+
+ if (*supportsSharedMemType && *supportsAnyDirectChannel) {
+ break;
+ }
+ }
+}
+
+void SensorsHidlTest::verifyRegisterDirectChannel(
+ std::shared_ptr<SensorsTestSharedMemory<SensorTypeVersion, EventType>> mem,
+ int32_t* directChannelHandle, bool supportsSharedMemType, bool supportsAnyDirectChannel) {
+ char* buffer = mem->getBuffer();
+ memset(buffer, 0xff, mem->getSize());
+
+ registerDirectChannel(mem->getSharedMemInfo(), [&](Result result, int32_t channelHandle) {
+ if (supportsSharedMemType) {
+ ASSERT_EQ(result, Result::OK);
+ ASSERT_GT(channelHandle, 0);
+
+ // Verify that the memory has been zeroed
+ for (size_t i = 0; i < mem->getSize(); i++) {
+ ASSERT_EQ(buffer[i], 0x00);
+ }
+ } else {
+ Result expectedResult =
+ supportsAnyDirectChannel ? Result::BAD_VALUE : Result::INVALID_OPERATION;
+ ASSERT_EQ(result, expectedResult);
+ ASSERT_EQ(channelHandle, -1);
+ }
+ *directChannelHandle = channelHandle;
+ });
+}
+
+void SensorsHidlTest::verifyConfigure(const SensorInfoType& sensor, SharedMemType memType,
+ int32_t directChannelHandle, bool supportsAnyDirectChannel) {
+ if (isDirectChannelTypeSupported(sensor, memType)) {
+ // Verify that each rate level is properly supported
+ checkRateLevel(sensor, directChannelHandle, RateLevel::NORMAL);
+ checkRateLevel(sensor, directChannelHandle, RateLevel::FAST);
+ checkRateLevel(sensor, directChannelHandle, RateLevel::VERY_FAST);
+ checkRateLevel(sensor, directChannelHandle, RateLevel::STOP);
+
+ // Verify that a sensor handle of -1 is only acceptable when using RateLevel::STOP
+ configDirectReport(-1 /* sensorHandle */, directChannelHandle, RateLevel::NORMAL,
+ [](Result result, int32_t /* reportToken */) {
+ ASSERT_EQ(result, Result::BAD_VALUE);
+ });
+ configDirectReport(
+ -1 /* sensorHandle */, directChannelHandle, RateLevel::STOP,
+ [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::OK); });
+ } else {
+ // directChannelHandle will be -1 here, HAL should either reject it as a bad value if there
+ // is some level of direct channel report, otherwise return INVALID_OPERATION if direct
+ // channel is not supported at all
+ Result expectedResult =
+ supportsAnyDirectChannel ? Result::BAD_VALUE : Result::INVALID_OPERATION;
+ configDirectReport(sensor.sensorHandle, directChannelHandle, RateLevel::NORMAL,
+ [expectedResult](Result result, int32_t /* reportToken */) {
+ ASSERT_EQ(result, expectedResult);
+ });
+ }
+}
+
+void SensorsHidlTest::verifyUnregisterDirectChannel(int32_t directChannelHandle,
+ bool supportsAnyDirectChannel) {
+ Result expectedResult = supportsAnyDirectChannel ? Result::OK : Result::INVALID_OPERATION;
+ ASSERT_EQ(unregisterDirectChannel(directChannelHandle), expectedResult);
+}
+
+void SensorsHidlTest::verifyDirectChannel(SharedMemType memType) {
+ constexpr size_t kNumEvents = 1;
+ constexpr size_t kMemSize = kNumEvents * kEventSize;
+
+ std::shared_ptr<SensorsTestSharedMemory<SensorTypeVersion, EventType>> mem(
+ SensorsTestSharedMemory<SensorTypeVersion, EventType>::create(memType, kMemSize));
+ ASSERT_NE(mem, nullptr);
+
+ bool supportsSharedMemType;
+ bool supportsAnyDirectChannel;
+ queryDirectChannelSupport(memType, &supportsSharedMemType, &supportsAnyDirectChannel);
+
+ for (const SensorInfoType& sensor : getSensorsList()) {
+ int32_t directChannelHandle = 0;
+ verifyRegisterDirectChannel(mem, &directChannelHandle, supportsSharedMemType,
+ supportsAnyDirectChannel);
+ verifyConfigure(sensor, memType, directChannelHandle, supportsAnyDirectChannel);
+ verifyUnregisterDirectChannel(directChannelHandle, supportsAnyDirectChannel);
+ }
+}
+
+TEST_P(SensorsHidlTest, DirectChannelAshmem) {
+ verifyDirectChannel(SharedMemType::ASHMEM);
+}
+
+TEST_P(SensorsHidlTest, DirectChannelGralloc) {
+ verifyDirectChannel(SharedMemType::GRALLOC);
+}
+
+bool SensorsHidlTest::getDirectChannelSensor(SensorInfoType* sensor, SharedMemType* memType,
+ RateLevel* rate) {
+ bool found = false;
+ for (const SensorInfoType& curSensor : getSensorsList()) {
+ if (isDirectChannelTypeSupported(curSensor, SharedMemType::ASHMEM)) {
+ *memType = SharedMemType::ASHMEM;
+ *sensor = curSensor;
+ found = true;
+ break;
+ } else if (isDirectChannelTypeSupported(curSensor, SharedMemType::GRALLOC)) {
+ *memType = SharedMemType::GRALLOC;
+ *sensor = curSensor;
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ // Find a supported rate level
+ constexpr int kNumRateLevels = 3;
+ RateLevel rates[kNumRateLevels] = {RateLevel::NORMAL, RateLevel::FAST,
+ RateLevel::VERY_FAST};
+ *rate = RateLevel::STOP;
+ for (int i = 0; i < kNumRateLevels; i++) {
+ if (isDirectReportRateSupported(*sensor, rates[i])) {
+ *rate = rates[i];
+ }
+ }
+
+ // At least one rate level must be supported
+ EXPECT_NE(*rate, RateLevel::STOP);
+ }
+ return found;
+}
+
+TEST_P(SensorsHidlTest, ConfigureDirectChannelWithInvalidHandle) {
+ SensorInfoType sensor;
+ SharedMemType memType;
+ RateLevel rate;
+ if (!getDirectChannelSensor(&sensor, &memType, &rate)) {
+ return;
+ }
+
+ // Verify that an invalid channel handle produces a BAD_VALUE result
+ configDirectReport(sensor.sensorHandle, -1, rate, [](Result result, int32_t /* reportToken */) {
+ ASSERT_EQ(result, Result::BAD_VALUE);
+ });
+}
+
+TEST_P(SensorsHidlTest, CleanupDirectConnectionOnInitialize) {
+ constexpr size_t kNumEvents = 1;
+ constexpr size_t kMemSize = kNumEvents * kEventSize;
+
+ SensorInfoType sensor;
+ SharedMemType memType;
+ RateLevel rate;
+
+ if (!getDirectChannelSensor(&sensor, &memType, &rate)) {
+ return;
+ }
+
+ std::shared_ptr<SensorsTestSharedMemory<SensorTypeVersion, EventType>> mem(
+ SensorsTestSharedMemory<SensorTypeVersion, EventType>::create(memType, kMemSize));
+ ASSERT_NE(mem, nullptr);
+
+ int32_t directChannelHandle = 0;
+ registerDirectChannel(mem->getSharedMemInfo(), [&](Result result, int32_t channelHandle) {
+ ASSERT_EQ(result, Result::OK);
+ directChannelHandle = channelHandle;
+ });
+
+ // Configure the channel and expect success
+ configDirectReport(
+ sensor.sensorHandle, directChannelHandle, rate,
+ [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::OK); });
+
+ // Call initialize() via the environment setup to cause the HAL to re-initialize
+ // Clear the active direct connections so they are not stopped during TearDown
+ auto handles = mDirectChannelHandles;
+ mDirectChannelHandles.clear();
+ getEnvironment()->HidlTearDown();
+ getEnvironment()->HidlSetUp();
+ if (HasFatalFailure()) {
+ return; // Exit early if resetting the environment failed
+ }
+
+ // Attempt to configure the direct channel and expect it to fail
+ configDirectReport(
+ sensor.sensorHandle, directChannelHandle, rate,
+ [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::BAD_VALUE); });
+
+ // Restore original handles, though they should already be deactivated
+ mDirectChannelHandles = handles;
+}
diff --git a/sensors/common/vts/utils/Android.bp b/sensors/common/vts/utils/Android.bp
index bb4d329..ca4346a 100644
--- a/sensors/common/vts/utils/Android.bp
+++ b/sensors/common/vts/utils/Android.bp
@@ -20,9 +20,6 @@
cflags: ["-DLOG_TAG=\"sensors_hidl_hal_test\""],
srcs: [
"GrallocWrapper.cpp",
- "SensorsHidlEnvironmentBase.cpp",
- "SensorsHidlTestBase.cpp",
- "SensorsTestSharedMemory.cpp",
],
export_include_dirs: [
"include",
@@ -30,6 +27,9 @@
local_include_dirs: [
"include/sensors-vts-utils",
],
+ shared_libs: [
+ "libutils",
+ ],
static_libs: [
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
@@ -37,5 +37,7 @@
"android.hardware.graphics.mapper@2.1",
"android.hardware.graphics.mapper@3.0",
"android.hardware.sensors@1.0",
+ "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
],
}
diff --git a/sensors/common/vts/utils/SensorsHidlEnvironmentBase.cpp b/sensors/common/vts/utils/SensorsHidlEnvironmentBase.cpp
deleted file mode 100644
index fa0e2e9..0000000
--- a/sensors/common/vts/utils/SensorsHidlEnvironmentBase.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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 "SensorsHidlEnvironmentBase.h"
-
-void SensorsHidlEnvironmentBase::HidlSetUp() {
- ASSERT_TRUE(resetHal()) << "could not get hidl service";
-
- mCollectionEnabled = false;
- startPollingThread();
-
- // In case framework just stopped for test and there is sensor events in the pipe,
- // wait some time for those events to be cleared to avoid them messing up the test.
- std::this_thread::sleep_for(std::chrono::seconds(3));
-}
-
-void SensorsHidlEnvironmentBase::HidlTearDown() {
- mStopThread = true;
- if (mPollThread.joinable()) {
- mPollThread.detach();
- }
-}
-
-void SensorsHidlEnvironmentBase::catEvents(std::vector<Event>* output) {
- std::lock_guard<std::mutex> lock(mEventsMutex);
- if (output) {
- output->insert(output->end(), mEvents.begin(), mEvents.end());
- }
- mEvents.clear();
-}
-
-void SensorsHidlEnvironmentBase::setCollection(bool enable) {
- std::lock_guard<std::mutex> lock(mEventsMutex);
- mCollectionEnabled = enable;
-}
-
-void SensorsHidlEnvironmentBase::addEvent(const Event& ev) {
- std::lock_guard<std::mutex> lock(mEventsMutex);
- if (mCollectionEnabled) {
- mEvents.push_back(ev);
- }
-
- if (mCallback != nullptr) {
- mCallback->onEvent(ev);
- }
-}
-
-void SensorsHidlEnvironmentBase::registerCallback(IEventCallback* callback) {
- std::lock_guard<std::mutex> lock(mEventsMutex);
- mCallback = callback;
-}
-
-void SensorsHidlEnvironmentBase::unregisterCallback() {
- std::lock_guard<std::mutex> lock(mEventsMutex);
- mCallback = nullptr;
-}
\ No newline at end of file
diff --git a/sensors/common/vts/utils/SensorsHidlTestBase.cpp b/sensors/common/vts/utils/SensorsHidlTestBase.cpp
deleted file mode 100644
index 18549df..0000000
--- a/sensors/common/vts/utils/SensorsHidlTestBase.cpp
+++ /dev/null
@@ -1,584 +0,0 @@
-/*
- * 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 "SensorsHidlTestBase.h"
-
-#include "sensors-vts-utils/GrallocWrapper.h"
-#include "sensors-vts-utils/SensorsTestSharedMemory.h"
-
-#include <hardware/sensors.h> // for sensor type strings
-#include <log/log.h>
-#include <utils/SystemClock.h>
-
-#include <cinttypes>
-
-using ::android::sp;
-using ::android::hardware::hidl_string;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::sensors::V1_0::SensorFlagShift;
-using ::android::hardware::sensors::V1_0::SensorsEventFormatOffset;
-
-const Vec3NormChecker SensorsHidlTestBase::sAccelNormChecker(
- Vec3NormChecker::byNominal(GRAVITY_EARTH, 1.0f /*m/s^2*/));
-const Vec3NormChecker SensorsHidlTestBase::sGyroNormChecker(
- Vec3NormChecker::byNominal(0.f, 0.1f /*rad/s*/));
-
-std::vector<Event> SensorsHidlTestBase::collectEvents(useconds_t timeLimitUs, size_t nEventLimit,
- bool clearBeforeStart,
- bool changeCollection) {
- return collectEvents(timeLimitUs, nEventLimit, getEnvironment(), clearBeforeStart,
- changeCollection);
-}
-
-std::vector<Event> SensorsHidlTestBase::collectEvents(useconds_t timeLimitUs, size_t nEventLimit,
- SensorsHidlEnvironmentBase* environment,
- bool clearBeforeStart,
- bool changeCollection) {
- std::vector<Event> events;
- constexpr useconds_t SLEEP_GRANULARITY = 100 * 1000; // granularity 100 ms
-
- ALOGI("collect max of %zu events for %d us, clearBeforeStart %d", nEventLimit, timeLimitUs,
- clearBeforeStart);
-
- if (changeCollection) {
- environment->setCollection(true);
- }
- if (clearBeforeStart) {
- environment->catEvents(nullptr);
- }
-
- while (timeLimitUs > 0) {
- useconds_t duration = std::min(SLEEP_GRANULARITY, timeLimitUs);
- usleep(duration);
- timeLimitUs -= duration;
-
- environment->catEvents(&events);
- if (events.size() >= nEventLimit) {
- break;
- }
- ALOGV("time to go = %d, events to go = %d", (int)timeLimitUs,
- (int)(nEventLimit - events.size()));
- }
-
- if (changeCollection) {
- environment->setCollection(false);
- }
- return events;
-}
-
-void SensorsHidlTestBase::assertTypeMatchStringType(SensorType type,
- const hidl_string& stringType) {
- if (type >= SensorType::DEVICE_PRIVATE_BASE) {
- return;
- }
-
- switch (type) {
-#define CHECK_TYPE_STRING_FOR_SENSOR_TYPE(type) \
- case SensorType::type: \
- ASSERT_STREQ(SENSOR_STRING_TYPE_##type, stringType.c_str()); \
- break;
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ACCELEROMETER);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ACCELEROMETER_UNCALIBRATED);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ADDITIONAL_INFO);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(AMBIENT_TEMPERATURE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(DEVICE_ORIENTATION);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(DYNAMIC_SENSOR_META);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GAME_ROTATION_VECTOR);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GEOMAGNETIC_ROTATION_VECTOR);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GLANCE_GESTURE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GRAVITY);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GYROSCOPE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GYROSCOPE_UNCALIBRATED);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(HEART_BEAT);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(HEART_RATE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(LIGHT);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(LINEAR_ACCELERATION);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(LOW_LATENCY_OFFBODY_DETECT);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(MAGNETIC_FIELD);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(MAGNETIC_FIELD_UNCALIBRATED);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(MOTION_DETECT);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ORIENTATION);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(PICK_UP_GESTURE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(POSE_6DOF);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(PRESSURE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(PROXIMITY);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(RELATIVE_HUMIDITY);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ROTATION_VECTOR);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(SIGNIFICANT_MOTION);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(STATIONARY_DETECT);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(STEP_COUNTER);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(STEP_DETECTOR);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(TEMPERATURE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(TILT_DETECTOR);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(WAKE_GESTURE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(WRIST_TILT_GESTURE);
- default:
- FAIL() << "Type " << static_cast<int>(type)
- << " in android defined range is not checked, "
- << "stringType = " << stringType;
-#undef CHECK_TYPE_STRING_FOR_SENSOR_TYPE
- }
-}
-
-void SensorsHidlTestBase::assertTypeMatchReportMode(SensorType type, SensorFlagBits reportMode) {
- if (type >= SensorType::DEVICE_PRIVATE_BASE) {
- return;
- }
-
- SensorFlagBits expected = expectedReportModeForType(type);
-
- ASSERT_TRUE(expected == (SensorFlagBits)-1 || expected == reportMode)
- << "reportMode=" << static_cast<int>(reportMode)
- << "expected=" << static_cast<int>(expected);
-}
-
-void SensorsHidlTestBase::assertDelayMatchReportMode(int32_t minDelay, int32_t maxDelay,
- SensorFlagBits reportMode) {
- switch (reportMode) {
- case SensorFlagBits::CONTINUOUS_MODE:
- ASSERT_LT(0, minDelay);
- ASSERT_LE(0, maxDelay);
- break;
- case SensorFlagBits::ON_CHANGE_MODE:
- ASSERT_LE(0, minDelay);
- ASSERT_LE(0, maxDelay);
- break;
- case SensorFlagBits::ONE_SHOT_MODE:
- ASSERT_EQ(-1, minDelay);
- ASSERT_EQ(0, maxDelay);
- break;
- case SensorFlagBits::SPECIAL_REPORTING_MODE:
- // do not enforce anything for special reporting mode
- break;
- default:
- FAIL() << "Report mode " << static_cast<int>(reportMode) << " not checked";
- }
-}
-
-// return -1 means no expectation for this type
-SensorFlagBits SensorsHidlTestBase::expectedReportModeForType(SensorType type) {
- switch (type) {
- case SensorType::ACCELEROMETER:
- case SensorType::ACCELEROMETER_UNCALIBRATED:
- case SensorType::GYROSCOPE:
- case SensorType::MAGNETIC_FIELD:
- case SensorType::ORIENTATION:
- case SensorType::PRESSURE:
- case SensorType::TEMPERATURE:
- case SensorType::GRAVITY:
- case SensorType::LINEAR_ACCELERATION:
- case SensorType::ROTATION_VECTOR:
- case SensorType::MAGNETIC_FIELD_UNCALIBRATED:
- case SensorType::GAME_ROTATION_VECTOR:
- case SensorType::GYROSCOPE_UNCALIBRATED:
- case SensorType::GEOMAGNETIC_ROTATION_VECTOR:
- case SensorType::POSE_6DOF:
- case SensorType::HEART_BEAT:
- return SensorFlagBits::CONTINUOUS_MODE;
-
- case SensorType::LIGHT:
- case SensorType::PROXIMITY:
- case SensorType::RELATIVE_HUMIDITY:
- case SensorType::AMBIENT_TEMPERATURE:
- case SensorType::HEART_RATE:
- case SensorType::DEVICE_ORIENTATION:
- case SensorType::STEP_COUNTER:
- case SensorType::LOW_LATENCY_OFFBODY_DETECT:
- return SensorFlagBits::ON_CHANGE_MODE;
-
- case SensorType::SIGNIFICANT_MOTION:
- case SensorType::WAKE_GESTURE:
- case SensorType::GLANCE_GESTURE:
- case SensorType::PICK_UP_GESTURE:
- case SensorType::MOTION_DETECT:
- case SensorType::STATIONARY_DETECT:
- return SensorFlagBits::ONE_SHOT_MODE;
-
- case SensorType::STEP_DETECTOR:
- case SensorType::TILT_DETECTOR:
- case SensorType::WRIST_TILT_GESTURE:
- case SensorType::DYNAMIC_SENSOR_META:
- return SensorFlagBits::SPECIAL_REPORTING_MODE;
-
- default:
- ALOGW("Type %d is not implemented in expectedReportModeForType", (int)type);
- return (SensorFlagBits)-1;
- }
-}
-
-bool SensorsHidlTestBase::isDirectReportRateSupported(SensorInfo sensor, RateLevel rate) {
- unsigned int r = static_cast<unsigned int>(sensor.flags & SensorFlagBits::MASK_DIRECT_REPORT) >>
- static_cast<unsigned int>(SensorFlagShift::DIRECT_REPORT);
- return r >= static_cast<unsigned int>(rate);
-}
-
-bool SensorsHidlTestBase::isDirectChannelTypeSupported(SensorInfo sensor, SharedMemType type) {
- switch (type) {
- case SharedMemType::ASHMEM:
- return (sensor.flags & SensorFlagBits::DIRECT_CHANNEL_ASHMEM) != 0;
- case SharedMemType::GRALLOC:
- return (sensor.flags & SensorFlagBits::DIRECT_CHANNEL_GRALLOC) != 0;
- default:
- return false;
- }
-}
-
-void SensorsHidlTestBase::testDirectReportOperation(SensorType type, SharedMemType memType,
- RateLevel rate,
- const SensorEventsChecker& checker) {
- constexpr size_t kEventSize = static_cast<size_t>(SensorsEventFormatOffset::TOTAL_LENGTH);
- constexpr size_t kNEvent = 4096;
- constexpr size_t kMemSize = kEventSize * kNEvent;
-
- constexpr float kNormalNominal = 50;
- constexpr float kFastNominal = 200;
- constexpr float kVeryFastNominal = 800;
-
- constexpr float kNominalTestTimeSec = 1.f;
- constexpr float kMaxTestTimeSec = kNominalTestTimeSec + 0.5f; // 0.5 second for initialization
-
- SensorInfo sensor = defaultSensorByType(type);
-
- if (!isValidType(sensor.type)) {
- // no default sensor of this type
- return;
- }
-
- if (!isDirectReportRateSupported(sensor, rate)) {
- return;
- }
-
- if (!isDirectChannelTypeSupported(sensor, memType)) {
- return;
- }
-
- std::unique_ptr<SensorsTestSharedMemory> mem(
- SensorsTestSharedMemory::create(memType, kMemSize));
- ASSERT_NE(mem, nullptr);
-
- char* buffer = mem->getBuffer();
- // fill memory with data
- for (size_t i = 0; i < kMemSize; ++i) {
- buffer[i] = '\xcc';
- }
-
- int32_t channelHandle;
- registerDirectChannel(mem->getSharedMemInfo(),
- [&channelHandle](auto result, auto channelHandle_) {
- ASSERT_EQ(result, Result::OK);
- channelHandle = channelHandle_;
- });
-
- // check memory is zeroed
- for (size_t i = 0; i < kMemSize; ++i) {
- ASSERT_EQ(buffer[i], '\0');
- }
-
- int32_t eventToken;
- configDirectReport(sensor.sensorHandle, channelHandle, rate,
- [&eventToken](auto result, auto token) {
- ASSERT_EQ(result, Result::OK);
- eventToken = token;
- });
-
- usleep(static_cast<useconds_t>(kMaxTestTimeSec * 1e6f));
- auto events = mem->parseEvents();
-
- // find norminal rate
- float nominalFreq = 0.f;
- switch (rate) {
- case RateLevel::NORMAL:
- nominalFreq = kNormalNominal;
- break;
- case RateLevel::FAST:
- nominalFreq = kFastNominal;
- break;
- case RateLevel::VERY_FAST:
- nominalFreq = kVeryFastNominal;
- break;
- case RateLevel::STOP:
- FAIL();
- }
-
- // allowed to be between 55% and 220% of nominal freq
- ASSERT_GT(events.size(), static_cast<size_t>(nominalFreq * 0.55f * kNominalTestTimeSec));
- ASSERT_LT(events.size(), static_cast<size_t>(nominalFreq * 2.2f * kMaxTestTimeSec));
-
- int64_t lastTimestamp = 0;
- bool typeErrorReported = false;
- bool tokenErrorReported = false;
- bool timestampErrorReported = false;
- std::vector<Event> sensorEvents;
- for (auto& e : events) {
- if (!tokenErrorReported) {
- EXPECT_EQ(eventToken, e.sensorHandle)
- << (tokenErrorReported = true,
- "Event token does not match that retured from configDirectReport");
- }
-
- if (isMetaSensorType(e.sensorType)) {
- continue;
- }
- sensorEvents.push_back(e);
-
- if (!typeErrorReported) {
- EXPECT_EQ(type, e.sensorType)
- << (typeErrorReported = true,
- "Type in event does not match type of sensor registered.");
- }
- if (!timestampErrorReported) {
- EXPECT_GT(e.timestamp, lastTimestamp)
- << (timestampErrorReported = true, "Timestamp not monotonically increasing");
- }
- lastTimestamp = e.timestamp;
- }
-
- std::string s;
- EXPECT_TRUE(checker.check(sensorEvents, &s)) << s;
-
- // stop sensor and unregister channel
- configDirectReport(sensor.sensorHandle, channelHandle, RateLevel::STOP,
- [](auto result, auto) { EXPECT_EQ(result, Result::OK); });
- EXPECT_EQ(unregisterDirectChannel(channelHandle), Result::OK);
-}
-
-void SensorsHidlTestBase::testStreamingOperation(SensorType type,
- std::chrono::nanoseconds samplingPeriod,
- std::chrono::seconds duration,
- const SensorEventsChecker& checker) {
- std::vector<Event> events;
- std::vector<Event> sensorEvents;
-
- const int64_t samplingPeriodInNs = samplingPeriod.count();
- const int64_t batchingPeriodInNs = 0; // no batching
- const useconds_t minTimeUs = std::chrono::microseconds(duration).count();
- const size_t minNEvent = duration / samplingPeriod;
-
- SensorInfo sensor = defaultSensorByType(type);
-
- if (!isValidType(sensor.type)) {
- // no default sensor of this type
- return;
- }
-
- if (std::chrono::microseconds(sensor.minDelay) > samplingPeriod) {
- // rate not supported
- return;
- }
-
- int32_t handle = sensor.sensorHandle;
-
- ASSERT_EQ(batch(handle, samplingPeriodInNs, batchingPeriodInNs), Result::OK);
- ASSERT_EQ(activate(handle, 1), Result::OK);
- events = collectEvents(minTimeUs, minNEvent, true /*clearBeforeStart*/);
- ASSERT_EQ(activate(handle, 0), Result::OK);
-
- ALOGI("Collected %zu samples", events.size());
-
- ASSERT_GT(events.size(), 0u);
-
- bool handleMismatchReported = false;
- bool metaSensorTypeErrorReported = false;
- for (auto& e : events) {
- if (e.sensorType == type) {
- // avoid generating hundreds of error
- if (!handleMismatchReported) {
- EXPECT_EQ(e.sensorHandle, handle)
- << (handleMismatchReported = true,
- "Event of the same type must come from the sensor registered");
- }
- sensorEvents.push_back(e);
- } else {
- // avoid generating hundreds of error
- if (!metaSensorTypeErrorReported) {
- EXPECT_TRUE(isMetaSensorType(e.sensorType))
- << (metaSensorTypeErrorReported = true,
- "Only meta types are allowed besides the type registered");
- }
- }
- }
-
- std::string s;
- EXPECT_TRUE(checker.check(sensorEvents, &s)) << s;
-
- EXPECT_GE(sensorEvents.size(),
- minNEvent / 2); // make sure returned events are not all meta
-}
-
-void SensorsHidlTestBase::testSamplingRateHotSwitchOperation(SensorType type, bool fastToSlow) {
- std::vector<Event> events1, events2;
-
- constexpr int64_t batchingPeriodInNs = 0; // no batching
- constexpr int64_t collectionTimeoutUs = 60000000; // 60s
- constexpr size_t minNEvent = 50;
-
- SensorInfo sensor = defaultSensorByType(type);
-
- if (!isValidType(sensor.type)) {
- // no default sensor of this type
- return;
- }
-
- int32_t handle = sensor.sensorHandle;
- int64_t minSamplingPeriodInNs = sensor.minDelay * 1000ll;
- int64_t maxSamplingPeriodInNs = sensor.maxDelay * 1000ll;
-
- if (minSamplingPeriodInNs == maxSamplingPeriodInNs) {
- // only support single rate
- return;
- }
-
- int64_t firstCollectionPeriod = fastToSlow ? minSamplingPeriodInNs : maxSamplingPeriodInNs;
- int64_t secondCollectionPeriod = !fastToSlow ? minSamplingPeriodInNs : maxSamplingPeriodInNs;
-
- // first collection
- ASSERT_EQ(batch(handle, firstCollectionPeriod, batchingPeriodInNs), Result::OK);
- ASSERT_EQ(activate(handle, 1), Result::OK);
-
- usleep(500000); // sleep 0.5 sec to wait for change rate to happen
- events1 = collectEvents(collectionTimeoutUs, minNEvent);
-
- // second collection, without stop sensor
- ASSERT_EQ(batch(handle, secondCollectionPeriod, batchingPeriodInNs), Result::OK);
-
- usleep(500000); // sleep 0.5 sec to wait for change rate to happen
- events2 = collectEvents(collectionTimeoutUs, minNEvent);
-
- // end of collection, stop sensor
- ASSERT_EQ(activate(handle, 0), Result::OK);
-
- ALOGI("Collected %zu fast samples and %zu slow samples", events1.size(), events2.size());
-
- ASSERT_GT(events1.size(), 0u);
- ASSERT_GT(events2.size(), 0u);
-
- int64_t minDelayAverageInterval, maxDelayAverageInterval;
- std::vector<Event>& minDelayEvents(fastToSlow ? events1 : events2);
- std::vector<Event>& maxDelayEvents(fastToSlow ? events2 : events1);
-
- size_t nEvent = 0;
- int64_t prevTimestamp = -1;
- int64_t timestampInterval = 0;
- for (auto& e : minDelayEvents) {
- if (e.sensorType == type) {
- ASSERT_EQ(e.sensorHandle, handle);
- if (prevTimestamp > 0) {
- timestampInterval += e.timestamp - prevTimestamp;
- }
- prevTimestamp = e.timestamp;
- ++nEvent;
- }
- }
- ASSERT_GT(nEvent, 2u);
- minDelayAverageInterval = timestampInterval / (nEvent - 1);
-
- nEvent = 0;
- prevTimestamp = -1;
- timestampInterval = 0;
- for (auto& e : maxDelayEvents) {
- if (e.sensorType == type) {
- ASSERT_EQ(e.sensorHandle, handle);
- if (prevTimestamp > 0) {
- timestampInterval += e.timestamp - prevTimestamp;
- }
- prevTimestamp = e.timestamp;
- ++nEvent;
- }
- }
- ASSERT_GT(nEvent, 2u);
- maxDelayAverageInterval = timestampInterval / (nEvent - 1);
-
- // change of rate is significant.
- ALOGI("min/maxDelayAverageInterval = %" PRId64 " %" PRId64, minDelayAverageInterval,
- maxDelayAverageInterval);
- EXPECT_GT((maxDelayAverageInterval - minDelayAverageInterval), minDelayAverageInterval / 10);
-
- // fastest rate sampling time is close to spec
- EXPECT_LT(std::abs(minDelayAverageInterval - minSamplingPeriodInNs),
- minSamplingPeriodInNs / 10);
-
- // slowest rate sampling time is close to spec
- EXPECT_LT(std::abs(maxDelayAverageInterval - maxSamplingPeriodInNs),
- maxSamplingPeriodInNs / 10);
-}
-
-void SensorsHidlTestBase::testBatchingOperation(SensorType type) {
- std::vector<Event> events;
-
- constexpr int64_t maxBatchingTestTimeNs = 30ull * 1000 * 1000 * 1000;
- constexpr int64_t oneSecondInNs = 1ull * 1000 * 1000 * 1000;
-
- SensorInfo sensor = defaultSensorByType(type);
-
- if (!isValidType(sensor.type)) {
- // no default sensor of this type
- return;
- }
-
- int32_t handle = sensor.sensorHandle;
- int64_t minSamplingPeriodInNs = sensor.minDelay * 1000ll;
- uint32_t minFifoCount = sensor.fifoReservedEventCount;
- int64_t batchingPeriodInNs = minFifoCount * minSamplingPeriodInNs;
-
- if (batchingPeriodInNs < oneSecondInNs) {
- // batching size too small to test reliably
- return;
- }
-
- batchingPeriodInNs = std::min(batchingPeriodInNs, maxBatchingTestTimeNs);
-
- ALOGI("Test batching for %d ms", (int)(batchingPeriodInNs / 1000 / 1000));
-
- int64_t allowedBatchDeliverTimeNs = std::max(oneSecondInNs, batchingPeriodInNs / 10);
-
- ASSERT_EQ(batch(handle, minSamplingPeriodInNs, INT64_MAX), Result::OK);
- ASSERT_EQ(activate(handle, 1), Result::OK);
-
- usleep(500000); // sleep 0.5 sec to wait for initialization
- ASSERT_EQ(flush(handle), Result::OK);
-
- // wait for 80% of the reserved batching period
- // there should not be any significant amount of events
- // since collection is not enabled all events will go down the drain
- usleep(batchingPeriodInNs / 1000 * 8 / 10);
-
- getEnvironment()->setCollection(true);
- // clean existing collections
- collectEvents(0 /*timeLimitUs*/, 0 /*nEventLimit*/, true /*clearBeforeStart*/,
- false /*change collection*/);
-
- // 0.8 + 0.2 times the batching period
- usleep(batchingPeriodInNs / 1000 * 8 / 10);
- ASSERT_EQ(flush(handle), Result::OK);
-
- // plus some time for the event to deliver
- events = collectEvents(allowedBatchDeliverTimeNs / 1000, minFifoCount,
- false /*clearBeforeStart*/, false /*change collection*/);
-
- getEnvironment()->setCollection(false);
- ASSERT_EQ(activate(handle, 0), Result::OK);
-
- size_t nEvent = 0;
- for (auto& e : events) {
- if (e.sensorType == type && e.sensorHandle == handle) {
- ++nEvent;
- }
- }
-
- // at least reach 90% of advertised capacity
- ASSERT_GT(nEvent, (size_t)(minFifoCount * 9 / 10));
-}
diff --git a/sensors/common/vts/utils/SensorsTestSharedMemory.cpp b/sensors/common/vts/utils/SensorsTestSharedMemory.cpp
deleted file mode 100644
index 3b068bd..0000000
--- a/sensors/common/vts/utils/SensorsTestSharedMemory.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * 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 "SensorsTestSharedMemory.h"
-
-#include <log/log.h>
-
-#include <sys/mman.h>
-#include <cinttypes>
-
-using namespace ::android::hardware::sensors::V1_0;
-
-SharedMemInfo SensorsTestSharedMemory::getSharedMemInfo() const {
- SharedMemInfo mem = {.type = mType,
- .format = SharedMemFormat::SENSORS_EVENT,
- .size = static_cast<uint32_t>(mSize),
- .memoryHandle = mNativeHandle};
- return mem;
-}
-
-char* SensorsTestSharedMemory::getBuffer() const {
- return mBuffer;
-}
-
-size_t SensorsTestSharedMemory::getSize() const {
- return mSize;
-}
-
-std::vector<Event> SensorsTestSharedMemory::parseEvents(int64_t lastCounter, size_t offset) const {
- constexpr size_t kEventSize = static_cast<size_t>(SensorsEventFormatOffset::TOTAL_LENGTH);
- constexpr size_t kOffsetSize = static_cast<size_t>(SensorsEventFormatOffset::SIZE_FIELD);
- constexpr size_t kOffsetToken = static_cast<size_t>(SensorsEventFormatOffset::REPORT_TOKEN);
- constexpr size_t kOffsetType = static_cast<size_t>(SensorsEventFormatOffset::SENSOR_TYPE);
- constexpr size_t kOffsetAtomicCounter =
- static_cast<size_t>(SensorsEventFormatOffset::ATOMIC_COUNTER);
- constexpr size_t kOffsetTimestamp = static_cast<size_t>(SensorsEventFormatOffset::TIMESTAMP);
- constexpr size_t kOffsetData = static_cast<size_t>(SensorsEventFormatOffset::DATA);
-
- std::vector<Event> events;
- std::vector<float> data(16);
-
- while (offset + kEventSize <= mSize) {
- int64_t atomicCounter =
- *reinterpret_cast<uint32_t*>(mBuffer + offset + kOffsetAtomicCounter);
- if (atomicCounter <= lastCounter) {
- ALOGV("atomicCounter = %" PRId64 ", lastCounter = %" PRId64, atomicCounter,
- lastCounter);
- break;
- }
-
- int32_t size = *reinterpret_cast<int32_t*>(mBuffer + offset + kOffsetSize);
- if (size != kEventSize) {
- // unknown error, events parsed may be wrong, remove all
- events.clear();
- break;
- }
-
- int32_t token = *reinterpret_cast<int32_t*>(mBuffer + offset + kOffsetToken);
- int32_t type = *reinterpret_cast<int32_t*>(mBuffer + offset + kOffsetType);
- int64_t timestamp = *reinterpret_cast<int64_t*>(mBuffer + offset + kOffsetTimestamp);
-
- ALOGV("offset = %zu, cnt %" PRId64 ", token %" PRId32 ", type %" PRId32
- ", timestamp %" PRId64,
- offset, atomicCounter, token, type, timestamp);
-
- Event event = {
- .timestamp = timestamp,
- .sensorHandle = token,
- .sensorType = static_cast<SensorType>(type),
- };
- event.u.data = android::hardware::hidl_array<float, 16>(
- reinterpret_cast<float*>(mBuffer + offset + kOffsetData));
-
- events.push_back(event);
-
- lastCounter = atomicCounter;
- offset += kEventSize;
- }
-
- return events;
-}
-
-SensorsTestSharedMemory::SensorsTestSharedMemory(SharedMemType type, size_t size)
- : mType(type), mSize(0), mBuffer(nullptr) {
- native_handle_t* handle = nullptr;
- char* buffer = nullptr;
- switch (type) {
- case SharedMemType::ASHMEM: {
- int fd;
- handle = ::native_handle_create(1 /*nFds*/, 0 /*nInts*/);
- if (handle != nullptr) {
- handle->data[0] = fd = ::ashmem_create_region("SensorsTestSharedMemory", size);
- if (handle->data[0] > 0) {
- // memory is pinned by default
- buffer = static_cast<char*>(
- ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
- if (buffer != reinterpret_cast<char*>(MAP_FAILED)) {
- break;
- }
- ::native_handle_close(handle);
- }
- ::native_handle_delete(handle);
- handle = nullptr;
- }
- break;
- }
- case SharedMemType::GRALLOC: {
- mGrallocWrapper = std::make_unique<::android::GrallocWrapper>();
- if (!mGrallocWrapper->isInitialized()) {
- break;
- }
-
- std::pair<native_handle_t*, void*> buf = mGrallocWrapper->allocate(size);
- handle = buf.first;
- buffer = static_cast<char*>(buf.second);
- break;
- }
- default:
- break;
- }
-
- if (buffer != nullptr) {
- mNativeHandle = handle;
- mSize = size;
- mBuffer = buffer;
- }
-}
-
-SensorsTestSharedMemory::~SensorsTestSharedMemory() {
- switch (mType) {
- case SharedMemType::ASHMEM: {
- if (mSize != 0) {
- ::munmap(mBuffer, mSize);
- mBuffer = nullptr;
-
- ::native_handle_close(mNativeHandle);
- ::native_handle_delete(mNativeHandle);
-
- mNativeHandle = nullptr;
- mSize = 0;
- }
- break;
- }
- case SharedMemType::GRALLOC: {
- if (mSize != 0) {
- mGrallocWrapper->freeBuffer(mNativeHandle);
- mNativeHandle = nullptr;
- mSize = 0;
- }
- break;
- }
- default: {
- if (mNativeHandle != nullptr || mSize != 0 || mBuffer != nullptr) {
- ALOGE(
- "SensorsTestSharedMemory %p not properly destructed: "
- "type %d, native handle %p, size %zu, buffer %p",
- this, static_cast<int>(mType), mNativeHandle, mSize, mBuffer);
- }
- break;
- }
- }
-}
-
-SensorsTestSharedMemory* SensorsTestSharedMemory::create(SharedMemType type, size_t size) {
- constexpr size_t kMaxSize = 128 * 1024 * 1024; // sensor test should not need more than 128M
- if (size == 0 || size >= kMaxSize) {
- return nullptr;
- }
-
- auto m = new SensorsTestSharedMemory(type, size);
- if (m->mSize != size || m->mBuffer == nullptr) {
- delete m;
- m = nullptr;
- }
- return m;
-}
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/SensorEventsChecker.h b/sensors/common/vts/utils/include/sensors-vts-utils/SensorEventsChecker.h
index b5daccc..d6d3227 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorEventsChecker.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorEventsChecker.h
@@ -17,26 +17,26 @@
#ifndef ANDROID_SENSOR_EVENTS_CHECKER_H
#define ANDROID_SENSOR_EVENTS_CHECKER_H
-#include <android/hardware/sensors/1.0/types.h>
-
#include <cmath>
+template <class EventType>
class SensorEventsChecker {
- public:
- using Event = ::android::hardware::sensors::V1_0::Event;
- virtual bool check(const std::vector<Event>& events, std::string* out) const = 0;
+ public:
+ virtual bool check(const std::vector<EventType>& events, std::string* out) const = 0;
virtual ~SensorEventsChecker() {}
};
-class NullChecker : public SensorEventsChecker {
- public:
- virtual bool check(const std::vector<Event>&, std::string*) const { return true; }
+template <class EventType>
+class NullChecker : public SensorEventsChecker<EventType> {
+ public:
+ virtual bool check(const std::vector<EventType>&, std::string*) const { return true; }
};
-class SensorEventPerEventChecker : public SensorEventsChecker {
- public:
- virtual bool checkEvent(const Event& event, std::string* out) const = 0;
- virtual bool check(const std::vector<Event>& events, std::string* out) const {
+template <class EventType>
+class SensorEventPerEventChecker : public SensorEventsChecker<EventType> {
+ public:
+ virtual bool checkEvent(const EventType& event, std::string* out) const = 0;
+ virtual bool check(const std::vector<EventType>& events, std::string* out) const {
for (const auto& e : events) {
if (!checkEvent(e, out)) {
return false;
@@ -46,14 +46,15 @@
}
};
-class Vec3NormChecker : public SensorEventPerEventChecker {
- public:
+template <class EventType>
+class Vec3NormChecker : public SensorEventPerEventChecker<EventType> {
+ public:
Vec3NormChecker(float min, float max) : mLowerLimit(min), mUpperLimit(max) {}
- static Vec3NormChecker byNominal(float nominal, float allowedError) {
- return Vec3NormChecker(nominal - allowedError, nominal + allowedError);
+ static Vec3NormChecker<EventType> byNominal(float nominal, float allowedError) {
+ return Vec3NormChecker<EventType>(nominal - allowedError, nominal + allowedError);
}
- virtual bool checkEvent(const Event& event, std::string* out) const {
+ virtual bool checkEvent(const EventType& event, std::string* out) const {
android::hardware::sensors::V1_0::Vec3 v = event.u.vec3;
float norm = std::sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
if (norm < mLowerLimit || norm > mUpperLimit) {
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlEnvironmentBase.h b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlEnvironmentBase.h
index dbc9392..781427d 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlEnvironmentBase.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlEnvironmentBase.h
@@ -17,7 +17,6 @@
#ifndef ANDROID_SENSORS_HIDL_ENVIRONMENT_BASE_H
#define ANDROID_SENSORS_HIDL_ENVIRONMENT_BASE_H
-#include <android/hardware/sensors/1.0/types.h>
#include <gtest/gtest.h>
#include <atomic>
@@ -26,27 +25,59 @@
#include <thread>
#include <vector>
+template <class Event>
class IEventCallback {
- public:
+ public:
virtual ~IEventCallback() = default;
- virtual void onEvent(const ::android::hardware::sensors::V1_0::Event& event) = 0;
+ virtual void onEvent(const Event& event) = 0;
};
+template <class Event>
class SensorsHidlEnvironmentBase {
public:
- using Event = ::android::hardware::sensors::V1_0::Event;
- virtual void HidlSetUp();
- virtual void HidlTearDown();
+ virtual void HidlSetUp() {
+ ASSERT_TRUE(resetHal()) << "could not get hidl service";
+
+ mCollectionEnabled = false;
+ startPollingThread();
+
+ // In case framework just stopped for test and there is sensor events in the pipe,
+ // wait some time for those events to be cleared to avoid them messing up the test.
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ }
+
+ virtual void HidlTearDown() {
+ mStopThread = true;
+ if (mPollThread.joinable()) {
+ mPollThread.join();
+ }
+ }
// Get and clear all events collected so far (like "cat" shell command).
// If output is nullptr, it clears all collected events.
- void catEvents(std::vector<Event>* output);
+ void catEvents(std::vector<Event>* output) {
+ std::lock_guard<std::mutex> lock(mEventsMutex);
+ if (output) {
+ output->insert(output->end(), mEvents.begin(), mEvents.end());
+ }
+ mEvents.clear();
+ }
// set sensor event collection status
- void setCollection(bool enable);
+ void setCollection(bool enable) {
+ std::lock_guard<std::mutex> lock(mEventsMutex);
+ mCollectionEnabled = enable;
+ }
- void registerCallback(IEventCallback* callback);
- void unregisterCallback();
+ void registerCallback(IEventCallback<Event>* callback) {
+ std::lock_guard<std::mutex> lock(mEventsMutex);
+ mCallback = callback;
+ }
+
+ void unregisterCallback() {
+ std::lock_guard<std::mutex> lock(mEventsMutex);
+ mCallback = nullptr;
+ }
protected:
SensorsHidlEnvironmentBase(const std::string& service_name)
@@ -55,7 +86,16 @@
}
virtual ~SensorsHidlEnvironmentBase(){};
- void addEvent(const Event& ev);
+ void addEvent(const Event& ev) {
+ std::lock_guard<std::mutex> lock(mEventsMutex);
+ if (mCollectionEnabled) {
+ mEvents.push_back(ev);
+ }
+
+ if (mCallback != nullptr) {
+ mCallback->onEvent(ev);
+ }
+ }
virtual void startPollingThread() = 0;
virtual bool resetHal() = 0;
@@ -67,9 +107,9 @@
std::vector<Event> mEvents;
std::mutex mEventsMutex;
- IEventCallback* mCallback;
+ IEventCallback<Event>* mCallback;
- GTEST_DISALLOW_COPY_AND_ASSIGN_(SensorsHidlEnvironmentBase);
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(SensorsHidlEnvironmentBase<Event>);
};
#endif // ANDROID_SENSORS_HIDL_ENVIRONMENT_BASE_H
\ No newline at end of file
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 5fb6c5c..03bec87 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
@@ -19,12 +19,15 @@
#include "sensors-vts-utils/SensorEventsChecker.h"
#include "sensors-vts-utils/SensorsHidlEnvironmentBase.h"
+#include "sensors-vts-utils/SensorsTestSharedMemory.h"
-#include <VtsHalHidlTargetTestBase.h>
#include <android/hardware/sensors/1.0/ISensors.h>
#include <android/hardware/sensors/1.0/types.h>
#include <gtest/gtest.h>
+#include <hardware/sensors.h>
+#include <log/log.h>
+#include <cinttypes>
#include <unordered_set>
#include <vector>
@@ -35,19 +38,130 @@
using ::android::sp;
using ::android::hardware::hidl_string;
-using ::android::hardware::sensors::V1_0::Event;
-using ::android::hardware::sensors::V1_0::ISensors;
using ::android::hardware::sensors::V1_0::RateLevel;
using ::android::hardware::sensors::V1_0::Result;
using ::android::hardware::sensors::V1_0::SensorFlagBits;
-using ::android::hardware::sensors::V1_0::SensorInfo;
-using ::android::hardware::sensors::V1_0::SensorType;
+using ::android::hardware::sensors::V1_0::SensorFlagShift;
+using ::android::hardware::sensors::V1_0::SensorsEventFormatOffset;
using ::android::hardware::sensors::V1_0::SharedMemInfo;
using ::android::hardware::sensors::V1_0::SharedMemType;
+template <class SensorTypeT>
+static void assertTypeMatchStringType(SensorTypeT type, const hidl_string& stringType) {
+ if (type >= SensorTypeT::DEVICE_PRIVATE_BASE) {
+ return;
+ }
+
+ switch (type) {
+#define CHECK_TYPE_STRING_FOR_SENSOR_TYPE(type) \
+ case SensorTypeT::type: \
+ ASSERT_STREQ(SENSOR_STRING_TYPE_##type, stringType.c_str()); \
+ break;
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ACCELEROMETER);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ACCELEROMETER_UNCALIBRATED);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ADDITIONAL_INFO);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(AMBIENT_TEMPERATURE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(DEVICE_ORIENTATION);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(DYNAMIC_SENSOR_META);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GAME_ROTATION_VECTOR);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GEOMAGNETIC_ROTATION_VECTOR);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GLANCE_GESTURE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GRAVITY);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GYROSCOPE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GYROSCOPE_UNCALIBRATED);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(HEART_BEAT);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(HEART_RATE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(LIGHT);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(LINEAR_ACCELERATION);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(LOW_LATENCY_OFFBODY_DETECT);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(MAGNETIC_FIELD);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(MAGNETIC_FIELD_UNCALIBRATED);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(MOTION_DETECT);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ORIENTATION);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(PICK_UP_GESTURE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(POSE_6DOF);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(PRESSURE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(PROXIMITY);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(RELATIVE_HUMIDITY);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ROTATION_VECTOR);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(SIGNIFICANT_MOTION);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(STATIONARY_DETECT);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(STEP_COUNTER);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(STEP_DETECTOR);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(TEMPERATURE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(TILT_DETECTOR);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(WAKE_GESTURE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(WRIST_TILT_GESTURE);
+ default:
+ FAIL() << "Type " << static_cast<int>(type)
+ << " in android defined range is not checked, "
+ << "stringType = " << stringType;
+#undef CHECK_TYPE_STRING_FOR_SENSOR_TYPE
+ }
+}
+
+template <class SensorTypeT>
+static SensorFlagBits expectedReportModeForType(SensorTypeT type) {
+ switch (type) {
+ case SensorTypeT::ACCELEROMETER:
+ case SensorTypeT::ACCELEROMETER_UNCALIBRATED:
+ case SensorTypeT::GYROSCOPE:
+ case SensorTypeT::MAGNETIC_FIELD:
+ case SensorTypeT::ORIENTATION:
+ case SensorTypeT::PRESSURE:
+ case SensorTypeT::TEMPERATURE:
+ case SensorTypeT::GRAVITY:
+ case SensorTypeT::LINEAR_ACCELERATION:
+ case SensorTypeT::ROTATION_VECTOR:
+ case SensorTypeT::MAGNETIC_FIELD_UNCALIBRATED:
+ case SensorTypeT::GAME_ROTATION_VECTOR:
+ case SensorTypeT::GYROSCOPE_UNCALIBRATED:
+ case SensorTypeT::GEOMAGNETIC_ROTATION_VECTOR:
+ case SensorTypeT::POSE_6DOF:
+ case SensorTypeT::HEART_BEAT:
+ return SensorFlagBits::CONTINUOUS_MODE;
+
+ case SensorTypeT::LIGHT:
+ case SensorTypeT::PROXIMITY:
+ case SensorTypeT::RELATIVE_HUMIDITY:
+ case SensorTypeT::AMBIENT_TEMPERATURE:
+ case SensorTypeT::HEART_RATE:
+ case SensorTypeT::DEVICE_ORIENTATION:
+ case SensorTypeT::STEP_COUNTER:
+ case SensorTypeT::LOW_LATENCY_OFFBODY_DETECT:
+ return SensorFlagBits::ON_CHANGE_MODE;
+
+ case SensorTypeT::SIGNIFICANT_MOTION:
+ case SensorTypeT::WAKE_GESTURE:
+ case SensorTypeT::GLANCE_GESTURE:
+ case SensorTypeT::PICK_UP_GESTURE:
+ case SensorTypeT::MOTION_DETECT:
+ case SensorTypeT::STATIONARY_DETECT:
+ return SensorFlagBits::ONE_SHOT_MODE;
+
+ case SensorTypeT::STEP_DETECTOR:
+ case SensorTypeT::TILT_DETECTOR:
+ case SensorTypeT::WRIST_TILT_GESTURE:
+ case SensorTypeT::DYNAMIC_SENSOR_META:
+ return SensorFlagBits::SPECIAL_REPORTING_MODE;
+
+ default:
+ ALOGW("Type %d is not implemented in expectedReportModeForType", (int)type);
+ return (SensorFlagBits)-1;
+ }
+}
+
+template <class SensorTypeVersion, class EventType, class SensorInfoType>
class SensorsHidlTestBase : public testing::TestWithParam<std::string> {
public:
- virtual SensorsHidlEnvironmentBase* getEnvironment() = 0;
+ using ISensors = ::android::hardware::sensors::V1_0::ISensors;
+
+ SensorsHidlTestBase()
+ : mAccelNormChecker(Vec3NormChecker<EventType>::byNominal(GRAVITY_EARTH, 1.0f /*m/s^2*/)),
+ mGyroNormChecker(Vec3NormChecker<EventType>::byNominal(0.f, 0.1f /*rad/s*/)) {}
+
+ virtual SensorsHidlEnvironmentBase<EventType>* getEnvironment() = 0;
+
virtual void SetUp() override {}
virtual void TearDown() override {
@@ -67,16 +181,13 @@
}
// implementation wrapper
- virtual SensorInfo defaultSensorByType(SensorType type) = 0;
+ virtual SensorInfoType defaultSensorByType(SensorTypeVersion type) = 0;
virtual Return<void> getSensorsList(ISensors::getSensorsList_cb _hidl_cb) = 0;
-
+ virtual Return<Result> injectSensorData(const EventType& event) = 0;
virtual Return<Result> activate(int32_t sensorHandle, bool enabled) = 0;
-
virtual Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
int64_t maxReportLatencyNs) = 0;
-
virtual Return<Result> flush(int32_t sensorHandle) = 0;
- virtual Return<Result> injectSensorData(const Event& event) = 0;
virtual Return<void> registerDirectChannel(const SharedMemInfo& mem,
ISensors::registerDirectChannel_cb _hidl_cb) = 0;
virtual Return<Result> unregisterDirectChannel(int32_t channelHandle) = 0;
@@ -84,12 +195,395 @@
RateLevel rate,
ISensors::configDirectReport_cb _hidl_cb) = 0;
- std::vector<Event> collectEvents(useconds_t timeLimitUs, size_t nEventLimit,
- bool clearBeforeStart = true, bool changeCollection = true);
- static std::vector<Event> collectEvents(useconds_t timeLimitUs, size_t nEventLimit,
- SensorsHidlEnvironmentBase* environment,
- bool clearBeforeStart = true,
- bool changeCollection = true);
+ std::vector<EventType> collectEvents(useconds_t timeLimitUs, size_t nEventLimit,
+ bool clearBeforeStart = true,
+ bool changeCollection = true) {
+ return collectEvents(timeLimitUs, nEventLimit, getEnvironment(), clearBeforeStart,
+ changeCollection);
+ }
+
+ std::vector<EventType> collectEvents(useconds_t timeLimitUs, size_t nEventLimit,
+ SensorsHidlEnvironmentBase<EventType>* environment,
+ bool clearBeforeStart = true,
+ bool changeCollection = true) {
+ std::vector<EventType> events;
+ constexpr useconds_t SLEEP_GRANULARITY = 100 * 1000; // granularity 100 ms
+
+ ALOGI("collect max of %zu events for %d us, clearBeforeStart %d", nEventLimit, timeLimitUs,
+ clearBeforeStart);
+
+ if (changeCollection) {
+ environment->setCollection(true);
+ }
+ if (clearBeforeStart) {
+ environment->catEvents(nullptr);
+ }
+
+ while (timeLimitUs > 0) {
+ useconds_t duration = std::min(SLEEP_GRANULARITY, timeLimitUs);
+ usleep(duration);
+ timeLimitUs -= duration;
+
+ environment->catEvents(&events);
+ if (events.size() >= nEventLimit) {
+ break;
+ }
+ ALOGV("time to go = %d, events to go = %d", (int)timeLimitUs,
+ (int)(nEventLimit - events.size()));
+ }
+
+ if (changeCollection) {
+ environment->setCollection(false);
+ }
+ return events;
+ }
+
+ void testStreamingOperation(SensorTypeVersion type, std::chrono::nanoseconds samplingPeriod,
+ std::chrono::seconds duration,
+ const SensorEventsChecker<EventType>& checker) {
+ std::vector<EventType> events;
+ std::vector<EventType> sensorEvents;
+
+ const int64_t samplingPeriodInNs = samplingPeriod.count();
+ const int64_t batchingPeriodInNs = 0; // no batching
+ const useconds_t minTimeUs = std::chrono::microseconds(duration).count();
+ const size_t minNEvent = duration / samplingPeriod;
+
+ SensorInfoType sensor = defaultSensorByType(type);
+
+ if (!isValidType(sensor.type)) {
+ // no default sensor of this type
+ return;
+ }
+
+ if (std::chrono::microseconds(sensor.minDelay) > samplingPeriod) {
+ // rate not supported
+ return;
+ }
+
+ int32_t handle = sensor.sensorHandle;
+
+ ASSERT_EQ(batch(handle, samplingPeriodInNs, batchingPeriodInNs), Result::OK);
+ ASSERT_EQ(activate(handle, 1), Result::OK);
+ events = collectEvents(minTimeUs, minNEvent, getEnvironment(), true /*clearBeforeStart*/);
+ ASSERT_EQ(activate(handle, 0), Result::OK);
+
+ ALOGI("Collected %zu samples", events.size());
+
+ ASSERT_GT(events.size(), 0u);
+
+ bool handleMismatchReported = false;
+ bool metaSensorTypeErrorReported = false;
+ for (auto& e : events) {
+ if (e.sensorType == type) {
+ // avoid generating hundreds of error
+ if (!handleMismatchReported) {
+ EXPECT_EQ(e.sensorHandle, handle)
+ << (handleMismatchReported = true,
+ "Event of the same type must come from the sensor registered");
+ }
+ sensorEvents.push_back(e);
+ } else {
+ // avoid generating hundreds of error
+ if (!metaSensorTypeErrorReported) {
+ EXPECT_TRUE(isMetaSensorType(e.sensorType))
+ << (metaSensorTypeErrorReported = true,
+ "Only meta types are allowed besides the type registered");
+ }
+ }
+ }
+
+ std::string s;
+ EXPECT_TRUE(checker.check(sensorEvents, &s)) << s;
+
+ EXPECT_GE(sensorEvents.size(),
+ minNEvent / 2); // make sure returned events are not all meta
+ }
+
+ void testSamplingRateHotSwitchOperation(SensorTypeVersion type, bool fastToSlow = true) {
+ std::vector<EventType> events1, events2;
+
+ constexpr int64_t batchingPeriodInNs = 0; // no batching
+ constexpr int64_t collectionTimeoutUs = 60000000; // 60s
+ constexpr size_t minNEvent = 50;
+
+ SensorInfoType sensor = defaultSensorByType(type);
+
+ if (!isValidType(sensor.type)) {
+ // no default sensor of this type
+ return;
+ }
+
+ int32_t handle = sensor.sensorHandle;
+ int64_t minSamplingPeriodInNs = sensor.minDelay * 1000ll;
+ int64_t maxSamplingPeriodInNs = sensor.maxDelay * 1000ll;
+
+ if (minSamplingPeriodInNs == maxSamplingPeriodInNs) {
+ // only support single rate
+ return;
+ }
+
+ int64_t firstCollectionPeriod = fastToSlow ? minSamplingPeriodInNs : maxSamplingPeriodInNs;
+ int64_t secondCollectionPeriod =
+ !fastToSlow ? minSamplingPeriodInNs : maxSamplingPeriodInNs;
+
+ // first collection
+ ASSERT_EQ(batch(handle, firstCollectionPeriod, batchingPeriodInNs), Result::OK);
+ ASSERT_EQ(activate(handle, 1), Result::OK);
+
+ usleep(500000); // sleep 0.5 sec to wait for change rate to happen
+ events1 = collectEvents(collectionTimeoutUs, minNEvent, getEnvironment());
+
+ // second collection, without stop sensor
+ ASSERT_EQ(batch(handle, secondCollectionPeriod, batchingPeriodInNs), Result::OK);
+
+ usleep(500000); // sleep 0.5 sec to wait for change rate to happen
+ events2 = collectEvents(collectionTimeoutUs, minNEvent, getEnvironment());
+
+ // end of collection, stop sensor
+ ASSERT_EQ(activate(handle, 0), Result::OK);
+
+ ALOGI("Collected %zu fast samples and %zu slow samples", events1.size(), events2.size());
+
+ ASSERT_GT(events1.size(), 0u);
+ ASSERT_GT(events2.size(), 0u);
+
+ int64_t minDelayAverageInterval, maxDelayAverageInterval;
+ std::vector<EventType>& minDelayEvents(fastToSlow ? events1 : events2);
+ std::vector<EventType>& maxDelayEvents(fastToSlow ? events2 : events1);
+
+ size_t nEvent = 0;
+ int64_t prevTimestamp = -1;
+ int64_t timestampInterval = 0;
+ for (auto& e : minDelayEvents) {
+ if (e.sensorType == type) {
+ ASSERT_EQ(e.sensorHandle, handle);
+ if (prevTimestamp > 0) {
+ timestampInterval += e.timestamp - prevTimestamp;
+ }
+ prevTimestamp = e.timestamp;
+ ++nEvent;
+ }
+ }
+ ASSERT_GT(nEvent, 2u);
+ minDelayAverageInterval = timestampInterval / (nEvent - 1);
+
+ nEvent = 0;
+ prevTimestamp = -1;
+ timestampInterval = 0;
+ for (auto& e : maxDelayEvents) {
+ if (e.sensorType == type) {
+ ASSERT_EQ(e.sensorHandle, handle);
+ if (prevTimestamp > 0) {
+ timestampInterval += e.timestamp - prevTimestamp;
+ }
+ prevTimestamp = e.timestamp;
+ ++nEvent;
+ }
+ }
+ ASSERT_GT(nEvent, 2u);
+ maxDelayAverageInterval = timestampInterval / (nEvent - 1);
+
+ // change of rate is significant.
+ ALOGI("min/maxDelayAverageInterval = %" PRId64 " %" PRId64, minDelayAverageInterval,
+ maxDelayAverageInterval);
+ EXPECT_GT((maxDelayAverageInterval - minDelayAverageInterval),
+ minDelayAverageInterval / 10);
+
+ // fastest rate sampling time is close to spec
+ EXPECT_LT(std::abs(minDelayAverageInterval - minSamplingPeriodInNs),
+ minSamplingPeriodInNs / 10);
+
+ // slowest rate sampling time is close to spec
+ EXPECT_LT(std::abs(maxDelayAverageInterval - maxSamplingPeriodInNs),
+ maxSamplingPeriodInNs / 10);
+ }
+
+ void testBatchingOperation(SensorTypeVersion type) {
+ std::vector<EventType> events;
+
+ constexpr int64_t maxBatchingTestTimeNs = 30ull * 1000 * 1000 * 1000;
+ constexpr int64_t oneSecondInNs = 1ull * 1000 * 1000 * 1000;
+
+ SensorInfoType sensor = defaultSensorByType(type);
+
+ if (!isValidType(sensor.type)) {
+ // no default sensor of this type
+ return;
+ }
+
+ int32_t handle = sensor.sensorHandle;
+ int64_t minSamplingPeriodInNs = sensor.minDelay * 1000ll;
+ uint32_t minFifoCount = sensor.fifoReservedEventCount;
+ int64_t batchingPeriodInNs = minFifoCount * minSamplingPeriodInNs;
+
+ if (batchingPeriodInNs < oneSecondInNs) {
+ // batching size too small to test reliably
+ return;
+ }
+
+ batchingPeriodInNs = std::min(batchingPeriodInNs, maxBatchingTestTimeNs);
+
+ ALOGI("Test batching for %d ms", (int)(batchingPeriodInNs / 1000 / 1000));
+
+ int64_t allowedBatchDeliverTimeNs = std::max(oneSecondInNs, batchingPeriodInNs / 10);
+
+ ASSERT_EQ(batch(handle, minSamplingPeriodInNs, INT64_MAX), Result::OK);
+ ASSERT_EQ(activate(handle, 1), Result::OK);
+
+ usleep(500000); // sleep 0.5 sec to wait for initialization
+ ASSERT_EQ(flush(handle), Result::OK);
+
+ // wait for 80% of the reserved batching period
+ // there should not be any significant amount of events
+ // since collection is not enabled all events will go down the drain
+ usleep(batchingPeriodInNs / 1000 * 8 / 10);
+
+ getEnvironment()->setCollection(true);
+ // clean existing collections
+ collectEvents(0 /*timeLimitUs*/, 0 /*nEventLimit*/, true /*clearBeforeStart*/,
+ false /*change collection*/);
+
+ // 0.8 + 0.2 times the batching period
+ usleep(batchingPeriodInNs / 1000 * 8 / 10);
+ ASSERT_EQ(flush(handle), Result::OK);
+
+ // plus some time for the event to deliver
+ events = collectEvents(allowedBatchDeliverTimeNs / 1000, minFifoCount,
+ false /*clearBeforeStart*/, false /*change collection*/);
+
+ getEnvironment()->setCollection(false);
+ ASSERT_EQ(activate(handle, 0), Result::OK);
+
+ size_t nEvent = 0;
+ for (auto& e : events) {
+ if (e.sensorType == type && e.sensorHandle == handle) {
+ ++nEvent;
+ }
+ }
+
+ // at least reach 90% of advertised capacity
+ ASSERT_GT(nEvent, (size_t)(minFifoCount * 9 / 10));
+ }
+
+ void testDirectReportOperation(SensorTypeVersion type, SharedMemType memType, RateLevel rate,
+ const SensorEventsChecker<EventType>& checker) {
+ constexpr size_t kEventSize = static_cast<size_t>(SensorsEventFormatOffset::TOTAL_LENGTH);
+ constexpr size_t kNEvent = 4096;
+ constexpr size_t kMemSize = kEventSize * kNEvent;
+
+ constexpr float kNormalNominal = 50;
+ constexpr float kFastNominal = 200;
+ constexpr float kVeryFastNominal = 800;
+
+ constexpr float kNominalTestTimeSec = 1.f;
+ constexpr float kMaxTestTimeSec =
+ kNominalTestTimeSec + 0.5f; // 0.5 second for initialization
+
+ SensorInfoType sensor = defaultSensorByType(type);
+
+ if (!isValidType(sensor.type)) {
+ // no default sensor of this type
+ return;
+ }
+
+ if (!isDirectReportRateSupported(sensor, rate)) {
+ return;
+ }
+
+ if (!isDirectChannelTypeSupported(sensor, memType)) {
+ return;
+ }
+
+ std::unique_ptr<SensorsTestSharedMemory<SensorTypeVersion, EventType>> mem(
+ SensorsTestSharedMemory<SensorTypeVersion, EventType>::create(memType, kMemSize));
+ ASSERT_NE(mem, nullptr);
+
+ char* buffer = mem->getBuffer();
+ // fill memory with data
+ for (size_t i = 0; i < kMemSize; ++i) {
+ buffer[i] = '\xcc';
+ }
+
+ int32_t channelHandle;
+ registerDirectChannel(mem->getSharedMemInfo(),
+ [&channelHandle](auto result, auto channelHandle_) {
+ ASSERT_EQ(result, Result::OK);
+ channelHandle = channelHandle_;
+ });
+
+ // check memory is zeroed
+ for (size_t i = 0; i < kMemSize; ++i) {
+ ASSERT_EQ(buffer[i], '\0');
+ }
+
+ int32_t eventToken;
+ configDirectReport(sensor.sensorHandle, channelHandle, rate,
+ [&eventToken](auto result, auto token) {
+ ASSERT_EQ(result, Result::OK);
+ eventToken = token;
+ });
+
+ usleep(static_cast<useconds_t>(kMaxTestTimeSec * 1e6f));
+ auto events = mem->parseEvents();
+
+ // find norminal rate
+ float nominalFreq = 0.f;
+ switch (rate) {
+ case RateLevel::NORMAL:
+ nominalFreq = kNormalNominal;
+ break;
+ case RateLevel::FAST:
+ nominalFreq = kFastNominal;
+ break;
+ case RateLevel::VERY_FAST:
+ nominalFreq = kVeryFastNominal;
+ break;
+ case RateLevel::STOP:
+ FAIL();
+ }
+
+ // allowed to be between 55% and 220% of nominal freq
+ ASSERT_GT(events.size(), static_cast<size_t>(nominalFreq * 0.55f * kNominalTestTimeSec));
+ ASSERT_LT(events.size(), static_cast<size_t>(nominalFreq * 2.2f * kMaxTestTimeSec));
+
+ int64_t lastTimestamp = 0;
+ bool typeErrorReported = false;
+ bool tokenErrorReported = false;
+ bool timestampErrorReported = false;
+ std::vector<EventType> sensorEvents;
+ for (auto& e : events) {
+ if (!tokenErrorReported) {
+ EXPECT_EQ(eventToken, e.sensorHandle)
+ << (tokenErrorReported = true,
+ "Event token does not match that retured from configDirectReport");
+ }
+
+ if (isMetaSensorType(e.sensorType)) {
+ continue;
+ }
+ sensorEvents.push_back(e);
+
+ if (!typeErrorReported) {
+ EXPECT_EQ(type, e.sensorType)
+ << (typeErrorReported = true,
+ "Type in event does not match type of sensor registered.");
+ }
+ if (!timestampErrorReported) {
+ EXPECT_GT(e.timestamp, lastTimestamp) << (timestampErrorReported = true,
+ "Timestamp not monotonically increasing");
+ }
+ lastTimestamp = e.timestamp;
+ }
+
+ std::string s;
+ EXPECT_TRUE(checker.check(sensorEvents, &s)) << s;
+
+ // stop sensor and unregister channel
+ configDirectReport(sensor.sensorHandle, channelHandle, RateLevel::STOP,
+ [](auto result, auto) { EXPECT_EQ(result, Result::OK); });
+ EXPECT_EQ(unregisterDirectChannel(channelHandle), Result::OK);
+ }
inline static SensorFlagBits extractReportMode(uint64_t flag) {
return (SensorFlagBits)(flag & ((uint64_t)SensorFlagBits::CONTINUOUS_MODE |
@@ -98,36 +592,75 @@
(uint64_t)SensorFlagBits::SPECIAL_REPORTING_MODE));
}
- inline static bool isMetaSensorType(SensorType type) {
- return (type == SensorType::META_DATA || type == SensorType::DYNAMIC_SENSOR_META ||
- type == SensorType::ADDITIONAL_INFO);
+ inline static bool isMetaSensorType(SensorTypeVersion type) {
+ return (type == SensorTypeVersion::META_DATA ||
+ type == SensorTypeVersion::DYNAMIC_SENSOR_META ||
+ type == SensorTypeVersion::ADDITIONAL_INFO);
}
- inline static bool isValidType(SensorType type) { return (int32_t)type > 0; }
+ inline static bool isValidType(SensorTypeVersion type) { return (int32_t)type > 0; }
- void testStreamingOperation(SensorType type, std::chrono::nanoseconds samplingPeriod,
- std::chrono::seconds duration, const SensorEventsChecker& checker);
- void testSamplingRateHotSwitchOperation(SensorType type, bool fastToSlow = true);
- void testBatchingOperation(SensorType type);
- void testDirectReportOperation(SensorType type, SharedMemType memType, RateLevel rate,
- const SensorEventsChecker& checker);
-
- static void assertTypeMatchStringType(SensorType type, const hidl_string& stringType);
- static void assertTypeMatchReportMode(SensorType type, SensorFlagBits reportMode);
static void assertDelayMatchReportMode(int32_t minDelay, int32_t maxDelay,
- SensorFlagBits reportMode);
- static SensorFlagBits expectedReportModeForType(SensorType type);
- static bool isDirectReportRateSupported(SensorInfo sensor, RateLevel rate);
- static bool isDirectChannelTypeSupported(SensorInfo sensor, SharedMemType type);
+ SensorFlagBits reportMode) {
+ switch (reportMode) {
+ case SensorFlagBits::CONTINUOUS_MODE:
+ ASSERT_LT(0, minDelay);
+ ASSERT_LE(0, maxDelay);
+ break;
+ case SensorFlagBits::ON_CHANGE_MODE:
+ ASSERT_LE(0, minDelay);
+ ASSERT_LE(0, maxDelay);
+ break;
+ case SensorFlagBits::ONE_SHOT_MODE:
+ ASSERT_EQ(-1, minDelay);
+ ASSERT_EQ(0, maxDelay);
+ break;
+ case SensorFlagBits::SPECIAL_REPORTING_MODE:
+ // do not enforce anything for special reporting mode
+ break;
+ default:
+ FAIL() << "Report mode " << static_cast<int>(reportMode) << " not checked";
+ }
+ }
- protected:
- // checkers
- static const Vec3NormChecker sAccelNormChecker;
- static const Vec3NormChecker sGyroNormChecker;
+ protected:
+ static void assertTypeMatchReportMode(SensorTypeVersion type, SensorFlagBits reportMode) {
+ if (type >= SensorTypeVersion::DEVICE_PRIVATE_BASE) {
+ return;
+ }
+
+ SensorFlagBits expected = expectedReportModeForType(type);
+
+ ASSERT_TRUE(expected == (SensorFlagBits)-1 || expected == reportMode)
+ << "reportMode=" << static_cast<int>(reportMode)
+ << "expected=" << static_cast<int>(expected);
+ }
+
+ static bool isDirectReportRateSupported(SensorInfoType sensor, RateLevel rate) {
+ unsigned int r =
+ static_cast<unsigned int>(sensor.flags & SensorFlagBits::MASK_DIRECT_REPORT) >>
+ static_cast<unsigned int>(SensorFlagShift::DIRECT_REPORT);
+ return r >= static_cast<unsigned int>(rate);
+ }
+
+ static bool isDirectChannelTypeSupported(SensorInfoType sensor, SharedMemType type) {
+ switch (type) {
+ case SharedMemType::ASHMEM:
+ return (sensor.flags & SensorFlagBits::DIRECT_CHANNEL_ASHMEM) != 0;
+ case SharedMemType::GRALLOC:
+ return (sensor.flags & SensorFlagBits::DIRECT_CHANNEL_GRALLOC) != 0;
+ default:
+ return false;
+ }
+ }
+
+ // Checkers
+ Vec3NormChecker<EventType> mAccelNormChecker;
+ Vec3NormChecker<EventType> mGyroNormChecker;
// all sensors and direct channnels used
std::unordered_set<int32_t> mSensorHandles;
std::unordered_set<int32_t> mDirectChannelHandles;
};
-#endif // ANDROID_SENSORS_HIDL_TEST_BASE_H
\ No newline at end of file
+#endif // ANDROID_SENSORS_HIDL_TEST_BASE_H
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsTestSharedMemory.h b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsTestSharedMemory.h
index 002f42c..39084a4 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsTestSharedMemory.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsTestSharedMemory.h
@@ -20,25 +20,177 @@
#include "GrallocWrapper.h"
#include <android-base/macros.h>
-#include <android/hardware/sensors/1.0/types.h>
+#include <log/log.h>
+
+#include <sys/mman.h>
+#include <cinttypes>
#include <cutils/ashmem.h>
+using namespace ::android::hardware::sensors::V1_0;
+
+template <class SensorTypeVersion, class EventType>
class SensorsTestSharedMemory {
- using SharedMemType = ::android::hardware::sensors::V1_0::SharedMemType;
- using SharedMemInfo = ::android::hardware::sensors::V1_0::SharedMemInfo;
- using Event = ::android::hardware::sensors::V1_0::Event;
+ public:
+ static SensorsTestSharedMemory* create(SharedMemType type, size_t size) {
+ constexpr size_t kMaxSize =
+ 128 * 1024 * 1024; // sensor test should not need more than 128M
+ if (size == 0 || size >= kMaxSize) {
+ return nullptr;
+ }
- public:
- static SensorsTestSharedMemory* create(SharedMemType type, size_t size);
- SharedMemInfo getSharedMemInfo() const;
- char* getBuffer() const;
- size_t getSize() const;
- std::vector<Event> parseEvents(int64_t lastCounter = -1, size_t offset = 0) const;
- virtual ~SensorsTestSharedMemory();
+ auto m = new SensorsTestSharedMemory<SensorTypeVersion, EventType>(type, size);
+ if (m->mSize != size || m->mBuffer == nullptr) {
+ delete m;
+ m = nullptr;
+ }
+ return m;
+ }
- private:
- SensorsTestSharedMemory(SharedMemType type, size_t size);
+ SharedMemInfo getSharedMemInfo() const {
+ SharedMemInfo mem = {.type = mType,
+ .format = SharedMemFormat::SENSORS_EVENT,
+ .size = static_cast<uint32_t>(mSize),
+ .memoryHandle = mNativeHandle};
+ return mem;
+ }
+ char* getBuffer() const { return mBuffer; }
+ size_t getSize() const { return mSize; }
+ std::vector<EventType> parseEvents(int64_t lastCounter = -1, size_t offset = 0) const {
+ constexpr size_t kEventSize = static_cast<size_t>(SensorsEventFormatOffset::TOTAL_LENGTH);
+ constexpr size_t kOffsetSize = static_cast<size_t>(SensorsEventFormatOffset::SIZE_FIELD);
+ constexpr size_t kOffsetToken = static_cast<size_t>(SensorsEventFormatOffset::REPORT_TOKEN);
+ constexpr size_t kOffsetType = static_cast<size_t>(SensorsEventFormatOffset::SENSOR_TYPE);
+ constexpr size_t kOffsetAtomicCounter =
+ static_cast<size_t>(SensorsEventFormatOffset::ATOMIC_COUNTER);
+ constexpr size_t kOffsetTimestamp =
+ static_cast<size_t>(SensorsEventFormatOffset::TIMESTAMP);
+ constexpr size_t kOffsetData = static_cast<size_t>(SensorsEventFormatOffset::DATA);
+
+ std::vector<EventType> events;
+ std::vector<float> data(16);
+
+ while (offset + kEventSize <= mSize) {
+ int64_t atomicCounter =
+ *reinterpret_cast<uint32_t*>(mBuffer + offset + kOffsetAtomicCounter);
+ if (atomicCounter <= lastCounter) {
+ ALOGV("atomicCounter = %" PRId64 ", lastCounter = %" PRId64, atomicCounter,
+ lastCounter);
+ break;
+ }
+
+ int32_t size = *reinterpret_cast<int32_t*>(mBuffer + offset + kOffsetSize);
+ if (size != kEventSize) {
+ // unknown error, events parsed may be wrong, remove all
+ events.clear();
+ break;
+ }
+
+ int32_t token = *reinterpret_cast<int32_t*>(mBuffer + offset + kOffsetToken);
+ int32_t type = *reinterpret_cast<int32_t*>(mBuffer + offset + kOffsetType);
+ int64_t timestamp = *reinterpret_cast<int64_t*>(mBuffer + offset + kOffsetTimestamp);
+
+ ALOGV("offset = %zu, cnt %" PRId64 ", token %" PRId32 ", type %" PRId32
+ ", timestamp %" PRId64,
+ offset, atomicCounter, token, type, timestamp);
+
+ EventType event = {
+ .timestamp = timestamp,
+ .sensorHandle = token,
+ .sensorType = static_cast<SensorTypeVersion>(type),
+ };
+ event.u.data = android::hardware::hidl_array<float, 16>(
+ reinterpret_cast<float*>(mBuffer + offset + kOffsetData));
+
+ events.push_back(event);
+
+ lastCounter = atomicCounter;
+ offset += kEventSize;
+ }
+
+ return events;
+ }
+
+ virtual ~SensorsTestSharedMemory() {
+ switch (mType) {
+ case SharedMemType::ASHMEM: {
+ if (mSize != 0) {
+ ::munmap(mBuffer, mSize);
+ mBuffer = nullptr;
+
+ ::native_handle_close(mNativeHandle);
+ ::native_handle_delete(mNativeHandle);
+
+ mNativeHandle = nullptr;
+ mSize = 0;
+ }
+ break;
+ }
+ case SharedMemType::GRALLOC: {
+ if (mSize != 0) {
+ mGrallocWrapper->freeBuffer(mNativeHandle);
+ mNativeHandle = nullptr;
+ mSize = 0;
+ }
+ break;
+ }
+ default: {
+ if (mNativeHandle != nullptr || mSize != 0 || mBuffer != nullptr) {
+ ALOGE("SensorsTestSharedMemory %p not properly destructed: "
+ "type %d, native handle %p, size %zu, buffer %p",
+ this, static_cast<int>(mType), mNativeHandle, mSize, mBuffer);
+ }
+ break;
+ }
+ }
+ }
+
+ private:
+ SensorsTestSharedMemory(SharedMemType type, size_t size)
+ : mType(type), mSize(0), mBuffer(nullptr) {
+ native_handle_t* handle = nullptr;
+ char* buffer = nullptr;
+ switch (type) {
+ case SharedMemType::ASHMEM: {
+ int fd;
+ handle = ::native_handle_create(1 /*nFds*/, 0 /*nInts*/);
+ if (handle != nullptr) {
+ handle->data[0] = fd = ::ashmem_create_region("SensorsTestSharedMemory", size);
+ if (handle->data[0] > 0) {
+ // memory is pinned by default
+ buffer = static_cast<char*>(
+ ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
+ if (buffer != reinterpret_cast<char*>(MAP_FAILED)) {
+ break;
+ }
+ ::native_handle_close(handle);
+ }
+ ::native_handle_delete(handle);
+ handle = nullptr;
+ }
+ break;
+ }
+ case SharedMemType::GRALLOC: {
+ mGrallocWrapper = std::make_unique<::android::GrallocWrapper>();
+ if (!mGrallocWrapper->isInitialized()) {
+ break;
+ }
+
+ std::pair<native_handle_t*, void*> buf = mGrallocWrapper->allocate(size);
+ handle = buf.first;
+ buffer = static_cast<char*>(buf.second);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (buffer != nullptr) {
+ mNativeHandle = handle;
+ mSize = size;
+ mBuffer = buffer;
+ }
+ }
SharedMemType mType;
native_handle_t* mNativeHandle;
diff --git a/soundtrigger/2.3/ISoundTriggerHw.hal b/soundtrigger/2.3/ISoundTriggerHw.hal
index 270b00e..3e761e5 100644
--- a/soundtrigger/2.3/ISoundTriggerHw.hal
+++ b/soundtrigger/2.3/ISoundTriggerHw.hal
@@ -114,8 +114,10 @@
* @return status Operation completion status: 0 in case of success
* -ENODEV if the native service cannot be reached
* -EINVAL invalid input parameter
- * @return retval ModelParameter structure indicating supported attributes
- * of the parameter for the given model handle
+ * @return retval OptionalModelParameterRange safe union structure wrapping
+ * ModelParameterRange. This structure indicates supported attributes
+ * of the parameter for the given model handle. If the parameter is not
+ * supported the Monostate of the union is used.
*/
queryParameter(SoundModelHandle modelHandle, ModelParameter modelParam)
generates (int32_t status, OptionalModelParameterRange retval);
diff --git a/soundtrigger/2.3/default/SoundTriggerHw.cpp b/soundtrigger/2.3/default/SoundTriggerHw.cpp
index d3136b9..8fe3108 100644
--- a/soundtrigger/2.3/default/SoundTriggerHw.cpp
+++ b/soundtrigger/2.3/default/SoundTriggerHw.cpp
@@ -889,7 +889,7 @@
int32_t status = mHwDevice->query_parameter(
mHwDevice, client->getHalHandle(), convertModelParameterToHal(modelParam), ¶mRange);
- if (status == 0) {
+ if (status == 0 && paramRange.is_supported) {
optionalParamRange.range({.start = paramRange.start, .end = paramRange.end});
}
_hidl_cb(status, optionalParamRange);
diff --git a/soundtrigger/2.3/types.hal b/soundtrigger/2.3/types.hal
index 730f969..10fc34e 100644
--- a/soundtrigger/2.3/types.hal
+++ b/soundtrigger/2.3/types.hal
@@ -66,7 +66,7 @@
* Bit field encoding of the AudioCapabilities
* supported by the firmware.
*/
- uint32_t audioCapabilities;
+ bitfield<AudioCapabilities> audioCapabilities;
};
/**
diff --git a/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfig.xsd b/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfig.xsd
new file mode 100644
index 0000000..45c01c1
--- /dev/null
+++ b/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfig.xsd
@@ -0,0 +1,111 @@
+<?xml version="1.0"?>
+<!-- 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.
+-->
+<xs:schema version="1.0"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <!-- List of the Tuner Resource Manager client use case priority hint. -->
+ <xs:simpleType name="version">
+ <xs:restriction base="xs:decimal">
+ <xs:enumeration value="1.0"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:complexType name="config">
+ <xs:sequence>
+ <xs:element name="useCaseDefault" type="useCaseDefault" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="useCasePreDefined" type="useCasePreDefined" minOccurs="0" maxOccurs="5"/>
+ <xs:element name="useCaseVendor" type="useCaseVendor" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="version" type="version"/>
+ </xs:complexType>
+
+ <xs:complexType name="useCaseDefault">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ useCaseDefault section:
+ Default value for predefined use cases priority hint.
+ "fgPriority": priority when the use case is in foreground.
+ "bgPriority": priority when the use case is in background.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="fgPriority" type="priority"/>
+ <xs:attribute name="bgPriority" type="priority"/>
+ </xs:complexType>
+
+ <xs:complexType name="useCasePreDefined">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ useCasePreDefined section:
+ A list of predefined use cases and their foreground/background priority hint.
+ Each use case has the following attributes:
+ "type": type of the use case. Pre-defined use cases start with "USE_CASE_"
+ and have been predefined in "predefinedUseCaseType".
+ "fgPriority": priority when the use case is in foreground.
+ "bgPriority": priority when the use case is in background.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="type" type="predefinedUseCaseType"/>
+ <xs:attribute name="fgPriority" type="priority"/>
+ <xs:attribute name="bgPriority" type="priority"/>
+ </xs:complexType>
+
+ <xs:complexType name="useCaseVendor">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ useCaseVendor section:
+ A list of vendor defined use cases and their foreground/background priority hint.
+ Each use case has the following attributes:
+ "type": type of the use case. Vendor defined use cases start with "VENDOR_USE_CASE_".
+ "fgPriority": priority when the use case is in foreground.
+ "bgPriority": priority when the use case is in background.
+ "id": Vendor defined use case must have an id greater than 1000 to be associated with.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="type" type="vendorUseCaseType"/>
+ <xs:attribute name="id" type="id"/>
+ <xs:attribute name="fgPriority" type="priority"/>
+ <xs:attribute name="bgPriority" type="priority"/>
+ </xs:complexType>
+
+ <xs:simpleType name="predefinedUseCaseType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="USE_CASE_RECORD"/>
+ <xs:enumeration value="USE_CASE_LIVE"/>
+ <xs:enumeration value="USE_CASE_PLAYBACK"/>
+ <xs:enumeration value="USE_CASE_SCAN"/>
+ <xs:enumeration value="USE_CASE_BACKGROUND"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="vendorUseCaseType">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="VENDOR_USE_CASE_[_A-Z0-9]+"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="priority">
+ <xs:restriction base="xs:integer">
+ <xs:minInclusive value="0"/>
+ <xs:maxInclusive value="1000"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="id">
+ <xs:restriction base="xs:integer">
+ <xs:minInclusive value="1001"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:element name="config" type="config"/>
+</xs:schema>
diff --git a/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfigSample.xml b/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfigSample.xml
new file mode 100644
index 0000000..938faeb
--- /dev/null
+++ b/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfigSample.xml
@@ -0,0 +1,47 @@
+<?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.
+-->
+<!-- A sample Tuner Resource Manager use case priority configuration xml -->
+<config version="1.0" xmlns:xi="http://www.w3.org/2001/XMLSchema">
+ <!-- useCaseDefault section:
+ Default value for predefined use cases priority hint.
+ "fgPriority": priority when the use case is in foreground. Value range [0-1000].
+ "bgPriority": priority when the use case is in background. Value range [0-1000].
+ -->
+ <useCaseDefault fgPriority="150" bgPriority="50"/>
+ <!-- useCasePreDefined section:
+ A list of predefined use cases and their foreground/background priority hint.
+ Each use case has the following attributes:
+ "type": type of the use case. Pre-defined use cases start with "USE_CASE_"
+ and could only use the types defined in "predefinedUseCaseType" in xsd.
+ "fgPriority": priority when the use case is in foreground. Value range [0-1000].
+ "bgPriority": priority when the use case is in background. Value range [0-1000].
+ -->
+ <useCasePreDefined type="USE_CASE_RECORD" fgPriority="600" bgPriority="500"/>
+ <useCasePreDefined type="USE_CASE_LIVE" fgPriority="490" bgPriority="400"/>
+ <useCasePreDefined type="USE_CASE_PLAYBACK" fgPriority="480" bgPriority="300"/>
+ <useCasePreDefined type="USE_CASE_SCAN" fgPriority="450" bgPriority="200"/>
+ <useCasePreDefined type="USE_CASE_BACKGROUND" fgPriority="180" bgPriority="100"/>
+ <!-- useCaseVendor section:
+ A list of vendor defined use cases and their foreground/background priority hint.
+ Each use case has the following attributes:
+ "type": type of the use case. Vendor defined use cases start with "VENDOR_USE_CASE_".
+ "fgPriority": priority when the use case is in foreground. Value range [0-1000].
+ "bgPriority": priority when the use case is in background. Value range [0-1000].
+ "id": Vendor defined use case must have an id greater than 1000 to be associated with.
+ -->
+ <useCaseVendor type="VENDOR_USE_CASE_SPECIAL_1" id="1001" fgPriority="300" bgPriority="80"/>
+ <useCaseVendor type="VENDOR_USE_CASE_SPECIAL_2" id="1002" fgPriority="200" bgPriority="40"/>
+</config>
diff --git a/tv/tuner/1.0/default/Frontend.cpp b/tv/tuner/1.0/default/Frontend.cpp
index 1e07edd..dd2f8a6 100644
--- a/tv/tuner/1.0/default/Frontend.cpp
+++ b/tv/tuner/1.0/default/Frontend.cpp
@@ -90,11 +90,66 @@
return Result::SUCCESS;
}
-Return<void> Frontend::getStatus(const hidl_vec<FrontendStatusType>& /* statusTypes */,
+Return<void> Frontend::getStatus(const hidl_vec<FrontendStatusType>& statusTypes,
getStatus_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
vector<FrontendStatus> statuses;
+ for (int i = 0; i < statusTypes.size(); i++) {
+ FrontendStatusType type = statusTypes[i];
+ FrontendStatus status;
+ // assign randomly selected values for testing.
+ switch (type) {
+ case FrontendStatusType::DEMOD_LOCK: {
+ status.isDemodLocked(true);
+ break;
+ }
+ case FrontendStatusType::SNR: {
+ status.snr(221);
+ break;
+ }
+ case FrontendStatusType::FEC: {
+ status.innerFec(FrontendInnerFec::FEC_2_9); // value = 1 << 7
+ break;
+ }
+ case FrontendStatusType::MODULATION: {
+ FrontendModulationStatus modulationStatus;
+ modulationStatus.isdbt(FrontendIsdbtModulation::MOD_16QAM); // value = 1 << 3
+ status.modulation(modulationStatus);
+ break;
+ }
+ case FrontendStatusType::PLP_ID: {
+ status.plpId(101); // type uint8_t
+ break;
+ }
+ case FrontendStatusType::LAYER_ERROR: {
+ vector<bool> v = {false, true, true};
+ status.isLayerError(v);
+ break;
+ }
+ case FrontendStatusType::ATSC3_PLP_INFO: {
+ vector<FrontendStatusAtsc3PlpInfo> v;
+ FrontendStatusAtsc3PlpInfo info1{
+ .plpId = 3,
+ .isLocked = false,
+ .uec = 313,
+ };
+ FrontendStatusAtsc3PlpInfo info2{
+ .plpId = 5,
+ .isLocked = true,
+ .uec = 515,
+ };
+ v.push_back(info1);
+ v.push_back(info2);
+ status.plpInfo(v);
+ break;
+ }
+ default: {
+ continue;
+ }
+ }
+ statuses.push_back(status);
+ }
_hidl_cb(Result::SUCCESS, statuses);
return Void();
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index c6017f0..4fd3355 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -109,7 +109,37 @@
Return<void> Tuner::getFrontendInfo(FrontendId /* frontendId */, getFrontendInfo_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
- FrontendInfo info;
+ vector<FrontendStatusType> statusCaps = {
+ FrontendStatusType::DEMOD_LOCK,
+ FrontendStatusType::SNR,
+ FrontendStatusType::FEC,
+ FrontendStatusType::MODULATION,
+ FrontendStatusType::PLP_ID,
+ FrontendStatusType::LAYER_ERROR,
+ FrontendStatusType::ATSC3_PLP_INFO,
+ };
+ FrontendInfo::FrontendCapabilities frontendCaps;
+ FrontendIsdbtCapabilities isdbtCaps{
+ .modeCap = FrontendIsdbtMode::MODE_1 | FrontendIsdbtMode::MODE_2,
+ .bandwidthCap = (unsigned int)FrontendIsdbtBandwidth::BANDWIDTH_6MHZ,
+ .modulationCap = (unsigned int)FrontendIsdbtModulation::MOD_16QAM,
+ // ISDBT shares coderate and guard interval with DVBT
+ .coderateCap = FrontendDvbtCoderate::CODERATE_4_5 | FrontendDvbtCoderate::CODERATE_6_7,
+ .guardIntervalCap = (unsigned int)FrontendDvbtGuardInterval::INTERVAL_1_128,
+ };
+ frontendCaps.isdbtCaps(isdbtCaps);
+ // assign randomly selected values for testing.
+ FrontendInfo info{
+ .type = FrontendType::ISDBT,
+ .minFrequency = 139,
+ .maxFrequency = 1139,
+ .minSymbolRate = 45,
+ .maxSymbolRate = 1145,
+ .acquireRange = 30,
+ .exclusiveGroupId = 57,
+ .statusCaps = statusCaps,
+ .frontendCaps = frontendCaps,
+ };
_hidl_cb(Result::SUCCESS, info);
return Void();
diff --git a/tv/tuner/1.0/types.hal b/tv/tuner/1.0/types.hal
index 2eaab36..dd2b48f 100644
--- a/tv/tuner/1.0/types.hal
+++ b/tv/tuner/1.0/types.hal
@@ -1425,18 +1425,6 @@
*/
LAYER_ERROR,
/**
- * CN value by VBER.
- */
- VBER_CN,
- /**
- * CN value by LBER.
- */
- LBER_CN,
- /**
- * CN value by XER.
- */
- XER_CN,
- /**
* Moduration Error Ratio.
*/
MER,
@@ -1559,21 +1547,6 @@
vec<bool> isLayerError;
/**
- * CN value by VBER measured by 0.001 dB
- */
- int32_t vberCn;
-
- /**
- * CN value by LBER measured by 0.001 dB
- */
- int32_t lberCn;
-
- /**
- * CN value by XER measured by 0.001 dB
- */
- int32_t xerCn;
-
- /**
* MER value measured by 0.001 dB
*/
int32_t mer;
@@ -1951,7 +1924,7 @@
};
@export
-enum Constant : uint16_t {
+enum Constant : uint32_t {
/**
* An invalid packet ID in transport stream according to ISO/IEC 13818-1.
*/
@@ -1960,6 +1933,14 @@
* An invalid Stream ID.
*/
INVALID_STREAM_ID = 0xFFFF,
+ /**
+ * An invalid Filter ID.
+ */
+ INVALID_FILTER_ID = 0xFFFFFFFF,
+ /**
+ * An invalid AV sync hardware ID.
+ */
+ INVALID_AV_SYNC_ID = 0xFFFFFFFF,
};
/**
@@ -2178,19 +2159,37 @@
*/
struct DemuxIpAddress {
safe_union SrcIpAddress {
+ /**
+ * 0.0.0.0 is invalid. should be ignored.
+ */
uint8_t[4] v4;
+ /**
+ * 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 is invalid. should be ignored.
+ */
uint8_t[16] v6;
} srcIpAddress;
safe_union DstIpAddress {
+ /**
+ * 0.0.0.0 is invalid. should be ignored.
+ */
uint8_t[4] v4;
+ /**
+ * 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 is invalid. should be ignored.
+ */
uint8_t[16] v6;
} dstIpAddress;
+ /**
+ * 0 is invalid. should be ignored.
+ */
uint16_t srcPort;
+ /**
+ * 0 is invalid. should be ignored.
+ */
uint16_t dstPort;
};
diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp
index 0d7131a..9236b95 100644
--- a/vibrator/aidl/default/Vibrator.cpp
+++ b/vibrator/aidl/default/Vibrator.cpp
@@ -139,6 +139,9 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
+ std::vector<CompositePrimitive> supported;
+ getSupportedPrimitives(&supported);
+
for (auto& e : composite) {
if (e.delayMs > kComposeDelayMaxMs) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
@@ -146,8 +149,7 @@
if (e.scale <= 0.0f || e.scale > 1.0f) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- if (e.primitive < CompositePrimitive::NOOP ||
- e.primitive > CompositePrimitive::LIGHT_TICK) {
+ if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
}
diff --git a/wifi/1.2/IWifiStaIface.hal b/wifi/1.2/IWifiStaIface.hal
index 3a7f777..d65b33b 100644
--- a/wifi/1.2/IWifiStaIface.hal
+++ b/wifi/1.2/IWifiStaIface.hal
@@ -23,7 +23,7 @@
/**
* Interface used to represent a single STA iface.
*
- * IWifiChip.createStaIface() may return a @1.2::IWifiStaIface when supported.
+ * IWifiChip.createStaIface() must return a @1.2::IWifiStaIface when supported.
*/
interface IWifiStaIface extends @1.0::IWifiStaIface {
/**
diff --git a/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp b/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp
index 6e55664..96656f3 100644
--- a/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp
+++ b/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp
@@ -439,7 +439,7 @@
// synchronization objects
std::mutex mtx_;
std::condition_variable cv_;
- int count_;
+ int count_ = 0;
protected:
android::sp<::android::hardware::wifi::V1_2::IWifiNanIface> iwifiNanIface;
diff --git a/wifi/1.3/IWifiStaIface.hal b/wifi/1.3/IWifiStaIface.hal
index 81c0c38..3a75509 100644
--- a/wifi/1.3/IWifiStaIface.hal
+++ b/wifi/1.3/IWifiStaIface.hal
@@ -23,7 +23,7 @@
/**
* Interface used to represent a single STA iface.
*
- * IWifiChip.createStaIface() may return a @1.3::IWifiStaIface when supported.
+ * IWifiChip.createStaIface() must return a @1.3::IWifiStaIface when supported.
*/
interface IWifiStaIface extends @1.2::IWifiStaIface {
/**
diff --git a/wifi/1.4/default/hidl_struct_util.cpp b/wifi/1.4/default/hidl_struct_util.cpp
index 4996e35..fd1d5b1 100644
--- a/wifi/1.4/default/hidl_struct_util.cpp
+++ b/wifi/1.4/default/hidl_struct_util.cpp
@@ -2715,6 +2715,21 @@
}
return true;
}
+
+legacy_hal::wifi_interface_type convertHidlIfaceTypeToLegacy(
+ IfaceType hidl_interface_type) {
+ switch (hidl_interface_type) {
+ case IfaceType::STA:
+ return legacy_hal::WIFI_INTERFACE_TYPE_STA;
+ case IfaceType::AP:
+ return legacy_hal::WIFI_INTERFACE_TYPE_AP;
+ case IfaceType::P2P:
+ return legacy_hal::WIFI_INTERFACE_TYPE_P2P;
+ case IfaceType::NAN:
+ return legacy_hal::WIFI_INTERFACE_TYPE_NAN;
+ }
+ CHECK(false);
+}
} // namespace hidl_struct_util
} // namespace implementation
} // namespace V1_4
diff --git a/wifi/1.4/default/hidl_struct_util.h b/wifi/1.4/default/hidl_struct_util.h
index d040c1f..929f877 100644
--- a/wifi/1.4/default/hidl_struct_util.h
+++ b/wifi/1.4/default/hidl_struct_util.h
@@ -65,6 +65,8 @@
bool convertLegacyWifiMacInfosToHidl(
const std::vector<legacy_hal::WifiMacInfo>& legacy_mac_infos,
std::vector<IWifiChipEventCallback::RadioModeInfo>* hidl_radio_mode_infos);
+legacy_hal::wifi_interface_type convertHidlIfaceTypeToLegacy(
+ IfaceType hidl_interface_type);
// STA iface conversion methods.
bool convertLegacyFeaturesToHidlStaCapabilities(
diff --git a/wifi/1.4/default/wifi.cpp b/wifi/1.4/default/wifi.cpp
index 4f48d7e..9c6b0f0 100644
--- a/wifi/1.4/default/wifi.cpp
+++ b/wifi/1.4/default/wifi.cpp
@@ -124,6 +124,8 @@
}
}
LOG(ERROR) << "Wifi HAL start failed";
+ // Clear the event callback objects since the HAL start failed.
+ event_cb_handler_.invalidate();
}
return wifi_status;
}
@@ -158,6 +160,8 @@
}
LOG(ERROR) << "Wifi HAL stop failed";
}
+ // Clear the event callback objects since the HAL is now stopped.
+ event_cb_handler_.invalidate();
return wifi_status;
}
diff --git a/wifi/1.4/default/wifi_chip.cpp b/wifi/1.4/default/wifi_chip.cpp
index 3498510..4c9fad1 100644
--- a/wifi/1.4/default/wifi_chip.cpp
+++ b/wifi/1.4/default/wifi_chip.cpp
@@ -797,6 +797,15 @@
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));
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to add interface: " << ifname << " "
+ << legacyErrorToString(legacy_status);
+ return {createWifiStatusFromLegacyError(legacy_status), {}};
+ }
sp<WifiApIface> iface = new WifiApIface(ifname, legacy_hal_, iface_util_);
ap_ifaces_.push_back(iface);
for (const auto& callback : event_cb_handler_.getCallbacks()) {
@@ -835,6 +844,12 @@
// 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);
+ }
invalidateAndClear(ap_ifaces_, iface);
for (const auto& callback : event_cb_handler_.getCallbacks()) {
if (!callback->onIfaceRemoved(IfaceType::AP, ifname).isOk()) {
@@ -944,6 +959,15 @@
return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
}
std::string ifname = allocateStaIfaceName();
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->createVirtualInterface(
+ ifname,
+ hidl_struct_util::convertHidlIfaceTypeToLegacy(IfaceType::STA));
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to add interface: " << ifname << " "
+ << legacyErrorToString(legacy_status);
+ return {createWifiStatusFromLegacyError(legacy_status), {}};
+ }
sp<WifiStaIface> iface = new WifiStaIface(ifname, legacy_hal_, iface_util_);
sta_ifaces_.push_back(iface);
for (const auto& callback : event_cb_handler_.getCallbacks()) {
@@ -979,6 +1003,12 @@
}
// Invalidate & remove any dependent objects first.
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);
+ }
invalidateAndClear(sta_ifaces_, iface);
for (const auto& callback : event_cb_handler_.getCallbacks()) {
if (!callback->onIfaceRemoved(IfaceType::STA, ifname).isOk()) {
diff --git a/wifi/1.4/default/wifi_legacy_hal.cpp b/wifi/1.4/default/wifi_legacy_hal.cpp
index 3ca3226..a040c89 100644
--- a/wifi/1.4/default/wifi_legacy_hal.cpp
+++ b/wifi/1.4/default/wifi_legacy_hal.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include <cutils/properties.h>
+#include <net/if.h>
#include "hidl_sync_util.h"
#include "wifi_legacy_hal.h"
@@ -1355,6 +1356,7 @@
LOG(ERROR) << "Failed to enumerate interface handles";
return status;
}
+ iface_name_to_handle_.clear();
for (int i = 0; i < num_iface_handles; ++i) {
std::array<char, IFNAMSIZ> iface_name_arr = {};
status = global_func_table_.wifi_get_iface_name(
@@ -1421,6 +1423,39 @@
return {status, std::move(cached_scan_results)};
}
+wifi_error WifiLegacyHal::createVirtualInterface(const std::string& ifname,
+ wifi_interface_type iftype) {
+ // Create the interface if it doesn't exist. If interface already exist,
+ // Vendor Hal should return WIFI_SUCCESS.
+ wifi_error status = global_func_table_.wifi_virtual_interface_create(
+ global_handle_, ifname.c_str(), iftype);
+ return handleVirtualInterfaceCreateOrDeleteStatus(ifname, status);
+}
+
+wifi_error WifiLegacyHal::deleteVirtualInterface(const std::string& ifname) {
+ // Delete the interface if it was created dynamically.
+ wifi_error status = global_func_table_.wifi_virtual_interface_delete(
+ global_handle_, ifname.c_str());
+ return handleVirtualInterfaceCreateOrDeleteStatus(ifname, status);
+}
+
+wifi_error WifiLegacyHal::handleVirtualInterfaceCreateOrDeleteStatus(
+ const std::string& ifname, wifi_error status) {
+ if (status == WIFI_SUCCESS) {
+ // refresh list of handlers now.
+ status = retrieveIfaceHandles();
+ } else if (status == WIFI_ERROR_NOT_SUPPORTED) {
+ // Vendor hal does not implement this API. Such vendor implementations
+ // are expected to create / delete interface by other means.
+
+ // check if interface exists.
+ if (if_nametoindex(ifname.c_str())) {
+ status = retrieveIfaceHandles();
+ }
+ }
+ return status;
+}
+
void WifiLegacyHal::invalidate() {
global_handle_ = nullptr;
iface_name_to_handle_.clear();
diff --git a/wifi/1.4/default/wifi_legacy_hal.h b/wifi/1.4/default/wifi_legacy_hal.h
index a7b40a0..72cf197 100644
--- a/wifi/1.4/default/wifi_legacy_hal.h
+++ b/wifi/1.4/default/wifi_legacy_hal.h
@@ -369,6 +369,11 @@
wifi_error setCountryCode(const std::string& iface_name,
std::array<int8_t, 2> code);
+ // interface functions.
+ wifi_error createVirtualInterface(const std::string& ifname,
+ wifi_interface_type iftype);
+ wifi_error deleteVirtualInterface(const std::string& ifname);
+
private:
// Retrieve interface handles for all the available interfaces.
wifi_error retrieveIfaceHandles();
@@ -380,6 +385,9 @@
std::pair<wifi_error, std::vector<wifi_cached_scan_results>>
getGscanCachedResults(const std::string& iface_name);
void invalidate();
+ // Handles wifi (error) status of Virtual interface create/delete
+ wifi_error handleVirtualInterfaceCreateOrDeleteStatus(
+ const std::string& ifname, wifi_error status);
// Global function table of legacy HAL.
wifi_hal_fn global_func_table_;
diff --git a/wifi/1.4/default/wifi_legacy_hal_stubs.cpp b/wifi/1.4/default/wifi_legacy_hal_stubs.cpp
index bbe470e..6945b4c 100644
--- a/wifi/1.4/default/wifi_legacy_hal_stubs.cpp
+++ b/wifi/1.4/default/wifi_legacy_hal_stubs.cpp
@@ -139,6 +139,8 @@
populateStubFor(&hal_fn->wifi_set_radio_mode_change_handler);
populateStubFor(&hal_fn->wifi_set_latency_mode);
populateStubFor(&hal_fn->wifi_set_thermal_mitigation_mode);
+ populateStubFor(&hal_fn->wifi_virtual_interface_create);
+ populateStubFor(&hal_fn->wifi_virtual_interface_delete);
return true;
}
} // namespace legacy_hal
diff --git a/wifi/1.4/vts/functional/Android.bp b/wifi/1.4/vts/functional/Android.bp
index 46ac3ee..d857be1 100644
--- a/wifi/1.4/vts/functional/Android.bp
+++ b/wifi/1.4/vts/functional/Android.bp
@@ -22,7 +22,8 @@
"VtsHalWifiV1_4TargetTest.cpp",
"wifi_ap_iface_hidl_test.cpp",
"wifi_chip_hidl_test.cpp",
- "wifi_nan_iface_hidl_test.cpp"
+ "wifi_nan_iface_hidl_test.cpp",
+ "wifi_rtt_controller_hidl_test.cpp",
],
static_libs: [
"VtsHalWifiV1_0TargetTestUtil",
@@ -31,7 +32,10 @@
"android.hardware.wifi@1.2",
"android.hardware.wifi@1.3",
"android.hardware.wifi@1.4",
- "libwifi-system-iface"
+ "libwifi-system-iface",
],
- test_suites: ["general-tests", "vts-core"],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
}
diff --git a/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp
index 7896067..1c39550 100644
--- a/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp
+++ b/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp
@@ -19,6 +19,7 @@
#undef NAN // NAN is defined in bionic/libc/include/math.h:38
+#include <android/hardware/wifi/1.3/IWifiStaIface.h>
#include <android/hardware/wifi/1.4/IWifi.h>
#include <android/hardware/wifi/1.4/IWifiChip.h>
#include <android/hardware/wifi/1.4/IWifiChipEventCallback.h>
@@ -34,10 +35,12 @@
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::WifiDebugRingBufferStatus;
using ::android::hardware::wifi::V1_0::WifiStatus;
using ::android::hardware::wifi::V1_0::WifiStatusCode;
+using ::android::hardware::wifi::V1_3::IWifiStaIface;
using ::android::hardware::wifi::V1_4::IWifiChip;
using ::android::hardware::wifi::V1_4::IWifiChipEventCallback;
@@ -110,6 +113,16 @@
};
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;
+ }
+
sp<IWifiChip> wifi_chip_;
private:
@@ -136,3 +149,31 @@
return;
}
}
+
+/*
+ * createRttController_1_4
+ * Ensures that an instance of the IWifiRttController proxy object is
+ * successfully created.
+ */
+TEST_P(WifiChipHidlTest, createRttController_1_4) {
+ configureChipForIfaceType(IfaceType::STA, true);
+
+ const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createStaIface);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface.first.code);
+ sp<IWifiStaIface> iface = IWifiStaIface::castFrom(status_and_iface.second);
+ EXPECT_NE(nullptr, iface.get());
+
+ const auto& status_and_controller =
+ HIDL_INVOKE(wifi_chip_, createRttController_1_4, iface);
+ if (status_and_controller.first.code !=
+ WifiStatusCode::ERROR_NOT_SUPPORTED) {
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_controller.first.code);
+ EXPECT_NE(nullptr, status_and_controller.second.get());
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, WifiChipHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+ ::android::hardware::wifi::V1_4::IWifi::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/wifi/1.4/vts/functional/wifi_nan_iface_hidl_test.cpp b/wifi/1.4/vts/functional/wifi_nan_iface_hidl_test.cpp
index 688faf1..24daee6 100644
--- a/wifi/1.4/vts/functional/wifi_nan_iface_hidl_test.cpp
+++ b/wifi/1.4/vts/functional/wifi_nan_iface_hidl_test.cpp
@@ -440,7 +440,7 @@
// synchronization objects
std::mutex mtx_;
std::condition_variable cv_;
- int count_;
+ int count_ = 0;
protected:
android::sp<::android::hardware::wifi::V1_4::IWifiNanIface> iwifiNanIface;
@@ -545,3 +545,9 @@
nanConfigRequest, nanConfigRequestSupp)
.code);
}
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, WifiNanIfaceHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+ ::android::hardware::wifi::V1_4::IWifi::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/wifi/1.4/vts/functional/wifi_rtt_controller_hidl_test.cpp b/wifi/1.4/vts/functional/wifi_rtt_controller_hidl_test.cpp
new file mode 100644
index 0000000..726470c
--- /dev/null
+++ b/wifi/1.4/vts/functional/wifi_rtt_controller_hidl_test.cpp
@@ -0,0 +1,224 @@
+/*
+ * 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.3/IWifiStaIface.h>
+#include <android/hardware/wifi/1.4/IWifi.h>
+#include <android/hardware/wifi/1.4/IWifiChip.h>
+#include <android/hardware/wifi/1.4/IWifiRttController.h>
+#include <android/hardware/wifi/1.4/IWifiRttControllerEventCallback.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_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::wifi::V1_0::CommandId;
+using ::android::hardware::wifi::V1_0::RttBw;
+using ::android::hardware::wifi::V1_0::RttPeerType;
+using ::android::hardware::wifi::V1_0::RttType;
+using ::android::hardware::wifi::V1_0::WifiChannelInfo;
+using ::android::hardware::wifi::V1_0::WifiChannelWidthInMhz;
+using ::android::hardware::wifi::V1_0::WifiStatus;
+using ::android::hardware::wifi::V1_0::WifiStatusCode;
+using ::android::hardware::wifi::V1_3::IWifiStaIface;
+using ::android::hardware::wifi::V1_4::IWifiChip;
+using ::android::hardware::wifi::V1_4::IWifiRttController;
+using ::android::hardware::wifi::V1_4::IWifiRttControllerEventCallback;
+using ::android::hardware::wifi::V1_4::RttCapabilities;
+using ::android::hardware::wifi::V1_4::RttConfig;
+using ::android::hardware::wifi::V1_4::RttPreamble;
+using ::android::hardware::wifi::V1_4::RttResponder;
+using ::android::hardware::wifi::V1_4::RttResult;
+
+/**
+ * Fixture to use for all RTT controller HIDL interface tests.
+ */
+class WifiRttControllerHidlTest : public ::testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ // Make sure to start with a clean state
+ stopWifi(GetInstanceName());
+
+ wifi_rtt_controller_ = getWifiRttController();
+ ASSERT_NE(nullptr, wifi_rtt_controller_.get());
+ }
+
+ virtual void TearDown() override { stopWifi(GetInstanceName()); }
+
+ // A simple test implementation of WifiChipEventCallback.
+ class WifiRttControllerEventCallback
+ : public ::testing::VtsHalHidlTargetCallbackBase<
+ WifiRttControllerHidlTest>,
+ public IWifiRttControllerEventCallback {
+ public:
+ WifiRttControllerEventCallback(){};
+
+ virtual ~WifiRttControllerEventCallback() = default;
+
+ Return<void> onResults(
+ CommandId cmdId __unused,
+ const hidl_vec<::android::hardware::wifi::V1_0::RttResult>& results
+ __unused) {
+ return Void();
+ };
+
+ Return<void> onResults_1_4(CommandId cmdId __unused,
+ const hidl_vec<RttResult>& results
+ __unused) {
+ return Void();
+ };
+ };
+
+ protected:
+ sp<IWifiRttController> wifi_rtt_controller_;
+
+ private:
+ std::string GetInstanceName() { return GetParam(); }
+
+ sp<IWifiRttController> getWifiRttController() {
+ const std::string& instance_name = GetInstanceName();
+
+ sp<IWifiChip> wifi_chip =
+ IWifiChip::castFrom(getWifiChip(instance_name));
+ EXPECT_NE(nullptr, wifi_chip.get());
+
+ sp<IWifiStaIface> wifi_sta_iface =
+ IWifiStaIface::castFrom(getWifiStaIface(instance_name));
+ EXPECT_NE(nullptr, wifi_sta_iface.get());
+
+ const auto& status_and_controller =
+ HIDL_INVOKE(wifi_chip, createRttController_1_4, wifi_sta_iface);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_controller.first.code);
+ EXPECT_NE(nullptr, status_and_controller.second.get());
+
+ return status_and_controller.second.get();
+ }
+};
+
+/*
+ * registerEventCallback_1_4
+ * This test case tests the registerEventCallback_1_4() API which registers
+ * a call back function with the hal implementation
+ *
+ * Note: it is not feasible to test the invocation of the call back function
+ * since event is triggered internally in the HAL implementation, and can not be
+ * triggered from the test case
+ */
+TEST_P(WifiRttControllerHidlTest, RegisterEventCallback_1_4) {
+ sp<WifiRttControllerEventCallback> wifiRttControllerEventCallback =
+ new WifiRttControllerEventCallback();
+ const auto& status =
+ HIDL_INVOKE(wifi_rtt_controller_, registerEventCallback_1_4,
+ wifiRttControllerEventCallback);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status.code);
+}
+
+/*
+ * rangeRequest_1_4
+ */
+TEST_P(WifiRttControllerHidlTest, RangeRequest_1_4) {
+ std::vector<RttConfig> configs;
+ RttConfig config;
+ int cmdId = 55;
+ // Set the config with test data
+ for (int i = 0; i < 6; i++) {
+ config.addr[i] = i;
+ }
+ config.type = RttType::ONE_SIDED;
+ config.peer = RttPeerType::STA;
+ config.channel.width = WifiChannelWidthInMhz::WIDTH_80;
+ config.channel.centerFreq = 5765;
+ config.channel.centerFreq0 = 5775;
+ config.channel.centerFreq1 = 0;
+ config.bw = RttBw::BW_80MHZ;
+ config.preamble = RttPreamble::HE;
+ config.mustRequestLci = false;
+ config.mustRequestLcr = false;
+ config.burstPeriod = 0;
+ config.numBurst = 0;
+ config.numFramesPerBurst = 8;
+ config.numRetriesPerRttFrame = 3;
+ config.numRetriesPerFtmr = 3;
+ config.burstDuration = 9;
+ // Insert config in the vector
+ configs.push_back(config);
+
+ // Invoke the call
+ const auto& status =
+ HIDL_INVOKE(wifi_rtt_controller_, rangeRequest_1_4, cmdId, configs);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status.code);
+}
+
+/*
+ * getCapabilities_1_4
+ */
+TEST_P(WifiRttControllerHidlTest, GetCapabilities_1_4) {
+ std::pair<WifiStatus, RttCapabilities> status_and_caps;
+
+ // Invoke the call
+ status_and_caps = HIDL_INVOKE(wifi_rtt_controller_, getCapabilities_1_4);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_caps.first.code);
+}
+
+/*
+ * getResponderInfo_1_4
+ */
+TEST_P(WifiRttControllerHidlTest, GetResponderInfo_1_4) {
+ std::pair<WifiStatus, RttResponder> status_and_info;
+
+ // Invoke the call
+ status_and_info = HIDL_INVOKE(wifi_rtt_controller_, getResponderInfo_1_4);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_info.first.code);
+}
+
+/*
+ * enableResponder_1_4
+ */
+TEST_P(WifiRttControllerHidlTest, EnableResponder_1_4) {
+ std::pair<WifiStatus, RttResponder> status_and_info;
+ int cmdId = 55;
+ WifiChannelInfo channelInfo;
+ channelInfo.width = WifiChannelWidthInMhz::WIDTH_80;
+ channelInfo.centerFreq = 5690;
+ channelInfo.centerFreq0 = 5690;
+ channelInfo.centerFreq1 = 0;
+
+ // Get the responder first
+ status_and_info = HIDL_INVOKE(wifi_rtt_controller_, getResponderInfo_1_4);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_info.first.code);
+
+ // Invoke the call
+ const auto& status =
+ HIDL_INVOKE(wifi_rtt_controller_, enableResponder_1_4, cmdId,
+ channelInfo, 10, status_and_info.second);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status.code);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, WifiRttControllerHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+ ::android::hardware::wifi::V1_4::IWifi::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_call_util.h b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_call_util.h
index 2f71ccb..ec7ebee 100644
--- a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_call_util.h
+++ b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_call_util.h
@@ -25,8 +25,6 @@
#include <type_traits>
#include <utility>
-#include <VtsHalHidlTargetTestBase.h>
-
namespace {
namespace detail {
template <typename>
diff --git a/wifi/hostapd/1.2/types.hal b/wifi/hostapd/1.2/types.hal
index 54e6529..9c187fa 100644
--- a/wifi/hostapd/1.2/types.hal
+++ b/wifi/hostapd/1.2/types.hal
@@ -38,6 +38,9 @@
WLAN_REASON_DISASSOC_AP_BUSY = 5,
};
+/**
+ * Mac Address type. 6 octets representing physical address of a device.
+ */
typedef uint8_t[6] MacAddress;
/**
diff --git a/wifi/hostapd/1.2/vts/functional/hostapd_hidl_test.cpp b/wifi/hostapd/1.2/vts/functional/hostapd_hidl_test.cpp
index 9f57934..2715891 100644
--- a/wifi/hostapd/1.2/vts/functional/hostapd_hidl_test.cpp
+++ b/wifi/hostapd/1.2/vts/functional/hostapd_hidl_test.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <VtsCoreUtil.h>
+
#include <android-base/logging.h>
#include <cutils/properties.h>
@@ -62,11 +64,19 @@
hostapd_instance_name_);
hostapd_ = IHostapd::getService(hostapd_instance_name_);
ASSERT_NE(hostapd_.get(), nullptr);
+ isAcsSupport_ = testing::checkSubstringInCommandOutput(
+ "/system/bin/cmd wifi get-softap-supported-features",
+ "wifi_softap_acs_supported");
+ isWpa3SaeSupport_ = testing::checkSubstringInCommandOutput(
+ "/system/bin/cmd wifi get-softap-supported-features",
+ "wifi_softap_wpa3_sae_supported");
}
virtual void TearDown() override { stopHostapd(hostapd_instance_name_); }
protected:
+ bool isWpa3SaeSupport_ = false;
+ bool isAcsSupport_ = false;
std::string getPrimaryWlanIfaceName() {
std::array<char, PROPERTY_VALUE_MAX> buffer;
auto res = property_get("ro.vendor.wifi.sap.interface", buffer.data(),
@@ -208,10 +218,10 @@
* Access point creation should pass.
*/
TEST_P(HostapdHidlTest, AddPskAccessPointWithAcs) {
+ if (!isAcsSupport_) GTEST_SKIP() << "Missing ACS support";
auto status = HIDL_INVOKE(hostapd_, addAccessPoint_1_2,
getIfaceParamsWithAcs(), getPskNwParams());
- // TODO: b/140172237, fix this in R.
- // EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+ EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
}
/**
@@ -219,11 +229,11 @@
* Access point creation should pass.
*/
TEST_P(HostapdHidlTest, AddPskAccessPointWithAcsAndFreqRange) {
+ if (!isAcsSupport_) GTEST_SKIP() << "Missing ACS support";
auto status =
HIDL_INVOKE(hostapd_, addAccessPoint_1_2,
getIfaceParamsWithAcsAndFreqRange(), getPskNwParams());
- // TODO: b/140172237, fix this in R
- // EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+ EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
}
/**
@@ -231,11 +241,11 @@
* Access point creation should fail.
*/
TEST_P(HostapdHidlTest, AddPskAccessPointWithAcsAndInvalidFreqRange) {
+ if (!isAcsSupport_) GTEST_SKIP() << "Missing ACS support";
auto status = HIDL_INVOKE(hostapd_, addAccessPoint_1_2,
getIfaceParamsWithAcsAndInvalidFreqRange(),
getPskNwParams());
- // TODO: b/140172237, fix this in R
- // EXPECT_NE(HostapdStatusCode::SUCCESS, status.code);
+ EXPECT_NE(HostapdStatusCode::SUCCESS, status.code);
}
/**
@@ -243,10 +253,10 @@
* Access point creation should pass.
*/
TEST_P(HostapdHidlTest, AddOpenAccessPointWithAcs) {
+ if (!isAcsSupport_) GTEST_SKIP() << "Missing ACS support";
auto status = HIDL_INVOKE(hostapd_, addAccessPoint_1_2,
getIfaceParamsWithAcs(), getOpenNwParams());
- // TODO: b/140172237, fix this in R
- // EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+ EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
}
/**
@@ -274,6 +284,7 @@
* Access point creation should pass.
*/
TEST_P(HostapdHidlTest, AddSaeTransitionAccessPointWithoutAcs) {
+ if (!isWpa3SaeSupport_) GTEST_SKIP() << "Missing SAE support";
auto status =
HIDL_INVOKE(hostapd_, addAccessPoint_1_2, getIfaceParamsWithoutAcs(),
getSaeTransitionNwParams());
@@ -285,6 +296,7 @@
* Access point creation should pass.
*/
TEST_P(HostapdHidlTest, AddSAEAccessPointWithoutAcs) {
+ if (!isWpa3SaeSupport_) GTEST_SKIP() << "Missing SAE support";
auto status = HIDL_INVOKE(hostapd_, addAccessPoint_1_2,
getIfaceParamsWithoutAcs(), getSaeNwParams());
EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
@@ -295,16 +307,15 @@
* Access point creation & removal should pass.
*/
TEST_P(HostapdHidlTest, RemoveAccessPointWithAcs) {
+ if (!isAcsSupport_) GTEST_SKIP() << "Missing ACS support";
auto status_1_2 = HIDL_INVOKE(hostapd_, addAccessPoint_1_2,
getIfaceParamsWithAcs(), getPskNwParams());
- // TODO: b/140172237, fix this in R
- /*
EXPECT_EQ(HostapdStatusCode::SUCCESS, status_1_2.code);
auto status =
HIDL_INVOKE(hostapd_, removeAccessPoint, getPrimaryWlanIfaceName());
- EXPECT_EQ(android::hardware::wifi::hostapd::V1_0::HostapdStatusCode::SUCCESS,
- status.code);
- */
+ EXPECT_EQ(
+ android::hardware::wifi::hostapd::V1_0::HostapdStatusCode::SUCCESS,
+ status.code);
}
/**
@@ -349,6 +360,7 @@
* Access point creation should fail.
*/
TEST_P(HostapdHidlTest, AddInvalidSaeTransitionAccessPointWithoutAcs) {
+ if (!isWpa3SaeSupport_) GTEST_SKIP() << "Missing SAE support";
auto status =
HIDL_INVOKE(hostapd_, addAccessPoint_1_2, getIfaceParamsWithoutAcs(),
getInvalidSaeTransitionNwParams());
@@ -360,6 +372,7 @@
* Access point creation should fail.
*/
TEST_P(HostapdHidlTest, AddInvalidSaeAccessPointWithoutAcs) {
+ if (!isWpa3SaeSupport_) GTEST_SKIP() << "Missing SAE support";
auto status =
HIDL_INVOKE(hostapd_, addAccessPoint_1_2, getIfaceParamsWithoutAcs(),
getInvalidSaeNwParams());
diff --git a/wifi/supplicant/1.0/vts/functional/Android.bp b/wifi/supplicant/1.0/vts/functional/Android.bp
index 332ee4a..8013906 100644
--- a/wifi/supplicant/1.0/vts/functional/Android.bp
+++ b/wifi/supplicant/1.0/vts/functional/Android.bp
@@ -46,6 +46,8 @@
"VtsHalWifiSupplicantV1_0TargetTestUtil",
"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@1.0",
"libgmock",
"libwifi-system",
diff --git a/wifi/supplicant/1.0/vts/functional/supplicant_sta_network_hidl_test.cpp b/wifi/supplicant/1.0/vts/functional/supplicant_sta_network_hidl_test.cpp
index 52f77a1..5467e02 100644
--- a/wifi/supplicant/1.0/vts/functional/supplicant_sta_network_hidl_test.cpp
+++ b/wifi/supplicant/1.0/vts/functional/supplicant_sta_network_hidl_test.cpp
@@ -19,6 +19,7 @@
#include <VtsCoreUtil.h>
#include <android/hardware/wifi/1.0/IWifi.h>
#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaNetwork.h>
+#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaNetwork.h>
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
@@ -93,6 +94,11 @@
EXPECT_TRUE(turnOnExcessiveLogging(supplicant_));
sta_network_ = createSupplicantStaNetwork(supplicant_);
ASSERT_NE(sta_network_.get(), nullptr);
+ /* variable used to check if the underlying HAL version is 1.3 or
+ * higher. This is to skip tests which are using deprecated methods.
+ */
+ v1_3 = ::android::hardware::wifi::supplicant::V1_3::
+ ISupplicantStaNetwork::castFrom(sta_network_);
ssid_.assign(kTestSsidStr, kTestSsidStr + strlen(kTestSsidStr));
}
@@ -114,6 +120,8 @@
});
}
+ sp<::android::hardware::wifi::supplicant::V1_3::ISupplicantStaNetwork>
+ v1_3 = nullptr;
bool isP2pOn_ = false;
sp<ISupplicant> supplicant_;
// ISupplicantStaNetwork object used for all tests in this fixture.
@@ -221,6 +229,9 @@
* SetGetKeyMgmt
*/
TEST_P(SupplicantStaNetworkHidlTest, SetGetKeyMgmt) {
+ if (v1_3 != nullptr) {
+ GTEST_SKIP() << "Skipping test since HAL is 1.3 or higher";
+ }
sta_network_->setKeyMgmt(kTestKeyMgmt, [](const SupplicantStatus& status) {
EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
});
@@ -235,6 +246,9 @@
* SetGetProto
*/
TEST_P(SupplicantStaNetworkHidlTest, SetGetProto) {
+ if (v1_3 != nullptr) {
+ GTEST_SKIP() << "Skipping test since HAL is 1.3 or higher";
+ }
sta_network_->setProto(kTestProto, [](const SupplicantStatus& status) {
EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
});
@@ -262,6 +276,9 @@
* SetGetGroupCipher
*/
TEST_P(SupplicantStaNetworkHidlTest, SetGetGroupCipher) {
+ if (v1_3 != nullptr) {
+ GTEST_SKIP() << "Skipping test since HAL is 1.3 or higher";
+ }
sta_network_->setGroupCipher(
kTestGroupCipher, [](const SupplicantStatus& status) {
EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
@@ -277,6 +294,9 @@
* SetGetPairwiseCipher
*/
TEST_P(SupplicantStaNetworkHidlTest, SetGetPairwiseCipher) {
+ if (v1_3 != nullptr) {
+ GTEST_SKIP() << "Skipping test since HAL is 1.3 or higher";
+ }
sta_network_->setPairwiseCipher(
kTestPairwiseCipher, [](const SupplicantStatus& status) {
EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
@@ -627,6 +647,9 @@
* Enable
*/
TEST_P(SupplicantStaNetworkHidlTest, Enable) {
+ if (v1_3 != nullptr) {
+ GTEST_SKIP() << "Skipping test since HAL is 1.3 or higher";
+ }
// wpa_supplicant doesn't perform any connection initiation
// unless atleast the Ssid and Ket mgmt params are set.
sta_network_->setSsid(ssid_, [](const SupplicantStatus& status) {
@@ -654,6 +677,9 @@
* Disable
*/
TEST_P(SupplicantStaNetworkHidlTest, Disable) {
+ if (v1_3 != nullptr) {
+ GTEST_SKIP() << "Skipping test since HAL is 1.3 or higher";
+ }
// wpa_supplicant doesn't perform any connection initiation
// unless atleast the Ssid and Ket mgmt params are set.
sta_network_->setSsid(ssid_, [](const SupplicantStatus& status) {
@@ -677,6 +703,9 @@
* Select.
*/
TEST_P(SupplicantStaNetworkHidlTest, Select) {
+ if (v1_3 != nullptr) {
+ GTEST_SKIP() << "Skipping test since HAL is 1.3 or higher";
+ }
// wpa_supplicant doesn't perform any connection initiation
// unless atleast the Ssid and Ket mgmt params are set.
sta_network_->setSsid(ssid_, [](const SupplicantStatus& status) {
@@ -788,6 +817,9 @@
* GetWpsNfcConfigurationToken
*/
TEST_P(SupplicantStaNetworkHidlTest, GetWpsNfcConfigurationToken) {
+ if (v1_3 != nullptr) {
+ GTEST_SKIP() << "Skipping test since HAL is 1.3 or higher";
+ }
ASSERT_EQ(SupplicantStatusCode::SUCCESS,
HIDL_INVOKE(sta_network_, setSsid, ssid_).code);
ASSERT_EQ(SupplicantStatusCode::SUCCESS,
@@ -808,4 +840,4 @@
android::hardware::getAllHalInstanceNames(IWifi::descriptor)),
testing::ValuesIn(android::hardware::getAllHalInstanceNames(
ISupplicant::descriptor))),
- android::hardware::PrintInstanceTupleNameToString<>);
\ No newline at end of file
+ android::hardware::PrintInstanceTupleNameToString<>);
diff --git a/wifi/supplicant/1.2/vts/functional/Android.bp b/wifi/supplicant/1.2/vts/functional/Android.bp
index 9781074..22dec84 100644
--- a/wifi/supplicant/1.2/vts/functional/Android.bp
+++ b/wifi/supplicant/1.2/vts/functional/Android.bp
@@ -68,7 +68,7 @@
name: "VtsHalWifiSupplicantP2pV1_2TargetTest",
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
- "VtsHalWifiSupplicantV1_2TargetTest.cpp",
+ "VtsHalWifiSupplicantP2pV1_2TargetTest.cpp",
"supplicant_p2p_iface_hidl_test.cpp",
],
static_libs: [
diff --git a/wifi/supplicant/1.2/vts/functional/VtsHalWifiSupplicantP2pV1_2TargetTest.cpp b/wifi/supplicant/1.2/vts/functional/VtsHalWifiSupplicantP2pV1_2TargetTest.cpp
new file mode 100644
index 0000000..22bf1db
--- /dev/null
+++ b/wifi/supplicant/1.2/vts/functional/VtsHalWifiSupplicantP2pV1_2TargetTest.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 <VtsCoreUtil.h>
+#include "supplicant_hidl_test_utils.h"
+
+// TODO(b/143892896): Remove this line after wifi_hidl_test_utils.cpp is
+// updated.
+WifiSupplicantHidlEnvironment* gEnv = nullptr;
+
+int main(int argc, char** argv) {
+ if (!::testing::deviceSupportsFeature("android.hardware.wifi.direct"))
+ return 0;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
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 8116c3f..f38dda4 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
@@ -111,9 +111,14 @@
// If DPP is not supported, we just pass the test.
sta_iface_->getKeyMgmtCapabilities(
[&](const SupplicantStatus& status, uint32_t keyMgmtMaskInternal) {
- EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ // Since getKeyMgmtCapabilities() is overridden by an
+ // upgraded API in newer HAL versions, allow for
+ // FAILURE_UNKNOWN and return DPP is not supported.
+ if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+ EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
- keyMgmtMask = keyMgmtMaskInternal;
+ keyMgmtMask = keyMgmtMaskInternal;
+ }
});
if (!(keyMgmtMask & ISupplicantStaNetwork::KeyMgmtMask::DPP)) {
@@ -268,8 +273,12 @@
* GetKeyMgmtCapabilities
*/
TEST_P(SupplicantStaIfaceHidlTest, GetKeyMgmtCapabilities) {
- sta_iface_->getKeyMgmtCapabilities(
- [&](const SupplicantStatus& status, uint32_t keyMgmtMask) {
+ sta_iface_->getKeyMgmtCapabilities([&](const SupplicantStatus& status,
+ uint32_t keyMgmtMask) {
+ // Since this API is overridden by an upgraded API in newer HAL
+ // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+ // longer supported on newer HAL.
+ if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
// Even though capabilities vary, these two are always set in HAL
@@ -277,7 +286,8 @@
EXPECT_TRUE(keyMgmtMask & ISupplicantStaNetwork::KeyMgmtMask::NONE);
EXPECT_TRUE(keyMgmtMask &
ISupplicantStaNetwork::KeyMgmtMask::IEEE8021X);
- });
+ }
+ });
}
/*
@@ -455,4 +465,4 @@
testing::ValuesIn(android::hardware::getAllHalInstanceNames(
android::hardware::wifi::supplicant::V1_2::ISupplicant::
descriptor))),
- android::hardware::PrintInstanceTupleNameToString<>);
\ No newline at end of file
+ android::hardware::PrintInstanceTupleNameToString<>);
diff --git a/wifi/supplicant/1.2/vts/functional/supplicant_sta_network_hidl_test.cpp b/wifi/supplicant/1.2/vts/functional/supplicant_sta_network_hidl_test.cpp
index 4c3d808..54ceb20 100644
--- a/wifi/supplicant/1.2/vts/functional/supplicant_sta_network_hidl_test.cpp
+++ b/wifi/supplicant/1.2/vts/functional/supplicant_sta_network_hidl_test.cpp
@@ -112,13 +112,23 @@
uint32_t keyMgmt = (uint32_t)ISupplicantStaNetwork::KeyMgmtMask::SAE;
sta_network_->setKeyMgmt_1_2(keyMgmt, [](const SupplicantStatus &status) {
- EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ // Since this API is overridden by an upgraded API in newer HAL
+ // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+ // longer supported on newer HALs.
+ if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+ EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ }
});
sta_network_->getKeyMgmt_1_2(
[&keyMgmt](const SupplicantStatus &status, uint32_t keyMgmtOut) {
- EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
- EXPECT_EQ(keyMgmtOut, keyMgmt);
+ // Since this API is overridden by an upgraded API in newer HAL
+ // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+ // longer supported on newer HALs.
+ if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+ EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ EXPECT_EQ(keyMgmtOut, keyMgmt);
+ }
});
}
@@ -131,14 +141,24 @@
sta_network_->setGroupCipher_1_2(
groupCipher, [](const SupplicantStatus &status) {
- EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ // Since this API is overridden by an upgraded API in newer HAL
+ // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+ // longer supported on newer HALs.
+ if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+ EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ }
});
sta_network_->getGroupCipher_1_2(
[&groupCipher](const SupplicantStatus &status,
uint32_t groupCipherOut) {
- EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
- EXPECT_EQ(groupCipherOut, groupCipher);
+ // Since this API is overridden by an upgraded API in newer HAL
+ // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+ // longer supported on newer HALs.
+ if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+ EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ EXPECT_EQ(groupCipherOut, groupCipher);
+ }
});
}
@@ -151,14 +171,24 @@
sta_network_->setPairwiseCipher_1_2(
pairwiseCipher, [](const SupplicantStatus &status) {
- EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ // Since this API is overridden by an upgraded API in newer HAL
+ // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+ // longer supported on newer HALs.
+ if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+ EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ }
});
sta_network_->getPairwiseCipher_1_2(
[&pairwiseCipher](const SupplicantStatus &status,
uint32_t pairwiseCipherOut) {
- EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
- EXPECT_EQ(pairwiseCipherOut, pairwiseCipher);
+ // Since this API is overridden by an upgraded API in newer HAL
+ // versions, allow FAILURE_UNKNOWN to indicate that the test is no
+ // longer supported on newer HALs.
+ if (status.code != SupplicantStatusCode::FAILURE_UNKNOWN) {
+ EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ EXPECT_EQ(pairwiseCipherOut, pairwiseCipher);
+ }
});
}
diff --git a/wifi/supplicant/1.3/ISupplicantStaIface.hal b/wifi/supplicant/1.3/ISupplicantStaIface.hal
index b501a95..4506f37 100644
--- a/wifi/supplicant/1.3/ISupplicantStaIface.hal
+++ b/wifi/supplicant/1.3/ISupplicantStaIface.hal
@@ -69,7 +69,7 @@
bitfield<WpaDriverCapabilitiesMask> driverCapabilitiesMask);
/**
- * Set MBO cellular data status.
+ * Set Wi-Fi Alliance Agile Multiband (MBO) cellular data status.
*
* @param available true means cellular data available, false otherwise.
* @return status Status of the operation.
@@ -93,8 +93,10 @@
generates (SupplicantStatus status, bitfield<KeyMgmtMask> keyMgmtMask);
/**
- * Flush FILS HLP IEs
- * Use this to flush all the HLP IEs in wpa_supplicant
+ * Flush fast initial link setup (IEEE 802.11ai FILS) HLP packets.
+ * Use this to flush all the higher layer protocol (HLP) packets added in
+ * wpa_supplicant to send in FILS (Re)Association Request frame
+ * (Eg: DHCP discover packet).
*
* @return status Status of the operation.
* Possible status codes:
@@ -106,11 +108,12 @@
filsHlpFlushRequest() generates (SupplicantStatus status);
/**
- * Add FILS HLP IEs
- * Use this to add a HLP IE to wpa_supplicant
+ * Add fast initial link setup (IEEE 802.11ai FILS) HLP packets.
+ * Use this to add higher layer protocol (HLP) packet in FILS (Re)Association Request frame
+ * (Eg: DHCP discover packet).
*
* @param dst_mac MAC address of the destination
- * @param pkt The contents of the HLP IE starting from ethertype
+ * @param pkt The contents of the HLP packet starting from ethertype
* @return status Status of the operation.
* Possible status codes:
* |SupplicantStatusCode.SUCCESS|,
diff --git a/wifi/supplicant/1.3/ISupplicantStaIfaceCallback.hal b/wifi/supplicant/1.3/ISupplicantStaIfaceCallback.hal
index 6828dcd..c5da29c 100644
--- a/wifi/supplicant/1.3/ISupplicantStaIfaceCallback.hal
+++ b/wifi/supplicant/1.3/ISupplicantStaIfaceCallback.hal
@@ -147,7 +147,7 @@
};
/**
- * Indicates PMK cache added event.
+ * Indicates pairwise master key (PMK) cache added event.
*
* @param expirationTimeInSec expiration time in seconds
* @param serializedEntry is serialized PMK cache entry, the content is
@@ -192,6 +192,9 @@
/**
* Indicates an EAP authentication failure.
+ * @param errorCode Error code for EAP authentication failure.
+ * Either standard error code (enum EapErrorCode) or
+ * private error code defined by network provider.
*/
oneway onEapFailure_1_3(uint32_t errorCode);
diff --git a/wifi/supplicant/1.3/ISupplicantStaNetwork.hal b/wifi/supplicant/1.3/ISupplicantStaNetwork.hal
index 0566a21..2505912 100644
--- a/wifi/supplicant/1.3/ISupplicantStaNetwork.hal
+++ b/wifi/supplicant/1.3/ISupplicantStaNetwork.hal
@@ -214,7 +214,7 @@
generates (SupplicantStatus status, bitfield<GroupCipherMask> groupCipherMask);
/**
- * Set WAPI certificate suite for this network.
+ * Set WAPI certificate suite name for this network.
*
* @param suite value to set.
* @return status Status of the operation.
@@ -227,7 +227,7 @@
setWapiCertSuite(string suite) generates (SupplicantStatus status);
/**
- * Get WAPI certificate suite set for this network.
+ * Get WAPI certificate suite name set for this network.
*
* @return status Status of the operation.
* Possible status codes:
@@ -239,7 +239,7 @@
getWapiCertSuite() generates (SupplicantStatus status, string suite);
/**
- * Add a PMK into supplicant PMK cache.
+ * Add a pairwise master key (PMK) into supplicant PMK cache.
*
* @param serializedEntry is serialized PMK cache entry, the content is
* opaque for the framework and depends on the native implementation.
@@ -278,7 +278,7 @@
getAuthAlg_1_3() generates (SupplicantStatus status, bitfield<AuthAlgMask> authAlgMask);
/**
- * Enable EAP ERP for this network.
+ * Enable Extensible Authentication (EAP) - Re-authentication Protocol (ERP) for this network.
*
* @param enable true to set, false otherwise.
* @return status Status of the operation.
diff --git a/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp b/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
index 13f3366..6be24bc 100644
--- a/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
+++ b/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
@@ -17,7 +17,6 @@
#include <android-base/logging.h>
#include <VtsCoreUtil.h>
-#include <VtsHalHidlTargetTestBase.h>
#include <android/hardware/wifi/1.0/IWifi.h>
#include <android/hardware/wifi/1.1/IWifi.h>
#include <android/hardware/wifi/supplicant/1.3/ISupplicant.h>
@@ -57,6 +56,8 @@
supplicant_ =
getSupplicant_1_3(supplicant_v1_3_instance_name_, isP2pOn_);
EXPECT_TRUE(turnOnExcessiveLogging(supplicant_));
+ sta_iface_ = getSupplicantStaIface_1_3(supplicant_);
+ ASSERT_NE(nullptr, sta_iface_.get());
sta_network_ = createSupplicantStaNetwork_1_3(supplicant_);
ASSERT_NE(sta_network_.get(), nullptr);
}