Merge "Add HAL API to support conservative mode for EAP-SIM/AKA/AKA'"
diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp
index 8c32f14..563ee62 100644
--- a/audio/aidl/Android.bp
+++ b/audio/aidl/Android.bp
@@ -23,9 +23,18 @@
default_applicable_licenses: ["hardware_interfaces_license"],
}
+aidl_interface_defaults {
+ name: "android.hardware.audio_defaults",
+ host_supported: true,
+ vendor_available: true,
+ stability: "vintf",
+}
+
aidl_interface {
name: "android.hardware.audio.common",
- vendor_available: true,
+ defaults: [
+ "android.hardware.audio_defaults",
+ ],
srcs: [
"android/hardware/audio/common/PlaybackTrackMetadata.aidl",
"android/hardware/audio/common/RecordTrackMetadata.aidl",
@@ -35,7 +44,6 @@
imports: [
"android.media.audio.common.types-V2",
],
- stability: "vintf",
backend: {
cpp: {
enabled: true,
@@ -87,15 +95,25 @@
],
}
+cc_defaults {
+ name: "latest_android_hardware_audio_common_ndk_shared",
+ shared_libs: [
+ latest_android_hardware_audio_common + "-ndk",
+ ],
+}
+
aidl_interface {
name: "android.hardware.audio.core",
- vendor_available: true,
+ defaults: [
+ "android.hardware.audio_defaults",
+ ],
srcs: [
"android/hardware/audio/core/AudioMode.aidl",
"android/hardware/audio/core/AudioPatch.aidl",
"android/hardware/audio/core/AudioRoute.aidl",
"android/hardware/audio/core/IConfig.aidl",
"android/hardware/audio/core/IModule.aidl",
+ "android/hardware/audio/core/IStreamCallback.aidl",
"android/hardware/audio/core/IStreamIn.aidl",
"android/hardware/audio/core/IStreamOut.aidl",
"android/hardware/audio/core/ITelephony.aidl",
@@ -110,7 +128,6 @@
"android.hardware.audio.common-V1",
"android.media.audio.common.types-V2",
],
- stability: "vintf",
backend: {
// The C++ backend is disabled transitively due to use of FMQ.
cpp: {
@@ -148,7 +165,9 @@
aidl_interface {
name: "android.hardware.audio.effect",
- vendor_available: true,
+ defaults: [
+ "android.hardware.audio_defaults",
+ ],
srcs: [
"android/hardware/audio/effect/BassBoost.aidl",
"android/hardware/audio/effect/Capability.aidl",
@@ -177,7 +196,6 @@
"android.hardware.audio.common-V1",
"android.media.audio.common.types-V2",
],
- stability: "vintf",
backend: {
// The C++ backend is disabled transitively due to use of FMQ.
cpp: {
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
index be382c5..7f960e0 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
@@ -76,6 +76,7 @@
android.hardware.audio.common.SourceMetadata sourceMetadata;
@nullable android.media.audio.common.AudioOffloadInfo offloadInfo;
long bufferSizeFrames;
+ @nullable android.hardware.audio.core.IStreamCallback callback;
}
@VintfStability
parcelable OpenOutputStreamReturn {
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamCallback.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamCallback.aidl
new file mode 100644
index 0000000..5a2ab78
--- /dev/null
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IStreamCallback.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.audio.core;
+@VintfStability
+interface IStreamCallback {
+ oneway void onTransferReady();
+ oneway void onError();
+ oneway void onDrainReady();
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl
index 80ee185..467d37b 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ModuleDebug.aidl
@@ -35,4 +35,5 @@
@JavaDerive(equals=true, toString=true) @VintfStability
parcelable ModuleDebug {
boolean simulateDeviceConnections;
+ int streamTransientStateDelayMs;
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
index 3a77ad1..3a4271b 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
@@ -42,8 +42,9 @@
const int LATENCY_UNKNOWN = -1;
@FixedSize @VintfStability
parcelable Position {
- long frames;
- long timeNs;
+ long frames = -1;
+ long timeNs = -1;
+ const long UNKNOWN = -1;
}
@Backing(type="int") @VintfStability
enum State {
@@ -53,14 +54,23 @@
PAUSED = 4,
DRAINING = 5,
DRAIN_PAUSED = 6,
+ TRANSFERRING = 7,
+ TRANSFER_PAUSED = 8,
ERROR = 100,
}
+ @Backing(type="byte") @VintfStability
+ enum DrainMode {
+ DRAIN_UNSPECIFIED = 0,
+ DRAIN_ALL = 1,
+ DRAIN_EARLY_NOTIFY = 2,
+ }
@FixedSize @VintfStability
union Command {
- int hal_reserved_exit;
+ int halReservedExit;
+ android.media.audio.common.Void getStatus;
android.media.audio.common.Void start;
int burst;
- android.media.audio.common.Void drain;
+ android.hardware.audio.core.StreamDescriptor.DrainMode drain;
android.media.audio.common.Void standby;
android.media.audio.common.Void pause;
android.media.audio.common.Void flush;
diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl
index be40051..974e7e8 100644
--- a/audio/aidl/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/android/hardware/audio/core/IModule.aidl
@@ -21,6 +21,7 @@
import android.hardware.audio.core.AudioMode;
import android.hardware.audio.core.AudioPatch;
import android.hardware.audio.core.AudioRoute;
+import android.hardware.audio.core.IStreamCallback;
import android.hardware.audio.core.IStreamIn;
import android.hardware.audio.core.IStreamOut;
import android.hardware.audio.core.ITelephony;
@@ -53,9 +54,13 @@
* the HAL module behavior that would otherwise require human intervention.
*
* The HAL module must throw an error if there is an attempt to change
- * the debug behavior for the aspect which is currently in use.
+ * the debug behavior for the aspect which is currently in use, or when
+ * the value of any of the debug flags is invalid. See 'ModuleDebug' for
+ * the full list of constraints.
*
* @param debug The debug options.
+ * @throws EX_ILLEGAL_ARGUMENT If some of the configuration parameters are
+ * invalid.
* @throws EX_ILLEGAL_STATE If the flag(s) being changed affect functionality
* which is currently in use.
*/
@@ -316,9 +321,13 @@
* 'setAudioPortConfig' method. Existence of an audio patch involving this
* port configuration is not required for successful opening of a stream.
*
- * If the port configuration has 'COMPRESS_OFFLOAD' output flag set,
- * the framework must provide additional information about the encoded
- * audio stream in 'offloadInfo' argument.
+ * If the port configuration has the 'COMPRESS_OFFLOAD' output flag set,
+ * the client must provide additional information about the encoded
+ * audio stream in the 'offloadInfo' argument.
+ *
+ * If the port configuration has the 'NON_BLOCKING' output flag set,
+ * the client must provide a callback for asynchronous notifications
+ * in the 'callback' argument.
*
* The requested buffer size is expressed in frames, thus the actual size
* in bytes depends on the audio port configuration. Also, the HAL module
@@ -354,6 +363,8 @@
* - If the offload info is not provided for an offload
* port configuration.
* - If a buffer of the requested size can not be provided.
+ * - If the callback is not provided for a non-blocking
+ * port configuration.
* @throws EX_ILLEGAL_STATE In the following cases:
* - If the port config already has a stream opened on it.
* - If the limit on the open stream count for the port has
@@ -372,6 +383,8 @@
@nullable AudioOffloadInfo offloadInfo;
/** Requested audio I/O buffer minimum size, in frames. */
long bufferSizeFrames;
+ /** Client callback interface for the non-blocking output mode. */
+ @nullable IStreamCallback callback;
}
@VintfStability
parcelable OpenOutputStreamReturn {
diff --git a/audio/aidl/android/hardware/audio/core/IStreamCallback.aidl b/audio/aidl/android/hardware/audio/core/IStreamCallback.aidl
new file mode 100644
index 0000000..440ab25
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/IStreamCallback.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.audio.core;
+
+/**
+ * This interface is used to indicate completion of asynchronous operations.
+ * See the state machines referenced by StreamDescriptor for details.
+ */
+@VintfStability
+oneway interface IStreamCallback {
+ /**
+ * Indicate that the stream is ready for next data exchange.
+ */
+ void onTransferReady();
+ /**
+ * Indicate that an irrecoverable error has occurred during the last I/O
+ * operation. After sending this callback, the stream enters the 'ERROR'
+ * state.
+ */
+ void onError();
+ /**
+ * Indicate that the stream has finished draining. This is only used
+ * for output streams because for input streams draining is performed
+ * by the client.
+ */
+ void onDrainReady();
+}
diff --git a/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl b/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl
index 858a9bd..871a5c9 100644
--- a/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl
+++ b/audio/aidl/android/hardware/audio/core/ModuleDebug.aidl
@@ -35,4 +35,19 @@
* profiles.
*/
boolean simulateDeviceConnections;
+ /**
+ * Must be non-negative. When set to non-zero, HAL module must delay
+ * transition from "transient" stream states (see StreamDescriptor.aidl)
+ * by the specified amount of milliseconds. The purpose of this delay
+ * is to allow VTS to test sending of stream commands while the stream is
+ * in a transient state. The delay must apply to newly created streams,
+ * it is not required to apply the delay to already opened streams.
+ *
+ * Note: the drawback of enabling this delay for asynchronous (non-blocking)
+ * modes is that sending of callbacks will also be delayed, because
+ * callbacks are sent once the stream state machine exits a transient
+ * state. Thus, it's not recommended to use it with tests that require
+ * waiting for an async callback.
+ */
+ int streamTransientStateDelayMs;
}
diff --git a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
index 2b1fc99..65ea9ef 100644
--- a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
+++ b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
@@ -84,13 +84,13 @@
* are different.
*
* State machines of both input and output streams start from the 'STANDBY'
- * state. Transitions between states happen naturally with changes in the
+ * state. Transitions between states happen naturally with changes in the
* states of the model elements. For simplicity, we restrict the change to one
* element only, for example, in the 'STANDBY' state, either the producer or the
* consumer can become active, but not both at the same time. States 'STANDBY',
* 'IDLE', 'READY', and '*PAUSED' are "stable"—they require an external event,
* whereas a change from the 'DRAINING' state can happen with time as the buffer
- * gets empty.
+ * gets empty, thus it's a "transient" state.
*
* The state machine for input streams is defined in the `stream-in-sm.gv` file,
* for output streams—in the `stream-out-sm.gv` file. State machines define how
@@ -100,6 +100,28 @@
* client can never observe a stream with a functioning command queue in this
* state. The 'ERROR' state is a special state which the state machine enters
* when an unrecoverable hardware error is detected by the HAL module.
+ *
+ * Non-blocking (asynchronous) modes introduce a new 'TRANSFERRING' state, which
+ * the state machine can enter after replying to the 'burst' command, instead of
+ * staying in the 'ACTIVE' state. In this case the client gets unblocked
+ * earlier, while the actual audio delivery to / from the observer is not
+ * complete yet. Once the HAL module is ready for the next transfer, it notifies
+ * the client via a oneway callback, and the machine switches to 'ACTIVE'
+ * state. The 'TRANSFERRING' state is thus "transient", similar to the
+ * 'DRAINING' state. For output streams, asynchronous transfer can be paused,
+ * and it's another new state: 'TRANSFER_PAUSED'. It differs from 'PAUSED' by
+ * the fact that no new writes are allowed. Please see 'stream-in-async-sm.gv'
+ * and 'stream-out-async-sm.gv' files for details. Below is the table summary
+ * for asynchronous only-states:
+ *
+ * Producer | Buffer state | Consumer | Applies | State
+ * active? | | active? | to |
+ * ==========|==============|==========|=========|==============================
+ * Yes | Not empty | Yes | Both | TRANSFERRING, s/w x-runs counted
+ * ----------|--------------|----------|---------|-----------------------------
+ * Yes | Not empty | No | Output | TRANSFER_PAUSED,
+ * | | | | h/w emits silence.
+ *
*/
@JavaDerive(equals=true, toString=true)
@VintfStability
@@ -116,10 +138,15 @@
@VintfStability
@FixedSize
parcelable Position {
+ /**
+ * The value used when the position can not be reported by the HAL
+ * module.
+ */
+ const long UNKNOWN = -1;
/** Frame count. */
- long frames;
+ long frames = UNKNOWN;
/** Timestamp in nanoseconds. */
- long timeNs;
+ long timeNs = UNKNOWN;
}
@VintfStability
@@ -166,11 +193,24 @@
/**
* Used for output streams only, pauses draining. This state is similar
* to the 'PAUSED' state, except that the client is not adding any
- * new data. If it emits a 'BURST' command, this brings the stream
+ * new data. If it emits a 'burst' command, this brings the stream
* into the regular 'PAUSED' state.
*/
DRAIN_PAUSED = 6,
/**
+ * Used only for streams in asynchronous mode. The stream enters this
+ * state after receiving a 'burst' command and returning control back
+ * to the client, thus unblocking it.
+ */
+ TRANSFERRING = 7,
+ /**
+ * Used only for output streams in asynchronous mode only. The stream
+ * enters this state after receiving a 'pause' command while being in
+ * the 'TRANSFERRING' state. Unlike 'PAUSED' state, this state does not
+ * accept new writes.
+ */
+ TRANSFER_PAUSED = 8,
+ /**
* The ERROR state is entered when the stream has encountered an
* irrecoverable error from the lower layer. After entering it, the
* stream can only be closed.
@@ -178,6 +218,29 @@
ERROR = 100,
}
+ @VintfStability
+ @Backing(type="byte")
+ enum DrainMode {
+ /**
+ * Unspecified—used with input streams only, because the client controls
+ * draining.
+ */
+ DRAIN_UNSPECIFIED = 0,
+ /**
+ * Used with output streams only, the HAL module indicates drain
+ * completion when all remaining audio data has been consumed.
+ */
+ DRAIN_ALL = 1,
+ /**
+ * Used with output streams only, the HAL module indicates drain
+ * completion shortly before all audio data has been consumed in order
+ * to give the client an opportunity to provide data for the next track
+ * for gapless playback. The exact amount of provided time is specific
+ * to the HAL implementation.
+ */
+ DRAIN_EARLY_NOTIFY = 2,
+ }
+
/**
* Used for sending commands to the HAL module. The client writes into
* the queue, the HAL module reads. The queue can only contain a single
@@ -198,7 +261,14 @@
* implementation must pass a random cookie as the command argument,
* which is only known to the implementation.
*/
- int hal_reserved_exit;
+ int halReservedExit;
+ /**
+ * Retrieve the current state of the stream. This command must be
+ * processed by the stream in any state. The stream must provide current
+ * positions, counters, and its state in the reply. This command must be
+ * handled by the HAL module without any observable side effects.
+ */
+ Void getStatus;
/**
* See the state machines on the applicability of this command to
* different states.
@@ -215,15 +285,14 @@
* read from the hardware into the 'audio.fmq' queue.
*
* In both cases it is allowed for this field to contain any
- * non-negative number. The value 0 can be used if the client only needs
- * to retrieve current positions and latency. Any sufficiently big value
- * which exceeds the size of the queue's area which is currently
- * available for reading or writing by the HAL module must be trimmed by
- * the HAL module to the available size. Note that the HAL module is
- * allowed to consume or provide less data than requested, and it must
- * return the amount of actually read or written data via the
- * 'Reply.fmqByteCount' field. Thus, only attempts to pass a negative
- * number must be constituted as a client's error.
+ * non-negative number. Any sufficiently big value which exceeds the
+ * size of the queue's area which is currently available for reading or
+ * writing by the HAL module must be trimmed by the HAL module to the
+ * available size. Note that the HAL module is allowed to consume or
+ * provide less data than requested, and it must return the amount of
+ * actually read or written data via the 'Reply.fmqByteCount'
+ * field. Thus, only attempts to pass a negative number must be
+ * constituted as a client's error.
*
* Differences for the MMap No IRQ mode:
*
@@ -233,13 +302,16 @@
* with sending of this command.
*
* - the value must always be set to 0.
+ *
+ * See the state machines on the applicability of this command to
+ * different states.
*/
int burst;
/**
* See the state machines on the applicability of this command to
* different states.
*/
- Void drain;
+ DrainMode drain;
/**
* See the state machines on the applicability of this command to
* different states.
@@ -286,10 +358,6 @@
* - STATUS_INVALID_OPERATION: the command is not applicable in the
* current state of the stream, or to this
* type of the stream;
- * - STATUS_NO_INIT: positions can not be reported because the mix port
- * is not connected to any producer or consumer, or
- * because the HAL module does not support positions
- * reporting for this AudioSource (on input streams).
* - STATUS_NOT_ENOUGH_DATA: a read or write error has
* occurred for the 'audio.fmq' queue;
*/
@@ -307,9 +375,11 @@
*/
int fmqByteCount;
/**
- * It is recommended to report the current position for any command.
- * If the position can not be reported, the 'status' field must be
- * set to 'NO_INIT'.
+ * It is recommended to report the current position for any command. If
+ * the position can not be reported, for example because the mix port is
+ * not connected to any producer or consumer, or because the HAL module
+ * does not support positions reporting for this AudioSource (on input
+ * streams), the 'Position::UNKNOWN' value must be used.
*
* For output streams: the moment when the specified stream position
* was presented to an external observer (i.e. presentation position).
@@ -401,6 +471,10 @@
* into 'reply' queue, and hangs on waiting on a read from
* the 'command' queue.
* 6. The client wakes up due to 5. and reads the reply.
+ * Note: in non-blocking mode, when the HAL module goes to
+ * the 'TRANSFERRING' state (as indicated by the 'reply.state'
+ * field), the client must wait for the 'IStreamCallback.onTransferReady'
+ * notification to arrive before starting the next burst.
*
* For input streams the following sequence of operations is used:
* 1. The client writes the BURST command into the 'command' queue,
@@ -415,6 +489,10 @@
* 5. The client wakes up due to 4.
* 6. The client reads the reply and audio data. The client must
* always read from the FMQ all the data it contains.
+ * Note: in non-blocking mode, when the HAL module goes to
+ * the 'TRANSFERRING' state (as indicated by the 'reply.state'
+ * field) the client must wait for the 'IStreamCallback.onTransferReady'
+ * notification to arrive before starting the next burst.
*
*/
MQDescriptor<byte, SynchronizedReadWrite> fmq;
diff --git a/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv b/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv
new file mode 100644
index 0000000..818b18e
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv
@@ -0,0 +1,47 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// To render: dot -Tpng stream-in-async-sm.gv -o stream-in-async-sm.png
+digraph stream_in_async_state_machine {
+ node [shape=doublecircle style=filled fillcolor=black width=0.5] I;
+ node [shape=point width=0.5] F;
+ node [shape=oval width=1];
+ node [fillcolor=lightgreen] STANDBY; // buffer is empty
+ node [fillcolor=tomato] CLOSED;
+ node [fillcolor=tomato] ERROR;
+ node [style=dashed] ANY_STATE;
+ node [fillcolor=lightblue style=filled];
+ // Note that when the producer (h/w) is passive, "burst" operations
+ // complete synchronously, bypassing the TRANSFERRING state.
+ I -> STANDBY;
+ STANDBY -> IDLE [label="start"]; // producer -> active
+ IDLE -> STANDBY [label="standby"]; // producer -> passive, buffer is cleared
+ IDLE -> TRANSFERRING [label="burst"]; // consumer -> active
+ ACTIVE -> PAUSED [label="pause"]; // consumer -> passive
+ ACTIVE -> DRAINING [label="drain"]; // producer -> passive
+ ACTIVE -> TRANSFERRING [label="burst"];
+ TRANSFERRING -> ACTIVE [label="←IStreamCallback.onTransferReady"];
+ TRANSFERRING -> PAUSED [label="pause"]; // consumer -> passive
+ TRANSFERRING -> DRAINING [label="drain"]; // producer -> passive
+ PAUSED -> TRANSFERRING [label="burst"]; // consumer -> active
+ PAUSED -> STANDBY [label="flush"]; // producer -> passive, buffer is cleared
+ DRAINING -> DRAINING [label="burst"];
+ DRAINING -> ACTIVE [label="start"]; // producer -> active
+ DRAINING -> STANDBY [label="<empty buffer>"]; // consumer deactivates
+ IDLE -> ERROR [label="←IStreamCallback.onError"];
+ PAUSED -> ERROR [label="←IStreamCallback.onError"];
+ TRANSFERRING -> ERROR [label="←IStreamCallback.onError"];
+ ANY_STATE -> CLOSED [label="→IStream*.close"];
+ CLOSED -> F;
+}
diff --git a/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv b/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
new file mode 100644
index 0000000..e25b15a
--- /dev/null
+++ b/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
@@ -0,0 +1,57 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// To render: dot -Tpng stream-out-async-sm.gv -o stream-out-async-sm.png
+digraph stream_out_async_state_machine {
+ node [shape=doublecircle style=filled fillcolor=black width=0.5] I;
+ node [shape=point width=0.5] F;
+ node [shape=oval width=1];
+ node [fillcolor=lightgreen] STANDBY; // buffer is empty
+ node [fillcolor=lightgreen] IDLE; // buffer is empty
+ node [fillcolor=tomato] CLOSED;
+ node [fillcolor=tomato] ERROR;
+ node [style=dashed] ANY_STATE;
+ node [fillcolor=lightblue style=filled];
+ // Note that when the consumer (h/w) is passive, "burst" operations
+ // complete synchronously, bypassing the TRANSFERRING state.
+ I -> STANDBY;
+ STANDBY -> IDLE [label="start"]; // consumer -> active
+ STANDBY -> PAUSED [label="burst"]; // producer -> active
+ IDLE -> STANDBY [label="standby"]; // consumer -> passive
+ IDLE -> TRANSFERRING [label="burst"]; // producer -> active
+ ACTIVE -> PAUSED [label="pause"]; // consumer -> passive (not consuming)
+ ACTIVE -> DRAINING [label="drain"]; // producer -> passive
+ ACTIVE -> TRANSFERRING [label="burst"]; // early unblocking
+ ACTIVE -> ACTIVE [label="burst"]; // full write
+ TRANSFERRING -> ACTIVE [label="←IStreamCallback.onTransferReady"];
+ TRANSFERRING -> TRANSFER_PAUSED [label="pause"]; // consumer -> passive (not consuming)
+ TRANSFERRING -> DRAINING [label="drain"]; // producer -> passive
+ TRANSFER_PAUSED -> TRANSFERRING [label="start"]; // consumer -> active
+ TRANSFER_PAUSED -> DRAIN_PAUSED [label="drain"]; // producer -> passive
+ TRANSFER_PAUSED -> IDLE [label="flush"]; // buffer is cleared
+ PAUSED -> PAUSED [label="burst"];
+ PAUSED -> ACTIVE [label="start"]; // consumer -> active
+ PAUSED -> IDLE [label="flush"]; // producer -> passive, buffer is cleared
+ DRAINING -> IDLE [label="←IStreamCallback.onDrainReady"];
+ DRAINING -> TRANSFERRING [label="burst"]; // producer -> active
+ DRAINING -> DRAIN_PAUSED [label="pause"]; // consumer -> passive (not consuming)
+ DRAIN_PAUSED -> DRAINING [label="start"]; // consumer -> active
+ DRAIN_PAUSED -> TRANSFER_PAUSED [label="burst"]; // producer -> active
+ DRAIN_PAUSED -> IDLE [label="flush"]; // buffer is cleared
+ IDLE -> ERROR [label="←IStreamCallback.onError"];
+ DRAINING -> ERROR [label="←IStreamCallback.onError"];
+ TRANSFERRING -> ERROR [label="←IStreamCallback.onError"];
+ ANY_STATE -> CLOSED [label="→IStream*.close"];
+ CLOSED -> F;
+}
diff --git a/audio/aidl/common/StreamWorker.cpp b/audio/aidl/common/StreamWorker.cpp
index dda0e4a..0d2121c 100644
--- a/audio/aidl/common/StreamWorker.cpp
+++ b/audio/aidl/common/StreamWorker.cpp
@@ -25,15 +25,20 @@
bool ThreadController::start(const std::string& name, int priority) {
mThreadName = name;
mThreadPriority = priority;
- mWorker = std::thread(&ThreadController::workerThread, this);
+ if (kTestSingleThread != name) {
+ mWorker = std::thread(&ThreadController::workerThread, this);
+ } else {
+ // Simulate the case when the workerThread completes prior
+ // to the moment when we being waiting for its start.
+ workerThread();
+ }
std::unique_lock<std::mutex> lock(mWorkerLock);
android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
mWorkerCv.wait(lock, [&]() {
android::base::ScopedLockAssertion lock_assertion(mWorkerLock);
- return mWorkerState == WorkerState::RUNNING || !mError.empty();
+ return mWorkerState != WorkerState::INITIAL || !mError.empty();
});
- mWorkerStateChangeRequest = false;
- return mWorkerState == WorkerState::RUNNING;
+ return mError.empty();
}
void ThreadController::stop() {
@@ -81,8 +86,8 @@
void ThreadController::workerThread() {
using Status = StreamLogic::Status;
- std::string error = mLogic->init();
- if (error.empty() && !mThreadName.empty()) {
+ std::string error;
+ if (!mThreadName.empty()) {
std::string compliantName(mThreadName.substr(0, 15));
if (int errCode = pthread_setname_np(pthread_self(), compliantName.c_str()); errCode != 0) {
error.append("Failed to set thread name: ").append(strerror(errCode));
@@ -94,6 +99,9 @@
error.append("Failed to set thread priority: ").append(strerror(errCode));
}
}
+ if (error.empty()) {
+ error.append(mLogic->init());
+ }
{
std::lock_guard<std::mutex> lock(mWorkerLock);
mWorkerState = error.empty() ? WorkerState::RUNNING : WorkerState::STOPPED;
diff --git a/audio/aidl/common/include/StreamWorker.h b/audio/aidl/common/include/StreamWorker.h
index ab2ec26..e9c1070 100644
--- a/audio/aidl/common/include/StreamWorker.h
+++ b/audio/aidl/common/include/StreamWorker.h
@@ -32,7 +32,7 @@
namespace internal {
class ThreadController {
- enum class WorkerState { STOPPED, RUNNING, PAUSE_REQUESTED, PAUSED, RESUME_REQUESTED };
+ enum class WorkerState { INITIAL, STOPPED, RUNNING, PAUSE_REQUESTED, PAUSED, RESUME_REQUESTED };
public:
explicit ThreadController(StreamLogic* logic) : mLogic(logic) {}
@@ -76,7 +76,7 @@
std::thread mWorker;
std::mutex mWorkerLock;
std::condition_variable mWorkerCv;
- WorkerState mWorkerState GUARDED_BY(mWorkerLock) = WorkerState::STOPPED;
+ WorkerState mWorkerState GUARDED_BY(mWorkerLock) = WorkerState::INITIAL;
std::string mError GUARDED_BY(mWorkerLock);
// The atomic lock-free variable is used to prevent priority inversions
// that can occur when a high priority worker tries to acquire the lock
@@ -90,6 +90,9 @@
std::atomic<bool> mWorkerStateChangeRequest GUARDED_BY(mWorkerLock) = false;
};
+// A special thread name used in tests only.
+static const std::string kTestSingleThread = "__testST__";
+
} // namespace internal
class StreamLogic {
diff --git a/audio/aidl/common/tests/streamworker_tests.cpp b/audio/aidl/common/tests/streamworker_tests.cpp
index 8ea8424..f7a30b9 100644
--- a/audio/aidl/common/tests/streamworker_tests.cpp
+++ b/audio/aidl/common/tests/streamworker_tests.cpp
@@ -283,4 +283,16 @@
EXPECT_EQ(priority, worker.getPriority());
}
+TEST_P(StreamWorkerTest, DeferredStartCheckNoError) {
+ stream.setStopStatus();
+ EXPECT_TRUE(worker.start(android::hardware::audio::common::internal::kTestSingleThread));
+ EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, DeferredStartCheckWithError) {
+ stream.setErrorStatus();
+ EXPECT_FALSE(worker.start(android::hardware::audio::common::internal::kTestSingleThread));
+ EXPECT_TRUE(worker.hasError());
+}
+
INSTANTIATE_TEST_SUITE_P(StreamWorker, StreamWorkerTest, testing::Bool());
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 6863fe3..9dbd61c 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -97,6 +97,7 @@
}
ndk::ScopedAStatus Module::createStreamContext(int32_t in_portConfigId, int64_t in_bufferSizeFrames,
+ std::shared_ptr<IStreamCallback> asyncCallback,
StreamContext* out_context) {
if (in_bufferSizeFrames <= 0) {
LOG(ERROR) << __func__ << ": non-positive buffer size " << in_bufferSizeFrames;
@@ -135,8 +136,8 @@
StreamContext temp(
std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
- frameSize,
- std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames));
+ frameSize, std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
+ asyncCallback, mDebug.streamTransientStateDelayMs);
if (temp.isValid()) {
*out_context = std::move(temp);
} else {
@@ -242,6 +243,11 @@
<< "while having external devices connected";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
+ if (in_debug.streamTransientStateDelayMs < 0) {
+ LOG(ERROR) << __func__ << ": streamTransientStateDelayMs is negative: "
+ << in_debug.streamTransientStateDelayMs;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
mDebug = in_debug;
return ndk::ScopedAStatus::ok();
}
@@ -456,7 +462,8 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
StreamContext context;
- if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames, &context);
+ if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames, nullptr,
+ &context);
!status.isOk()) {
return status;
}
@@ -496,8 +503,16 @@
<< " has COMPRESS_OFFLOAD flag set, requires offload info";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
+ const bool isNonBlocking = isBitPositionFlagSet(port->flags.get<AudioIoFlags::Tag::output>(),
+ AudioOutputFlags::NON_BLOCKING);
+ if (isNonBlocking && in_args.callback == nullptr) {
+ LOG(ERROR) << __func__ << ": port id " << port->id
+ << " has NON_BLOCKING flag set, requires async callback";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
StreamContext context;
- if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames, &context);
+ if (auto status = createStreamContext(in_args.portConfigId, in_args.bufferSizeFrames,
+ isNonBlocking ? in_args.callback : nullptr, &context);
!status.isOk()) {
return status;
}
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 21dc4b6..d7c352f 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -87,32 +87,46 @@
void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
bool isConnected) const {
+ reply->status = STATUS_OK;
if (isConnected) {
- reply->status = STATUS_OK;
reply->observable.frames = mFrameCount;
reply->observable.timeNs = ::android::elapsedRealtimeNano();
} else {
- reply->status = STATUS_NO_INIT;
+ reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
+ reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
}
}
+void StreamWorkerCommonLogic::populateReplyWrongState(
+ StreamDescriptor::Reply* reply, const StreamDescriptor::Command& command) const {
+ LOG(WARNING) << "command '" << toString(command.getTag())
+ << "' can not be handled in the state " << toString(mState);
+ reply->status = STATUS_INVALID_OPERATION;
+}
+
const std::string StreamInWorkerLogic::kThreadName = "reader";
StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() {
+ // Note: for input streams, draining is driven by the client, thus
+ // "empty buffer" condition can only happen while handling the 'burst'
+ // command. Thus, unlike for output streams, it does not make sense to
+ // delay the 'DRAINING' state here by 'mTransientStateDelayMs'.
+ // TODO: Add a delay for transitions of async operations when/if they added.
+
StreamDescriptor::Command command{};
if (!mCommandMQ->readBlocking(&command, 1)) {
LOG(ERROR) << __func__ << ": reading of command from MQ failed";
mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
}
+ LOG(DEBUG) << __func__ << ": received command " << command.toString() << " in " << kThreadName;
StreamDescriptor::Reply reply{};
reply.status = STATUS_BAD_VALUE;
using Tag = StreamDescriptor::Command::Tag;
switch (command.getTag()) {
- case Tag::hal_reserved_exit:
- if (const int32_t cookie = command.get<Tag::hal_reserved_exit>();
+ case Tag::halReservedExit:
+ if (const int32_t cookie = command.get<Tag::halReservedExit>();
cookie == mInternalCommandCookie) {
- LOG(DEBUG) << __func__ << ": received EXIT command";
setClosed();
// This is an internal command, no need to reply.
return Status::EXIT;
@@ -120,8 +134,10 @@
LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
}
break;
+ case Tag::getStatus:
+ populateReply(&reply, mIsConnected);
+ break;
case Tag::start:
- LOG(DEBUG) << __func__ << ": received START read command";
if (mState == StreamDescriptor::State::STANDBY ||
mState == StreamDescriptor::State::DRAINING) {
populateReply(&reply, mIsConnected);
@@ -129,15 +145,13 @@
? StreamDescriptor::State::IDLE
: StreamDescriptor::State::ACTIVE;
} else {
- LOG(WARNING) << __func__ << ": START command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
+ populateReplyWrongState(&reply, command);
}
break;
case Tag::burst:
if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
- LOG(DEBUG) << __func__ << ": received BURST read command for " << fmqByteCount
- << " bytes";
+ LOG(DEBUG) << __func__ << ": '" << toString(command.getTag()) << "' command for "
+ << fmqByteCount << " bytes";
if (mState == StreamDescriptor::State::IDLE ||
mState == StreamDescriptor::State::ACTIVE ||
mState == StreamDescriptor::State::PAUSED ||
@@ -151,69 +165,61 @@
} else if (mState == StreamDescriptor::State::DRAINING) {
// To simplify the reference code, we assume that the read operation
// has consumed all the data remaining in the hardware buffer.
- // TODO: Provide parametrization on the duration of draining to test
- // handling of commands during the 'DRAINING' state.
+ // In a real implementation, here we would either remain in
+ // the 'DRAINING' state, or transfer to 'STANDBY' depending on the
+ // buffer state.
mState = StreamDescriptor::State::STANDBY;
}
} else {
- LOG(WARNING) << __func__ << ": BURST command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
+ populateReplyWrongState(&reply, command);
}
} else {
LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
}
break;
case Tag::drain:
- LOG(DEBUG) << __func__ << ": received DRAIN read command";
- if (mState == StreamDescriptor::State::ACTIVE) {
- usleep(1000); // Simulate a blocking call into the driver.
- populateReply(&reply, mIsConnected);
- // Can switch the state to ERROR if a driver error occurs.
- mState = StreamDescriptor::State::DRAINING;
+ if (command.get<Tag::drain>() == StreamDescriptor::DrainMode::DRAIN_UNSPECIFIED) {
+ if (mState == StreamDescriptor::State::ACTIVE) {
+ usleep(1000); // Simulate a blocking call into the driver.
+ populateReply(&reply, mIsConnected);
+ // Can switch the state to ERROR if a driver error occurs.
+ mState = StreamDescriptor::State::DRAINING;
+ } else {
+ populateReplyWrongState(&reply, command);
+ }
} else {
- LOG(WARNING) << __func__ << ": DRAIN command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
+ LOG(WARNING) << __func__
+ << ": invalid drain mode: " << toString(command.get<Tag::drain>());
}
break;
case Tag::standby:
- LOG(DEBUG) << __func__ << ": received STANDBY read command";
if (mState == StreamDescriptor::State::IDLE) {
usleep(1000); // Simulate a blocking call into the driver.
populateReply(&reply, mIsConnected);
// Can switch the state to ERROR if a driver error occurs.
mState = StreamDescriptor::State::STANDBY;
} else {
- LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
+ populateReplyWrongState(&reply, command);
}
break;
case Tag::pause:
- LOG(DEBUG) << __func__ << ": received PAUSE read command";
if (mState == StreamDescriptor::State::ACTIVE) {
usleep(1000); // Simulate a blocking call into the driver.
populateReply(&reply, mIsConnected);
// Can switch the state to ERROR if a driver error occurs.
mState = StreamDescriptor::State::PAUSED;
} else {
- LOG(WARNING) << __func__ << ": PAUSE command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
+ populateReplyWrongState(&reply, command);
}
break;
case Tag::flush:
- LOG(DEBUG) << __func__ << ": received FLUSH read command";
if (mState == StreamDescriptor::State::PAUSED) {
usleep(1000); // Simulate a blocking call into the driver.
populateReply(&reply, mIsConnected);
// Can switch the state to ERROR if a driver error occurs.
mState = StreamDescriptor::State::STANDBY;
} else {
- LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
+ populateReplyWrongState(&reply, command);
}
break;
}
@@ -261,20 +267,52 @@
const std::string StreamOutWorkerLogic::kThreadName = "writer";
StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
+ if (mState == StreamDescriptor::State::DRAINING ||
+ mState == StreamDescriptor::State::TRANSFERRING) {
+ if (auto stateDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::steady_clock::now() - mTransientStateStart);
+ stateDurationMs >= mTransientStateDelayMs) {
+ if (mAsyncCallback == nullptr) {
+ // In blocking mode, mState can only be DRAINING.
+ mState = StreamDescriptor::State::IDLE;
+ } else {
+ // In a real implementation, the driver should notify the HAL about
+ // drain or transfer completion. In the stub, we switch unconditionally.
+ if (mState == StreamDescriptor::State::DRAINING) {
+ mState = StreamDescriptor::State::IDLE;
+ ndk::ScopedAStatus status = mAsyncCallback->onDrainReady();
+ if (!status.isOk()) {
+ LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
+ }
+ } else {
+ mState = StreamDescriptor::State::ACTIVE;
+ ndk::ScopedAStatus status = mAsyncCallback->onTransferReady();
+ if (!status.isOk()) {
+ LOG(ERROR) << __func__ << ": error from onTransferReady: " << status;
+ }
+ }
+ }
+ if (mTransientStateDelayMs.count() != 0) {
+ LOG(DEBUG) << __func__ << ": switched to state " << toString(mState)
+ << " after a timeout";
+ }
+ }
+ }
+
StreamDescriptor::Command command{};
if (!mCommandMQ->readBlocking(&command, 1)) {
LOG(ERROR) << __func__ << ": reading of command from MQ failed";
mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
}
+ LOG(DEBUG) << __func__ << ": received command " << command.toString() << " in " << kThreadName;
StreamDescriptor::Reply reply{};
reply.status = STATUS_BAD_VALUE;
using Tag = StreamDescriptor::Command::Tag;
switch (command.getTag()) {
- case Tag::hal_reserved_exit:
- if (const int32_t cookie = command.get<Tag::hal_reserved_exit>();
+ case Tag::halReservedExit:
+ if (const int32_t cookie = command.get<Tag::halReservedExit>();
cookie == mInternalCommandCookie) {
- LOG(DEBUG) << __func__ << ": received EXIT command";
setClosed();
// This is an internal command, no need to reply.
return Status::EXIT;
@@ -282,8 +320,11 @@
LOG(WARNING) << __func__ << ": EXIT command has a bad cookie: " << cookie;
}
break;
- case Tag::start:
- LOG(DEBUG) << __func__ << ": received START write command";
+ case Tag::getStatus:
+ populateReply(&reply, mIsConnected);
+ break;
+ case Tag::start: {
+ bool commandAccepted = true;
switch (mState) {
case StreamDescriptor::State::STANDBY:
mState = StreamDescriptor::State::IDLE;
@@ -292,97 +333,112 @@
mState = StreamDescriptor::State::ACTIVE;
break;
case StreamDescriptor::State::DRAIN_PAUSED:
- mState = StreamDescriptor::State::PAUSED;
+ switchToTransientState(StreamDescriptor::State::DRAINING);
+ break;
+ case StreamDescriptor::State::TRANSFER_PAUSED:
+ switchToTransientState(StreamDescriptor::State::TRANSFERRING);
break;
default:
- LOG(WARNING) << __func__ << ": START command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
+ populateReplyWrongState(&reply, command);
+ commandAccepted = false;
}
- if (reply.status != STATUS_INVALID_OPERATION) {
+ if (commandAccepted) {
populateReply(&reply, mIsConnected);
}
- break;
+ } break;
case Tag::burst:
if (const int32_t fmqByteCount = command.get<Tag::burst>(); fmqByteCount >= 0) {
- LOG(DEBUG) << __func__ << ": received BURST write command for " << fmqByteCount
- << " bytes";
- if (mState !=
- StreamDescriptor::State::ERROR) { // BURST can be handled in all valid states
+ LOG(DEBUG) << __func__ << ": '" << toString(command.getTag()) << "' command for "
+ << fmqByteCount << " bytes";
+ if (mState != StreamDescriptor::State::ERROR &&
+ mState != StreamDescriptor::State::TRANSFERRING &&
+ mState != StreamDescriptor::State::TRANSFER_PAUSED) {
if (!write(fmqByteCount, &reply)) {
mState = StreamDescriptor::State::ERROR;
}
if (mState == StreamDescriptor::State::STANDBY ||
- mState == StreamDescriptor::State::DRAIN_PAUSED) {
- mState = StreamDescriptor::State::PAUSED;
+ mState == StreamDescriptor::State::DRAIN_PAUSED ||
+ mState == StreamDescriptor::State::PAUSED) {
+ if (mAsyncCallback == nullptr ||
+ mState != StreamDescriptor::State::DRAIN_PAUSED) {
+ mState = StreamDescriptor::State::PAUSED;
+ } else {
+ mState = StreamDescriptor::State::TRANSFER_PAUSED;
+ }
} else if (mState == StreamDescriptor::State::IDLE ||
- mState == StreamDescriptor::State::DRAINING) {
- mState = StreamDescriptor::State::ACTIVE;
- } // When in 'ACTIVE' and 'PAUSED' do not need to change the state.
+ mState == StreamDescriptor::State::DRAINING ||
+ mState == StreamDescriptor::State::ACTIVE) {
+ if (mAsyncCallback == nullptr || reply.fmqByteCount == fmqByteCount) {
+ mState = StreamDescriptor::State::ACTIVE;
+ } else {
+ switchToTransientState(StreamDescriptor::State::TRANSFERRING);
+ }
+ }
} else {
- LOG(WARNING) << __func__ << ": BURST command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
+ populateReplyWrongState(&reply, command);
}
} else {
LOG(WARNING) << __func__ << ": invalid burst byte count: " << fmqByteCount;
}
break;
case Tag::drain:
- LOG(DEBUG) << __func__ << ": received DRAIN write command";
- if (mState == StreamDescriptor::State::ACTIVE) {
- usleep(1000); // Simulate a blocking call into the driver.
- populateReply(&reply, mIsConnected);
- // Can switch the state to ERROR if a driver error occurs.
- mState = StreamDescriptor::State::IDLE;
- // Since there is no actual hardware that would be draining the buffer,
- // in order to simplify the reference code, we assume that draining
- // happens instantly, thus skipping the 'DRAINING' state.
- // TODO: Provide parametrization on the duration of draining to test
- // handling of commands during the 'DRAINING' state.
+ if (command.get<Tag::drain>() == StreamDescriptor::DrainMode::DRAIN_ALL ||
+ command.get<Tag::drain>() == StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY) {
+ if (mState == StreamDescriptor::State::ACTIVE ||
+ mState == StreamDescriptor::State::TRANSFERRING) {
+ usleep(1000); // Simulate a blocking call into the driver.
+ populateReply(&reply, mIsConnected);
+ // Can switch the state to ERROR if a driver error occurs.
+ switchToTransientState(StreamDescriptor::State::DRAINING);
+ } else if (mState == StreamDescriptor::State::TRANSFER_PAUSED) {
+ mState = StreamDescriptor::State::DRAIN_PAUSED;
+ populateReply(&reply, mIsConnected);
+ } else {
+ populateReplyWrongState(&reply, command);
+ }
} else {
- LOG(WARNING) << __func__ << ": DRAIN command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
+ LOG(WARNING) << __func__
+ << ": invalid drain mode: " << toString(command.get<Tag::drain>());
}
break;
case Tag::standby:
- LOG(DEBUG) << __func__ << ": received STANDBY write command";
if (mState == StreamDescriptor::State::IDLE) {
usleep(1000); // Simulate a blocking call into the driver.
populateReply(&reply, mIsConnected);
// Can switch the state to ERROR if a driver error occurs.
mState = StreamDescriptor::State::STANDBY;
} else {
- LOG(WARNING) << __func__ << ": STANDBY command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
+ populateReplyWrongState(&reply, command);
}
break;
- case Tag::pause:
- LOG(DEBUG) << __func__ << ": received PAUSE write command";
- if (mState == StreamDescriptor::State::ACTIVE ||
- mState == StreamDescriptor::State::DRAINING) {
+ case Tag::pause: {
+ bool commandAccepted = true;
+ switch (mState) {
+ case StreamDescriptor::State::ACTIVE:
+ mState = StreamDescriptor::State::PAUSED;
+ break;
+ case StreamDescriptor::State::DRAINING:
+ mState = StreamDescriptor::State::DRAIN_PAUSED;
+ break;
+ case StreamDescriptor::State::TRANSFERRING:
+ mState = StreamDescriptor::State::TRANSFER_PAUSED;
+ break;
+ default:
+ populateReplyWrongState(&reply, command);
+ commandAccepted = false;
+ }
+ if (commandAccepted) {
populateReply(&reply, mIsConnected);
- mState = mState == StreamDescriptor::State::ACTIVE
- ? StreamDescriptor::State::PAUSED
- : StreamDescriptor::State::DRAIN_PAUSED;
- } else {
- LOG(WARNING) << __func__ << ": PAUSE command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
}
- break;
+ } break;
case Tag::flush:
- LOG(DEBUG) << __func__ << ": received FLUSH write command";
if (mState == StreamDescriptor::State::PAUSED ||
- mState == StreamDescriptor::State::DRAIN_PAUSED) {
+ mState == StreamDescriptor::State::DRAIN_PAUSED ||
+ mState == StreamDescriptor::State::TRANSFER_PAUSED) {
populateReply(&reply, mIsConnected);
mState = StreamDescriptor::State::IDLE;
} else {
- LOG(WARNING) << __func__ << ": FLUSH command can not be handled in the state "
- << toString(mState);
- reply.status = STATUS_INVALID_OPERATION;
+ populateReplyWrongState(&reply, command);
}
break;
}
@@ -450,9 +506,8 @@
void StreamCommon<Metadata, StreamWorker>::stopWorker() {
if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) {
LOG(DEBUG) << __func__ << ": asking the worker to exit...";
- auto cmd =
- StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::hal_reserved_exit>(
- mContext.getInternalCommandCookie());
+ auto cmd = StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::halReservedExit>(
+ mContext.getInternalCommandCookie());
// Note: never call 'pause' and 'resume' methods of StreamWorker
// in the HAL implementation. These methods are to be used by
// the client side only. Preventing the worker loop from running
diff --git a/audio/aidl/default/config/audio_policy_configuration.xsd b/audio/aidl/default/config/audio_policy_configuration.xsd
index 823b217..2c18a1e 100644
--- a/audio/aidl/default/config/audio_policy_configuration.xsd
+++ b/audio/aidl/default/config/audio_policy_configuration.xsd
@@ -424,6 +424,8 @@
<xs:enumeration value="AUDIO_FORMAT_DRA"/>
<xs:enumeration value="AUDIO_FORMAT_APTX_ADAPTIVE_QLEA"/>
<xs:enumeration value="AUDIO_FORMAT_APTX_ADAPTIVE_R4"/>
+ <xs:enumeration value="AUDIO_FORMAT_DTS_HD_MA"/>
+ <xs:enumeration value="AUDIO_FORMAT_DTS_UHD_P2"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="extendableAudioFormat">
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 0086743..f7b85ed 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -86,6 +86,7 @@
void cleanUpPatch(int32_t patchId);
ndk::ScopedAStatus createStreamContext(
int32_t in_portConfigId, int64_t in_bufferSizeFrames,
+ std::shared_ptr<IStreamCallback> asyncCallback,
::aidl::android::hardware::audio::core::StreamContext* out_context);
ndk::ScopedAStatus findPortIdForNewStream(
int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port);
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index 5ee0f82..3c96973 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -17,6 +17,7 @@
#pragma once
#include <atomic>
+#include <chrono>
#include <cstdlib>
#include <map>
#include <memory>
@@ -28,6 +29,7 @@
#include <aidl/android/hardware/audio/common/SourceMetadata.h>
#include <aidl/android/hardware/audio/core/BnStreamIn.h>
#include <aidl/android/hardware/audio/core/BnStreamOut.h>
+#include <aidl/android/hardware/audio/core/IStreamCallback.h>
#include <aidl/android/hardware/audio/core/StreamDescriptor.h>
#include <aidl/android/media/audio/common/AudioOffloadInfo.h>
#include <fmq/AidlMessageQueue.h>
@@ -59,33 +61,42 @@
StreamContext() = default;
StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
- size_t frameSize, std::unique_ptr<DataMQ> dataMQ)
+ size_t frameSize, std::unique_ptr<DataMQ> dataMQ,
+ std::shared_ptr<IStreamCallback> asyncCallback, int transientStateDelayMs)
: mCommandMQ(std::move(commandMQ)),
mInternalCommandCookie(std::rand()),
mReplyMQ(std::move(replyMQ)),
mFrameSize(frameSize),
- mDataMQ(std::move(dataMQ)) {}
+ mDataMQ(std::move(dataMQ)),
+ mAsyncCallback(asyncCallback),
+ mTransientStateDelayMs(transientStateDelayMs) {}
StreamContext(StreamContext&& other)
: mCommandMQ(std::move(other.mCommandMQ)),
mInternalCommandCookie(other.mInternalCommandCookie),
mReplyMQ(std::move(other.mReplyMQ)),
mFrameSize(other.mFrameSize),
- mDataMQ(std::move(other.mDataMQ)) {}
+ mDataMQ(std::move(other.mDataMQ)),
+ mAsyncCallback(other.mAsyncCallback),
+ mTransientStateDelayMs(other.mTransientStateDelayMs) {}
StreamContext& operator=(StreamContext&& other) {
mCommandMQ = std::move(other.mCommandMQ);
mInternalCommandCookie = other.mInternalCommandCookie;
mReplyMQ = std::move(other.mReplyMQ);
mFrameSize = other.mFrameSize;
mDataMQ = std::move(other.mDataMQ);
+ mAsyncCallback = other.mAsyncCallback;
+ mTransientStateDelayMs = other.mTransientStateDelayMs;
return *this;
}
void fillDescriptor(StreamDescriptor* desc);
+ std::shared_ptr<IStreamCallback> getAsyncCallback() const { return mAsyncCallback; }
CommandMQ* getCommandMQ() const { return mCommandMQ.get(); }
DataMQ* getDataMQ() const { return mDataMQ.get(); }
size_t getFrameSize() const { return mFrameSize; }
int getInternalCommandCookie() const { return mInternalCommandCookie; }
ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
+ int getTransientStateDelayMs() const { return mTransientStateDelayMs; }
bool isValid() const;
void reset();
@@ -95,6 +106,8 @@
std::unique_ptr<ReplyMQ> mReplyMQ;
size_t mFrameSize;
std::unique_ptr<DataMQ> mDataMQ;
+ std::shared_ptr<IStreamCallback> mAsyncCallback;
+ int mTransientStateDelayMs;
};
class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic {
@@ -111,9 +124,17 @@
mFrameSize(context.getFrameSize()),
mCommandMQ(context.getCommandMQ()),
mReplyMQ(context.getReplyMQ()),
- mDataMQ(context.getDataMQ()) {}
+ mDataMQ(context.getDataMQ()),
+ mAsyncCallback(context.getAsyncCallback()),
+ mTransientStateDelayMs(context.getTransientStateDelayMs()) {}
std::string init() override;
void populateReply(StreamDescriptor::Reply* reply, bool isConnected) const;
+ void populateReplyWrongState(StreamDescriptor::Reply* reply,
+ const StreamDescriptor::Command& command) const;
+ void switchToTransientState(StreamDescriptor::State state) {
+ mState = state;
+ mTransientStateStart = std::chrono::steady_clock::now();
+ }
// Atomic fields are used both by the main and worker threads.
std::atomic<bool> mIsConnected = false;
@@ -125,6 +146,9 @@
StreamContext::CommandMQ* mCommandMQ;
StreamContext::ReplyMQ* mReplyMQ;
StreamContext::DataMQ* mDataMQ;
+ std::shared_ptr<IStreamCallback> mAsyncCallback;
+ const std::chrono::duration<int, std::milli> mTransientStateDelayMs;
+ std::chrono::time_point<std::chrono::steady_clock> mTransientStateStart;
// We use an array and the "size" field instead of a vector to be able to detect
// memory allocation issues.
std::unique_ptr<int8_t[]> mDataBuffer;
diff --git a/audio/aidl/vts/ModuleConfig.cpp b/audio/aidl/vts/ModuleConfig.cpp
index 33c5b72..c081402 100644
--- a/audio/aidl/vts/ModuleConfig.cpp
+++ b/audio/aidl/vts/ModuleConfig.cpp
@@ -125,21 +125,21 @@
return result;
}
+std::vector<AudioPort> ModuleConfig::getNonBlockingMixPorts(bool attachedOnly,
+ bool singlePort) const {
+ return findMixPorts(false /*isInput*/, singlePort, [&](const AudioPort& port) {
+ return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
+ AudioOutputFlags::NON_BLOCKING) &&
+ (!attachedOnly || !getAttachedSinkDevicesPortsForMixPort(port).empty());
+ });
+}
+
std::vector<AudioPort> ModuleConfig::getOffloadMixPorts(bool attachedOnly, bool singlePort) const {
- std::vector<AudioPort> result;
- const auto mixPorts = getMixPorts(false /*isInput*/);
- auto offloadPortIt = mixPorts.begin();
- while (offloadPortIt != mixPorts.end()) {
- offloadPortIt = std::find_if(offloadPortIt, mixPorts.end(), [&](const AudioPort& port) {
- return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
- AudioOutputFlags::COMPRESS_OFFLOAD) &&
- (!attachedOnly || !getAttachedSinkDevicesPortsForMixPort(port).empty());
- });
- if (offloadPortIt == mixPorts.end()) break;
- result.push_back(*offloadPortIt++);
- if (singlePort) break;
- }
- return result;
+ return findMixPorts(false /*isInput*/, singlePort, [&](const AudioPort& port) {
+ return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
+ AudioOutputFlags::COMPRESS_OFFLOAD) &&
+ (!attachedOnly || !getAttachedSinkDevicesPortsForMixPort(port).empty());
+ });
}
std::vector<AudioPort> ModuleConfig::getAttachedDevicesPortsForMixPort(
@@ -343,6 +343,19 @@
profile.sampleRates.empty() || profile.channelMasks.empty();
}
+std::vector<AudioPort> ModuleConfig::findMixPorts(
+ bool isInput, bool singlePort, std::function<bool(const AudioPort&)> pred) const {
+ std::vector<AudioPort> result;
+ const auto mixPorts = getMixPorts(isInput);
+ for (auto mixPortIt = mixPorts.begin(); mixPortIt != mixPorts.end();) {
+ mixPortIt = std::find_if(mixPortIt, mixPorts.end(), pred);
+ if (mixPortIt == mixPorts.end()) break;
+ result.push_back(*mixPortIt++);
+ if (singlePort) break;
+ }
+ return result;
+}
+
std::vector<AudioPortConfig> ModuleConfig::generateAudioMixPortConfigs(
const std::vector<AudioPort>& ports, bool isInput, bool singleProfile) const {
std::vector<AudioPortConfig> result;
diff --git a/audio/aidl/vts/ModuleConfig.h b/audio/aidl/vts/ModuleConfig.h
index dc109a7..a85aa7f 100644
--- a/audio/aidl/vts/ModuleConfig.h
+++ b/audio/aidl/vts/ModuleConfig.h
@@ -16,6 +16,7 @@
#pragma once
+#include <functional>
#include <optional>
#include <set>
#include <utility>
@@ -48,6 +49,8 @@
std::vector<aidl::android::media::audio::common::AudioPort> getMixPorts(bool isInput) const {
return isInput ? getInputMixPorts() : getOutputMixPorts();
}
+ std::vector<aidl::android::media::audio::common::AudioPort> getNonBlockingMixPorts(
+ bool attachedOnly, bool singlePort) const;
std::vector<aidl::android::media::audio::common::AudioPort> getOffloadMixPorts(
bool attachedOnly, bool singlePort) const;
@@ -121,6 +124,9 @@
std::string toString() const;
private:
+ std::vector<aidl::android::media::audio::common::AudioPort> findMixPorts(
+ bool isInput, bool singlePort,
+ std::function<bool(const aidl::android::media::audio::common::AudioPort&)> pred) const;
std::vector<aidl::android::media::audio::common::AudioPortConfig> generateAudioMixPortConfigs(
const std::vector<aidl::android::media::audio::common::AudioPort>& ports, bool isInput,
bool singleProfile) const;
diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
index 5e9aa7f..79b20fe 100644
--- a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
@@ -15,12 +15,16 @@
*/
#include <algorithm>
+#include <chrono>
#include <cmath>
+#include <condition_variable>
#include <limits>
#include <memory>
+#include <mutex>
#include <optional>
#include <set>
#include <string>
+#include <variant>
#include <vector>
#define LOG_TAG "VtsHalAudioCore"
@@ -30,6 +34,7 @@
#include <Utils.h>
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
+#include <aidl/android/hardware/audio/core/BnStreamCallback.h>
#include <aidl/android/hardware/audio/core/IModule.h>
#include <aidl/android/hardware/audio/core/ITelephony.h>
#include <aidl/android/media/audio/common/AudioIoFlags.h>
@@ -389,15 +394,132 @@
std::unique_ptr<DataMQ> mDataMQ;
};
-class StreamLogicDriver {
+struct StreamEventReceiver {
+ virtual ~StreamEventReceiver() = default;
+ enum class Event { None, DrainReady, Error, TransferReady };
+ virtual std::tuple<int, Event> getLastEvent() const = 0;
+ virtual std::tuple<int, Event> waitForEvent(int clientEventSeq) = 0;
+ static constexpr int kEventSeqInit = -1;
+};
+std::string toString(StreamEventReceiver::Event event) {
+ switch (event) {
+ case StreamEventReceiver::Event::None:
+ return "None";
+ case StreamEventReceiver::Event::DrainReady:
+ return "DrainReady";
+ case StreamEventReceiver::Event::Error:
+ return "Error";
+ case StreamEventReceiver::Event::TransferReady:
+ return "TransferReady";
+ }
+ return std::to_string(static_cast<int32_t>(event));
+}
+
+// Transition to the next state happens either due to a command from the client,
+// or after an event received from the server.
+using TransitionTrigger = std::variant<StreamDescriptor::Command, StreamEventReceiver::Event>;
+using StateTransition = std::pair<TransitionTrigger, StreamDescriptor::State>;
+struct StateSequence {
+ virtual ~StateSequence() = default;
+ virtual void rewind() = 0;
+ virtual bool done() const = 0;
+ virtual TransitionTrigger getTrigger() = 0;
+ virtual std::set<StreamDescriptor::State> getExpectedStates() = 0;
+ virtual void advance(StreamDescriptor::State state) = 0;
+};
+
+static const StreamDescriptor::Command kGetStatusCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::getStatus>(Void{});
+static const StreamDescriptor::Command kStartCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::start>(Void{});
+static const StreamDescriptor::Command kBurstCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::burst>(0);
+static const StreamDescriptor::Command kDrainInCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::drain>(
+ StreamDescriptor::DrainMode::DRAIN_UNSPECIFIED);
+static const StreamDescriptor::Command kDrainOutAllCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::drain>(
+ StreamDescriptor::DrainMode::DRAIN_ALL);
+static const StreamDescriptor::Command kDrainOutEarlyCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::drain>(
+ StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY);
+static const StreamDescriptor::Command kStandbyCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::standby>(Void{});
+static const StreamDescriptor::Command kPauseCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::pause>(Void{});
+static const StreamDescriptor::Command kFlushCommand =
+ StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::flush>(Void{});
+static const StreamEventReceiver::Event kTransferReadyEvent =
+ StreamEventReceiver::Event::TransferReady;
+static const StreamEventReceiver::Event kDrainReadyEvent = StreamEventReceiver::Event::DrainReady;
+
+// Handle possible bifurcations:
+// - on burst and on start: 'TRANSFERRING' -> {'ACTIVE', 'TRANSFERRING'}
+// - on pause: 'TRANSFER_PAUSED' -> {'PAUSED', 'TRANSFER_PAUSED'}
+// It is assumed that the 'steps' provided on the construction contain the sequence
+// for the async case, which gets corrected in the case when the HAL decided to do
+// a synchronous transfer.
+class SmartStateSequence : public StateSequence {
public:
+ explicit SmartStateSequence(const std::vector<StateTransition>& steps) : mSteps(steps) {}
+ explicit SmartStateSequence(std::vector<StateTransition>&& steps) : mSteps(std::move(steps)) {}
+ void rewind() override { mCurrentStep = 0; }
+ bool done() const override { return mCurrentStep >= mSteps.size(); }
+ TransitionTrigger getTrigger() override { return mSteps[mCurrentStep].first; }
+ std::set<StreamDescriptor::State> getExpectedStates() override {
+ std::set<StreamDescriptor::State> result = {getState()};
+ if (isBurstBifurcation() || isStartBifurcation()) {
+ result.insert(StreamDescriptor::State::ACTIVE);
+ } else if (isPauseBifurcation()) {
+ result.insert(StreamDescriptor::State::PAUSED);
+ }
+ return result;
+ }
+ void advance(StreamDescriptor::State state) override {
+ if (isBurstBifurcation() && state == StreamDescriptor::State::ACTIVE &&
+ mCurrentStep + 1 < mSteps.size() &&
+ mSteps[mCurrentStep + 1].first == TransitionTrigger{kTransferReadyEvent}) {
+ mCurrentStep++;
+ }
+ mCurrentStep++;
+ }
+
+ private:
+ StreamDescriptor::State getState() const { return mSteps[mCurrentStep].second; }
+ bool isBurstBifurcation() {
+ return getTrigger() == TransitionTrigger{kBurstCommand}&& getState() ==
+ StreamDescriptor::State::TRANSFERRING;
+ }
+ bool isPauseBifurcation() {
+ return getTrigger() == TransitionTrigger{kPauseCommand}&& getState() ==
+ StreamDescriptor::State::TRANSFER_PAUSED;
+ }
+ bool isStartBifurcation() {
+ return getTrigger() == TransitionTrigger{kStartCommand}&& getState() ==
+ StreamDescriptor::State::TRANSFERRING;
+ }
+ const std::vector<StateTransition> mSteps;
+ size_t mCurrentStep = 0;
+};
+
+std::string toString(const TransitionTrigger& trigger) {
+ if (std::holds_alternative<StreamDescriptor::Command>(trigger)) {
+ return std::string("'")
+ .append(toString(std::get<StreamDescriptor::Command>(trigger).getTag()))
+ .append("' command");
+ }
+ return std::string("'")
+ .append(toString(std::get<StreamEventReceiver::Event>(trigger)))
+ .append("' event");
+}
+
+struct StreamLogicDriver {
virtual ~StreamLogicDriver() = default;
// Return 'true' to stop the worker.
virtual bool done() = 0;
// For 'Writer' logic, if the 'actualSize' is 0, write is skipped.
// The 'fmqByteCount' from the returned command is passed as is to the HAL.
- virtual StreamDescriptor::Command getNextCommand(int maxDataSize,
- int* actualSize = nullptr) = 0;
+ virtual TransitionTrigger getNextTrigger(int maxDataSize, int* actualSize = nullptr) = 0;
// Return 'true' to indicate that no further processing is needed,
// for example, the driver is expecting a bad status to be returned.
// The logic cycle will return with 'CONTINUE' status. Otherwise,
@@ -410,46 +532,102 @@
class StreamCommonLogic : public StreamLogic {
protected:
- StreamCommonLogic(const StreamContext& context, StreamLogicDriver* driver)
+ StreamCommonLogic(const StreamContext& context, StreamLogicDriver* driver,
+ StreamEventReceiver* eventReceiver)
: mCommandMQ(context.getCommandMQ()),
mReplyMQ(context.getReplyMQ()),
mDataMQ(context.getDataMQ()),
mData(context.getBufferSizeBytes()),
- mDriver(driver) {}
+ mDriver(driver),
+ mEventReceiver(eventReceiver) {}
StreamContext::CommandMQ* getCommandMQ() const { return mCommandMQ; }
StreamContext::ReplyMQ* getReplyMQ() const { return mReplyMQ; }
+ StreamContext::DataMQ* getDataMQ() const { return mDataMQ; }
StreamLogicDriver* getDriver() const { return mDriver; }
+ StreamEventReceiver* getEventReceiver() const { return mEventReceiver; }
- std::string init() override { return ""; }
+ std::string init() override {
+ LOG(DEBUG) << __func__;
+ return "";
+ }
+ std::optional<StreamDescriptor::Command> maybeGetNextCommand(int* actualSize = nullptr) {
+ TransitionTrigger trigger = mDriver->getNextTrigger(mData.size(), actualSize);
+ if (StreamEventReceiver::Event* expEvent =
+ std::get_if<StreamEventReceiver::Event>(&trigger);
+ expEvent != nullptr) {
+ auto [eventSeq, event] = mEventReceiver->waitForEvent(mLastEventSeq);
+ mLastEventSeq = eventSeq;
+ if (event != *expEvent) {
+ LOG(ERROR) << __func__ << ": expected event " << toString(*expEvent) << ", got "
+ << toString(event);
+ return {};
+ }
+ // If we were waiting for an event, the new stream state must be retrieved
+ // via 'getStatus'.
+ return StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::getStatus>(
+ Void{});
+ }
+ return std::get<StreamDescriptor::Command>(trigger);
+ }
+ bool readDataFromMQ(size_t readCount) {
+ std::vector<int8_t> data(readCount);
+ if (mDataMQ->read(data.data(), readCount)) {
+ memcpy(mData.data(), data.data(), std::min(mData.size(), data.size()));
+ return true;
+ }
+ LOG(ERROR) << __func__ << ": reading of " << readCount << " bytes from MQ failed";
+ return false;
+ }
+ bool writeDataToMQ() {
+ if (mDataMQ->write(mData.data(), mData.size())) {
+ return true;
+ }
+ LOG(ERROR) << __func__ << ": writing of " << mData.size() << " bytes to MQ failed";
+ return false;
+ }
+ private:
StreamContext::CommandMQ* mCommandMQ;
StreamContext::ReplyMQ* mReplyMQ;
StreamContext::DataMQ* mDataMQ;
std::vector<int8_t> mData;
StreamLogicDriver* const mDriver;
+ StreamEventReceiver* const mEventReceiver;
+ int mLastEventSeq = StreamEventReceiver::kEventSeqInit;
};
class StreamReaderLogic : public StreamCommonLogic {
public:
- StreamReaderLogic(const StreamContext& context, StreamLogicDriver* driver)
- : StreamCommonLogic(context, driver) {}
+ StreamReaderLogic(const StreamContext& context, StreamLogicDriver* driver,
+ StreamEventReceiver* eventReceiver)
+ : StreamCommonLogic(context, driver, eventReceiver) {}
protected:
Status cycle() override {
if (getDriver()->done()) {
+ LOG(DEBUG) << __func__ << ": clean exit";
return Status::EXIT;
}
- StreamDescriptor::Command command = getDriver()->getNextCommand(mData.size());
- if (!mCommandMQ->writeBlocking(&command, 1)) {
+ StreamDescriptor::Command command;
+ if (auto maybeCommand = maybeGetNextCommand(); maybeCommand.has_value()) {
+ command = std::move(maybeCommand.value());
+ } else {
+ LOG(ERROR) << __func__ << ": no next command";
+ return Status::ABORT;
+ }
+ LOG(DEBUG) << "Writing command: " << command.toString();
+ if (!getCommandMQ()->writeBlocking(&command, 1)) {
LOG(ERROR) << __func__ << ": writing of command into MQ failed";
return Status::ABORT;
}
StreamDescriptor::Reply reply{};
- if (!mReplyMQ->readBlocking(&reply, 1)) {
- LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
+ LOG(DEBUG) << "Reading reply...";
+ if (!getReplyMQ()->readBlocking(&reply, 1)) {
return Status::ABORT;
}
+ LOG(DEBUG) << "Reply received: " << reply.toString();
if (getDriver()->interceptRawReply(reply)) {
+ LOG(DEBUG) << __func__ << ": reply has been intercepted by the driver";
return Status::CONTINUE;
}
if (reply.status != STATUS_OK) {
@@ -463,11 +641,11 @@
<< ": received invalid byte count in the reply: " << reply.fmqByteCount;
return Status::ABORT;
}
- if (static_cast<size_t>(reply.fmqByteCount) != mDataMQ->availableToRead()) {
+ if (static_cast<size_t>(reply.fmqByteCount) != getDataMQ()->availableToRead()) {
LOG(ERROR) << __func__
<< ": the byte count in the reply is not the same as the amount of "
<< "data available in the MQ: " << reply.fmqByteCount
- << " != " << mDataMQ->availableToRead();
+ << " != " << getDataMQ()->availableToRead();
}
if (reply.latencyMs < 0 && reply.latencyMs != StreamDescriptor::LATENCY_UNKNOWN) {
LOG(ERROR) << __func__ << ": received invalid latency value: " << reply.latencyMs;
@@ -484,10 +662,8 @@
return Status::ABORT;
}
const bool acceptedReply = getDriver()->processValidReply(reply);
- if (const size_t readCount = mDataMQ->availableToRead(); readCount > 0) {
- std::vector<int8_t> data(readCount);
- if (mDataMQ->read(data.data(), readCount)) {
- memcpy(mData.data(), data.data(), std::min(mData.size(), data.size()));
+ if (const size_t readCount = getDataMQ()->availableToRead(); readCount > 0) {
+ if (readDataFromMQ(readCount)) {
goto checkAcceptedReply;
}
LOG(ERROR) << __func__ << ": reading of " << readCount << " data bytes from MQ failed";
@@ -505,29 +681,39 @@
class StreamWriterLogic : public StreamCommonLogic {
public:
- StreamWriterLogic(const StreamContext& context, StreamLogicDriver* driver)
- : StreamCommonLogic(context, driver) {}
+ StreamWriterLogic(const StreamContext& context, StreamLogicDriver* driver,
+ StreamEventReceiver* eventReceiver)
+ : StreamCommonLogic(context, driver, eventReceiver) {}
protected:
Status cycle() override {
if (getDriver()->done()) {
+ LOG(DEBUG) << __func__ << ": clean exit";
return Status::EXIT;
}
int actualSize = 0;
- StreamDescriptor::Command command = getDriver()->getNextCommand(mData.size(), &actualSize);
- if (actualSize != 0 && !mDataMQ->write(mData.data(), mData.size())) {
- LOG(ERROR) << __func__ << ": writing of " << mData.size() << " bytes to MQ failed";
+ StreamDescriptor::Command command;
+ if (auto maybeCommand = maybeGetNextCommand(&actualSize); maybeCommand.has_value()) {
+ command = std::move(maybeCommand.value());
+ } else {
+ LOG(ERROR) << __func__ << ": no next command";
return Status::ABORT;
}
- if (!mCommandMQ->writeBlocking(&command, 1)) {
+ if (actualSize != 0 && !writeDataToMQ()) {
+ return Status::ABORT;
+ }
+ LOG(DEBUG) << "Writing command: " << command.toString();
+ if (!getCommandMQ()->writeBlocking(&command, 1)) {
LOG(ERROR) << __func__ << ": writing of command into MQ failed";
return Status::ABORT;
}
StreamDescriptor::Reply reply{};
- if (!mReplyMQ->readBlocking(&reply, 1)) {
+ LOG(DEBUG) << "Reading reply...";
+ if (!getReplyMQ()->readBlocking(&reply, 1)) {
LOG(ERROR) << __func__ << ": reading of reply from MQ failed";
return Status::ABORT;
}
+ LOG(DEBUG) << "Reply received: " << reply.toString();
if (getDriver()->interceptRawReply(reply)) {
return Status::CONTINUE;
}
@@ -542,10 +728,10 @@
<< ": received invalid byte count in the reply: " << reply.fmqByteCount;
return Status::ABORT;
}
- if (mDataMQ->availableToWrite() != mDataMQ->getQuantumCount()) {
+ if (getDataMQ()->availableToWrite() != getDataMQ()->getQuantumCount()) {
LOG(ERROR) << __func__ << ": the HAL module did not consume all data from the data MQ: "
- << "available to write " << mDataMQ->availableToWrite()
- << ", total size: " << mDataMQ->getQuantumCount();
+ << "available to write " << getDataMQ()->availableToWrite()
+ << ", total size: " << getDataMQ()->getQuantumCount();
return Status::ABORT;
}
if (reply.latencyMs < 0 && reply.latencyMs != StreamDescriptor::LATENCY_UNKNOWN) {
@@ -571,6 +757,71 @@
};
using StreamWriter = StreamWorker<StreamWriterLogic>;
+class DefaultStreamCallback : public ::aidl::android::hardware::audio::core::BnStreamCallback,
+ public StreamEventReceiver {
+ ndk::ScopedAStatus onTransferReady() override {
+ LOG(DEBUG) << __func__;
+ putLastEvent(Event::TransferReady);
+ return ndk::ScopedAStatus::ok();
+ }
+ ndk::ScopedAStatus onError() override {
+ LOG(DEBUG) << __func__;
+ putLastEvent(Event::Error);
+ return ndk::ScopedAStatus::ok();
+ }
+ ndk::ScopedAStatus onDrainReady() override {
+ LOG(DEBUG) << __func__;
+ putLastEvent(Event::DrainReady);
+ return ndk::ScopedAStatus::ok();
+ }
+
+ public:
+ // To avoid timing out the whole test suite in case no event is received
+ // from the HAL, use a local timeout for event waiting.
+ static constexpr auto kEventTimeoutMs = std::chrono::milliseconds(1000);
+
+ StreamEventReceiver* getEventReceiver() { return this; }
+ std::tuple<int, Event> getLastEvent() const override {
+ std::lock_guard l(mLock);
+ return getLastEvent_l();
+ }
+ std::tuple<int, Event> waitForEvent(int clientEventSeq) override {
+ std::unique_lock l(mLock);
+ android::base::ScopedLockAssertion lock_assertion(mLock);
+ LOG(DEBUG) << __func__ << ": client " << clientEventSeq << ", last " << mLastEventSeq;
+ if (mCv.wait_for(l, kEventTimeoutMs, [&]() {
+ android::base::ScopedLockAssertion lock_assertion(mLock);
+ return clientEventSeq < mLastEventSeq;
+ })) {
+ } else {
+ LOG(WARNING) << __func__ << ": timed out waiting for an event";
+ putLastEvent_l(Event::None);
+ }
+ return getLastEvent_l();
+ }
+
+ private:
+ std::tuple<int, Event> getLastEvent_l() const REQUIRES(mLock) {
+ return std::make_tuple(mLastEventSeq, mLastEvent);
+ }
+ void putLastEvent(Event event) {
+ {
+ std::lock_guard l(mLock);
+ putLastEvent_l(event);
+ }
+ mCv.notify_one();
+ }
+ void putLastEvent_l(Event event) REQUIRES(mLock) {
+ mLastEventSeq++;
+ mLastEvent = event;
+ }
+
+ mutable std::mutex mLock;
+ std::condition_variable mCv;
+ int mLastEventSeq GUARDED_BY(mLock) = kEventSeqInit;
+ Event mLastEvent GUARDED_BY(mLock) = Event::None;
+};
+
template <typename T>
struct IOTraits {
static constexpr bool is_input = std::is_same_v<T, IStreamIn>;
@@ -607,6 +858,7 @@
}
Stream* get() const { return mStream.get(); }
const StreamContext* getContext() const { return mContext ? &(mContext.value()) : nullptr; }
+ StreamEventReceiver* getEventReceiver() { return mStreamCallback->getEventReceiver(); }
std::shared_ptr<Stream> getSharedPointer() const { return mStream; }
const AudioPortConfig& getPortConfig() const { return mPortConfig.get(); }
int32_t getPortId() const { return mPortConfig.getId(); }
@@ -616,6 +868,7 @@
std::shared_ptr<Stream> mStream;
StreamDescriptor mDescriptor;
std::optional<StreamContext> mContext;
+ std::shared_ptr<DefaultStreamCallback> mStreamCallback;
};
SinkMetadata GenerateSinkMetadata(const AudioPortConfig& portConfig) {
@@ -636,11 +889,15 @@
args.portConfigId = portConfig.id;
args.sinkMetadata = GenerateSinkMetadata(portConfig);
args.bufferSizeFrames = bufferSizeFrames;
+ auto callback = ndk::SharedRefBase::make<DefaultStreamCallback>();
+ // TODO: Uncomment when support for asynchronous input is implemented.
+ // args.callback = callback;
aidl::android::hardware::audio::core::IModule::OpenInputStreamReturn ret;
ScopedAStatus status = module->openInputStream(args, &ret);
if (status.isOk()) {
mStream = std::move(ret.stream);
mDescriptor = std::move(ret.desc);
+ mStreamCallback = std::move(callback);
}
return status;
}
@@ -665,11 +922,14 @@
args.sourceMetadata = GenerateSourceMetadata(portConfig);
args.offloadInfo = ModuleConfig::generateOffloadInfoIfNeeded(portConfig);
args.bufferSizeFrames = bufferSizeFrames;
+ auto callback = ndk::SharedRefBase::make<DefaultStreamCallback>();
+ args.callback = callback;
aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
ScopedAStatus status = module->openOutputStream(args, &ret);
if (status.isOk()) {
mStream = std::move(ret.stream);
mDescriptor = std::move(ret.desc);
+ mStreamCallback = std::move(callback);
}
return status;
}
@@ -1379,10 +1639,10 @@
}
}
+using CommandSequence = std::vector<StreamDescriptor::Command>;
class StreamLogicDriverInvalidCommand : public StreamLogicDriver {
public:
- StreamLogicDriverInvalidCommand(const std::vector<StreamDescriptor::Command>& commands)
- : mCommands(commands) {}
+ StreamLogicDriverInvalidCommand(const CommandSequence& commands) : mCommands(commands) {}
std::string getUnexpectedStatuses() {
// This method is intended to be called after the worker thread has joined,
@@ -1396,25 +1656,29 @@
}
bool done() override { return mNextCommand >= mCommands.size(); }
- StreamDescriptor::Command getNextCommand(int, int* actualSize) override {
+ TransitionTrigger getNextTrigger(int, int* actualSize) override {
if (actualSize != nullptr) *actualSize = 0;
return mCommands[mNextCommand++];
}
bool interceptRawReply(const StreamDescriptor::Reply& reply) override {
- if (reply.status != STATUS_BAD_VALUE) {
- std::string s = mCommands[mNextCommand - 1].toString();
+ const size_t currentCommand = mNextCommand - 1; // increased by getNextTrigger
+ const bool isLastCommand = currentCommand == mCommands.size() - 1;
+ // All but the last command should run correctly. The last command must return 'BAD_VALUE'
+ // status.
+ if ((!isLastCommand && reply.status != STATUS_OK) ||
+ (isLastCommand && reply.status != STATUS_BAD_VALUE)) {
+ std::string s = mCommands[currentCommand].toString();
s.append(", ").append(statusToString(reply.status));
mStatuses.push_back(std::move(s));
- // If the HAL does not recognize the command as invalid,
- // retrieve the data etc.
- return reply.status != STATUS_OK;
+ // Process the reply, since the worker exits in case of an error.
+ return false;
}
- return true;
+ return isLastCommand;
}
bool processValidReply(const StreamDescriptor::Reply&) override { return true; }
private:
- const std::vector<StreamDescriptor::Command> mCommands;
+ const CommandSequence mCommands;
size_t mNextCommand = 0;
std::vector<std::string> mStatuses;
};
@@ -1556,22 +1820,46 @@
}
void SendInvalidCommandImpl(const AudioPortConfig& portConfig) {
- std::vector<StreamDescriptor::Command> commands = {
- StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::hal_reserved_exit>(
- 0),
- // TODO: For proper testing of input streams, need to put the stream into
- // a state which accepts BURST commands.
- StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::burst>(-1),
- StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::burst>(
- std::numeric_limits<int32_t>::min()),
- };
- WithStream<Stream> stream(portConfig);
- ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
- StreamLogicDriverInvalidCommand driver(commands);
- typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver);
- ASSERT_TRUE(worker.start());
- worker.join();
- EXPECT_EQ("", driver.getUnexpectedStatuses());
+ using TestSequence = std::pair<std::string, CommandSequence>;
+ // The last command in 'CommandSequence' is the one that must trigger
+ // an error status. All preceding commands are to put the state machine
+ // into a state which accepts the last command.
+ std::vector<TestSequence> sequences{
+ std::make_pair(std::string("HalReservedExit"),
+ std::vector{StreamDescriptor::Command::make<
+ StreamDescriptor::Command::Tag::halReservedExit>(0)}),
+ std::make_pair(std::string("BurstNeg"),
+ std::vector{kStartCommand,
+ StreamDescriptor::Command::make<
+ StreamDescriptor::Command::Tag::burst>(-1)}),
+ std::make_pair(
+ std::string("BurstMinInt"),
+ std::vector{kStartCommand, StreamDescriptor::Command::make<
+ StreamDescriptor::Command::Tag::burst>(
+ std::numeric_limits<int32_t>::min())})};
+ if (IOTraits<Stream>::is_input) {
+ sequences.emplace_back("DrainAll",
+ std::vector{kStartCommand, kBurstCommand, kDrainOutAllCommand});
+ sequences.emplace_back(
+ "DrainEarly", std::vector{kStartCommand, kBurstCommand, kDrainOutEarlyCommand});
+ } else {
+ sequences.emplace_back("DrainUnspecified",
+ std::vector{kStartCommand, kBurstCommand, kDrainInCommand});
+ }
+ for (const auto& seq : sequences) {
+ SCOPED_TRACE(std::string("Sequence ").append(seq.first));
+ LOG(DEBUG) << __func__ << ": Sequence " << seq.first;
+ WithStream<Stream> stream(portConfig);
+ ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
+ StreamLogicDriverInvalidCommand driver(seq.second);
+ typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver,
+ stream.getEventReceiver());
+ LOG(DEBUG) << __func__ << ": starting worker...";
+ ASSERT_TRUE(worker.start());
+ LOG(DEBUG) << __func__ << ": joining worker...";
+ worker.join();
+ EXPECT_EQ("", driver.getUnexpectedStatuses());
+ }
}
};
using AudioStreamIn = AudioStream<IStreamIn>;
@@ -1615,27 +1903,51 @@
GTEST_SKIP()
<< "No mix port for compressed offload that could be routed to attached devices";
}
- const auto portConfig =
- moduleConfig->getSingleConfigForMixPort(false, *offloadMixPorts.begin());
- ASSERT_TRUE(portConfig.has_value())
- << "No profiles specified for the compressed offload mix port";
+ const auto config = moduleConfig->getSingleConfigForMixPort(false, *offloadMixPorts.begin());
+ ASSERT_TRUE(config.has_value()) << "No profiles specified for the compressed offload mix port";
+ WithAudioPortConfig portConfig(config.value());
+ ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get()));
StreamDescriptor descriptor;
std::shared_ptr<IStreamOut> ignored;
aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
- args.portConfigId = portConfig.value().id;
- args.sourceMetadata = GenerateSourceMetadata(portConfig.value());
+ args.portConfigId = portConfig.getId();
+ args.sourceMetadata = GenerateSourceMetadata(portConfig.get());
args.bufferSizeFrames = kDefaultBufferSizeFrames;
aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openOutputStream(args, &ret))
<< "when no offload info is provided for a compressed offload mix port";
}
-using CommandAndState = std::pair<StreamDescriptor::Command, StreamDescriptor::State>;
+TEST_P(AudioStreamOut, RequireAsyncCallback) {
+ const auto nonBlockingMixPorts =
+ moduleConfig->getNonBlockingMixPorts(true /*attachedOnly*/, true /*singlePort*/);
+ if (nonBlockingMixPorts.empty()) {
+ GTEST_SKIP()
+ << "No mix port for non-blocking output that could be routed to attached devices";
+ }
+ const auto config =
+ moduleConfig->getSingleConfigForMixPort(false, *nonBlockingMixPorts.begin());
+ ASSERT_TRUE(config.has_value()) << "No profiles specified for the non-blocking mix port";
+ WithAudioPortConfig portConfig(config.value());
+ ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get()));
+ StreamDescriptor descriptor;
+ std::shared_ptr<IStreamOut> ignored;
+ aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
+ args.portConfigId = portConfig.getId();
+ args.sourceMetadata = GenerateSourceMetadata(portConfig.get());
+ args.offloadInfo = ModuleConfig::generateOffloadInfoIfNeeded(portConfig.get());
+ args.bufferSizeFrames = kDefaultBufferSizeFrames;
+ aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
+ EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openOutputStream(args, &ret))
+ << "when no async callback is provided for a non-blocking mix port";
+}
class StreamLogicDefaultDriver : public StreamLogicDriver {
public:
- explicit StreamLogicDefaultDriver(const std::vector<CommandAndState>& commands)
- : mCommands(commands) {}
+ explicit StreamLogicDefaultDriver(std::shared_ptr<StateSequence> commands)
+ : mCommands(commands) {
+ mCommands->rewind();
+ }
// The three methods below is intended to be called after the worker
// thread has joined, thus no extra synchronization is needed.
@@ -1643,59 +1955,72 @@
bool hasRetrogradeObservablePosition() const { return mRetrogradeObservablePosition; }
std::string getUnexpectedStateTransition() const { return mUnexpectedTransition; }
- bool done() override { return mNextCommand >= mCommands.size(); }
- StreamDescriptor::Command getNextCommand(int maxDataSize, int* actualSize) override {
- auto command = mCommands[mNextCommand++].first;
- if (command.getTag() == StreamDescriptor::Command::Tag::burst) {
- if (actualSize != nullptr) {
- // In the output scenario, reduce slightly the fmqByteCount to verify
- // that the HAL module always consumes all data from the MQ.
- if (maxDataSize > 1) maxDataSize--;
- *actualSize = maxDataSize;
+ bool done() override { return mCommands->done(); }
+ TransitionTrigger getNextTrigger(int maxDataSize, int* actualSize) override {
+ auto trigger = mCommands->getTrigger();
+ if (StreamDescriptor::Command* command = std::get_if<StreamDescriptor::Command>(&trigger);
+ command != nullptr) {
+ if (command->getTag() == StreamDescriptor::Command::Tag::burst) {
+ if (actualSize != nullptr) {
+ // In the output scenario, reduce slightly the fmqByteCount to verify
+ // that the HAL module always consumes all data from the MQ.
+ if (maxDataSize > 1) maxDataSize--;
+ *actualSize = maxDataSize;
+ }
+ command->set<StreamDescriptor::Command::Tag::burst>(maxDataSize);
+ } else {
+ if (actualSize != nullptr) *actualSize = 0;
}
- command.set<StreamDescriptor::Command::Tag::burst>(maxDataSize);
- } else {
- if (actualSize != nullptr) *actualSize = 0;
}
- return command;
+ return trigger;
}
bool interceptRawReply(const StreamDescriptor::Reply&) override { return false; }
bool processValidReply(const StreamDescriptor::Reply& reply) override {
- if (mPreviousFrames.has_value()) {
- if (reply.observable.frames > mPreviousFrames.value()) {
- mObservablePositionIncrease = true;
- } else if (reply.observable.frames < mPreviousFrames.value()) {
- mRetrogradeObservablePosition = true;
+ if (reply.observable.frames != StreamDescriptor::Position::UNKNOWN) {
+ if (mPreviousFrames.has_value()) {
+ if (reply.observable.frames > mPreviousFrames.value()) {
+ mObservablePositionIncrease = true;
+ } else if (reply.observable.frames < mPreviousFrames.value()) {
+ mRetrogradeObservablePosition = true;
+ }
}
+ mPreviousFrames = reply.observable.frames;
}
- mPreviousFrames = reply.observable.frames;
- const auto& lastCommandState = mCommands[mNextCommand - 1];
- if (lastCommandState.second != reply.state) {
- std::string s = std::string("Unexpected transition from the state ")
- .append(mPreviousState)
- .append(" to ")
- .append(toString(reply.state))
- .append(" caused by the command ")
- .append(lastCommandState.first.toString());
+ auto expected = mCommands->getExpectedStates();
+ if (expected.count(reply.state) == 0) {
+ std::string s =
+ std::string("Unexpected transition from the state ")
+ .append(mPreviousState.has_value() ? toString(mPreviousState.value())
+ : "<initial state>")
+ .append(" to ")
+ .append(toString(reply.state))
+ .append(" (expected one of ")
+ .append(::android::internal::ToString(expected))
+ .append(") caused by the ")
+ .append(toString(mCommands->getTrigger()));
LOG(ERROR) << __func__ << ": " << s;
mUnexpectedTransition = std::move(s);
return false;
}
+ mCommands->advance(reply.state);
+ mPreviousState = reply.state;
return true;
}
protected:
- const std::vector<CommandAndState>& mCommands;
- size_t mNextCommand = 0;
+ std::shared_ptr<StateSequence> mCommands;
+ std::optional<StreamDescriptor::State> mPreviousState;
std::optional<int64_t> mPreviousFrames;
- std::string mPreviousState = "<initial state>";
bool mObservablePositionIncrease = false;
bool mRetrogradeObservablePosition = false;
std::string mUnexpectedTransition;
};
-using NamedCommandSequence = std::pair<std::string, std::vector<CommandAndState>>;
+enum { NAMED_CMD_NAME, NAMED_CMD_DELAY_MS, NAMED_CMD_STREAM_TYPE, NAMED_CMD_CMDS };
+enum class StreamTypeFilter { ANY, SYNC, ASYNC };
+using NamedCommandSequence =
+ std::tuple<std::string, int, StreamTypeFilter, std::shared_ptr<StateSequence>>;
enum { PARAM_MODULE_NAME, PARAM_CMD_SEQ, PARAM_SETUP_SEQ };
using StreamIoTestParameters =
std::tuple<std::string /*moduleName*/, NamedCommandSequence, bool /*useSetupSequence2*/>;
@@ -1716,7 +2041,29 @@
}
for (const auto& portConfig : allPortConfigs) {
SCOPED_TRACE(portConfig.toString());
- const auto& commandsAndStates = std::get<PARAM_CMD_SEQ>(GetParam()).second;
+ const bool isNonBlocking =
+ IOTraits<Stream>::is_input
+ ? false
+ :
+ // TODO: Uncomment when support for asynchronous input is implemented.
+ /*isBitPositionFlagSet(
+ portConfig.flags.value().template get<AudioIoFlags::Tag::input>(),
+ AudioInputFlags::NON_BLOCKING) :*/
+ isBitPositionFlagSet(portConfig.flags.value()
+ .template get<AudioIoFlags::Tag::output>(),
+ AudioOutputFlags::NON_BLOCKING);
+ if (auto streamType =
+ std::get<NAMED_CMD_STREAM_TYPE>(std::get<PARAM_CMD_SEQ>(GetParam()));
+ (isNonBlocking && streamType == StreamTypeFilter::SYNC) ||
+ (!isNonBlocking && streamType == StreamTypeFilter::ASYNC)) {
+ continue;
+ }
+ WithDebugFlags delayTransientStates = WithDebugFlags::createNested(debug);
+ delayTransientStates.flags().streamTransientStateDelayMs =
+ std::get<NAMED_CMD_DELAY_MS>(std::get<PARAM_CMD_SEQ>(GetParam()));
+ ASSERT_NO_FATAL_FAILURE(delayTransientStates.SetUp(module.get()));
+ const auto& commandsAndStates =
+ std::get<NAMED_CMD_CMDS>(std::get<PARAM_CMD_SEQ>(GetParam()));
if (!std::get<PARAM_SETUP_SEQ>(GetParam())) {
ASSERT_NO_FATAL_FAILURE(RunStreamIoCommandsImplSeq1(portConfig, commandsAndStates));
} else {
@@ -1732,7 +2079,7 @@
// Set up a patch first, then open a stream.
void RunStreamIoCommandsImplSeq1(const AudioPortConfig& portConfig,
- const std::vector<CommandAndState>& commandsAndStates) {
+ std::shared_ptr<StateSequence> commandsAndStates) {
auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
IOTraits<Stream>::is_input, portConfig);
ASSERT_FALSE(devicePorts.empty());
@@ -1743,9 +2090,12 @@
WithStream<Stream> stream(patch.getPortConfig(IOTraits<Stream>::is_input));
ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
StreamLogicDefaultDriver driver(commandsAndStates);
- typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver);
+ typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver,
+ stream.getEventReceiver());
+ LOG(DEBUG) << __func__ << ": starting worker...";
ASSERT_TRUE(worker.start());
+ LOG(DEBUG) << __func__ << ": joining worker...";
worker.join();
EXPECT_FALSE(worker.hasError()) << worker.getError();
EXPECT_EQ("", driver.getUnexpectedStateTransition());
@@ -1757,11 +2107,12 @@
// Open a stream, then set up a patch for it.
void RunStreamIoCommandsImplSeq2(const AudioPortConfig& portConfig,
- const std::vector<CommandAndState>& commandsAndStates) {
+ std::shared_ptr<StateSequence> commandsAndStates) {
WithStream<Stream> stream(portConfig);
ASSERT_NO_FATAL_FAILURE(stream.SetUp(module.get(), kDefaultBufferSizeFrames));
StreamLogicDefaultDriver driver(commandsAndStates);
- typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver);
+ typename IOTraits<Stream>::Worker worker(*stream.getContext(), &driver,
+ stream.getEventReceiver());
auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort(
IOTraits<Stream>::is_input, portConfig);
@@ -1770,7 +2121,9 @@
WithAudioPatch patch(IOTraits<Stream>::is_input, stream.getPortConfig(), devicePortConfig);
ASSERT_NO_FATAL_FAILURE(patch.SetUp(module.get()));
+ LOG(DEBUG) << __func__ << ": starting worker...";
ASSERT_TRUE(worker.start());
+ LOG(DEBUG) << __func__ << ": joining worker...";
worker.join();
EXPECT_FALSE(worker.hasError()) << worker.getError();
EXPECT_EQ("", driver.getUnexpectedStateTransition());
@@ -1975,103 +2328,210 @@
android::PrintInstanceNameToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioStreamOut);
-static const StreamDescriptor::Command kStartCommand =
- StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::start>(Void{});
-static const StreamDescriptor::Command kBurstCommand =
- StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::burst>(0);
-static const StreamDescriptor::Command kDrainCommand =
- StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::drain>(Void{});
-static const StreamDescriptor::Command kStandbyCommand =
- StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::standby>(Void{});
-static const StreamDescriptor::Command kPauseCommand =
- StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::pause>(Void{});
-static const StreamDescriptor::Command kFlushCommand =
- StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::flush>(Void{});
-static const NamedCommandSequence kReadOrWriteSeq =
- std::make_pair(std::string("ReadOrWrite"),
- std::vector<CommandAndState>{
- std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
- std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
- std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
- std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE)});
-static const NamedCommandSequence kDrainInSeq =
- std::make_pair(std::string("Drain"),
- std::vector<CommandAndState>{
- std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
- std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
- std::make_pair(kDrainCommand, StreamDescriptor::State::DRAINING),
- std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE),
- std::make_pair(kDrainCommand, StreamDescriptor::State::DRAINING),
- // TODO: This will need to be changed once DRAIN starts taking time.
- std::make_pair(kBurstCommand, StreamDescriptor::State::STANDBY)});
-static const NamedCommandSequence kDrainOutSeq =
- std::make_pair(std::string("Drain"),
- std::vector<CommandAndState>{
- std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
- std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
- // TODO: This will need to be changed once DRAIN starts taking time.
- std::make_pair(kDrainCommand, StreamDescriptor::State::IDLE)});
-// TODO: This will need to be changed once DRAIN starts taking time so we can pause it.
-static const NamedCommandSequence kDrainPauseOutSeq = std::make_pair(
- std::string("DrainPause"),
- std::vector<CommandAndState>{std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
- std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
- std::make_pair(kDrainCommand, StreamDescriptor::State::IDLE)});
-static const NamedCommandSequence kStandbySeq =
- std::make_pair(std::string("Standby"),
- std::vector<CommandAndState>{
- std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
- std::make_pair(kStandbyCommand, StreamDescriptor::State::STANDBY),
- // Perform a read or write in order to advance observable position
- // (this is verified by tests).
- std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
- std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE)});
+// This is the value used in test sequences for which the test needs to ensure
+// that the HAL stays in a transient state long enough to receive the next command.
+static const int kStreamTransientStateTransitionDelayMs = 3000;
+
+// TODO: Add async test cases for input once it is implemented.
+
+std::shared_ptr<StateSequence> makeBurstCommands(bool isSync, size_t burstCount) {
+ const auto burst =
+ isSync ? std::vector<StateTransition>{std::make_pair(kBurstCommand,
+ StreamDescriptor::State::ACTIVE)}
+ : std::vector<StateTransition>{
+ std::make_pair(kBurstCommand, StreamDescriptor::State::TRANSFERRING),
+ std::make_pair(kTransferReadyEvent, StreamDescriptor::State::ACTIVE)};
+ std::vector<StateTransition> result{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE)};
+ for (size_t i = 0; i < burstCount; ++i) {
+ result.insert(result.end(), burst.begin(), burst.end());
+ }
+ return std::make_shared<SmartStateSequence>(result);
+}
+static const NamedCommandSequence kReadSeq =
+ std::make_tuple(std::string("Read"), 0, StreamTypeFilter::ANY, makeBurstCommands(true, 3));
+static const NamedCommandSequence kWriteSyncSeq = std::make_tuple(
+ std::string("Write"), 0, StreamTypeFilter::SYNC, makeBurstCommands(true, 3));
+static const NamedCommandSequence kWriteAsyncSeq = std::make_tuple(
+ std::string("Write"), 0, StreamTypeFilter::ASYNC, makeBurstCommands(false, 3));
+
+std::shared_ptr<StateSequence> makeAsyncDrainCommands(bool isInput) {
+ return std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, isInput ? StreamDescriptor::State::ACTIVE
+ : StreamDescriptor::State::TRANSFERRING),
+ std::make_pair(isInput ? kDrainInCommand : kDrainOutAllCommand,
+ StreamDescriptor::State::DRAINING),
+ isInput ? std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE)
+ : std::make_pair(kBurstCommand, StreamDescriptor::State::TRANSFERRING),
+ std::make_pair(isInput ? kDrainInCommand : kDrainOutAllCommand,
+ StreamDescriptor::State::DRAINING)});
+}
+static const NamedCommandSequence kWriteDrainAsyncSeq =
+ std::make_tuple(std::string("WriteDrain"), kStreamTransientStateTransitionDelayMs,
+ StreamTypeFilter::ASYNC, makeAsyncDrainCommands(false));
+static const NamedCommandSequence kDrainInSeq = std::make_tuple(
+ std::string("Drain"), 0, StreamTypeFilter::ANY, makeAsyncDrainCommands(true));
+
+std::shared_ptr<StateSequence> makeDrainOutCommands(bool isSync) {
+ return std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kDrainOutAllCommand, StreamDescriptor::State::DRAINING),
+ std::make_pair(isSync ? TransitionTrigger(kGetStatusCommand)
+ : TransitionTrigger(kDrainReadyEvent),
+ StreamDescriptor::State::IDLE)});
+}
+static const NamedCommandSequence kDrainOutSyncSeq = std::make_tuple(
+ std::string("Drain"), 0, StreamTypeFilter::SYNC, makeDrainOutCommands(true));
+static const NamedCommandSequence kDrainOutAsyncSeq = std::make_tuple(
+ std::string("Drain"), 0, StreamTypeFilter::ASYNC, makeDrainOutCommands(false));
+
+std::shared_ptr<StateSequence> makeDrainOutPauseCommands(bool isSync) {
+ return std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, isSync ? StreamDescriptor::State::ACTIVE
+ : StreamDescriptor::State::TRANSFERRING),
+ std::make_pair(kDrainOutAllCommand, StreamDescriptor::State::DRAINING),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::DRAIN_PAUSED),
+ std::make_pair(kStartCommand, StreamDescriptor::State::DRAINING),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::DRAIN_PAUSED),
+ std::make_pair(kBurstCommand, isSync ? StreamDescriptor::State::PAUSED
+ : StreamDescriptor::State::TRANSFER_PAUSED)});
+}
+static const NamedCommandSequence kDrainPauseOutSyncSeq =
+ std::make_tuple(std::string("DrainPause"), kStreamTransientStateTransitionDelayMs,
+ StreamTypeFilter::SYNC, makeDrainOutPauseCommands(true));
+static const NamedCommandSequence kDrainPauseOutAsyncSeq =
+ std::make_tuple(std::string("DrainPause"), kStreamTransientStateTransitionDelayMs,
+ StreamTypeFilter::ASYNC, makeDrainOutPauseCommands(false));
+
+// This sequence also verifies that the capture / presentation position is not reset on standby.
+std::shared_ptr<StateSequence> makeStandbyCommands(bool isInput, bool isSync) {
+ return std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kStandbyCommand, StreamDescriptor::State::STANDBY),
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, isInput || isSync
+ ? StreamDescriptor::State::ACTIVE
+ : StreamDescriptor::State::TRANSFERRING),
+ std::make_pair(kPauseCommand, isInput || isSync
+ ? StreamDescriptor::State::PAUSED
+ : StreamDescriptor::State::TRANSFER_PAUSED),
+ std::make_pair(kFlushCommand, isInput ? StreamDescriptor::State::STANDBY
+ : StreamDescriptor::State::IDLE),
+ std::make_pair(isInput ? kGetStatusCommand : kStandbyCommand, // no-op for input
+ StreamDescriptor::State::STANDBY),
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, isInput || isSync
+ ? StreamDescriptor::State::ACTIVE
+ : StreamDescriptor::State::TRANSFERRING)});
+}
+static const NamedCommandSequence kStandbyInSeq = std::make_tuple(
+ std::string("Standby"), 0, StreamTypeFilter::ANY, makeStandbyCommands(true, false));
+static const NamedCommandSequence kStandbyOutSyncSeq = std::make_tuple(
+ std::string("Standby"), 0, StreamTypeFilter::SYNC, makeStandbyCommands(false, true));
+static const NamedCommandSequence kStandbyOutAsyncSeq =
+ std::make_tuple(std::string("Standby"), kStreamTransientStateTransitionDelayMs,
+ StreamTypeFilter::ASYNC, makeStandbyCommands(false, false));
+
static const NamedCommandSequence kPauseInSeq =
- std::make_pair(std::string("Pause"),
- std::vector<CommandAndState>{
- std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
- std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
- std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
- std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
- std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
- std::make_pair(kFlushCommand, StreamDescriptor::State::STANDBY)});
-static const NamedCommandSequence kPauseOutSeq =
- std::make_pair(std::string("Pause"),
- std::vector<CommandAndState>{
- std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
- std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
- std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
- std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE),
- std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
- std::make_pair(kBurstCommand, StreamDescriptor::State::PAUSED),
- std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE),
- std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED)});
-static const NamedCommandSequence kFlushInSeq =
- std::make_pair(std::string("Flush"),
- std::vector<CommandAndState>{
- std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
- std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
- std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
- std::make_pair(kFlushCommand, StreamDescriptor::State::STANDBY)});
-static const NamedCommandSequence kFlushOutSeq = std::make_pair(
- std::string("Flush"),
- std::vector<CommandAndState>{std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
- std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
- std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
- std::make_pair(kFlushCommand, StreamDescriptor::State::IDLE)});
+ std::make_tuple(std::string("Pause"), 0, StreamTypeFilter::ANY,
+ std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+ std::make_pair(kFlushCommand, StreamDescriptor::State::STANDBY)}));
+static const NamedCommandSequence kPauseOutSyncSeq =
+ std::make_tuple(std::string("Pause"), 0, StreamTypeFilter::SYNC,
+ std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+ std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::PAUSED),
+ std::make_pair(kStartCommand, StreamDescriptor::State::ACTIVE),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::PAUSED)}));
+/* TODO: Figure out a better way for testing sync/async bursts
+static const NamedCommandSequence kPauseOutAsyncSeq = std::make_tuple(
+ std::string("Pause"), kStreamTransientStateTransitionDelayMs, StreamTypeFilter::ASYNC,
+ std::make_shared<StaticStateSequence>(std::vector<StateTransition>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::TRANSFERRING),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::TRANSFER_PAUSED),
+ std::make_pair(kStartCommand, StreamDescriptor::State::TRANSFERRING),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::TRANSFER_PAUSED),
+ std::make_pair(kDrainOutAllCommand, StreamDescriptor::State::DRAIN_PAUSED),
+ std::make_pair(kBurstCommand, StreamDescriptor::State::TRANSFER_PAUSED)}));
+*/
+
+std::shared_ptr<StateSequence> makeFlushCommands(bool isInput, bool isSync) {
+ return std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, isInput || isSync
+ ? StreamDescriptor::State::ACTIVE
+ : StreamDescriptor::State::TRANSFERRING),
+ std::make_pair(kPauseCommand, isInput || isSync
+ ? StreamDescriptor::State::PAUSED
+ : StreamDescriptor::State::TRANSFER_PAUSED),
+ std::make_pair(kFlushCommand, isInput ? StreamDescriptor::State::STANDBY
+ : StreamDescriptor::State::IDLE)});
+}
+static const NamedCommandSequence kFlushInSeq = std::make_tuple(
+ std::string("Flush"), 0, StreamTypeFilter::ANY, makeFlushCommands(true, false));
+static const NamedCommandSequence kFlushOutSyncSeq = std::make_tuple(
+ std::string("Flush"), 0, StreamTypeFilter::SYNC, makeFlushCommands(false, true));
+static const NamedCommandSequence kFlushOutAsyncSeq =
+ std::make_tuple(std::string("Flush"), kStreamTransientStateTransitionDelayMs,
+ StreamTypeFilter::ASYNC, makeFlushCommands(false, false));
+
+std::shared_ptr<StateSequence> makeDrainPauseFlushOutCommands(bool isSync) {
+ return std::make_shared<SmartStateSequence>(std::vector<StateTransition>{
+ std::make_pair(kStartCommand, StreamDescriptor::State::IDLE),
+ std::make_pair(kBurstCommand, isSync ? StreamDescriptor::State::ACTIVE
+ : StreamDescriptor::State::TRANSFERRING),
+ std::make_pair(kDrainOutAllCommand, StreamDescriptor::State::DRAINING),
+ std::make_pair(kPauseCommand, StreamDescriptor::State::DRAIN_PAUSED),
+ std::make_pair(kFlushCommand, StreamDescriptor::State::IDLE)});
+}
+static const NamedCommandSequence kDrainPauseFlushOutSyncSeq =
+ std::make_tuple(std::string("DrainPauseFlush"), kStreamTransientStateTransitionDelayMs,
+ StreamTypeFilter::SYNC, makeDrainPauseFlushOutCommands(true));
+static const NamedCommandSequence kDrainPauseFlushOutAsyncSeq =
+ std::make_tuple(std::string("DrainPauseFlush"), kStreamTransientStateTransitionDelayMs,
+ StreamTypeFilter::ASYNC, makeDrainPauseFlushOutCommands(false));
+
+// Note, this isn't the "official" enum printer, it is only used to make the test name suffix.
+std::string PrintStreamFilterToString(StreamTypeFilter filter) {
+ switch (filter) {
+ case StreamTypeFilter::ANY:
+ return "";
+ case StreamTypeFilter::SYNC:
+ return "Sync";
+ case StreamTypeFilter::ASYNC:
+ return "Async";
+ }
+ return std::string("Unknown").append(std::to_string(static_cast<int32_t>(filter)));
+}
std::string GetStreamIoTestName(const testing::TestParamInfo<StreamIoTestParameters>& info) {
return android::PrintInstanceNameToString(
testing::TestParamInfo<std::string>{std::get<PARAM_MODULE_NAME>(info.param),
info.index})
.append("_")
- .append(std::get<PARAM_CMD_SEQ>(info.param).first)
+ .append(std::get<NAMED_CMD_NAME>(std::get<PARAM_CMD_SEQ>(info.param)))
+ .append(PrintStreamFilterToString(
+ std::get<NAMED_CMD_STREAM_TYPE>(std::get<PARAM_CMD_SEQ>(info.param))))
.append("_SetupSeq")
.append(std::get<PARAM_SETUP_SEQ>(info.param) ? "2" : "1");
}
+
INSTANTIATE_TEST_SUITE_P(
AudioStreamIoInTest, AudioStreamIoIn,
testing::Combine(testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
- testing::Values(kReadOrWriteSeq, kDrainInSeq, kStandbySeq, kPauseInSeq,
+ testing::Values(kReadSeq, kDrainInSeq, kStandbyInSeq, kPauseInSeq,
kFlushInSeq),
testing::Values(false, true)),
GetStreamIoTestName);
@@ -2079,8 +2539,13 @@
INSTANTIATE_TEST_SUITE_P(
AudioStreamIoOutTest, AudioStreamIoOut,
testing::Combine(testing::ValuesIn(android::getAidlHalInstanceNames(IModule::descriptor)),
- testing::Values(kReadOrWriteSeq, kDrainOutSeq, kDrainPauseOutSeq,
- kStandbySeq, kPauseOutSeq, kFlushOutSeq),
+ testing::Values(kWriteSyncSeq, kWriteAsyncSeq, kWriteDrainAsyncSeq,
+ kDrainOutSyncSeq, kDrainPauseOutSyncSeq,
+ kDrainPauseOutAsyncSeq, kStandbyOutSyncSeq,
+ kStandbyOutAsyncSeq,
+ kPauseOutSyncSeq, // kPauseOutAsyncSeq,
+ kFlushOutSyncSeq, kFlushOutAsyncSeq,
+ kDrainPauseFlushOutSyncSeq, kDrainPauseFlushOutAsyncSeq),
testing::Values(false, true)),
GetStreamIoTestName);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioStreamIoOut);
@@ -2095,7 +2560,6 @@
void OnTestStart(const ::testing::TestInfo& test_info) override {
TraceTestState("Started", test_info);
}
-
void OnTestEnd(const ::testing::TestInfo& test_info) override {
TraceTestState("Completed", test_info);
}
@@ -2109,6 +2573,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
+ android::base::SetMinimumLogSeverity(::android::base::DEBUG);
ABinderProcess_setThreadPoolMaxThreadCount(1);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
diff --git a/audio/common/all-versions/default/service/android.hardware.audio.service.rc b/audio/common/all-versions/default/service/android.hardware.audio.service.rc
index 45fef9a..f859f21 100644
--- a/audio/common/all-versions/default/service/android.hardware.audio.service.rc
+++ b/audio/common/all-versions/default/service/android.hardware.audio.service.rc
@@ -4,6 +4,8 @@
# media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub
capabilities BLOCK_SUSPEND
+ # setting RLIMIT_RTPRIO allows binder RT priority inheritance
+ rlimit rtprio 10 10
ioprio rt 4
task_profiles ProcessCapacityHigh HighPerformance
onrestart restart audioserver
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index 0ca6a58..98e49a2 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -1274,6 +1274,8 @@
if (!xsd::isTelephonyDevice(address.deviceType)) {
metadata.source = toString(xsd::AudioSource::AUDIO_SOURCE_UNPROCESSED);
metadata.channelMask = getConfig().base.channelMask;
+ } else {
+ address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT);
}
#if MAJOR_VERSION == 7 && MINOR_VERSION >= 1
auto flagsIt = std::find(flags.begin(), flags.end(),
diff --git a/automotive/remoteaccess/test_grpc_server/README.md b/automotive/remoteaccess/test_grpc_server/README.md
index 28035de..6bc1829 100644
--- a/automotive/remoteaccess/test_grpc_server/README.md
+++ b/automotive/remoteaccess/test_grpc_server/README.md
@@ -79,6 +79,8 @@
* `make -j TestWakeupClientServer`
+* `make -j ApPowerControlLib`
+
## How to push the test wakeup client to a TCU which runs Android.
* Make the target device writable:
@@ -97,6 +99,8 @@
* `adb push vendor/bin/TestWakeupClientServer /vendor/bin`
+* `adb push vendor/lib/ApPowerControlLib.so /vendor/lib`
+
* `adb shell`
* `su`
diff --git a/automotive/remoteaccess/test_grpc_server/impl/Android.bp b/automotive/remoteaccess/test_grpc_server/impl/Android.bp
index e978c8c..152b528 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/Android.bp
+++ b/automotive/remoteaccess/test_grpc_server/impl/Android.bp
@@ -31,6 +31,7 @@
"libutils",
"libgrpc++",
"libprotobuf-cpp-full",
+ "//hardware/interfaces/automotive/remoteaccess/test_grpc_server/lib:ApPowerControlLib",
],
whole_static_libs: [
"wakeup_client_protos",
diff --git a/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h b/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
index 12bd93b..6b86b35 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
+++ b/automotive/remoteaccess/test_grpc_server/impl/include/TestWakeupClientServiceImpl.h
@@ -119,7 +119,7 @@
// A variable to notify server is stopping.
std::condition_variable mServerStoppedCv;
// Whether wakeup AP is required for executing tasks.
- std::atomic<bool> mWakeupRequired = false;
+ std::atomic<bool> mWakeupRequired = true;
std::mutex mLock;
bool mServerStopped GUARDED_BY(mLock);
diff --git a/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp b/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
index 795265f..7dcd31e 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
+++ b/automotive/remoteaccess/test_grpc_server/impl/src/TestWakeupClientServiceImpl.cpp
@@ -16,6 +16,8 @@
#include "TestWakeupClientServiceImpl.h"
+#include "ApPowerControl.h"
+
#include <android-base/stringprintf.h>
#include <inttypes.h>
#include <utils/Looper.h>
@@ -245,8 +247,7 @@
}
void TestWakeupClientServiceImpl::wakeupApplicationProcessor() {
- printf("Waking up application processor...\n");
- // TODO(b/254547153): Send can bus message using socket CAN once we know what the message is.
+ wakeupAp();
}
} // namespace remoteaccess
diff --git a/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp b/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
index 52698b5..d3f519c 100644
--- a/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
+++ b/automotive/remoteaccess/test_grpc_server/impl/src/main.cpp
@@ -28,20 +28,23 @@
using ::grpc::ServerBuilder;
using ::grpc::ServerWriter;
-void RunServer() {
- std::string serverAddress(GRPC_SERVICE_ADDRESS);
+void RunServer(const std::string& serviceAddr) {
std::shared_ptr<TestWakeupClientServiceImpl> service =
std::make_unique<TestWakeupClientServiceImpl>();
ServerBuilder builder;
- builder.AddListeningPort(serverAddress, grpc::InsecureServerCredentials());
+ builder.AddListeningPort(serviceAddr, grpc::InsecureServerCredentials());
builder.RegisterService(service.get());
std::unique_ptr<Server> server(builder.BuildAndStart());
- printf("Test Remote Access GRPC Server listening on %s\n", serverAddress.c_str());
+ printf("Test Remote Access GRPC Server listening on %s\n", serviceAddr.c_str());
server->Wait();
}
int main(int argc, char** argv) {
- RunServer();
+ std::string serviceAddr = GRPC_SERVICE_ADDRESS;
+ if (argc > 1) {
+ serviceAddr = argv[1];
+ }
+ RunServer(serviceAddr);
return 0;
}
diff --git a/automotive/remoteaccess/test_grpc_server/lib/Android.bp b/automotive/remoteaccess/test_grpc_server/lib/Android.bp
new file mode 100644
index 0000000..7e95f53
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/lib/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+soong_namespace {}
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_shared {
+ name: "ApPowerControlLib",
+ vendor: true,
+ srcs: ["*.cpp"],
+ local_include_dirs: ["."],
+ export_include_dirs: ["."],
+}
diff --git a/automotive/remoteaccess/test_grpc_server/lib/ApPowerControl.cpp b/automotive/remoteaccess/test_grpc_server/lib/ApPowerControl.cpp
new file mode 100644
index 0000000..862fed1
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/lib/ApPowerControl.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ApPowerControl.h"
+
+#include <cstdio>
+
+void wakeupAp() {
+ printf("Waking up application processor...\n");
+}
diff --git a/automotive/remoteaccess/test_grpc_server/lib/ApPowerControl.h b/automotive/remoteaccess/test_grpc_server/lib/ApPowerControl.h
new file mode 100644
index 0000000..9560576
--- /dev/null
+++ b/automotive/remoteaccess/test_grpc_server/lib/ApPowerControl.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Wakeup application processor if not already waken up.
+void wakeupAp();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 7c8e1f5..9e4f252 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -1632,10 +1632,8 @@
*
* The Android properties are:
*
- * int32Values[0] : Input code identifying the function representing this event. Valid event
- * types are defined by CustomInputType.CUSTOM_EVENT_F1 up to
- * CustomInputType.CUSTOM_EVENT_F10. They represent the custom event to be
- * defined by OEM partners.
+ * int32Values[0] : Input code identifying the function representing this event. OEMs are free
+ * to use any signed 32 bits number to represent the input code value.
* int32Values[1] : target display type defined in VehicleDisplay. Events not tied to specific
* display must be sent to VehicleDisplay#MAIN.
* int32Values[2] : repeat counter, if 0 then event is not repeated. Values 1 or above means
@@ -5326,10 +5324,11 @@
*/
enum CustomInputType : int32_t {
/**
- * Ten functions representing the custom input code to be defined and implemented by OEM
- * partners.
+ * Ten optional function codes to be used in case OEM don't need more than 10 input code values.
*
- * OEMs need to formally contact Android team if more than 10 functions are required.
+ * OEMs are free to use any signed 32 bits number to represent the input code value.
+ * The following function keys are only for convenience and any other integer values are
+ * also allowed.
*/
CUSTOM_EVENT_F1 = 1001,
CUSTOM_EVENT_F2 = 1002,
diff --git a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
index 0bf0ed4..ab64e07 100644
--- a/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl/aidl_api/android.hardware.automotive.vehicle/current/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -156,7 +156,11 @@
SEAT_HEADREST_FORE_AFT_POS = 356518809,
SEAT_HEADREST_FORE_AFT_MOVE = 356518810,
SEAT_EASY_ACCESS_ENABLED = 354421661,
+ SEAT_AIRBAG_ENABLED = 354421662,
SEAT_CUSHION_SIDE_SUPPORT_POS = 356518815,
+ SEAT_CUSHION_SIDE_SUPPORT_MOVE = 356518816,
+ SEAT_LUMBAR_VERTICAL_POS = 356518817,
+ SEAT_LUMBAR_VERTICAL_MOVE = 356518818,
SEAT_OCCUPANCY = 356518832,
WINDOW_POS = 322964416,
WINDOW_MOVE = 322964417,
@@ -220,4 +224,5 @@
VEHICLE_CURB_WEIGHT = 289410886,
GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT = 289410887,
SUPPORTED_PROPERTY_IDS = 289476424,
+ SHUTDOWN_REQUEST = 289410889,
}
diff --git a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
index 8f00dca..baca8ab 100644
--- a/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -97,8 +97,12 @@
INFO_FUEL_TYPE = 0x0105 + 0x10000000 + 0x01000000
+ 0x00410000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
/**
- * Battery capacity of the vehicle, if EV or hybrid. This is the nominal
- * battery capacity when the vehicle is new.
+ * Nominal battery capacity for EV or hybrid vehicle
+ *
+ * Returns the nominal battery capacity, if EV or hybrid. This is the battery capacity when the
+ * vehicle is new. This value might be different from EV_CURRENT_BATTERY_CAPACITY because
+ * EV_CURRENT_BATTERY_CAPACITY returns the real-time battery capacity taking into account
+ * factors such as battery aging and temperature dependency.
*
* @change_mode VehiclePropertyChangeMode.STATIC
* @access VehiclePropertyAccess.READ
@@ -1150,7 +1154,7 @@
* It is assumed that AP's power state is controlled by a separate power
* controller.
*
- * For configuration information, VehiclePropConfig.configArray can have bit flag combining
+ * For configuration information, VehiclePropConfig.configArray must have bit flag combining
* values in VehicleApPowerStateConfigFlag.
*
* int32Values[0] : VehicleApPowerStateReq enum value
@@ -1733,6 +1737,22 @@
SEAT_EASY_ACCESS_ENABLED =
0x0B9D + VehiclePropertyGroup.SYSTEM + VehicleArea.SEAT + VehiclePropertyType.BOOLEAN,
/**
+ * Represents feature to enable/disable a seat's ability to deploy airbag(s) when triggered
+ * (e.g. by a crash).
+ *
+ * If true, it means the seat's airbags are enabled, and if triggered (e.g. by a crash), they
+ * will deploy. If false, it means the seat's airbags are disabled, and they will not deploy
+ * under any circumstance. This property does not indicate if the airbags are deployed or not.
+ *
+ * This property can be set to VehiclePropertyAccess.READ read only for the sake of regulation
+ * or safety concerns.
+ *
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
+ */
+ SEAT_AIRBAG_ENABLED =
+ 0x0B9E + VehiclePropertyGroup.SYSTEM + VehicleArea.SEAT + VehiclePropertyType.BOOLEAN,
+ /**
* Represents property for seat’s hipside (bottom cushion’s side) support position.
*
* The maxInt32Value and minInt32Value in VehicleAreaConfig must be defined. All integers
@@ -1749,6 +1769,62 @@
SEAT_CUSHION_SIDE_SUPPORT_POS =
0x0B9F + VehiclePropertyGroup.SYSTEM + VehicleArea.SEAT + VehiclePropertyType.INT32,
/**
+ * Represents property for movement direction and speed of seat cushion side support.
+ *
+ * The maxInt32Value and minInt32Value in VehicleAreaConfig must be defined. All integers
+ * between minInt32Value and maxInt32Value must be supported.
+ *
+ * maxInt32Value in default area's VehicleAreaConfig represents the maximum movement speed of
+ * the seat cushion side support in the growing wider direction (i.e. less support).
+ * minInt32Value in default area's VehicleAreaConfig represents the maximum movement speed of
+ * the seat cushion side support in the growing thinner direction (i.e. more support).
+ *
+ * Larger absolute values, either positive or negative, indicate a faster movement speed. Once
+ * the seat cushion side support reaches the positional limit, the value resets to 0.
+ *
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
+ */
+ SEAT_CUSHION_SIDE_SUPPORT_MOVE =
+ 0x0BA0 + VehiclePropertyGroup.SYSTEM + VehicleArea.SEAT + VehiclePropertyType.INT32,
+ /**
+ * Represents property for seat’s lumbar support vertical position.
+ *
+ * The maxInt32Value and minInt32Value in VehicleAreaConfig must be defined. All integers
+ * between minInt32Value and maxInt32Value are supported.
+ *
+ * maxInt32Value indicates the highest position.
+ * minInt32Value indicates the lowest position.
+ *
+ * This value is not in any particular unit but in a specified range of steps.
+ *
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
+ */
+ SEAT_LUMBAR_VERTICAL_POS =
+ 0x0BA1 + VehiclePropertyGroup.SYSTEM + VehicleArea.SEAT + VehiclePropertyType.INT32,
+ /**
+ * Represents property for vertical movement direction and speed of seat lumbar support.
+ *
+ * The maxInt32Value and minInt32Value in VehicleAreaConfig must be defined. All integers
+ * between minInt32Value and maxInt32Value must be supported.
+ *
+ * maxInt32Value in default area's VehicleAreaConfig indicates the lumbar support is moving at
+ * the fastest upward speed.
+ * minInt32Value in default area's VehicleAreaConfig indicates the lumbar support is moving at
+ * the fastest downward speed.
+ *
+ * Larger absolute values, either positive or negative, indicate a faster movement speed. Once
+ * the seat cushion side support reaches the positional limit, the value resets to 0.
+ *
+ * This value is not in any particular unit but in a specified range of steps.
+ *
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.READ_WRITE
+ */
+ SEAT_LUMBAR_VERTICAL_MOVE =
+ 0x0BA2 + VehiclePropertyGroup.SYSTEM + VehicleArea.SEAT + VehiclePropertyType.INT32,
+ /**
* Seat Occupancy
*
* Indicates whether a particular seat is occupied or not, to the best of the car's ability
@@ -3064,4 +3140,41 @@
*/
SUPPORTED_PROPERTY_IDS = 0x0F48 + 0x10000000 + 0x01000000
+ 0x00410000, // VehiclePropertyGroup:SYSTEM,VehicleArea:GLOBAL,VehiclePropertyType:INT32_VEC
+
+ /**
+ * Request the head unit to be shutdown.
+ *
+ * <p>This usually involves telling a separate system outside the head unit (e.g. a power
+ * controller) to prepare shutting down the head unit.
+ *
+ * <p>This does not mean the head unit will shutdown immediately.
+ *
+ * <p>This means that another system will start sending a shutdown signal to the head unit,
+ * which will cause VHAL to send SHUTDOWN_PREPARE message to Android. Android will then start
+ * the shut down process by handling the message.
+ *
+ * <p>This property is only for issuing a request and only supports writing. Every time this
+ * property value is set, the request to shutdown will be issued no matter what the current
+ * property value is. The current property value is meaningless.
+ *
+ * <p>Since this property is write-only, subscribing is not allowed and no property change
+ * event will be generated.
+ *
+ * <p>The value to set indicates the shutdown option, it must be one of
+ * {@code VehicleApPowerStateShutdownParam}, e.g.,
+ * VehicleApPowerStateShutdownParam.SLEEP_IMMEDIATELY. This shutdown option might not be honored
+ * if the system doesn't support such option. In such case, an error will not be returned.
+ *
+ * <p>For configuration information, VehiclePropConfig.configArray must have bit flag combining
+ * values in {@code VehicleApPowerStateConfigFlag} to indicate which shutdown options are
+ * supported.
+ *
+ * <p>Returns error if failed to send the shutdown request to the other system.
+ *
+ * @change_mode VehiclePropertyChangeMode.ON_CHANGE
+ * @access VehiclePropertyAccess.WRITE
+ * @data_enum VehicleApPowerStateShutdownParam
+ */
+ SHUTDOWN_REQUEST =
+ 0x0F49 + VehiclePropertyGroup.SYSTEM + VehicleArea.GLOBAL + VehiclePropertyType.INT32,
}
diff --git a/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
index 7f9551f..85ca474 100644
--- a/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
+++ b/automotive/vehicle/aidl/generated_lib/cpp/AccessForVehicleProperty.h
@@ -156,7 +156,11 @@
{VehicleProperty::SEAT_HEADREST_FORE_AFT_POS, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::SEAT_HEADREST_FORE_AFT_MOVE, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::SEAT_EASY_ACCESS_ENABLED, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_AIRBAG_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::SEAT_CUSHION_SIDE_SUPPORT_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_CUSHION_SIDE_SUPPORT_MOVE, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_LUMBAR_VERTICAL_POS, VehiclePropertyAccess::READ_WRITE},
+ {VehicleProperty::SEAT_LUMBAR_VERTICAL_MOVE, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::SEAT_OCCUPANCY, VehiclePropertyAccess::READ},
{VehicleProperty::WINDOW_POS, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::WINDOW_MOVE, VehiclePropertyAccess::READ_WRITE},
@@ -220,6 +224,7 @@
{VehicleProperty::VEHICLE_CURB_WEIGHT, VehiclePropertyAccess::READ},
{VehicleProperty::GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyAccess::READ},
{VehicleProperty::SUPPORTED_PROPERTY_IDS, VehiclePropertyAccess::READ},
+ {VehicleProperty::SHUTDOWN_REQUEST, VehiclePropertyAccess::WRITE},
};
} // namespace vehicle
diff --git a/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h b/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
index a875864..f95580a 100644
--- a/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
+++ b/automotive/vehicle/aidl/generated_lib/cpp/ChangeModeForVehicleProperty.h
@@ -156,7 +156,11 @@
{VehicleProperty::SEAT_HEADREST_FORE_AFT_POS, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_HEADREST_FORE_AFT_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_EASY_ACCESS_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_AIRBAG_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_CUSHION_SIDE_SUPPORT_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_CUSHION_SIDE_SUPPORT_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_LUMBAR_VERTICAL_POS, VehiclePropertyChangeMode::ON_CHANGE},
+ {VehicleProperty::SEAT_LUMBAR_VERTICAL_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_OCCUPANCY, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::WINDOW_POS, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::WINDOW_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
@@ -220,6 +224,7 @@
{VehicleProperty::VEHICLE_CURB_WEIGHT, VehiclePropertyChangeMode::STATIC},
{VehicleProperty::GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyChangeMode::STATIC},
{VehicleProperty::SUPPORTED_PROPERTY_IDS, VehiclePropertyChangeMode::STATIC},
+ {VehicleProperty::SHUTDOWN_REQUEST, VehiclePropertyChangeMode::ON_CHANGE},
};
} // namespace vehicle
diff --git a/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
index 1586858..d0caed2 100644
--- a/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
+++ b/automotive/vehicle/aidl/generated_lib/java/AccessForVehicleProperty.java
@@ -148,7 +148,11 @@
Map.entry(VehicleProperty.SEAT_HEADREST_FORE_AFT_POS, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.SEAT_HEADREST_FORE_AFT_MOVE, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.SEAT_EASY_ACCESS_ENABLED, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_AIRBAG_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.SEAT_CUSHION_SIDE_SUPPORT_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_CUSHION_SIDE_SUPPORT_MOVE, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_LUMBAR_VERTICAL_POS, VehiclePropertyAccess.READ_WRITE),
+ Map.entry(VehicleProperty.SEAT_LUMBAR_VERTICAL_MOVE, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.SEAT_OCCUPANCY, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.WINDOW_POS, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.WINDOW_MOVE, VehiclePropertyAccess.READ_WRITE),
@@ -211,7 +215,8 @@
Map.entry(VehicleProperty.TRAILER_PRESENT, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.VEHICLE_CURB_WEIGHT, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyAccess.READ),
- Map.entry(VehicleProperty.SUPPORTED_PROPERTY_IDS, VehiclePropertyAccess.READ)
+ Map.entry(VehicleProperty.SUPPORTED_PROPERTY_IDS, VehiclePropertyAccess.READ),
+ Map.entry(VehicleProperty.SHUTDOWN_REQUEST, VehiclePropertyAccess.WRITE)
);
}
diff --git a/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java b/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
index 6f1ebe3..96fe0a5 100644
--- a/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
+++ b/automotive/vehicle/aidl/generated_lib/java/ChangeModeForVehicleProperty.java
@@ -148,7 +148,11 @@
Map.entry(VehicleProperty.SEAT_HEADREST_FORE_AFT_POS, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_HEADREST_FORE_AFT_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_EASY_ACCESS_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_AIRBAG_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_CUSHION_SIDE_SUPPORT_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_CUSHION_SIDE_SUPPORT_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_LUMBAR_VERTICAL_POS, VehiclePropertyChangeMode.ON_CHANGE),
+ Map.entry(VehicleProperty.SEAT_LUMBAR_VERTICAL_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_OCCUPANCY, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.WINDOW_POS, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.WINDOW_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
@@ -211,7 +215,8 @@
Map.entry(VehicleProperty.TRAILER_PRESENT, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.VEHICLE_CURB_WEIGHT, VehiclePropertyChangeMode.STATIC),
Map.entry(VehicleProperty.GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, VehiclePropertyChangeMode.STATIC),
- Map.entry(VehicleProperty.SUPPORTED_PROPERTY_IDS, VehiclePropertyChangeMode.STATIC)
+ Map.entry(VehicleProperty.SUPPORTED_PROPERTY_IDS, VehiclePropertyChangeMode.STATIC),
+ Map.entry(VehicleProperty.SHUTDOWN_REQUEST, VehiclePropertyChangeMode.ON_CHANGE)
);
}
diff --git a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
index 7f2d8b2..bd80ad5 100644
--- a/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
+++ b/automotive/vehicle/aidl/impl/default_config/config/DefaultProperties.json
@@ -1052,6 +1052,22 @@
]
},
{
+ "property": "VehicleProperty::SEAT_AIRBAG_ENABLED",
+ "defaultValue": {
+ "int32Values": [
+ 1
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT"
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT"
+ }
+ ]
+ },
+ {
"property": "VehicleProperty::SEAT_CUSHION_SIDE_SUPPORT_POS",
"defaultValue": {
"int32Values": [
@@ -1087,6 +1103,111 @@
]
},
{
+ "property": "VehicleProperty::SEAT_CUSHION_SIDE_SUPPORT_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_LUMBAR_VERTICAL_POS",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -10,
+ "maxInt32Value": 10
+ }
+ ]
+ },
+ {
+ "property": "VehicleProperty::SEAT_LUMBAR_VERTICAL_MOVE",
+ "defaultValue": {
+ "int32Values": [
+ 0
+ ]
+ },
+ "areas": [
+ {
+ "areaId": "Constants::SEAT_1_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_1_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_LEFT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_RIGHT",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ },
+ {
+ "areaId": "Constants::SEAT_2_CENTER",
+ "minInt32Value": -1,
+ "maxInt32Value": 1
+ }
+ ]
+ },
+ {
"property": "VehicleProperty::SEAT_OCCUPANCY",
"areas": [
{
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
index d87e5aa..736ecaa 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -320,7 +320,11 @@
if (updatedValue != nullptr) {
ALOGI("onSetProperty(): updating property returned by HAL: %s",
updatedValue->toString().c_str());
- if (auto writeResult = mServerSidePropStore->writeValue(std::move(result.value()));
+ // Update timestamp otherwise writeValue might fail because the timestamp is outdated.
+ updatedValue->timestamp = elapsedRealtimeNano();
+ if (auto writeResult = mServerSidePropStore->writeValue(
+ std::move(result.value()),
+ /*updateStatus=*/true, VehiclePropertyStore::EventMode::ALWAYS);
!writeResult.ok()) {
return StatusError(getErrorCode(writeResult))
<< "failed to write value into property store, error: "
@@ -623,11 +627,7 @@
} else if (EqualsIgnoreCase(option, "--inject-event")) {
result.buffer = dumpInjectEvent(options);
} else if (EqualsIgnoreCase(option, kUserHalDumpOption)) {
- if (options.size() == 1) {
- result.buffer = mFakeUserHal->showDumpHelp();
- } else {
- result.buffer = mFakeUserHal->dump(options[1]);
- }
+ result.buffer = mFakeUserHal->dump();
} else if (EqualsIgnoreCase(option, "--genfakedata")) {
result.buffer = genFakeDataCommand(options);
} else {
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
index c230c51..0184462 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -1223,6 +1223,8 @@
ASSERT_EQ(events.size(), static_cast<size_t>(1));
events[0].timestamp = 0;
+ // The returned event will have area ID 0.
+ valueToSet.areaId = 0;
ASSERT_EQ(events[0], valueToSet);
// Try to get switch_user again, should return default value.
@@ -1277,6 +1279,8 @@
auto events = getChangedProperties();
ASSERT_EQ(events.size(), static_cast<size_t>(1));
events[0].timestamp = 0;
+ // The returned event will have area ID 0.
+ valueToSet.areaId = 0;
EXPECT_EQ(events[0], valueToSet);
// Try to get create_user again, should return default value.
@@ -1330,7 +1334,7 @@
ASSERT_EQ(events.size(), static_cast<size_t>(1));
events[0].timestamp = 0;
EXPECT_EQ(events[0], (VehiclePropValue{
- .areaId = 1,
+ .areaId = 0,
.prop = toInt(VehicleProperty::INITIAL_USER_INFO),
.value.int32Values = {3, 1, 11},
}));
@@ -1516,26 +1520,16 @@
ASSERT_THAT(result.buffer, ContainsRegex("Invalid option: --invalid"));
}
-TEST_F(FakeVehicleHardwareTest, testDumpFakeUserHalHelp) {
- std::vector<std::string> options;
- options.push_back("--user-hal");
-
- DumpResult result = getHardware()->dump(options);
- ASSERT_FALSE(result.callerShouldDumpState);
- ASSERT_NE(result.buffer, "");
- ASSERT_THAT(result.buffer, ContainsRegex("dumps state used for user management"));
-}
-
TEST_F(FakeVehicleHardwareTest, testDumpFakeUserHal) {
std::vector<std::string> options;
options.push_back("--user-hal");
- // Indent: " ".
- options.push_back(" ");
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_NE(result.buffer, "");
- ASSERT_THAT(result.buffer, ContainsRegex(" No InitialUserInfo response\n"));
+ ASSERT_THAT(result.buffer,
+ ContainsRegex("No InitialUserInfo response\nNo SwitchUser response\nNo CreateUser "
+ "response\nNo SetUserIdentificationAssociation response\n"));
}
struct SetPropTestCase {
diff --git a/automotive/vehicle/aidl/impl/fake_impl/userhal/include/FakeUserHal.h b/automotive/vehicle/aidl/impl/fake_impl/userhal/include/FakeUserHal.h
index 4ae9c8c..fcbe8fd 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/userhal/include/FakeUserHal.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/userhal/include/FakeUserHal.h
@@ -64,7 +64,7 @@
std::string showDumpHelp() const;
// Dump its contents.
- std::string dump(std::string indent) const;
+ std::string dump() const;
private:
const std::shared_ptr<VehiclePropValuePool> mValuePool;
diff --git a/automotive/vehicle/aidl/impl/fake_impl/userhal/src/FakeUserHal.cpp b/automotive/vehicle/aidl/impl/fake_impl/userhal/src/FakeUserHal.cpp
index 7748fb6..878c2e7 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/userhal/src/FakeUserHal.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/userhal/src/FakeUserHal.cpp
@@ -328,6 +328,9 @@
<< "invalid action on lshal response: " << response->toString();
}
+ // Update area ID to 0 since this is a global property (and the area ID was only set to emulate
+ // the request id behavior).
+ response->areaId = 0;
ALOGD("updating property to: %s", response->toString().c_str());
return response;
}
@@ -336,33 +339,31 @@
return fmt::format("{}: dumps state used for user management\n", kUserHalDumpOption);
}
-std::string FakeUserHal::dump(std::string indent) const {
+std::string FakeUserHal::dump() const {
std::scoped_lock<std::mutex> lockGuard(mLock);
std::string info;
if (mInitialUserResponseFromCmd != nullptr) {
- info += fmt::format("{}InitialUserInfo response: {}\n", indent,
+ info += fmt::format("InitialUserInfo response: {}\n",
mInitialUserResponseFromCmd->toString());
} else {
- info += fmt::format("{}No InitialUserInfo response\n", indent);
+ info += "No InitialUserInfo response\n";
}
if (mSwitchUserResponseFromCmd != nullptr) {
- info += fmt::format("{}SwitchUser response: {}\n", indent,
- mSwitchUserResponseFromCmd->toString());
+ info += fmt::format("SwitchUser response: {}\n", mSwitchUserResponseFromCmd->toString());
} else {
- info += fmt::format("{}No SwitchUser response\n", indent);
+ info += "No SwitchUser response\n";
}
if (mCreateUserResponseFromCmd != nullptr) {
- info += fmt::format("{}CreateUser response: {}\n", indent,
- mCreateUserResponseFromCmd->toString());
+ info += fmt::format("CreateUser response: {}\n", mCreateUserResponseFromCmd->toString());
} else {
- info += fmt::format("{}No CreateUser response\n", indent);
+ info += "No CreateUser response\n";
}
if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
- info += fmt::format("{}SetUserIdentificationAssociation response: {}\n", indent,
+ info += fmt::format("SetUserIdentificationAssociation response: {}\n",
mSetUserIdentificationAssociationResponseFromCmd->toString());
} else {
- info += fmt::format("{}No SetUserIdentificationAssociation response\n", indent);
+ info += "No SetUserIdentificationAssociation response\n";
}
return info;
}
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index d447bf8..62e9dc8 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -792,7 +792,7 @@
DumpResult result = mVehicleHardware->dump(options);
dprintf(fd, "%s", (result.buffer + "\n").c_str());
if (!result.callerShouldDumpState) {
- dprintf(fd, "Skip dumping Vehicle HAL State.\n");
+ ALOGE("Skip dumping Vehicle HAL State.");
return STATUS_OK;
}
dprintf(fd, "Vehicle HAL State: \n");
diff --git a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
index 11db8e0..7eefe2a 100644
--- a/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
+++ b/automotive/vehicle/vts/src/VtsHalAutomotiveVehicle_TargetTest.cpp
@@ -547,12 +547,36 @@
VehicleArea::SEAT, VehiclePropertyType::BOOLEAN);
}
+TEST_P(VtsHalAutomotiveVehicleTargetTest, verifySeatAirbagEnabledConfig) {
+ verifyProperty(VehicleProperty::SEAT_AIRBAG_ENABLED, VehiclePropertyAccess::READ_WRITE,
+ VehiclePropertyChangeMode::ON_CHANGE, VehiclePropertyGroup::SYSTEM,
+ VehicleArea::SEAT, VehiclePropertyType::BOOLEAN);
+}
+
TEST_P(VtsHalAutomotiveVehicleTargetTest, verifySeatCushionSideSupportPosConfig) {
verifyProperty(VehicleProperty::SEAT_CUSHION_SIDE_SUPPORT_POS,
VehiclePropertyAccess::READ_WRITE, VehiclePropertyChangeMode::ON_CHANGE,
VehiclePropertyGroup::SYSTEM, VehicleArea::SEAT, VehiclePropertyType::INT32);
}
+TEST_P(VtsHalAutomotiveVehicleTargetTest, verifySeatCushionSideSupportMoveConfig) {
+ verifyProperty(VehicleProperty::SEAT_CUSHION_SIDE_SUPPORT_MOVE,
+ VehiclePropertyAccess::READ_WRITE, VehiclePropertyChangeMode::ON_CHANGE,
+ VehiclePropertyGroup::SYSTEM, VehicleArea::SEAT, VehiclePropertyType::INT32);
+}
+
+TEST_P(VtsHalAutomotiveVehicleTargetTest, verifySeatLumbarVerticalPosConfig) {
+ verifyProperty(VehicleProperty::SEAT_LUMBAR_VERTICAL_POS, VehiclePropertyAccess::READ_WRITE,
+ VehiclePropertyChangeMode::ON_CHANGE, VehiclePropertyGroup::SYSTEM,
+ VehicleArea::SEAT, VehiclePropertyType::INT32);
+}
+
+TEST_P(VtsHalAutomotiveVehicleTargetTest, verifySeatLumbarVerticalMoveConfig) {
+ verifyProperty(VehicleProperty::SEAT_LUMBAR_VERTICAL_MOVE, VehiclePropertyAccess::READ_WRITE,
+ VehiclePropertyChangeMode::ON_CHANGE, VehiclePropertyGroup::SYSTEM,
+ VehicleArea::SEAT, VehiclePropertyType::INT32);
+}
+
std::vector<ServiceDescriptor> getDescriptors() {
std::vector<ServiceDescriptor> descriptors;
for (std::string name : getAidlHalInstanceNames(IVehicle::descriptor)) {
diff --git a/biometrics/fingerprint/aidl/OWNERS b/biometrics/OWNERS
similarity index 100%
rename from biometrics/fingerprint/aidl/OWNERS
rename to biometrics/OWNERS
diff --git a/biometrics/common/aidl/OWNERS b/biometrics/common/aidl/OWNERS
deleted file mode 100644
index 36d7261..0000000
--- a/biometrics/common/aidl/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-ilyamaty@google.com
-kchyn@google.com
diff --git a/biometrics/face/1.0/vts/functional/OWNERS b/biometrics/face/1.0/vts/functional/OWNERS
deleted file mode 100644
index 7651b69..0000000
--- a/biometrics/face/1.0/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 432605
-ilyamaty@google.com
diff --git a/biometrics/face/aidl/OWNERS b/biometrics/face/aidl/OWNERS
deleted file mode 100644
index 36d7261..0000000
--- a/biometrics/face/aidl/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-ilyamaty@google.com
-kchyn@google.com
diff --git a/biometrics/fingerprint/2.1/vts/functional/OWNERS b/biometrics/fingerprint/2.1/vts/functional/OWNERS
deleted file mode 100644
index 0014ce9..0000000
--- a/biometrics/fingerprint/2.1/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 114777
-ilyamaty@google.com
diff --git a/biometrics/fingerprint/2.2/vts/functional/OWNERS b/biometrics/fingerprint/2.2/vts/functional/OWNERS
deleted file mode 100644
index 0014ce9..0000000
--- a/biometrics/fingerprint/2.2/vts/functional/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 114777
-ilyamaty@google.com
diff --git a/camera/common/1.0/default/Android.bp b/camera/common/default/Android.bp
similarity index 78%
rename from camera/common/1.0/default/Android.bp
rename to camera/common/default/Android.bp
index 4a5ca83..e8c8f9d 100644
--- a/camera/common/1.0/default/Android.bp
+++ b/camera/common/default/Android.bp
@@ -8,7 +8,7 @@
}
cc_library_static {
- name: "android.hardware.camera.common@1.0-helper",
+ name: "android.hardware.camera.common-helper",
vendor_available: true,
defaults: ["hidl_defaults"],
srcs: [
@@ -18,6 +18,7 @@
"VendorTagDescriptor.cpp",
"HandleImporter.cpp",
"Exif.cpp",
+ "SimpleThread.cpp",
],
cflags: [
"-Werror",
@@ -37,3 +38,11 @@
include_dirs: ["system/media/private/camera/include"],
export_include_dirs: ["include"],
}
+
+// NOTE: Deprecated module kept for compatibility reasons.
+// Depend on "android.hardware.camera.common-helper" instead
+cc_library_static {
+ name: "android.hardware.camera.common@1.0-helper",
+ vendor_available: true,
+ whole_static_libs: ["android.hardware.camera.common-helper"],
+}
diff --git a/camera/common/1.0/default/CameraMetadata.cpp b/camera/common/default/CameraMetadata.cpp
similarity index 74%
rename from camera/common/1.0/default/CameraMetadata.cpp
rename to camera/common/default/CameraMetadata.cpp
index eb1bd1c..ed56261 100644
--- a/camera/common/1.0/default/CameraMetadata.cpp
+++ b/camera/common/default/CameraMetadata.cpp
@@ -27,44 +27,36 @@
namespace hardware {
namespace camera {
namespace common {
-namespace V1_0 {
namespace helper {
-#define ALIGN_TO(val, alignment) \
- (((uintptr_t)(val) + ((alignment) - 1)) & ~((alignment) - 1))
+#define ALIGN_TO(val, alignment) (((uintptr_t)(val) + ((alignment)-1)) & ~((alignment)-1))
-CameraMetadata::CameraMetadata() :
- mBuffer(NULL), mLocked(false) {
-}
+CameraMetadata::CameraMetadata() : mBuffer(NULL), mLocked(false) {}
-CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) :
- mLocked(false)
-{
+CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) : mLocked(false) {
mBuffer = allocate_camera_metadata(entryCapacity, dataCapacity);
}
-CameraMetadata::CameraMetadata(const CameraMetadata &other) :
- mLocked(false) {
+CameraMetadata::CameraMetadata(const CameraMetadata& other) : mLocked(false) {
mBuffer = clone_camera_metadata(other.mBuffer);
}
-CameraMetadata::CameraMetadata(camera_metadata_t *buffer) :
- mBuffer(NULL), mLocked(false) {
+CameraMetadata::CameraMetadata(camera_metadata_t* buffer) : mBuffer(NULL), mLocked(false) {
acquire(buffer);
}
-CameraMetadata &CameraMetadata::operator=(const CameraMetadata &other) {
+CameraMetadata& CameraMetadata::operator=(const CameraMetadata& other) {
return operator=(other.mBuffer);
}
-CameraMetadata &CameraMetadata::operator=(const camera_metadata_t *buffer) {
+CameraMetadata& CameraMetadata::operator=(const camera_metadata_t* buffer) {
if (mLocked) {
ALOGE("%s: Assignment to a locked CameraMetadata!", __FUNCTION__);
return *this;
}
if (CC_LIKELY(buffer != mBuffer)) {
- camera_metadata_t *newBuffer = clone_camera_metadata(buffer);
+ camera_metadata_t* newBuffer = clone_camera_metadata(buffer);
clear();
mBuffer = newBuffer;
}
@@ -81,14 +73,13 @@
return mBuffer;
}
-status_t CameraMetadata::unlock(const camera_metadata_t *buffer) const {
+status_t CameraMetadata::unlock(const camera_metadata_t* buffer) const {
if (!mLocked) {
ALOGE("%s: Can't unlock a non-locked CameraMetadata!", __FUNCTION__);
return INVALID_OPERATION;
}
if (buffer != mBuffer) {
- ALOGE("%s: Can't unlock CameraMetadata with wrong pointer!",
- __FUNCTION__);
+ ALOGE("%s: Can't unlock CameraMetadata with wrong pointer!", __FUNCTION__);
return BAD_VALUE;
}
mLocked = false;
@@ -100,7 +91,7 @@
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return NULL;
}
- camera_metadata_t *released = mBuffer;
+ camera_metadata_t* released = mBuffer;
mBuffer = NULL;
return released;
}
@@ -116,7 +107,7 @@
}
}
-void CameraMetadata::acquire(camera_metadata_t *buffer) {
+void CameraMetadata::acquire(camera_metadata_t* buffer) {
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return;
@@ -124,12 +115,11 @@
clear();
mBuffer = buffer;
- ALOGE_IF(validate_camera_metadata_structure(mBuffer, /*size*/NULL) != OK,
- "%s: Failed to validate metadata structure %p",
- __FUNCTION__, buffer);
+ ALOGE_IF(validate_camera_metadata_structure(mBuffer, /*size*/ NULL) != OK,
+ "%s: Failed to validate metadata structure %p", __FUNCTION__, buffer);
}
-void CameraMetadata::acquire(CameraMetadata &other) {
+void CameraMetadata::acquire(CameraMetadata& other) {
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return;
@@ -137,7 +127,7 @@
acquire(other.release());
}
-status_t CameraMetadata::append(const CameraMetadata &other) {
+status_t CameraMetadata::append(const CameraMetadata& other) {
return append(other.mBuffer);
}
@@ -154,8 +144,7 @@
}
size_t CameraMetadata::entryCount() const {
- return (mBuffer == NULL) ? 0 :
- get_camera_metadata_entry_count(mBuffer);
+ return (mBuffer == NULL) ? 0 : get_camera_metadata_entry_count(mBuffer);
}
bool CameraMetadata::isEmpty() const {
@@ -172,11 +161,11 @@
status_t CameraMetadata::checkType(uint32_t tag, uint8_t expectedType) {
int tagType = get_local_camera_metadata_tag_type(tag, mBuffer);
- if ( CC_UNLIKELY(tagType == -1)) {
+ if (CC_UNLIKELY(tagType == -1)) {
ALOGE("Update metadata entry: Unknown tag %d", tag);
return INVALID_OPERATION;
}
- if ( CC_UNLIKELY(tagType != expectedType) ) {
+ if (CC_UNLIKELY(tagType != expectedType)) {
ALOGE("Mismatched tag type when updating entry %s (%d) of type %s; "
"got type %s data instead ",
get_local_camera_metadata_tag_name(tag, mBuffer), tag,
@@ -186,112 +175,105 @@
return OK;
}
-status_t CameraMetadata::update(uint32_t tag,
- const int32_t *data, size_t data_count) {
+status_t CameraMetadata::update(uint32_t tag, const int32_t* data, size_t data_count) {
status_t res;
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
}
- if ( (res = checkType(tag, TYPE_INT32)) != OK) {
+ if ((res = checkType(tag, TYPE_INT32)) != OK) {
return res;
}
return updateImpl(tag, (const void*)data, data_count);
}
-status_t CameraMetadata::update(uint32_t tag,
- const uint8_t *data, size_t data_count) {
+status_t CameraMetadata::update(uint32_t tag, const uint8_t* data, size_t data_count) {
status_t res;
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
}
- if ( (res = checkType(tag, TYPE_BYTE)) != OK) {
+ if ((res = checkType(tag, TYPE_BYTE)) != OK) {
return res;
}
return updateImpl(tag, (const void*)data, data_count);
}
-status_t CameraMetadata::update(uint32_t tag,
- const float *data, size_t data_count) {
+status_t CameraMetadata::update(uint32_t tag, const float* data, size_t data_count) {
status_t res;
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
}
- if ( (res = checkType(tag, TYPE_FLOAT)) != OK) {
+ if ((res = checkType(tag, TYPE_FLOAT)) != OK) {
return res;
}
return updateImpl(tag, (const void*)data, data_count);
}
-status_t CameraMetadata::update(uint32_t tag,
- const int64_t *data, size_t data_count) {
+status_t CameraMetadata::update(uint32_t tag, const int64_t* data, size_t data_count) {
status_t res;
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
}
- if ( (res = checkType(tag, TYPE_INT64)) != OK) {
+ if ((res = checkType(tag, TYPE_INT64)) != OK) {
return res;
}
return updateImpl(tag, (const void*)data, data_count);
}
-status_t CameraMetadata::update(uint32_t tag,
- const double *data, size_t data_count) {
+status_t CameraMetadata::update(uint32_t tag, const double* data, size_t data_count) {
status_t res;
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
}
- if ( (res = checkType(tag, TYPE_DOUBLE)) != OK) {
+ if ((res = checkType(tag, TYPE_DOUBLE)) != OK) {
return res;
}
return updateImpl(tag, (const void*)data, data_count);
}
-status_t CameraMetadata::update(uint32_t tag,
- const camera_metadata_rational_t *data, size_t data_count) {
+status_t CameraMetadata::update(uint32_t tag, const camera_metadata_rational_t* data,
+ size_t data_count) {
status_t res;
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
}
- if ( (res = checkType(tag, TYPE_RATIONAL)) != OK) {
+ if ((res = checkType(tag, TYPE_RATIONAL)) != OK) {
return res;
}
return updateImpl(tag, (const void*)data, data_count);
}
-status_t CameraMetadata::update(uint32_t tag,
- const String8 &string) {
+status_t CameraMetadata::update(uint32_t tag, const String8& string) {
status_t res;
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
}
- if ( (res = checkType(tag, TYPE_BYTE)) != OK) {
+ if ((res = checkType(tag, TYPE_BYTE)) != OK) {
return res;
}
// string.size() doesn't count the null termination character.
return updateImpl(tag, (const void*)string.string(), string.size() + 1);
}
-status_t CameraMetadata::update(const camera_metadata_ro_entry &entry) {
+status_t CameraMetadata::update(const camera_metadata_ro_entry& entry) {
status_t res;
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
}
- if ( (res = checkType(entry.tag, entry.type)) != OK) {
+ if ((res = checkType(entry.tag, entry.type)) != OK) {
return res;
}
return updateImpl(entry.tag, (const void*)entry.data.u8, entry.count);
}
-status_t CameraMetadata::updateImpl(uint32_t tag, const void *data,
- size_t data_count) {
+status_t CameraMetadata::updateImpl(uint32_t tag, const void* data, size_t data_count) {
status_t res;
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
@@ -308,13 +290,11 @@
uintptr_t bufAddr = reinterpret_cast<uintptr_t>(mBuffer);
uintptr_t dataAddr = reinterpret_cast<uintptr_t>(data);
if (dataAddr > bufAddr && dataAddr < (bufAddr + bufferSize)) {
- ALOGE("%s: Update attempted with data from the same metadata buffer!",
- __FUNCTION__);
+ ALOGE("%s: Update attempted with data from the same metadata buffer!", __FUNCTION__);
return INVALID_OPERATION;
}
- size_t data_size = calculate_camera_metadata_entry_data_size(type,
- data_count);
+ size_t data_size = calculate_camera_metadata_entry_data_size(type, data_count);
res = resizeIfNeeded(1, data_size);
@@ -322,11 +302,9 @@
camera_metadata_entry_t entry;
res = find_camera_metadata_entry(mBuffer, tag, &entry);
if (res == NAME_NOT_FOUND) {
- res = add_camera_metadata_entry(mBuffer,
- tag, data, data_count);
+ res = add_camera_metadata_entry(mBuffer, tag, data, data_count);
} else if (res == OK) {
- res = update_camera_metadata_entry(mBuffer,
- entry.index, data, data_count, NULL);
+ res = update_camera_metadata_entry(mBuffer, entry.index, data, data_count, NULL);
}
}
@@ -337,11 +315,10 @@
}
IF_ALOGV() {
- ALOGE_IF(validate_camera_metadata_structure(mBuffer, /*size*/NULL) !=
- OK,
+ ALOGE_IF(validate_camera_metadata_structure(mBuffer, /*size*/ NULL) != OK,
- "%s: Failed to validate metadata structure after update %p",
- __FUNCTION__, mBuffer);
+ "%s: Failed to validate metadata structure after update %p", __FUNCTION__,
+ mBuffer);
}
return res;
@@ -361,7 +338,7 @@
return entry;
}
res = find_camera_metadata_entry(mBuffer, tag, &entry);
- if (CC_UNLIKELY( res != OK )) {
+ if (CC_UNLIKELY(res != OK)) {
entry.count = 0;
entry.data.u8 = NULL;
}
@@ -372,7 +349,7 @@
status_t res;
camera_metadata_ro_entry entry;
res = find_camera_metadata_ro_entry(mBuffer, tag, &entry);
- if (CC_UNLIKELY( res != OK )) {
+ if (CC_UNLIKELY(res != OK)) {
entry.count = 0;
entry.data.u8 = NULL;
}
@@ -418,23 +395,17 @@
} else {
size_t currentEntryCount = get_camera_metadata_entry_count(mBuffer);
size_t currentEntryCap = get_camera_metadata_entry_capacity(mBuffer);
- size_t newEntryCount = currentEntryCount +
- extraEntries;
- newEntryCount = (newEntryCount > currentEntryCap) ?
- newEntryCount * 2 : currentEntryCap;
+ size_t newEntryCount = currentEntryCount + extraEntries;
+ newEntryCount = (newEntryCount > currentEntryCap) ? newEntryCount * 2 : currentEntryCap;
size_t currentDataCount = get_camera_metadata_data_count(mBuffer);
size_t currentDataCap = get_camera_metadata_data_capacity(mBuffer);
- size_t newDataCount = currentDataCount +
- extraData;
- newDataCount = (newDataCount > currentDataCap) ?
- newDataCount * 2 : currentDataCap;
+ size_t newDataCount = currentDataCount + extraData;
+ newDataCount = (newDataCount > currentDataCap) ? newDataCount * 2 : currentDataCap;
- if (newEntryCount > currentEntryCap ||
- newDataCount > currentDataCap) {
- camera_metadata_t *oldBuffer = mBuffer;
- mBuffer = allocate_camera_metadata(newEntryCount,
- newDataCount);
+ if (newEntryCount > currentEntryCap || newDataCount > currentDataCap) {
+ camera_metadata_t* oldBuffer = mBuffer;
+ mBuffer = allocate_camera_metadata(newEntryCount, newDataCount);
if (mBuffer == NULL) {
ALOGE("%s: Can't allocate larger metadata buffer", __FUNCTION__);
return NO_MEMORY;
@@ -462,14 +433,13 @@
mBuffer = otherBuf;
}
-status_t CameraMetadata::getTagFromName(const char *name,
- const VendorTagDescriptor* vTags, uint32_t *tag) {
-
+status_t CameraMetadata::getTagFromName(const char* name, const VendorTagDescriptor* vTags,
+ uint32_t* tag) {
if (name == nullptr || tag == nullptr) return BAD_VALUE;
size_t nameLength = strlen(name);
- const SortedVector<String8> *vendorSections;
+ const SortedVector<String8>* vendorSections;
size_t vendorSectionCount = 0;
if (vTags != NULL) {
@@ -478,18 +448,18 @@
}
// First, find the section by the longest string match
- const char *section = NULL;
+ const char* section = NULL;
size_t sectionIndex = 0;
size_t sectionLength = 0;
size_t totalSectionCount = ANDROID_SECTION_COUNT + vendorSectionCount;
for (size_t i = 0; i < totalSectionCount; ++i) {
-
- const char *str = (i < ANDROID_SECTION_COUNT) ? camera_metadata_section_names[i] :
- (*vendorSections)[i - ANDROID_SECTION_COUNT].string();
+ const char* str = (i < ANDROID_SECTION_COUNT)
+ ? camera_metadata_section_names[i]
+ : (*vendorSections)[i - ANDROID_SECTION_COUNT].string();
ALOGV("%s: Trying to match against section '%s'", __FUNCTION__, str);
- if (strstr(name, str) == name) { // name begins with the section name
+ if (strstr(name, str) == name) { // name begins with the section name
size_t strLength = strlen(str);
ALOGV("%s: Name begins with section name", __FUNCTION__);
@@ -508,12 +478,11 @@
if (section == NULL) {
return NAME_NOT_FOUND;
} else {
- ALOGV("%s: Found matched section '%s' (%zu)",
- __FUNCTION__, section, sectionIndex);
+ ALOGV("%s: Found matched section '%s' (%zu)", __FUNCTION__, section, sectionIndex);
}
// Get the tag name component of the name
- const char *nameTagName = name + sectionLength + 1; // x.y.z -> z
+ const char* nameTagName = name + sectionLength + 1; // x.y.z -> z
if (sectionLength + 1 >= nameLength) {
return BAD_VALUE;
}
@@ -522,16 +491,15 @@
uint32_t candidateTag = 0;
if (sectionIndex < ANDROID_SECTION_COUNT) {
// Match built-in tags (typically android.*)
- uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd)
+ uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd)
tagBegin = camera_metadata_section_bounds[sectionIndex][0];
tagEnd = camera_metadata_section_bounds[sectionIndex][1];
for (candidateTag = tagBegin; candidateTag < tagEnd; ++candidateTag) {
- const char *tagName = get_camera_metadata_tag_name(candidateTag);
+ const char* tagName = get_camera_metadata_tag_name(candidateTag);
if (strcmp(nameTagName, tagName) == 0) {
- ALOGV("%s: Found matched tag '%s' (%d)",
- __FUNCTION__, tagName, candidateTag);
+ ALOGV("%s: Found matched tag '%s' (%d)", __FUNCTION__, tagName, candidateTag);
break;
}
}
@@ -554,10 +522,8 @@
return OK;
}
-
-} // namespace helper
-} // namespace V1_0
-} // namespace common
-} // namespace camera
-} // namespace hardware
-} // namespace android
+} // namespace helper
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
diff --git a/camera/common/1.0/default/CameraModule.cpp b/camera/common/default/CameraModule.cpp
similarity index 84%
rename from camera/common/1.0/default/CameraModule.cpp
rename to camera/common/default/CameraModule.cpp
index 16fb85c..9960842 100644
--- a/camera/common/1.0/default/CameraModule.cpp
+++ b/camera/common/default/CameraModule.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "CamComm1.0-CamModule"
#define ATRACE_TAG ATRACE_TAG_CAMERA
-//#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
#include <utils/Trace.h>
@@ -26,11 +26,9 @@
namespace hardware {
namespace camera {
namespace common {
-namespace V1_0 {
namespace helper {
-void CameraModule::deriveCameraCharacteristicsKeys(
- uint32_t deviceVersion, CameraMetadata &chars) {
+void CameraModule::deriveCameraCharacteristicsKeys(uint32_t deviceVersion, CameraMetadata& chars) {
ATRACE_CALL();
Vector<int32_t> derivedCharKeys;
@@ -40,9 +38,9 @@
if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_3) {
Vector<uint8_t> controlModes;
uint8_t data = ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE;
- chars.update(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &data, /*count*/1);
+ chars.update(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &data, /*count*/ 1);
data = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE;
- chars.update(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &data, /*count*/1);
+ chars.update(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &data, /*count*/ 1);
controlModes.push(ANDROID_CONTROL_MODE_AUTO);
camera_metadata_entry entry = chars.find(ANDROID_CONTROL_AVAILABLE_SCENE_MODES);
if (entry.count > 1 || entry.data.u8[0] != ANDROID_CONTROL_SCENE_MODE_DISABLED) {
@@ -121,14 +119,14 @@
if (entry.count > 0) {
Vector<int32_t> highSpeedConfig;
for (size_t i = 0; i < entry.count; i += 4) {
- highSpeedConfig.add(entry.data.i32[i]); // width
- highSpeedConfig.add(entry.data.i32[i + 1]); // height
- highSpeedConfig.add(entry.data.i32[i + 2]); // fps_min
- highSpeedConfig.add(entry.data.i32[i + 3]); // fps_max
- highSpeedConfig.add(1); // batchSize_max. default to 1 for HAL3.2
+ highSpeedConfig.add(entry.data.i32[i]); // width
+ highSpeedConfig.add(entry.data.i32[i + 1]); // height
+ highSpeedConfig.add(entry.data.i32[i + 2]); // fps_min
+ highSpeedConfig.add(entry.data.i32[i + 3]); // fps_max
+ highSpeedConfig.add(1); // batchSize_max. default to 1 for HAL3.2
}
chars.update(ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS,
- highSpeedConfig);
+ highSpeedConfig);
}
}
@@ -145,25 +143,23 @@
const int STREAM_IS_INPUT_OFFSET = 3;
Vector<int32_t> rawOpaqueSizes;
- for (size_t i=0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) {
+ for (size_t i = 0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) {
int32_t format = entry.data.i32[i + STREAM_FORMAT_OFFSET];
int32_t width = entry.data.i32[i + STREAM_WIDTH_OFFSET];
int32_t height = entry.data.i32[i + STREAM_HEIGHT_OFFSET];
int32_t isInput = entry.data.i32[i + STREAM_IS_INPUT_OFFSET];
if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
- format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
+ format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
supportRawOpaque = true;
rawOpaqueSizes.push(width);
rawOpaqueSizes.push(height);
// 2 bytes per pixel. This rough estimation is only used when
// HAL does not fill in the opaque raw size
- rawOpaqueSizes.push(width * height *2);
+ rawOpaqueSizes.push(width * height * 2);
}
if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
- (format == HAL_PIXEL_FORMAT_RAW16 ||
- format == HAL_PIXEL_FORMAT_RAW10 ||
- format == HAL_PIXEL_FORMAT_RAW12 ||
- format == HAL_PIXEL_FORMAT_RAW_OPAQUE)) {
+ (format == HAL_PIXEL_FORMAT_RAW16 || format == HAL_PIXEL_FORMAT_RAW10 ||
+ format == HAL_PIXEL_FORMAT_RAW12 || format == HAL_PIXEL_FORMAT_RAW_OPAQUE)) {
supportAnyRaw = true;
}
}
@@ -183,9 +179,7 @@
entry = chars.find(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE);
if (entry.count == 0) {
// Fill in default value (100, 100)
- chars.update(
- ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE,
- defaultRange, 2);
+ chars.update(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE, defaultRange, 2);
derivedCharKeys.push(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE);
// Actual request/results will be derived by camera device.
derivedRequestKeys.push(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST);
@@ -197,22 +191,19 @@
// Add those newly added keys to AVAILABLE_CHARACTERISTICS_KEYS
// This has to be done at this end of this function.
if (derivedCharKeys.size() > 0) {
- appendAvailableKeys(
- chars, ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, derivedCharKeys);
+ appendAvailableKeys(chars, ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, derivedCharKeys);
}
if (derivedRequestKeys.size() > 0) {
- appendAvailableKeys(
- chars, ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, derivedRequestKeys);
+ appendAvailableKeys(chars, ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, derivedRequestKeys);
}
if (derivedResultKeys.size() > 0) {
- appendAvailableKeys(
- chars, ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, derivedResultKeys);
+ appendAvailableKeys(chars, ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, derivedResultKeys);
}
return;
}
-void CameraModule::appendAvailableKeys(CameraMetadata &chars,
- int32_t keyTag, const Vector<int32_t>& appendKeys) {
+void CameraModule::appendAvailableKeys(CameraMetadata& chars, int32_t keyTag,
+ const Vector<int32_t>& appendKeys) {
camera_metadata_entry entry = chars.find(keyTag);
Vector<int32_t> availableKeys;
availableKeys.setCapacity(entry.count + appendKeys.size());
@@ -225,7 +216,7 @@
chars.update(keyTag, availableKeys);
}
-CameraModule::CameraModule(camera_module_t *module) : mNumberOfCameras(0) {
+CameraModule::CameraModule(camera_module_t* module) : mNumberOfCameras(0) {
if (module == NULL) {
ALOGE("%s: camera hardware module must not be null", __FUNCTION__);
assert(0);
@@ -233,8 +224,7 @@
mModule = module;
}
-CameraModule::~CameraModule()
-{
+CameraModule::~CameraModule() {
while (mCameraInfoMap.size() > 0) {
camera_info cameraInfo = mCameraInfoMap.editValueAt(0);
if (cameraInfo.static_camera_characteristics != NULL) {
@@ -256,8 +246,7 @@
int CameraModule::init() {
ATRACE_CALL();
int res = OK;
- if (getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4 &&
- mModule->init != NULL) {
+ if (getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4 && mModule->init != NULL) {
ATRACE_BEGIN("camera_module->init");
res = mModule->init();
ATRACE_END();
@@ -267,7 +256,7 @@
return res;
}
-int CameraModule::getCameraInfo(int cameraId, struct camera_info *info) {
+int CameraModule::getCameraInfo(int cameraId, struct camera_info* info) {
ATRACE_CALL();
Mutex::Autolock lock(mCameraInfoLock);
if (cameraId < 0) {
@@ -318,7 +307,7 @@
return OK;
}
-int CameraModule::getPhysicalCameraInfo(int physicalCameraId, camera_metadata_t **physicalInfo) {
+int CameraModule::getPhysicalCameraInfo(int physicalCameraId, camera_metadata_t** physicalInfo) {
ATRACE_CALL();
Mutex::Autolock lock(mCameraInfoLock);
if (physicalCameraId < mNumberOfCameras) {
@@ -330,7 +319,7 @@
int apiVersion = mModule->common.module_api_version;
if (apiVersion < CAMERA_MODULE_API_VERSION_2_5) {
ALOGE("%s: Module version must be at least 2.5 to handle getPhysicalCameraInfo",
- __FUNCTION__);
+ __FUNCTION__);
return -ENODEV;
}
if (mModule->get_physical_camera_info == nullptr) {
@@ -341,7 +330,7 @@
ssize_t index = mPhysicalCameraInfoMap.indexOfKey(physicalCameraId);
if (index == NAME_NOT_FOUND) {
// Get physical camera characteristics, and cache it
- camera_metadata_t *info = nullptr;
+ camera_metadata_t* info = nullptr;
ATRACE_BEGIN("camera_module->get_physical_camera_info");
int ret = mModule->get_physical_camera_info(physicalCameraId, &info);
ATRACE_END();
@@ -396,8 +385,7 @@
return mModule->open_legacy != NULL;
}
-int CameraModule::openLegacy(
- const char* id, uint32_t halVersion, struct hw_device_t** device) {
+int CameraModule::openLegacy(const char* id, uint32_t halVersion, struct hw_device_t** device) {
int res;
ATRACE_BEGIN("camera_module->open_legacy");
res = mModule->open_legacy(&mModule->common, id, halVersion, device);
@@ -413,7 +401,7 @@
return numCameras;
}
-int CameraModule::setCallbacks(const camera_module_callbacks_t *callbacks) {
+int CameraModule::setCallbacks(const camera_module_callbacks_t* callbacks) {
int res = OK;
ATRACE_BEGIN("camera_module->set_callbacks");
if (getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_1) {
@@ -438,8 +426,7 @@
bool CameraModule::isSetTorchModeSupported() const {
if (getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4) {
if (mModule->set_torch_mode == NULL) {
- ALOGE("%s: Module 2.4 device must support set torch API!",
- __FUNCTION__);
+ ALOGE("%s: Module 2.4 device must support set torch API!", __FUNCTION__);
return false;
}
return true;
@@ -457,7 +444,7 @@
return res;
}
-int CameraModule::isStreamCombinationSupported(int cameraId, camera_stream_combination_t *streams) {
+int CameraModule::isStreamCombinationSupported(int cameraId, camera_stream_combination_t* streams) {
int res = INVALID_OPERATION;
if (mModule->is_stream_combination_supported != NULL) {
ATRACE_BEGIN("camera_module->is_stream_combination_supported");
@@ -468,44 +455,41 @@
}
void CameraModule::notifyDeviceStateChange(uint64_t deviceState) {
- if (getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_5 &&
- mModule->notify_device_state_change != NULL) {
- ATRACE_BEGIN("camera_module->notify_device_state_change");
- ALOGI("%s: calling notify_device_state_change with state %" PRId64, __FUNCTION__,
- deviceState);
- mModule->notify_device_state_change(deviceState);
- ATRACE_END();
- }
+ if (getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_5 &&
+ mModule->notify_device_state_change != NULL) {
+ ATRACE_BEGIN("camera_module->notify_device_state_change");
+ ALOGI("%s: calling notify_device_state_change with state %" PRId64, __FUNCTION__,
+ deviceState);
+ mModule->notify_device_state_change(deviceState);
+ ATRACE_END();
+ }
}
-bool CameraModule::isLogicalMultiCamera(
- const common::V1_0::helper::CameraMetadata& metadata,
- std::unordered_set<std::string>* physicalCameraIds) {
+bool CameraModule::isLogicalMultiCamera(const common::helper::CameraMetadata& metadata,
+ std::unordered_set<std::string>* physicalCameraIds) {
if (physicalCameraIds == nullptr) {
ALOGE("%s: physicalCameraIds must not be null", __FUNCTION__);
return false;
}
bool isLogicalMultiCamera = false;
- camera_metadata_ro_entry_t capabilities =
- metadata.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+ camera_metadata_ro_entry_t capabilities = metadata.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
for (size_t i = 0; i < capabilities.count; i++) {
if (capabilities.data.u8[i] ==
- ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
isLogicalMultiCamera = true;
break;
}
}
if (isLogicalMultiCamera) {
- camera_metadata_ro_entry_t entry =
- metadata.find(ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
+ camera_metadata_ro_entry_t entry = metadata.find(ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
const uint8_t* ids = entry.data.u8;
size_t start = 0;
for (size_t i = 0; i < entry.count; ++i) {
if (ids[i] == '\0') {
if (start != i) {
- const char* physicalId = reinterpret_cast<const char*>(ids+start);
+ const char* physicalId = reinterpret_cast<const char*>(ids + start);
physicalCameraIds->emplace(physicalId);
}
start = i + 1;
@@ -516,7 +500,7 @@
}
status_t CameraModule::filterOpenErrorCode(status_t err) {
- switch(err) {
+ switch (err) {
case NO_ERROR:
case -EBUSY:
case -EINVAL:
@@ -533,9 +517,9 @@
// static_camera_characteristics
if (getDeviceVersion(cameraId) >= CAMERA_DEVICE_API_VERSION_3_0) {
std::unordered_set<std::string> physicalIds;
- camera_metadata_t *metadata = const_cast<camera_metadata_t*>(
+ camera_metadata_t* metadata = const_cast<camera_metadata_t*>(
mCameraInfoMap.valueFor(cameraId).static_camera_characteristics);
- common::V1_0::helper::CameraMetadata hidlMetadata(metadata);
+ common::helper::CameraMetadata hidlMetadata(metadata);
if (isLogicalMultiCamera(hidlMetadata, &physicalIds)) {
for (const auto& id : physicalIds) {
@@ -545,7 +529,7 @@
mPhysicalCameraInfoMap.removeItem(idInt);
} else {
ALOGE("%s: Cannot find corresponding static metadata for physical id %s",
- __FUNCTION__, id.c_str());
+ __FUNCTION__, id.c_str());
}
}
}
@@ -575,9 +559,8 @@
return mModule->common.dso;
}
-} // namespace helper
-} // namespace V1_0
-} // namespace common
-} // namespace camera
-} // namespace hardware
-} // namespace android
+} // namespace helper
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
diff --git a/camera/common/1.0/default/CameraParameters.cpp b/camera/common/default/CameraParameters.cpp
similarity index 77%
rename from camera/common/1.0/default/CameraParameters.cpp
rename to camera/common/default/CameraParameters.cpp
index e707b08..37e28a2 100644
--- a/camera/common/1.0/default/CameraParameters.cpp
+++ b/camera/common/default/CameraParameters.cpp
@@ -18,17 +18,16 @@
#define LOG_TAG "CameraParams"
#include <log/log.h>
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
+#include <system/graphics.h>
#include <unistd.h>
#include "CameraParameters.h"
-#include <system/graphics.h>
namespace android {
namespace hardware {
namespace camera {
namespace common {
-namespace V1_0 {
namespace helper {
// Parameter keys to communicate between camera application and driver.
@@ -79,7 +78,8 @@
const char CameraParameters::KEY_AUTO_EXPOSURE_LOCK[] = "auto-exposure-lock";
const char CameraParameters::KEY_AUTO_EXPOSURE_LOCK_SUPPORTED[] = "auto-exposure-lock-supported";
const char CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK[] = "auto-whitebalance-lock";
-const char CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED[] = "auto-whitebalance-lock-supported";
+const char CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED[] =
+ "auto-whitebalance-lock-supported";
const char CameraParameters::KEY_MAX_NUM_METERING_AREAS[] = "max-num-metering-areas";
const char CameraParameters::KEY_METERING_AREAS[] = "metering-areas";
const char CameraParameters::KEY_ZOOM[] = "zoom";
@@ -91,7 +91,8 @@
const char CameraParameters::KEY_VIDEO_FRAME_FORMAT[] = "video-frame-format";
const char CameraParameters::KEY_VIDEO_SIZE[] = "video-size";
const char CameraParameters::KEY_SUPPORTED_VIDEO_SIZES[] = "video-size-values";
-const char CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[] = "preferred-preview-size-for-video";
+const char CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[] =
+ "preferred-preview-size-for-video";
const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW[] = "max-num-detected-faces-hw";
const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW[] = "max-num-detected-faces-sw";
const char CameraParameters::KEY_RECORDING_HINT[] = "recording-hint";
@@ -160,7 +161,7 @@
const char CameraParameters::PIXEL_FORMAT_YUV422SP[] = "yuv422sp";
const char CameraParameters::PIXEL_FORMAT_YUV420SP[] = "yuv420sp";
const char CameraParameters::PIXEL_FORMAT_YUV422I[] = "yuv422i-yuyv";
-const char CameraParameters::PIXEL_FORMAT_YUV420P[] = "yuv420p";
+const char CameraParameters::PIXEL_FORMAT_YUV420P[] = "yuv420p";
const char CameraParameters::PIXEL_FORMAT_RGB565[] = "rgb565";
const char CameraParameters::PIXEL_FORMAT_RGBA8888[] = "rgba8888";
const char CameraParameters::PIXEL_FORMAT_JPEG[] = "jpeg";
@@ -180,17 +181,11 @@
const char CameraParameters::LIGHTFX_LOWLIGHT[] = "low-light";
const char CameraParameters::LIGHTFX_HDR[] = "high-dynamic-range";
-CameraParameters::CameraParameters()
- : mMap()
-{
-}
+CameraParameters::CameraParameters() : mMap() {}
-CameraParameters::~CameraParameters()
-{
-}
+CameraParameters::~CameraParameters() {}
-String8 CameraParameters::flatten() const
-{
+String8 CameraParameters::flatten() const {
String8 flattened("");
size_t size = mMap.size();
@@ -202,31 +197,28 @@
flattened += k;
flattened += "=";
flattened += v;
- if (i != size-1)
- flattened += ";";
+ if (i != size - 1) flattened += ";";
}
return flattened;
}
-void CameraParameters::unflatten(const String8 ¶ms)
-{
- const char *a = params.string();
- const char *b;
+void CameraParameters::unflatten(const String8& params) {
+ const char* a = params.string();
+ const char* b;
mMap.clear();
for (;;) {
// Find the bounds of the key name.
b = strchr(a, '=');
- if (b == 0)
- break;
+ if (b == 0) break;
// Create the key string.
- String8 k(a, (size_t)(b-a));
+ String8 k(a, (size_t)(b - a));
// Find the value.
- a = b+1;
+ a = b + 1;
b = strchr(a, ';');
if (b == 0) {
// If there's no semicolon, this is the last item.
@@ -235,15 +227,13 @@
break;
}
- String8 v(a, (size_t)(b-a));
+ String8 v(a, (size_t)(b - a));
mMap.add(k, v);
- a = b+1;
+ a = b + 1;
}
}
-
-void CameraParameters::set(const char *key, const char *value)
-{
+void CameraParameters::set(const char* key, const char* value) {
// i think i can do this with strspn()
if (strchr(key, '=') || strchr(key, ';')) {
// ALOGE("Key \"%s\"contains invalid character (= or ;)", key);
@@ -258,54 +248,44 @@
mMap.replaceValueFor(String8(key), String8(value));
}
-void CameraParameters::set(const char *key, int value)
-{
+void CameraParameters::set(const char* key, int value) {
char str[16];
sprintf(str, "%d", value);
set(key, str);
}
-void CameraParameters::setFloat(const char *key, float value)
-{
+void CameraParameters::setFloat(const char* key, float value) {
char str[16]; // 14 should be enough. We overestimate to be safe.
snprintf(str, sizeof(str), "%g", value);
set(key, str);
}
-const char *CameraParameters::get(const char *key) const
-{
+const char* CameraParameters::get(const char* key) const {
String8 v = mMap.valueFor(String8(key));
- if (v.length() == 0)
- return 0;
+ if (v.length() == 0) return 0;
return v.string();
}
-int CameraParameters::getInt(const char *key) const
-{
- const char *v = get(key);
- if (v == 0)
- return -1;
+int CameraParameters::getInt(const char* key) const {
+ const char* v = get(key);
+ if (v == 0) return -1;
return strtol(v, 0, 0);
}
-float CameraParameters::getFloat(const char *key) const
-{
- const char *v = get(key);
+float CameraParameters::getFloat(const char* key) const {
+ const char* v = get(key);
if (v == 0) return -1;
return strtof(v, 0);
}
-void CameraParameters::remove(const char *key)
-{
+void CameraParameters::remove(const char* key) {
mMap.removeItem(String8(key));
}
// Parse string like "640x480" or "10000,20000"
-static int parse_pair(const char *str, int *first, int *second, char delim,
- char **endptr = NULL)
-{
+static int parse_pair(const char* str, int* first, int* second, char delim, char** endptr = NULL) {
// Find the first integer.
- char *end;
+ char* end;
int w = (int)strtol(str, &end, 10);
// If a delimeter does not immediately follow, give up.
if (*end != delim) {
@@ -314,7 +294,7 @@
}
// Find the second integer, immediately after the delimeter.
- int h = (int)strtol(end+1, &end, 10);
+ int h = (int)strtol(end + 1, &end, 10);
*first = w;
*second = h;
@@ -326,18 +306,16 @@
return 0;
}
-static void parseSizesList(const char *sizesStr, Vector<Size> &sizes)
-{
+static void parseSizesList(const char* sizesStr, Vector<Size>& sizes) {
if (sizesStr == 0) {
return;
}
- char *sizeStartPtr = (char *)sizesStr;
+ char* sizeStartPtr = (char*)sizesStr;
while (true) {
int width, height;
- int success = parse_pair(sizeStartPtr, &width, &height, 'x',
- &sizeStartPtr);
+ int success = parse_pair(sizeStartPtr, &width, &height, 'x', &sizeStartPtr);
if (success == -1 || (*sizeStartPtr != ',' && *sizeStartPtr != '\0')) {
ALOGE("Picture sizes string \"%s\" contains invalid character.", sizesStr);
return;
@@ -351,119 +329,101 @@
}
}
-void CameraParameters::setPreviewSize(int width, int height)
-{
+void CameraParameters::setPreviewSize(int width, int height) {
char str[32];
sprintf(str, "%dx%d", width, height);
set(KEY_PREVIEW_SIZE, str);
}
-void CameraParameters::getPreviewSize(int *width, int *height) const
-{
+void CameraParameters::getPreviewSize(int* width, int* height) const {
*width = *height = -1;
// Get the current string, if it doesn't exist, leave the -1x-1
- const char *p = get(KEY_PREVIEW_SIZE);
- if (p == 0) return;
+ const char* p = get(KEY_PREVIEW_SIZE);
+ if (p == 0) return;
parse_pair(p, width, height, 'x');
}
-void CameraParameters::getPreferredPreviewSizeForVideo(int *width, int *height) const
-{
+void CameraParameters::getPreferredPreviewSizeForVideo(int* width, int* height) const {
*width = *height = -1;
- const char *p = get(KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO);
- if (p == 0) return;
+ const char* p = get(KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO);
+ if (p == 0) return;
parse_pair(p, width, height, 'x');
}
-void CameraParameters::getSupportedPreviewSizes(Vector<Size> &sizes) const
-{
- const char *previewSizesStr = get(KEY_SUPPORTED_PREVIEW_SIZES);
+void CameraParameters::getSupportedPreviewSizes(Vector<Size>& sizes) const {
+ const char* previewSizesStr = get(KEY_SUPPORTED_PREVIEW_SIZES);
parseSizesList(previewSizesStr, sizes);
}
-void CameraParameters::setVideoSize(int width, int height)
-{
+void CameraParameters::setVideoSize(int width, int height) {
char str[32];
sprintf(str, "%dx%d", width, height);
set(KEY_VIDEO_SIZE, str);
}
-void CameraParameters::getVideoSize(int *width, int *height) const
-{
+void CameraParameters::getVideoSize(int* width, int* height) const {
*width = *height = -1;
- const char *p = get(KEY_VIDEO_SIZE);
+ const char* p = get(KEY_VIDEO_SIZE);
if (p == 0) return;
parse_pair(p, width, height, 'x');
}
-void CameraParameters::getSupportedVideoSizes(Vector<Size> &sizes) const
-{
- const char *videoSizesStr = get(KEY_SUPPORTED_VIDEO_SIZES);
+void CameraParameters::getSupportedVideoSizes(Vector<Size>& sizes) const {
+ const char* videoSizesStr = get(KEY_SUPPORTED_VIDEO_SIZES);
parseSizesList(videoSizesStr, sizes);
}
-void CameraParameters::setPreviewFrameRate(int fps)
-{
+void CameraParameters::setPreviewFrameRate(int fps) {
set(KEY_PREVIEW_FRAME_RATE, fps);
}
-int CameraParameters::getPreviewFrameRate() const
-{
+int CameraParameters::getPreviewFrameRate() const {
return getInt(KEY_PREVIEW_FRAME_RATE);
}
-void CameraParameters::getPreviewFpsRange(int *min_fps, int *max_fps) const
-{
+void CameraParameters::getPreviewFpsRange(int* min_fps, int* max_fps) const {
*min_fps = *max_fps = -1;
- const char *p = get(KEY_PREVIEW_FPS_RANGE);
+ const char* p = get(KEY_PREVIEW_FPS_RANGE);
if (p == 0) return;
parse_pair(p, min_fps, max_fps, ',');
}
-void CameraParameters::setPreviewFormat(const char *format)
-{
+void CameraParameters::setPreviewFormat(const char* format) {
set(KEY_PREVIEW_FORMAT, format);
}
-const char *CameraParameters::getPreviewFormat() const
-{
+const char* CameraParameters::getPreviewFormat() const {
return get(KEY_PREVIEW_FORMAT);
}
-void CameraParameters::setPictureSize(int width, int height)
-{
+void CameraParameters::setPictureSize(int width, int height) {
char str[32];
sprintf(str, "%dx%d", width, height);
set(KEY_PICTURE_SIZE, str);
}
-void CameraParameters::getPictureSize(int *width, int *height) const
-{
+void CameraParameters::getPictureSize(int* width, int* height) const {
*width = *height = -1;
// Get the current string, if it doesn't exist, leave the -1x-1
- const char *p = get(KEY_PICTURE_SIZE);
+ const char* p = get(KEY_PICTURE_SIZE);
if (p == 0) return;
parse_pair(p, width, height, 'x');
}
-void CameraParameters::getSupportedPictureSizes(Vector<Size> &sizes) const
-{
- const char *pictureSizesStr = get(KEY_SUPPORTED_PICTURE_SIZES);
+void CameraParameters::getSupportedPictureSizes(Vector<Size>& sizes) const {
+ const char* pictureSizesStr = get(KEY_SUPPORTED_PICTURE_SIZES);
parseSizesList(pictureSizesStr, sizes);
}
-void CameraParameters::setPictureFormat(const char *format)
-{
+void CameraParameters::setPictureFormat(const char* format) {
set(KEY_PICTURE_FORMAT, format);
}
-const char *CameraParameters::getPictureFormat() const
-{
+const char* CameraParameters::getPictureFormat() const {
return get(KEY_PICTURE_FORMAT);
}
-void CameraParameters::dump() const
-{
+void CameraParameters::dump() const {
ALOGD("dump: mMap.size = %zu", mMap.size());
for (size_t i = 0; i < mMap.size(); i++) {
String8 k, v;
@@ -473,8 +433,7 @@
}
}
-status_t CameraParameters::dump(int fd, const Vector<String16>& /*args*/) const
-{
+status_t CameraParameters::dump(int fd, const Vector<String16>& /*args*/) const {
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
@@ -492,8 +451,7 @@
}
void CameraParameters::getSupportedPreviewFormats(Vector<int>& formats) const {
- const char* supportedPreviewFormats =
- get(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS);
+ const char* supportedPreviewFormats = get(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS);
if (supportedPreviewFormats == NULL) {
ALOGW("%s: No supported preview formats.", __FUNCTION__);
@@ -515,35 +473,31 @@
fmtStr.unlockBuffer(fmtStr.size());
}
-
int CameraParameters::previewFormatToEnum(const char* format) {
- return
- !format ?
- HAL_PIXEL_FORMAT_YCrCb_420_SP :
- !strcmp(format, PIXEL_FORMAT_YUV422SP) ?
- HAL_PIXEL_FORMAT_YCbCr_422_SP : // NV16
- !strcmp(format, PIXEL_FORMAT_YUV420SP) ?
- HAL_PIXEL_FORMAT_YCrCb_420_SP : // NV21
- !strcmp(format, PIXEL_FORMAT_YUV422I) ?
- HAL_PIXEL_FORMAT_YCbCr_422_I : // YUY2
- !strcmp(format, PIXEL_FORMAT_YUV420P) ?
- HAL_PIXEL_FORMAT_YV12 : // YV12
- !strcmp(format, PIXEL_FORMAT_RGB565) ?
- HAL_PIXEL_FORMAT_RGB_565 : // RGB565
- !strcmp(format, PIXEL_FORMAT_RGBA8888) ?
- HAL_PIXEL_FORMAT_RGBA_8888 : // RGB8888
- !strcmp(format, PIXEL_FORMAT_BAYER_RGGB) ?
- HAL_PIXEL_FORMAT_RAW16 : // Raw sensor data
- -1;
+ return !format ? HAL_PIXEL_FORMAT_YCrCb_420_SP
+ : !strcmp(format, PIXEL_FORMAT_YUV422SP) ? HAL_PIXEL_FORMAT_YCbCr_422_SP
+ : // NV16
+ !strcmp(format, PIXEL_FORMAT_YUV420SP) ? HAL_PIXEL_FORMAT_YCrCb_420_SP
+ : // NV21
+ !strcmp(format, PIXEL_FORMAT_YUV422I) ? HAL_PIXEL_FORMAT_YCbCr_422_I
+ : // YUY2
+ !strcmp(format, PIXEL_FORMAT_YUV420P) ? HAL_PIXEL_FORMAT_YV12
+ : // YV12
+ !strcmp(format, PIXEL_FORMAT_RGB565) ? HAL_PIXEL_FORMAT_RGB_565
+ : // RGB565
+ !strcmp(format, PIXEL_FORMAT_RGBA8888) ? HAL_PIXEL_FORMAT_RGBA_8888
+ : // RGB8888
+ !strcmp(format, PIXEL_FORMAT_BAYER_RGGB) ? HAL_PIXEL_FORMAT_RAW16
+ : // Raw sensor data
+ -1;
}
bool CameraParameters::isEmpty() const {
return mMap.isEmpty();
}
-};
-};
-};
-};
-};
-}; // namespace android
+}; // namespace helper
+}; // namespace common
+}; // namespace camera
+}; // namespace hardware
+}; // namespace android
diff --git a/camera/common/1.0/default/Exif.cpp b/camera/common/default/Exif.cpp
similarity index 83%
rename from camera/common/1.0/default/Exif.cpp
rename to camera/common/default/Exif.cpp
index 413b6bb..f4b2a31 100644
--- a/camera/common/1.0/default/Exif.cpp
+++ b/camera/common/default/Exif.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "CamComm1.0-Exif"
#define ATRACE_TAG ATRACE_TAG_CAMERA
-//#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
#include <android/log.h>
@@ -41,15 +41,12 @@
} // namespace std
-
namespace android {
namespace hardware {
namespace camera {
namespace common {
-namespace V1_0 {
namespace helper {
-
class ExifUtilsImpl : public ExifUtils {
public:
ExifUtilsImpl();
@@ -61,8 +58,7 @@
virtual bool initialize();
// set all known fields from a metadata structure
- virtual bool setFromMetadata(const CameraMetadata& metadata,
- const size_t imageWidth,
+ virtual bool setFromMetadata(const CameraMetadata& metadata, const size_t imageWidth,
const size_t imageHeight);
// sets the len aperture.
@@ -254,7 +250,6 @@
// Returns false if memory allocation fails.
virtual bool setExifVersion(const std::string& exif_version);
-
// Resets the pointers and memories.
virtual void reset();
@@ -262,8 +257,7 @@
// if the tag exists.
// Returns the entry of the tag. The reference count of returned ExifEntry is
// two.
- virtual std::unique_ptr<ExifEntry> addVariableLengthEntry(ExifIfd ifd,
- ExifTag tag,
+ virtual std::unique_ptr<ExifEntry> addVariableLengthEntry(ExifIfd ifd, ExifTag tag,
ExifFormat format,
uint64_t components,
unsigned int size);
@@ -275,32 +269,17 @@
virtual std::unique_ptr<ExifEntry> addEntry(ExifIfd ifd, ExifTag tag);
// Helpe functions to add exif data with different types.
- virtual bool setShort(ExifIfd ifd,
- ExifTag tag,
- uint16_t value,
- const std::string& msg);
+ virtual bool setShort(ExifIfd ifd, ExifTag tag, uint16_t value, const std::string& msg);
- virtual bool setLong(ExifIfd ifd,
- ExifTag tag,
- uint32_t value,
- const std::string& msg);
+ virtual bool setLong(ExifIfd ifd, ExifTag tag, uint32_t value, const std::string& msg);
- virtual bool setRational(ExifIfd ifd,
- ExifTag tag,
- uint32_t numerator,
- uint32_t denominator,
+ virtual bool setRational(ExifIfd ifd, ExifTag tag, uint32_t numerator, uint32_t denominator,
const std::string& msg);
- virtual bool setSRational(ExifIfd ifd,
- ExifTag tag,
- int32_t numerator,
- int32_t denominator,
+ virtual bool setSRational(ExifIfd ifd, ExifTag tag, int32_t numerator, int32_t denominator,
const std::string& msg);
- virtual bool setString(ExifIfd ifd,
- ExifTag tag,
- ExifFormat format,
- const std::string& buffer,
+ virtual bool setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::string& buffer,
const std::string& msg);
// Destroys the buffer of APP1 segment if exists.
@@ -313,37 +292,31 @@
uint8_t* app1_buffer_;
// The length of |app1_buffer_|.
unsigned int app1_length_;
-
};
-#define SET_SHORT(ifd, tag, value) \
- do { \
- if (setShort(ifd, tag, value, #tag) == false) \
- return false; \
+#define SET_SHORT(ifd, tag, value) \
+ do { \
+ if (setShort(ifd, tag, value, #tag) == false) return false; \
} while (0);
-#define SET_LONG(ifd, tag, value) \
- do { \
- if (setLong(ifd, tag, value, #tag) == false) \
- return false; \
+#define SET_LONG(ifd, tag, value) \
+ do { \
+ if (setLong(ifd, tag, value, #tag) == false) return false; \
} while (0);
-#define SET_RATIONAL(ifd, tag, numerator, denominator) \
- do { \
- if (setRational(ifd, tag, numerator, denominator, #tag) == false) \
- return false; \
+#define SET_RATIONAL(ifd, tag, numerator, denominator) \
+ do { \
+ if (setRational(ifd, tag, numerator, denominator, #tag) == false) return false; \
} while (0);
-#define SET_SRATIONAL(ifd, tag, numerator, denominator) \
- do { \
- if (setSRational(ifd, tag, numerator, denominator, #tag) == false) \
- return false; \
+#define SET_SRATIONAL(ifd, tag, numerator, denominator) \
+ do { \
+ if (setSRational(ifd, tag, numerator, denominator, #tag) == false) return false; \
} while (0);
#define SET_STRING(ifd, tag, format, buffer) \
do { \
- if (setString(ifd, tag, format, buffer, #tag) == false) \
- return false; \
+ if (setString(ifd, tag, format, buffer, #tag) == false) return false; \
} while (0);
// This comes from the Exif Version 2.2 standard table 6.
@@ -353,30 +326,25 @@
// Take the integer part of |num|.
ExifLong degrees = static_cast<ExifLong>(num);
ExifLong minutes = static_cast<ExifLong>(60 * (num - degrees));
- ExifLong microseconds =
- static_cast<ExifLong>(3600000000u * (num - degrees - minutes / 60.0));
+ ExifLong microseconds = static_cast<ExifLong>(3600000000u * (num - degrees - minutes / 60.0));
exif_set_rational(data, EXIF_BYTE_ORDER_INTEL, {degrees, 1});
- exif_set_rational(data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
- {minutes, 1});
+ exif_set_rational(data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL, {minutes, 1});
exif_set_rational(data + 2 * sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
- {microseconds, 1000000});
+ {microseconds, 1000000});
}
-ExifUtils *ExifUtils::create() {
+ExifUtils* ExifUtils::create() {
return new ExifUtilsImpl();
}
-ExifUtils::~ExifUtils() {
-}
+ExifUtils::~ExifUtils() {}
-ExifUtilsImpl::ExifUtilsImpl()
- : exif_data_(nullptr), app1_buffer_(nullptr), app1_length_(0) {}
+ExifUtilsImpl::ExifUtilsImpl() : exif_data_(nullptr), app1_buffer_(nullptr), app1_length_(0) {}
ExifUtilsImpl::~ExifUtilsImpl() {
reset();
}
-
bool ExifUtilsImpl::initialize() {
reset();
exif_data_ = exif_data_new();
@@ -403,8 +371,7 @@
}
bool ExifUtilsImpl::setBrightness(int32_t numerator, int32_t denominator) {
- SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_BRIGHTNESS_VALUE, numerator,
- denominator);
+ SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_BRIGHTNESS_VALUE, numerator, denominator);
return true;
}
@@ -413,10 +380,9 @@
return true;
}
-bool ExifUtilsImpl::setComponentsConfiguration(
- const std::string& components_configuration) {
- SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_COMPONENTS_CONFIGURATION,
- EXIF_FORMAT_UNDEFINED, components_configuration);
+bool ExifUtilsImpl::setComponentsConfiguration(const std::string& components_configuration) {
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_COMPONENTS_CONFIGURATION, EXIF_FORMAT_UNDEFINED,
+ components_configuration);
return true;
}
@@ -433,37 +399,31 @@
bool ExifUtilsImpl::setDateTime(const struct tm& t) {
// The length is 20 bytes including NULL for termination in Exif standard.
char str[20];
- int result = snprintf(str, sizeof(str), "%04i:%02i:%02i %02i:%02i:%02i",
- t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour,
- t.tm_min, t.tm_sec);
+ int result = snprintf(str, sizeof(str), "%04i:%02i:%02i %02i:%02i:%02i", t.tm_year + 1900,
+ t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
if (result != sizeof(str) - 1) {
ALOGW("%s: Input time is invalid", __FUNCTION__);
return false;
}
std::string buffer(str);
SET_STRING(EXIF_IFD_0, EXIF_TAG_DATE_TIME, EXIF_FORMAT_ASCII, buffer);
- SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, EXIF_FORMAT_ASCII,
- buffer);
- SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED, EXIF_FORMAT_ASCII,
- buffer);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, EXIF_FORMAT_ASCII, buffer);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED, EXIF_FORMAT_ASCII, buffer);
return true;
}
bool ExifUtilsImpl::setDescription(const std::string& description) {
- SET_STRING(EXIF_IFD_0, EXIF_TAG_IMAGE_DESCRIPTION, EXIF_FORMAT_ASCII,
- description);
+ SET_STRING(EXIF_IFD_0, EXIF_TAG_IMAGE_DESCRIPTION, EXIF_FORMAT_ASCII, description);
return true;
}
bool ExifUtilsImpl::setDigitalZoomRatio(uint32_t numerator, uint32_t denominator) {
- SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_DIGITAL_ZOOM_RATIO, numerator,
- denominator);
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_DIGITAL_ZOOM_RATIO, numerator, denominator);
return true;
}
bool ExifUtilsImpl::setExposureBias(int32_t numerator, int32_t denominator) {
- SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_BIAS_VALUE, numerator,
- denominator);
+ SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_BIAS_VALUE, numerator, denominator);
return true;
}
@@ -526,7 +486,7 @@
return false;
}
exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
- {static_cast<ExifLong>(altitude * 1000), 1000});
+ {static_cast<ExifLong>(altitude * 1000), 1000});
return true;
}
@@ -588,26 +548,23 @@
}
bool ExifUtilsImpl::setGpsProcessingMethod(const std::string& method) {
- std::string buffer =
- std::string(gExifAsciiPrefix, sizeof(gExifAsciiPrefix)) + method;
+ std::string buffer = std::string(gExifAsciiPrefix, sizeof(gExifAsciiPrefix)) + method;
SET_STRING(EXIF_IFD_GPS, static_cast<ExifTag>(EXIF_TAG_GPS_PROCESSING_METHOD),
- EXIF_FORMAT_UNDEFINED, buffer);
+ EXIF_FORMAT_UNDEFINED, buffer);
return true;
}
bool ExifUtilsImpl::setGpsTimestamp(const struct tm& t) {
const ExifTag dateTag = static_cast<ExifTag>(EXIF_TAG_GPS_DATE_STAMP);
const size_t kGpsDateStampSize = 11;
- std::unique_ptr<ExifEntry> entry =
- addVariableLengthEntry(EXIF_IFD_GPS, dateTag, EXIF_FORMAT_ASCII,
- kGpsDateStampSize, kGpsDateStampSize);
+ std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
+ EXIF_IFD_GPS, dateTag, EXIF_FORMAT_ASCII, kGpsDateStampSize, kGpsDateStampSize);
if (!entry) {
ALOGE("%s: Adding GPSDateStamp exif entry failed", __FUNCTION__);
return false;
}
- int result =
- snprintf(reinterpret_cast<char*>(entry->data), kGpsDateStampSize,
- "%04i:%02i:%02i", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday);
+ int result = snprintf(reinterpret_cast<char*>(entry->data), kGpsDateStampSize, "%04i:%02i:%02i",
+ t.tm_year + 1900, t.tm_mon + 1, t.tm_mday);
if (result != kGpsDateStampSize - 1) {
ALOGW("%s: Input time is invalid", __FUNCTION__);
return false;
@@ -615,18 +572,16 @@
const ExifTag timeTag = static_cast<ExifTag>(EXIF_TAG_GPS_TIME_STAMP);
entry = addVariableLengthEntry(EXIF_IFD_GPS, timeTag, EXIF_FORMAT_RATIONAL, 3,
- 3 * sizeof(ExifRational));
+ 3 * sizeof(ExifRational));
if (!entry) {
ALOGE("%s: Adding GPSTimeStamp exif entry failed", __FUNCTION__);
return false;
}
- exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
- {static_cast<ExifLong>(t.tm_hour), 1});
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL, {static_cast<ExifLong>(t.tm_hour), 1});
exif_set_rational(entry->data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
- {static_cast<ExifLong>(t.tm_min), 1});
- exif_set_rational(entry->data + 2 * sizeof(ExifRational),
- EXIF_BYTE_ORDER_INTEL,
- {static_cast<ExifLong>(t.tm_sec), 1});
+ {static_cast<ExifLong>(t.tm_min), 1});
+ exif_set_rational(entry->data + 2 * sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(t.tm_sec), 1});
return true;
}
@@ -654,8 +609,7 @@
}
bool ExifUtilsImpl::setMaxAperture(uint32_t numerator, uint32_t denominator) {
- SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_MAX_APERTURE_VALUE, numerator,
- denominator);
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_MAX_APERTURE_VALUE, numerator, denominator);
return true;
}
@@ -714,24 +668,19 @@
}
bool ExifUtilsImpl::setShutterSpeed(int32_t numerator, int32_t denominator) {
- SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SHUTTER_SPEED_VALUE, numerator,
- denominator);
+ SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SHUTTER_SPEED_VALUE, numerator, denominator);
return true;
}
bool ExifUtilsImpl::setSubjectDistance(uint32_t numerator, uint32_t denominator) {
- SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SUBJECT_DISTANCE, numerator,
- denominator);
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SUBJECT_DISTANCE, numerator, denominator);
return true;
}
bool ExifUtilsImpl::setSubsecTime(const std::string& subsec_time) {
- SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME, EXIF_FORMAT_ASCII,
- subsec_time);
- SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_ORIGINAL, EXIF_FORMAT_ASCII,
- subsec_time);
- SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_DIGITIZED, EXIF_FORMAT_ASCII,
- subsec_time);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME, EXIF_FORMAT_ASCII, subsec_time);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_ORIGINAL, EXIF_FORMAT_ASCII, subsec_time);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_DIGITIZED, EXIF_FORMAT_ASCII, subsec_time);
return true;
}
@@ -816,8 +765,7 @@
}
}
-std::unique_ptr<ExifEntry> ExifUtilsImpl::addVariableLengthEntry(ExifIfd ifd,
- ExifTag tag,
+std::unique_ptr<ExifEntry> ExifUtilsImpl::addVariableLengthEntry(ExifIfd ifd, ExifTag tag,
ExifFormat format,
uint64_t components,
unsigned int size) {
@@ -872,10 +820,7 @@
return entry;
}
-bool ExifUtilsImpl::setShort(ExifIfd ifd,
- ExifTag tag,
- uint16_t value,
- const std::string& msg) {
+bool ExifUtilsImpl::setShort(ExifIfd ifd, ExifTag tag, uint16_t value, const std::string& msg) {
std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
if (!entry) {
ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
@@ -885,10 +830,7 @@
return true;
}
-bool ExifUtilsImpl::setLong(ExifIfd ifd,
- ExifTag tag,
- uint32_t value,
- const std::string& msg) {
+bool ExifUtilsImpl::setLong(ExifIfd ifd, ExifTag tag, uint32_t value, const std::string& msg) {
std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
if (!entry) {
ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
@@ -898,41 +840,30 @@
return true;
}
-bool ExifUtilsImpl::setRational(ExifIfd ifd,
- ExifTag tag,
- uint32_t numerator,
- uint32_t denominator,
+bool ExifUtilsImpl::setRational(ExifIfd ifd, ExifTag tag, uint32_t numerator, uint32_t denominator,
const std::string& msg) {
std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
if (!entry) {
ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
return false;
}
- exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
- {numerator, denominator});
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL, {numerator, denominator});
return true;
}
-bool ExifUtilsImpl::setSRational(ExifIfd ifd,
- ExifTag tag,
- int32_t numerator,
- int32_t denominator,
+bool ExifUtilsImpl::setSRational(ExifIfd ifd, ExifTag tag, int32_t numerator, int32_t denominator,
const std::string& msg) {
std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
if (!entry) {
ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
return false;
}
- exif_set_srational(entry->data, EXIF_BYTE_ORDER_INTEL,
- {numerator, denominator});
+ exif_set_srational(entry->data, EXIF_BYTE_ORDER_INTEL, {numerator, denominator});
return true;
}
-bool ExifUtilsImpl::setString(ExifIfd ifd,
- ExifTag tag,
- ExifFormat format,
- const std::string& buffer,
- const std::string& msg) {
+bool ExifUtilsImpl::setString(ExifIfd ifd, ExifTag tag, ExifFormat format,
+ const std::string& buffer, const std::string& msg) {
size_t entry_size = buffer.length();
// Since the exif format is undefined, NULL termination is not necessary.
if (format == EXIF_FORMAT_ASCII) {
@@ -959,13 +890,11 @@
app1_length_ = 0;
}
-bool ExifUtilsImpl::setFromMetadata(const CameraMetadata& metadata,
- const size_t imageWidth,
+bool ExifUtilsImpl::setFromMetadata(const CameraMetadata& metadata, const size_t imageWidth,
const size_t imageHeight) {
// How precise the float-to-rational conversion for EXIF tags would be.
constexpr int kRationalPrecision = 10000;
- if (!setImageWidth(imageWidth) ||
- !setImageHeight(imageHeight)) {
+ if (!setImageWidth(imageWidth) || !setImageHeight(imageHeight)) {
ALOGE("%s: setting image resolution failed.", __FUNCTION__);
return false;
}
@@ -984,9 +913,8 @@
if (entry.count) {
focal_length = entry.data.f[0];
- if (!setFocalLength(
- static_cast<uint32_t>(focal_length * kRationalPrecision),
- kRationalPrecision)) {
+ if (!setFocalLength(static_cast<uint32_t>(focal_length * kRationalPrecision),
+ kRationalPrecision)) {
ALOGE("%s: setting focal length failed.", __FUNCTION__);
return false;
}
@@ -1048,7 +976,7 @@
if (metadata.exists(ANDROID_SENSOR_EXPOSURE_TIME)) {
entry = metadata.find(ANDROID_SENSOR_EXPOSURE_TIME);
// int64_t of nanoseconds
- if (!setExposureTime(entry.data.i64[0],1000000000u)) {
+ if (!setExposureTime(entry.data.i64[0], 1000000000u)) {
ALOGE("%s: setting exposure time failed.", __FUNCTION__);
return false;
}
@@ -1057,8 +985,7 @@
if (metadata.exists(ANDROID_LENS_APERTURE)) {
const int kAperturePrecision = 10000;
entry = metadata.find(ANDROID_LENS_APERTURE);
- if (!setFNumber(entry.data.f[0] * kAperturePrecision,
- kAperturePrecision)) {
+ if (!setFNumber(entry.data.f[0] * kAperturePrecision, kAperturePrecision)) {
ALOGE("%s: setting F number failed.", __FUNCTION__);
return false;
}
@@ -1073,7 +1000,7 @@
return false;
}
} else {
- ALOGE("%s: Unsupported flash info: %d",__FUNCTION__, entry.data.u8[0]);
+ ALOGE("%s: Unsupported flash info: %d", __FUNCTION__, entry.data.u8[0]);
return false;
}
}
@@ -1107,9 +1034,8 @@
return true;
}
-} // namespace helper
-} // namespace V1_0
-} // namespace common
-} // namespace camera
-} // namespace hardware
-} // namespace android
+} // namespace helper
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
diff --git a/camera/common/1.0/default/HandleImporter.cpp b/camera/common/default/HandleImporter.cpp
similarity index 74%
rename from camera/common/1.0/default/HandleImporter.cpp
rename to camera/common/default/HandleImporter.cpp
index d2fdf02..1145baa 100644
--- a/camera/common/1.0/default/HandleImporter.cpp
+++ b/camera/common/default/HandleImporter.cpp
@@ -18,14 +18,13 @@
#include "HandleImporter.h"
#include <gralloctypes/Gralloc4.h>
-#include "aidl/android/hardware/graphics/common/Smpte2086.h"
#include <log/log.h>
+#include "aidl/android/hardware/graphics/common/Smpte2086.h"
namespace android {
namespace hardware {
namespace camera {
namespace common {
-namespace V1_0 {
namespace helper {
using aidl::android::hardware::graphics::common::PlaneLayout;
@@ -75,20 +74,18 @@
mInitialized = false;
}
-template<class M, class E>
+template <class M, class E>
bool HandleImporter::importBufferInternal(const sp<M> mapper, buffer_handle_t& handle) {
E error;
buffer_handle_t importedHandle;
auto ret = mapper->importBuffer(
- hidl_handle(handle),
- [&](const auto& tmpError, const auto& tmpBufferHandle) {
- error = tmpError;
- importedHandle = static_cast<buffer_handle_t>(tmpBufferHandle);
- });
+ hidl_handle(handle), [&](const auto& tmpError, const auto& tmpBufferHandle) {
+ error = tmpError;
+ importedHandle = static_cast<buffer_handle_t>(tmpBufferHandle);
+ });
if (!ret.isOk()) {
- ALOGE("%s: mapper importBuffer failed: %s",
- __FUNCTION__, ret.description().c_str());
+ ALOGE("%s: mapper importBuffer failed: %s", __FUNCTION__, ret.description().c_str());
return false;
}
@@ -100,61 +97,62 @@
return true;
}
-template<class M, class E>
+template <class M, class E>
YCbCrLayout HandleImporter::lockYCbCrInternal(const sp<M> mapper, buffer_handle_t& buf,
- uint64_t cpuUsage, const IMapper::Rect& accessRegion) {
+ uint64_t cpuUsage,
+ const IMapper::Rect& accessRegion) {
hidl_handle acquireFenceHandle;
auto buffer = const_cast<native_handle_t*>(buf);
YCbCrLayout layout = {};
- typename M::Rect accessRegionCopy = {accessRegion.left, accessRegion.top,
- accessRegion.width, accessRegion.height};
+ typename M::Rect accessRegionCopy = {accessRegion.left, accessRegion.top, accessRegion.width,
+ accessRegion.height};
mapper->lockYCbCr(buffer, cpuUsage, accessRegionCopy, acquireFenceHandle,
- [&](const auto& tmpError, const auto& tmpLayout) {
- if (tmpError == E::NONE) {
- // Member by member copy from different versions of YCbCrLayout.
- layout.y = tmpLayout.y;
- layout.cb = tmpLayout.cb;
- layout.cr = tmpLayout.cr;
- layout.yStride = tmpLayout.yStride;
- layout.cStride = tmpLayout.cStride;
- layout.chromaStep = tmpLayout.chromaStep;
- } else {
- ALOGE("%s: failed to lockYCbCr error %d!", __FUNCTION__, tmpError);
- }
- });
+ [&](const auto& tmpError, const auto& tmpLayout) {
+ if (tmpError == E::NONE) {
+ // Member by member copy from different versions of YCbCrLayout.
+ layout.y = tmpLayout.y;
+ layout.cb = tmpLayout.cb;
+ layout.cr = tmpLayout.cr;
+ layout.yStride = tmpLayout.yStride;
+ layout.cStride = tmpLayout.cStride;
+ layout.chromaStep = tmpLayout.chromaStep;
+ } else {
+ ALOGE("%s: failed to lockYCbCr error %d!", __FUNCTION__, tmpError);
+ }
+ });
return layout;
}
bool isMetadataPesent(const sp<IMapperV4> mapper, const buffer_handle_t& buf,
- MetadataType metadataType) {
+ MetadataType metadataType) {
auto buffer = const_cast<native_handle_t*>(buf);
bool ret = false;
hidl_vec<uint8_t> vec;
- mapper->get(buffer, metadataType, [&] (const auto& tmpError,
- const auto& tmpMetadata) {
- if (tmpError == MapperErrorV4::NONE) {
- vec = tmpMetadata;
- } else {
- ALOGE("%s: failed to get metadata %d!", __FUNCTION__, tmpError);
- }});
+ mapper->get(buffer, metadataType, [&](const auto& tmpError, const auto& tmpMetadata) {
+ if (tmpError == MapperErrorV4::NONE) {
+ vec = tmpMetadata;
+ } else {
+ ALOGE("%s: failed to get metadata %d!", __FUNCTION__, tmpError);
+ }
+ });
if (vec.size() > 0) {
- if (metadataType == gralloc4::MetadataType_Smpte2086){
- std::optional<Smpte2086> realSmpte2086;
- gralloc4::decodeSmpte2086(vec, &realSmpte2086);
- ret = realSmpte2086.has_value();
- } else if (metadataType == gralloc4::MetadataType_Smpte2094_10) {
- std::optional<std::vector<uint8_t>> realSmpte2094_10;
- gralloc4::decodeSmpte2094_10(vec, &realSmpte2094_10);
- ret = realSmpte2094_10.has_value();
- } else if (metadataType == gralloc4::MetadataType_Smpte2094_40) {
- std::optional<std::vector<uint8_t>> realSmpte2094_40;
- gralloc4::decodeSmpte2094_40(vec, &realSmpte2094_40);
- ret = realSmpte2094_40.has_value();
- } else {
- ALOGE("%s: Unknown metadata type!", __FUNCTION__);
- }
+ if (metadataType == gralloc4::MetadataType_Smpte2086) {
+ std::optional<Smpte2086> realSmpte2086;
+ gralloc4::decodeSmpte2086(vec, &realSmpte2086);
+ ret = realSmpte2086.has_value();
+ } else if (metadataType == gralloc4::MetadataType_Smpte2094_10) {
+ std::optional<std::vector<uint8_t>> realSmpte2094_10;
+ gralloc4::decodeSmpte2094_10(vec, &realSmpte2094_10);
+ ret = realSmpte2094_10.has_value();
+ } else if (metadataType == gralloc4::MetadataType_Smpte2094_40) {
+ std::optional<std::vector<uint8_t>> realSmpte2094_40;
+ gralloc4::decodeSmpte2094_40(vec, &realSmpte2094_40);
+ ret = realSmpte2094_40.has_value();
+ } else {
+ ALOGE("%s: Unknown metadata type!", __FUNCTION__);
+ }
}
return ret;
@@ -239,31 +237,29 @@
return layout;
}
-template<class M, class E>
+template <class M, class E>
int HandleImporter::unlockInternal(const sp<M> mapper, buffer_handle_t& buf) {
int releaseFence = -1;
auto buffer = const_cast<native_handle_t*>(buf);
- mapper->unlock(
- buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) {
- if (tmpError == E::NONE) {
- auto fenceHandle = tmpReleaseFence.getNativeHandle();
- if (fenceHandle) {
- if (fenceHandle->numInts != 0 || fenceHandle->numFds != 1) {
- ALOGE("%s: bad release fence numInts %d numFds %d",
- __FUNCTION__, fenceHandle->numInts, fenceHandle->numFds);
- return;
- }
- releaseFence = dup(fenceHandle->data[0]);
- if (releaseFence < 0) {
- ALOGE("%s: bad release fence FD %d",
- __FUNCTION__, releaseFence);
- }
+ mapper->unlock(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) {
+ if (tmpError == E::NONE) {
+ auto fenceHandle = tmpReleaseFence.getNativeHandle();
+ if (fenceHandle) {
+ if (fenceHandle->numInts != 0 || fenceHandle->numFds != 1) {
+ ALOGE("%s: bad release fence numInts %d numFds %d", __FUNCTION__,
+ fenceHandle->numInts, fenceHandle->numFds);
+ return;
}
- } else {
- ALOGE("%s: failed to unlock error %d!", __FUNCTION__, tmpError);
+ releaseFence = dup(fenceHandle->data[0]);
+ if (releaseFence < 0) {
+ ALOGE("%s: bad release fence FD %d", __FUNCTION__, releaseFence);
+ }
}
- });
+ } else {
+ ALOGE("%s: failed to unlock error %d!", __FUNCTION__, tmpError);
+ }
+ });
return releaseFence;
}
@@ -315,14 +311,12 @@
} else if (mMapperV3 != nullptr) {
auto ret = mMapperV3->freeBuffer(const_cast<native_handle_t*>(handle));
if (!ret.isOk()) {
- ALOGE("%s: mapper freeBuffer failed: %s",
- __FUNCTION__, ret.description().c_str());
+ ALOGE("%s: mapper freeBuffer failed: %s", __FUNCTION__, ret.description().c_str());
}
} else {
auto ret = mMapperV2->freeBuffer(const_cast<native_handle_t*>(handle));
if (!ret.isOk()) {
- ALOGE("%s: mapper freeBuffer failed: %s",
- __FUNCTION__, ret.description().c_str());
+ ALOGE("%s: mapper freeBuffer failed: %s", __FUNCTION__, ret.description().c_str());
}
}
}
@@ -337,8 +331,7 @@
return false;
}
} else {
- ALOGE("invalid fence handle with %d file descriptors",
- handle->numFds);
+ ALOGE("invalid fence handle with %d file descriptors", handle->numFds);
return false;
}
@@ -351,8 +344,7 @@
}
}
-void* HandleImporter::lock(
- buffer_handle_t& buf, uint64_t cpuUsage, size_t size) {
+void* HandleImporter::lock(buffer_handle_t& buf, uint64_t cpuUsage, size_t size) {
IMapper::Rect accessRegion{0, 0, static_cast<int>(size), 1};
return lock(buf, cpuUsage, accessRegion);
}
@@ -401,13 +393,13 @@
});
} else {
mMapperV2->lock(buffer, cpuUsage, accessRegion, acquireFenceHandle,
- [&](const auto& tmpError, const auto& tmpPtr) {
- if (tmpError == MapperErrorV2::NONE) {
- ret = tmpPtr;
- } else {
- ALOGE("%s: failed to lock error %d!", __FUNCTION__, tmpError);
- }
- });
+ [&](const auto& tmpError, const auto& tmpPtr) {
+ if (tmpError == MapperErrorV2::NONE) {
+ ret = tmpPtr;
+ } else {
+ ALOGE("%s: failed to lock error %d!", __FUNCTION__, tmpError);
+ }
+ });
}
ALOGV("%s: ptr %p accessRegion.top: %d accessRegion.left: %d accessRegion.width: %d "
@@ -417,9 +409,8 @@
return ret;
}
-YCbCrLayout HandleImporter::lockYCbCr(
- buffer_handle_t& buf, uint64_t cpuUsage,
- const IMapper::Rect& accessRegion) {
+YCbCrLayout HandleImporter::lockYCbCr(buffer_handle_t& buf, uint64_t cpuUsage,
+ const IMapper::Rect& accessRegion) {
Mutex::Autolock lock(mLock);
if (!mInitialized) {
@@ -431,20 +422,18 @@
}
if (mMapperV3 != nullptr) {
- return lockYCbCrInternal<IMapperV3, MapperErrorV3>(
- mMapperV3, buf, cpuUsage, accessRegion);
+ return lockYCbCrInternal<IMapperV3, MapperErrorV3>(mMapperV3, buf, cpuUsage, accessRegion);
}
if (mMapperV2 != nullptr) {
- return lockYCbCrInternal<IMapper, MapperErrorV2>(
- mMapperV2, buf, cpuUsage, accessRegion);
+ return lockYCbCrInternal<IMapper, MapperErrorV2>(mMapperV2, buf, cpuUsage, accessRegion);
}
ALOGE("%s: mMapperV4, mMapperV3 and mMapperV2 are all null!", __FUNCTION__);
return {};
}
-status_t HandleImporter::getMonoPlanarStrideBytes(buffer_handle_t &buf, uint32_t *stride /*out*/) {
+status_t HandleImporter::getMonoPlanarStrideBytes(buffer_handle_t& buf, uint32_t* stride /*out*/) {
if (stride == nullptr) {
return BAD_VALUE;
}
@@ -458,7 +447,7 @@
if (mMapperV4 != nullptr) {
std::vector<PlaneLayout> planeLayouts = getPlaneLayouts(mMapperV4, buf);
if (planeLayouts.size() != 1) {
- ALOGE("%s: Unexpected number of planes %zu!", __FUNCTION__, planeLayouts.size());
+ ALOGE("%s: Unexpected number of planes %zu!", __FUNCTION__, planeLayouts.size());
return BAD_VALUE;
}
@@ -534,10 +523,8 @@
return false;
}
-
-} // namespace helper
-} // namespace V1_0
-} // namespace common
-} // namespace camera
-} // namespace hardware
-} // namespace android
+} // namespace helper
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
diff --git a/camera/common/1.0/default/OWNERS b/camera/common/default/OWNERS
similarity index 100%
rename from camera/common/1.0/default/OWNERS
rename to camera/common/default/OWNERS
diff --git a/camera/common/default/SimpleThread.cpp b/camera/common/default/SimpleThread.cpp
new file mode 100644
index 0000000..46e89ba
--- /dev/null
+++ b/camera/common/default/SimpleThread.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SimpleThread.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace common {
+namespace helper {
+
+SimpleThread::SimpleThread() : mDone(true), mThread() {}
+SimpleThread::~SimpleThread() {
+ // Safe to call requestExitAndWait() from the destructor because requestExitAndWait() ensures
+ // that the thread is joinable before joining on it. This is different from how
+ // android::Thread worked.
+ requestExitAndWait();
+}
+
+void SimpleThread::run() {
+ requestExitAndWait(); // Exit current execution, if any.
+
+ // start thread
+ mDone.store(false, std::memory_order_release);
+ mThread = std::thread(&SimpleThread::runLoop, this);
+}
+
+void SimpleThread::requestExitAndWait() {
+ // Signal thread to stop
+ mDone.store(true, std::memory_order_release);
+
+ // Wait for thread to exit if needed. This should happen in no more than one iteration of
+ // threadLoop
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+ mThread = std::thread();
+}
+
+void SimpleThread::runLoop() {
+ while (!exitPending()) {
+ if (!threadLoop()) {
+ break;
+ }
+ }
+}
+
+} // namespace helper
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/camera/common/1.0/default/VendorTagDescriptor.cpp b/camera/common/default/VendorTagDescriptor.cpp
similarity index 91%
rename from camera/common/1.0/default/VendorTagDescriptor.cpp
rename to camera/common/default/VendorTagDescriptor.cpp
index d2bee85..1282bd0 100644
--- a/camera/common/1.0/default/VendorTagDescriptor.cpp
+++ b/camera/common/default/VendorTagDescriptor.cpp
@@ -16,9 +16,9 @@
#define LOG_TAG "CamComm1.0-VTDesc"
+#include <camera_metadata_hidden.h>
#include <log/log.h>
#include <system/camera_metadata.h>
-#include <camera_metadata_hidden.h>
#include <utils/Errors.h>
#include <utils/Mutex.h>
#include <utils/SortedVector.h>
@@ -36,15 +36,12 @@
VendorTagDescriptor::~VendorTagDescriptor() {
size_t len = mReverseMapping.size();
- for (size_t i = 0; i < len; ++i) {
+ for (size_t i = 0; i < len; ++i) {
delete mReverseMapping[i];
}
}
-VendorTagDescriptor::VendorTagDescriptor() :
- mTagCount(0),
- mVendorOps() {
-}
+VendorTagDescriptor::VendorTagDescriptor() : mTagCount(0), mVendorOps() {}
VendorTagDescriptor::VendorTagDescriptor(const VendorTagDescriptor& src) {
copyFrom(src);
@@ -127,7 +124,8 @@
return &mSections;
}
-status_t VendorTagDescriptor::lookupTag(const String8& name, const String8& section, /*out*/uint32_t* tag) const {
+status_t VendorTagDescriptor::lookupTag(const String8& name, const String8& section,
+ /*out*/ uint32_t* tag) const {
ssize_t index = mReverseMapping.indexOfKey(section);
if (index < 0) {
ALOGE("%s: Section '%s' does not exist.", __FUNCTION__, section.string());
@@ -147,18 +145,16 @@
}
void VendorTagDescriptor::dump(int fd, int verbosity, int indentation) const {
-
size_t size = mTagToNameMap.size();
if (size == 0) {
- dprintf(fd, "%*sDumping configured vendor tag descriptors: None set\n",
- indentation, "");
+ dprintf(fd, "%*sDumping configured vendor tag descriptors: None set\n", indentation, "");
return;
}
- dprintf(fd, "%*sDumping configured vendor tag descriptors: %zu entries\n",
- indentation, "", size);
+ dprintf(fd, "%*sDumping configured vendor tag descriptors: %zu entries\n", indentation, "",
+ size);
for (size_t i = 0; i < size; ++i) {
- uint32_t tag = mTagToNameMap.keyAt(i);
+ uint32_t tag = mTagToNameMap.keyAt(i);
if (verbosity < 1) {
dprintf(fd, "%*s0x%x\n", indentation + 2, "", tag);
@@ -168,12 +164,11 @@
uint32_t sectionId = mTagToSectionMap.valueFor(tag);
String8 sectionName = mSections[sectionId];
int type = mTagToTypeMap.at(tag);
- const char* typeName = (type >= 0 && type < NUM_TYPES) ?
- camera_metadata_type_names[type] : "UNKNOWN";
- dprintf(fd, "%*s0x%x (%s) with type %d (%s) defined in section %s\n", indentation + 2,
- "", tag, name.string(), type, typeName, sectionName.string());
+ const char* typeName =
+ (type >= 0 && type < NUM_TYPES) ? camera_metadata_type_names[type] : "UNKNOWN";
+ dprintf(fd, "%*s0x%x (%s) with type %d (%s) defined in section %s\n", indentation + 2, "",
+ tag, name.string(), type, typeName, sectionName.string());
}
-
}
int VendorTagDescriptorCache::getTagCount(metadata_vendor_id_t id) const {
@@ -240,7 +235,7 @@
}
int32_t VendorTagDescriptorCache::addVendorDescriptor(
- metadata_vendor_id_t id, sp<hardware::camera::common::V1_0::helper::VendorTagDescriptor> desc) {
+ metadata_vendor_id_t id, sp<hardware::camera::common::helper::VendorTagDescriptor> desc) {
auto entry = mVendorMap.find(id);
if (entry != mVendorMap.end()) {
ALOGE("%s: Vendor descriptor with same id already present!", __func__);
@@ -252,8 +247,8 @@
}
int32_t VendorTagDescriptorCache::getVendorTagDescriptor(
- metadata_vendor_id_t id,
- sp<hardware::camera::common::V1_0::helper::VendorTagDescriptor>* desc /*out*/) {
+ metadata_vendor_id_t id,
+ sp<hardware::camera::common::helper::VendorTagDescriptor>* desc /*out*/) {
auto entry = mVendorMap.find(id);
if (entry == mVendorMap.end()) {
return NAME_NOT_FOUND;
@@ -263,12 +258,11 @@
return NO_ERROR;
}
-} // namespace params
-} // namespace camera2
+} // namespace params
+} // namespace camera2
namespace camera {
namespace common {
-namespace V1_0 {
namespace helper {
extern "C" {
@@ -292,8 +286,8 @@
static sp<VendorTagDescriptorCache> sGlobalVendorTagDescriptorCache;
status_t VendorTagDescriptor::createDescriptorFromOps(const vendor_tag_ops_t* vOps,
- /*out*/
- sp<VendorTagDescriptor>& descriptor) {
+ /*out*/
+ sp<VendorTagDescriptor>& descriptor) {
if (vOps == NULL) {
ALOGE("%s: vendor_tag_ops argument was NULL.", __FUNCTION__);
return BAD_VALUE;
@@ -307,9 +301,9 @@
Vector<uint32_t> tagArray;
LOG_ALWAYS_FATAL_IF(tagArray.resize(tagCount) != tagCount,
- "%s: too many (%u) vendor tags defined.", __FUNCTION__, tagCount);
+ "%s: too many (%u) vendor tags defined.", __FUNCTION__, tagCount);
- vOps->get_all_tags(vOps, /*out*/tagArray.editArray());
+ vOps->get_all_tags(vOps, /*out*/ tagArray.editArray());
sp<VendorTagDescriptor> desc = new VendorTagDescriptor();
desc->mTagCount = tagCount;
@@ -323,13 +317,13 @@
ALOGE("%s: vendor tag %d not in vendor tag section.", __FUNCTION__, tag);
return BAD_VALUE;
}
- const char *tagName = vOps->get_tag_name(vOps, tag);
+ const char* tagName = vOps->get_tag_name(vOps, tag);
if (tagName == NULL) {
ALOGE("%s: no tag name defined for vendor tag %d.", __FUNCTION__, tag);
return BAD_VALUE;
}
desc->mTagToNameMap.add(tag, String8(tagName));
- const char *sectionName = vOps->get_section_name(vOps, tag);
+ const char* sectionName = vOps->get_section_name(vOps, tag);
if (sectionName == NULL) {
ALOGE("%s: no section name defined for vendor tag %d.", __FUNCTION__, tag);
return BAD_VALUE;
@@ -386,9 +380,9 @@
opsPtr->get_tag_name = vendor_tag_descriptor_get_tag_name;
opsPtr->get_tag_type = vendor_tag_descriptor_get_tag_type;
}
- if((res = set_camera_metadata_vendor_ops(opsPtr)) != OK) {
- ALOGE("%s: Could not set vendor tag descriptor, received error %s (%d)."
- , __FUNCTION__, strerror(-res), res);
+ if ((res = set_camera_metadata_vendor_ops(opsPtr)) != OK) {
+ ALOGE("%s: Could not set vendor tag descriptor, received error %s (%d).", __FUNCTION__,
+ strerror(-res), res);
}
return res;
}
@@ -405,7 +399,7 @@
}
status_t VendorTagDescriptorCache::setAsGlobalVendorTagCache(
- const sp<VendorTagDescriptorCache>& cache) {
+ const sp<VendorTagDescriptorCache>& cache) {
status_t res = OK;
Mutex::Autolock al(sLock);
sGlobalVendorTagDescriptorCache = cache;
@@ -530,9 +524,8 @@
} /* extern "C" */
-} // namespace helper
-} // namespace V1_0
-} // namespace common
-} // namespace camera
-} // namespace hardware
-} // namespace android
+} // namespace helper
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
diff --git a/camera/common/1.0/default/include/CameraMetadata.h b/camera/common/default/include/CameraMetadata.h
similarity index 76%
rename from camera/common/1.0/default/include/CameraMetadata.h
rename to camera/common/default/include/CameraMetadata.h
index d5e4d56..b67914e 100644
--- a/camera/common/1.0/default/include/CameraMetadata.h
+++ b/camera/common/default/include/CameraMetadata.h
@@ -26,7 +26,6 @@
namespace hardware {
namespace camera {
namespace common {
-namespace V1_0 {
namespace helper {
class VendorTagDescriptor;
@@ -46,15 +45,15 @@
~CameraMetadata();
/** Takes ownership of passed-in buffer */
- CameraMetadata(camera_metadata_t *buffer);
+ CameraMetadata(camera_metadata_t* buffer);
/** Clones the metadata */
- CameraMetadata(const CameraMetadata &other);
+ CameraMetadata(const CameraMetadata& other);
/**
* Assignment clones metadata buffer.
*/
- CameraMetadata &operator=(const CameraMetadata &other);
- CameraMetadata &operator=(const camera_metadata_t *buffer);
+ CameraMetadata& operator=(const CameraMetadata& other);
+ CameraMetadata& operator=(const camera_metadata_t* buffer);
/**
* Get reference to the underlying metadata buffer. Ownership remains with
@@ -71,7 +70,7 @@
* from getAndLock must be provided to guarantee that the right object is
* being unlocked.
*/
- status_t unlock(const camera_metadata_t *buffer) const;
+ status_t unlock(const camera_metadata_t* buffer) const;
/**
* Release a raw metadata buffer to the caller. After this call,
@@ -98,12 +97,12 @@
* Acquires raw buffer from other CameraMetadata object. After the call, the argument
* object no longer has any metadata.
*/
- void acquire(CameraMetadata &other);
+ void acquire(CameraMetadata& other);
/**
* Append metadata from another CameraMetadata object.
*/
- status_t append(const CameraMetadata &other);
+ status_t append(const CameraMetadata& other);
/**
* Append metadata from a raw camera_metadata buffer
@@ -130,24 +129,16 @@
* will reallocate the buffer if insufficient space exists. Overloaded for
* the various types of valid data.
*/
- status_t update(uint32_t tag,
- const uint8_t *data, size_t data_count);
- status_t update(uint32_t tag,
- const int32_t *data, size_t data_count);
- status_t update(uint32_t tag,
- const float *data, size_t data_count);
- status_t update(uint32_t tag,
- const int64_t *data, size_t data_count);
- status_t update(uint32_t tag,
- const double *data, size_t data_count);
- status_t update(uint32_t tag,
- const camera_metadata_rational_t *data, size_t data_count);
- status_t update(uint32_t tag,
- const String8 &string);
- status_t update(const camera_metadata_ro_entry &entry);
+ status_t update(uint32_t tag, const uint8_t* data, size_t data_count);
+ status_t update(uint32_t tag, const int32_t* data, size_t data_count);
+ status_t update(uint32_t tag, const float* data, size_t data_count);
+ status_t update(uint32_t tag, const int64_t* data, size_t data_count);
+ status_t update(uint32_t tag, const double* data, size_t data_count);
+ status_t update(uint32_t tag, const camera_metadata_rational_t* data, size_t data_count);
+ status_t update(uint32_t tag, const String8& string);
+ status_t update(const camera_metadata_ro_entry& entry);
-
- template<typename T>
+ template <typename T>
status_t update(uint32_t tag, Vector<T> data) {
return update(tag, data.array(), data.size());
}
@@ -177,7 +168,7 @@
* Swap the underlying camera metadata between this and the other
* metadata object.
*/
- void swap(CameraMetadata &other);
+ void swap(CameraMetadata& other);
/**
* Dump contents into FD for debugging. The verbosity levels are
@@ -196,12 +187,12 @@
*
* This is a slow method.
*/
- static status_t getTagFromName(const char *name,
- const VendorTagDescriptor* vTags, uint32_t *tag);
+ static status_t getTagFromName(const char* name, const VendorTagDescriptor* vTags,
+ uint32_t* tag);
private:
- camera_metadata_t *mBuffer;
- mutable bool mLocked;
+ camera_metadata_t* mBuffer;
+ mutable bool mLocked;
/**
* Check if tag has a given type
@@ -211,20 +202,25 @@
/**
* Base update entry method
*/
- status_t updateImpl(uint32_t tag, const void *data, size_t data_count);
+ status_t updateImpl(uint32_t tag, const void* data, size_t data_count);
/**
* Resize metadata buffer if needed by reallocating it and copying it over.
*/
status_t resizeIfNeeded(size_t extraEntries, size_t extraData);
-
};
-} // namespace helper
-} // namespace V1_0
-} // namespace common
-} // namespace camera
-} // namespace hardware
-} // namespace android
+} // namespace helper
+
+// NOTE: Deprecated namespace. This namespace should no longer be used.
+namespace V1_0::helper {
+// Export symbols to the old namespace to preserve compatibility
+typedef android::hardware::camera::common::helper::CameraMetadata CameraMetadata;
+} // namespace V1_0::helper
+
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
#endif
diff --git a/camera/common/1.0/default/include/CameraModule.h b/camera/common/default/include/CameraModule.h
similarity index 72%
rename from camera/common/1.0/default/include/CameraModule.h
rename to camera/common/default/include/CameraModule.h
index c89e934..5c1f8ec 100644
--- a/camera/common/1.0/default/include/CameraModule.h
+++ b/camera/common/default/include/CameraModule.h
@@ -21,8 +21,8 @@
#include <unordered_set>
#include <hardware/camera.h>
-#include <utils/Mutex.h>
#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include "CameraMetadata.h"
@@ -31,7 +31,6 @@
namespace hardware {
namespace camera {
namespace common {
-namespace V1_0 {
namespace helper {
/**
* A wrapper class for HAL camera module.
@@ -41,21 +40,21 @@
* camera characteristics keys defined in newer HAL version on an older HAL.
*/
class CameraModule : public RefBase {
-public:
- explicit CameraModule(camera_module_t *module);
+ public:
+ explicit CameraModule(camera_module_t* module);
virtual ~CameraModule();
// Must be called after construction
// Returns OK on success, NO_INIT on failure
int init();
- int getCameraInfo(int cameraId, struct camera_info *info);
+ int getCameraInfo(int cameraId, struct camera_info* info);
int getDeviceVersion(int cameraId);
int getNumberOfCameras(void);
int open(const char* id, struct hw_device_t** device);
bool isOpenLegacyDefined() const;
int openLegacy(const char* id, uint32_t halVersion, struct hw_device_t** device);
- int setCallbacks(const camera_module_callbacks_t *callbacks);
+ int setCallbacks(const camera_module_callbacks_t* callbacks);
bool isVendorTagDefined() const;
void getVendorTagOps(vendor_tag_ops_t* ops);
bool isSetTorchModeSupported() const;
@@ -65,25 +64,24 @@
uint16_t getHalApiVersion() const;
const char* getModuleAuthor() const;
// Only used by CameraModuleFixture native test. Do NOT use elsewhere.
- void *getDso();
+ void* getDso();
// Only used by CameraProvider
void removeCamera(int cameraId);
- int getPhysicalCameraInfo(int physicalCameraId, camera_metadata_t **physicalInfo);
- int isStreamCombinationSupported(int cameraId, camera_stream_combination_t *streams);
+ int getPhysicalCameraInfo(int physicalCameraId, camera_metadata_t** physicalInfo);
+ int isStreamCombinationSupported(int cameraId, camera_stream_combination_t* streams);
void notifyDeviceStateChange(uint64_t deviceState);
- static bool isLogicalMultiCamera(
- const common::V1_0::helper::CameraMetadata& metadata,
- std::unordered_set<std::string>* physicalCameraIds);
+ static bool isLogicalMultiCamera(const common::helper::CameraMetadata& metadata,
+ std::unordered_set<std::string>* physicalCameraIds);
-private:
+ private:
// Derive camera characteristics keys defined after HAL device version
- static void deriveCameraCharacteristicsKeys(uint32_t deviceVersion, CameraMetadata &chars);
+ static void deriveCameraCharacteristicsKeys(uint32_t deviceVersion, CameraMetadata& chars);
// Helper function to append available[request|result|chars]Keys
- static void appendAvailableKeys(CameraMetadata &chars,
- int32_t keyTag, const Vector<int32_t>& appendKeys);
+ static void appendAvailableKeys(CameraMetadata& chars, int32_t keyTag,
+ const Vector<int32_t>& appendKeys);
status_t filterOpenErrorCode(status_t err);
- camera_module_t *mModule;
+ camera_module_t* mModule;
int mNumberOfCameras;
KeyedVector<int, camera_info> mCameraInfoMap;
KeyedVector<int, int> mDeviceVersionMap;
@@ -91,11 +89,17 @@
Mutex mCameraInfoLock;
};
-} // namespace helper
-} // namespace V1_0
-} // namespace common
-} // namespace camera
-} // namespace hardware
-} // namespace android
+} // namespace helper
+
+// NOTE: Deprecated namespace. This namespace should no longer be used for the following symbols
+namespace V1_0::helper {
+// Export symbols to the old namespace to preserve compatibility
+typedef android::hardware::camera::common::helper::CameraModule CameraModule;
+} // namespace V1_0::helper
+
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
#endif
diff --git a/camera/common/1.0/default/include/CameraParameters.h b/camera/common/default/include/CameraParameters.h
similarity index 94%
rename from camera/common/1.0/default/include/CameraParameters.h
rename to camera/common/default/include/CameraParameters.h
index e4ff6f2..d2b5075 100644
--- a/camera/common/1.0/default/include/CameraParameters.h
+++ b/camera/common/default/include/CameraParameters.h
@@ -24,7 +24,6 @@
namespace hardware {
namespace camera {
namespace common {
-namespace V1_0 {
namespace helper {
struct Size {
@@ -42,28 +41,27 @@
}
};
-class CameraParameters
-{
-public:
+class CameraParameters {
+ public:
CameraParameters();
- CameraParameters(const String8 ¶ms) { unflatten(params); }
+ CameraParameters(const String8& params) { unflatten(params); }
~CameraParameters();
String8 flatten() const;
- void unflatten(const String8 ¶ms);
+ void unflatten(const String8& params);
- void set(const char *key, const char *value);
- void set(const char *key, int value);
- void setFloat(const char *key, float value);
- const char *get(const char *key) const;
- int getInt(const char *key) const;
- float getFloat(const char *key) const;
+ void set(const char* key, const char* value);
+ void set(const char* key, int value);
+ void setFloat(const char* key, float value);
+ const char* get(const char* key) const;
+ int getInt(const char* key) const;
+ float getFloat(const char* key) const;
- void remove(const char *key);
+ void remove(const char* key);
void setPreviewSize(int width, int height);
- void getPreviewSize(int *width, int *height) const;
- void getSupportedPreviewSizes(Vector<Size> &sizes) const;
+ void getPreviewSize(int* width, int* height) const;
+ void getSupportedPreviewSizes(Vector<Size>& sizes) const;
// Set the dimensions in pixels to the given width and height
// for video frames. The given width and height must be one
@@ -76,14 +74,14 @@
// supported dimensions returned from getSupportedVideoSizes().
// Must not be called if getSupportedVideoSizes() returns an
// empty Vector of Size.
- void getVideoSize(int *width, int *height) const;
+ void getVideoSize(int* width, int* height) const;
// Retrieve a Vector of supported dimensions (width and height)
// in pixels for video frames. If sizes returned from the method
// is empty, the camera does not support calls to setVideoSize()
// or getVideoSize(). In adddition, it also indicates that
// the camera only has a single output, and does not have
// separate output for video frames and preview frame.
- void getSupportedVideoSizes(Vector<Size> &sizes) const;
+ void getSupportedVideoSizes(Vector<Size>& sizes) const;
// Retrieve the preferred preview size (width and height) in pixels
// for video recording. The given width and height must be one of
// supported preview sizes returned from getSupportedPreviewSizes().
@@ -91,18 +89,18 @@
// Vector of Size. If getSupportedVideoSizes() returns an empty
// Vector of Size, the width and height returned from this method
// is invalid, and is "-1x-1".
- void getPreferredPreviewSizeForVideo(int *width, int *height) const;
+ void getPreferredPreviewSizeForVideo(int* width, int* height) const;
void setPreviewFrameRate(int fps);
int getPreviewFrameRate() const;
- void getPreviewFpsRange(int *min_fps, int *max_fps) const;
- void setPreviewFormat(const char *format);
- const char *getPreviewFormat() const;
+ void getPreviewFpsRange(int* min_fps, int* max_fps) const;
+ void setPreviewFormat(const char* format);
+ const char* getPreviewFormat() const;
void setPictureSize(int width, int height);
- void getPictureSize(int *width, int *height) const;
- void getSupportedPictureSizes(Vector<Size> &sizes) const;
- void setPictureFormat(const char *format);
- const char *getPictureFormat() const;
+ void getPictureSize(int* width, int* height) const;
+ void getSupportedPictureSizes(Vector<Size>& sizes) const;
+ void setPictureFormat(const char* format);
+ const char* getPictureFormat() const;
void dump() const;
status_t dump(int fd, const Vector<String16>& args) const;
@@ -619,9 +617,9 @@
// Pixel color formats for KEY_PREVIEW_FORMAT, KEY_PICTURE_FORMAT,
// and KEY_VIDEO_FRAME_FORMAT
static const char PIXEL_FORMAT_YUV422SP[];
- static const char PIXEL_FORMAT_YUV420SP[]; // NV21
- static const char PIXEL_FORMAT_YUV422I[]; // YUY2
- static const char PIXEL_FORMAT_YUV420P[]; // YV12
+ static const char PIXEL_FORMAT_YUV420SP[]; // NV21
+ static const char PIXEL_FORMAT_YUV422I[]; // YUY2
+ static const char PIXEL_FORMAT_YUV420P[]; // YV12
static const char PIXEL_FORMAT_RGB565[];
static const char PIXEL_FORMAT_RGBA8888[];
static const char PIXEL_FORMAT_JPEG[];
@@ -695,15 +693,22 @@
*/
static int previewFormatToEnum(const char* format);
-private:
- DefaultKeyedVector<String8,String8> mMap;
+ private:
+ DefaultKeyedVector<String8, String8> mMap;
};
-};
-};
-};
-};
-};
-}; // namespace
+}; // namespace helper
+
+// NOTE: Deprecated namespace. This namespace should no longer be used for the following symbols
+namespace V1_0::helper {
+// Export symbols to the old namespace to preserve compatibility
+typedef android::hardware::camera::common::helper::CameraParameters CameraParameters;
+typedef android::hardware::camera::common::helper::Size Size;
+} // namespace V1_0::helper
+
+}; // namespace common
+}; // namespace camera
+}; // namespace hardware
+}; // namespace android
#endif
diff --git a/camera/common/1.0/default/include/Exif.h b/camera/common/default/include/Exif.h
similarity index 95%
rename from camera/common/1.0/default/include/Exif.h
rename to camera/common/default/include/Exif.h
index dc31679..6974b8e 100644
--- a/camera/common/1.0/default/include/Exif.h
+++ b/camera/common/default/include/Exif.h
@@ -23,10 +23,8 @@
namespace hardware {
namespace camera {
namespace common {
-namespace V1_0 {
namespace helper {
-
// This is based on the original ChromeOS ARC implementation of a V4L2 HAL
// ExifUtils can generate APP1 segment with tags which caller set. ExifUtils can
@@ -44,8 +42,7 @@
// uint8_t* app1Buffer = new uint8_t[app1Length];
// memcpy(app1Buffer, utils->GetApp1Buffer(), app1Length);
class ExifUtils {
-
- public:
+ public:
virtual ~ExifUtils();
static ExifUtils* create();
@@ -55,8 +52,7 @@
virtual bool initialize() = 0;
// Set all known fields from a metadata structure
- virtual bool setFromMetadata(const CameraMetadata& metadata,
- const size_t imageWidth,
+ virtual bool setFromMetadata(const CameraMetadata& metadata, const size_t imageWidth,
const size_t imageHeight) = 0;
// Sets the len aperture.
@@ -244,13 +240,17 @@
virtual unsigned int getApp1Length() = 0;
};
+} // namespace helper
-} // namespace helper
-} // namespace V1_0
-} // namespace common
-} // namespace camera
-} // namespace hardware
-} // namespace android
+// NOTE: Deprecated namespace. This namespace should no longer be used for the following symbols
+namespace V1_0::helper {
+// Export symbols to the old namespace to preserve compatibility
+typedef android::hardware::camera::common::helper::ExifUtils ExifUtils;
+} // namespace V1_0::helper
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
#endif // ANDROID_HARDWARE_INTERFACES_CAMERA_COMMON_1_0_EXIF_H
diff --git a/camera/common/1.0/default/include/HandleImporter.h b/camera/common/default/include/HandleImporter.h
similarity index 80%
rename from camera/common/1.0/default/include/HandleImporter.h
rename to camera/common/default/include/HandleImporter.h
index 83fa755..5408ba9 100644
--- a/camera/common/1.0/default/include/HandleImporter.h
+++ b/camera/common/default/include/HandleImporter.h
@@ -30,12 +30,11 @@
namespace hardware {
namespace camera {
namespace common {
-namespace V1_0 {
namespace helper {
// Borrowed from graphics HAL. Use this until gralloc mapper HAL is working
class HandleImporter {
-public:
+ public:
HandleImporter();
// In IComposer, any buffer_handle_t is owned by the caller and we need to
@@ -59,23 +58,23 @@
// Query the stride of the first plane in bytes.
status_t getMonoPlanarStrideBytes(buffer_handle_t& buf, uint32_t* stride /*out*/);
- int unlock(buffer_handle_t& buf); // returns release fence
+ int unlock(buffer_handle_t& buf); // returns release fence
// Query Gralloc4 metadata
bool isSmpte2086Present(const buffer_handle_t& buf);
bool isSmpte2094_10Present(const buffer_handle_t& buf);
bool isSmpte2094_40Present(const buffer_handle_t& buf);
-private:
+ private:
void initializeLocked();
void cleanup();
- template<class M, class E>
+ template <class M, class E>
bool importBufferInternal(const sp<M> mapper, buffer_handle_t& handle);
- template<class M, class E>
+ template <class M, class E>
YCbCrLayout lockYCbCrInternal(const sp<M> mapper, buffer_handle_t& buf, uint64_t cpuUsage,
- const IMapper::Rect& accessRegion);
- template<class M, class E>
+ const IMapper::Rect& accessRegion);
+ template <class M, class E>
int unlockInternal(const sp<M> mapper, buffer_handle_t& buf);
Mutex mLock;
@@ -85,11 +84,17 @@
sp<graphics::mapper::V4_0::IMapper> mMapperV4;
};
-} // namespace helper
-} // namespace V1_0
-} // namespace common
-} // namespace camera
-} // namespace hardware
-} // namespace android
+} // namespace helper
-#endif // CAMERA_COMMON_1_0_HANDLEIMPORTED_H
+// NOTE: Deprecated namespace. This namespace should no longer be used for the following symbols
+namespace V1_0::helper {
+// Export symbols to the old namespace to preserve compatibility
+typedef android::hardware::camera::common::helper::HandleImporter HandleImporter;
+} // namespace V1_0::helper
+
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+#endif // CAMERA_COMMON_1_0_HANDLEIMPORTED_H
diff --git a/camera/common/default/include/SimpleThread.h b/camera/common/default/include/SimpleThread.h
new file mode 100644
index 0000000..d1becd6
--- /dev/null
+++ b/camera/common/default/include/SimpleThread.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HARDWARE_INTERFACES_CAMERA_COMMON_SIMPLETHREAD_H_
+#define HARDWARE_INTERFACES_CAMERA_COMMON_SIMPLETHREAD_H_
+
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace common {
+namespace helper {
+
+// A simple looper based on std::thread.
+class SimpleThread {
+ public:
+ SimpleThread();
+ virtual ~SimpleThread();
+
+ // Explicit call to start execution of the thread. No thread is created before this function
+ // is called.
+ virtual void run() final;
+ virtual void requestExitAndWait() final;
+
+ protected:
+ // Main logic of the thread. This function is called repeatedly until it returns false.
+ // Thread execution stops if this function returns false.
+ virtual bool threadLoop() = 0;
+
+ // Returns true if the thread execution should stop. Should be used by threadLoop to check if
+ // the thread has been requested to exit.
+ virtual inline bool exitPending() final { return mDone.load(std::memory_order_acquire); }
+
+ private:
+ // Wraps threadLoop in a simple while loop that allows safe exit
+ virtual void runLoop() final;
+
+ // Flag to signal end of thread execution. This flag is checked before every iteration
+ // of threadLoop.
+ std::atomic_bool mDone;
+ std::thread mThread;
+};
+
+} // namespace helper
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+#endif // HARDWARE_INTERFACES_CAMERA_COMMON_SIMPLETHREAD_H_
diff --git a/camera/common/1.0/default/include/VendorTagDescriptor.h b/camera/common/default/include/VendorTagDescriptor.h
similarity index 61%
rename from camera/common/1.0/default/include/VendorTagDescriptor.h
rename to camera/common/default/include/VendorTagDescriptor.h
index 0f54db5..3133c26 100644
--- a/camera/common/1.0/default/include/VendorTagDescriptor.h
+++ b/camera/common/default/include/VendorTagDescriptor.h
@@ -17,11 +17,11 @@
#ifndef CAMERA_COMMON_1_0_VENDORTAGDESCRIPTOR_H
#define CAMERA_COMMON_1_0_VENDORTAGDESCRIPTOR_H
-#include <utils/Vector.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/RefBase.h>
#include <system/camera_vendor_tags.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
#include <stdint.h>
#include <unordered_map>
@@ -37,78 +37,77 @@
* information enumerated by the HAL to clients of the camera service.
*/
class VendorTagDescriptor {
- public:
- virtual ~VendorTagDescriptor();
+ public:
+ virtual ~VendorTagDescriptor();
- VendorTagDescriptor();
- VendorTagDescriptor(const VendorTagDescriptor& src);
- VendorTagDescriptor& operator=(const VendorTagDescriptor& rhs);
+ VendorTagDescriptor();
+ VendorTagDescriptor(const VendorTagDescriptor& src);
+ VendorTagDescriptor& operator=(const VendorTagDescriptor& rhs);
- void copyFrom(const VendorTagDescriptor& src);
+ void copyFrom(const VendorTagDescriptor& src);
- /**
- * The following 'get*' methods implement the corresponding
- * functions defined in
- * system/media/camera/include/system/camera_vendor_tags.h
- */
+ /**
+ * The following 'get*' methods implement the corresponding
+ * functions defined in
+ * system/media/camera/include/system/camera_vendor_tags.h
+ */
- // Returns the number of vendor tags defined.
- int getTagCount() const;
+ // Returns the number of vendor tags defined.
+ int getTagCount() const;
- // Returns an array containing the id's of vendor tags defined.
- void getTagArray(uint32_t* tagArray) const;
+ // Returns an array containing the id's of vendor tags defined.
+ void getTagArray(uint32_t* tagArray) const;
- // Returns the section name string for a given vendor tag id.
- const char* getSectionName(uint32_t tag) const;
+ // Returns the section name string for a given vendor tag id.
+ const char* getSectionName(uint32_t tag) const;
- // Returns the index in section vectors returned in getAllSectionNames()
- // for a given vendor tag id. -1 if input tag does not exist.
- ssize_t getSectionIndex(uint32_t tag) const;
+ // Returns the index in section vectors returned in getAllSectionNames()
+ // for a given vendor tag id. -1 if input tag does not exist.
+ ssize_t getSectionIndex(uint32_t tag) const;
- // Returns the tag name string for a given vendor tag id.
- const char* getTagName(uint32_t tag) const;
+ // Returns the tag name string for a given vendor tag id.
+ const char* getTagName(uint32_t tag) const;
- // Returns the tag type for a given vendor tag id.
- int getTagType(uint32_t tag) const;
+ // Returns the tag type for a given vendor tag id.
+ int getTagType(uint32_t tag) const;
- /**
- * Convenience method to get a vector containing all vendor tag
- * sections, or an empty vector if none are defined.
- * The pointer is valid for the lifetime of the VendorTagDescriptor,
- * or until copyFrom is invoked.
- */
- const SortedVector<String8>* getAllSectionNames() const;
+ /**
+ * Convenience method to get a vector containing all vendor tag
+ * sections, or an empty vector if none are defined.
+ * The pointer is valid for the lifetime of the VendorTagDescriptor,
+ * or until copyFrom is invoked.
+ */
+ const SortedVector<String8>* getAllSectionNames() const;
- /**
- * Lookup the tag id for a given tag name and section.
- *
- * Returns OK on success, or a negative error code.
- */
- status_t lookupTag(const String8& name, const String8& section, /*out*/uint32_t* tag) const;
+ /**
+ * Lookup the tag id for a given tag name and section.
+ *
+ * Returns OK on success, or a negative error code.
+ */
+ status_t lookupTag(const String8& name, const String8& section, /*out*/ uint32_t* tag) const;
- /**
- * Dump the currently configured vendor tags to a file descriptor.
- */
- void dump(int fd, int verbosity, int indentation) const;
+ /**
+ * Dump the currently configured vendor tags to a file descriptor.
+ */
+ void dump(int fd, int verbosity, int indentation) const;
- protected:
- KeyedVector<String8, KeyedVector<String8, uint32_t>*> mReverseMapping;
- KeyedVector<uint32_t, String8> mTagToNameMap;
- KeyedVector<uint32_t, uint32_t> mTagToSectionMap; // Value is offset in mSections
+ protected:
+ KeyedVector<String8, KeyedVector<String8, uint32_t>*> mReverseMapping;
+ KeyedVector<uint32_t, String8> mTagToNameMap;
+ KeyedVector<uint32_t, uint32_t> mTagToSectionMap; // Value is offset in mSections
- std::unordered_map<uint32_t, int32_t> mTagToTypeMap;
- SortedVector<String8> mSections;
- // must be int32_t to be compatible with Parcel::writeInt32
- int32_t mTagCount;
+ std::unordered_map<uint32_t, int32_t> mTagToTypeMap;
+ SortedVector<String8> mSections;
+ // must be int32_t to be compatible with Parcel::writeInt32
+ int32_t mTagCount;
- vendor_tag_ops mVendorOps;
+ vendor_tag_ops mVendorOps;
};
} /* namespace params */
} /* namespace camera2 */
namespace camera {
namespace common {
-namespace V1_0 {
namespace helper {
/**
@@ -119,12 +118,9 @@
* Parcelable objects cannot require being kept in an sp<> and still work with auto-generated AIDL
* interface implementations.
*/
-class VendorTagDescriptor :
- public ::android::hardware::camera2::params::VendorTagDescriptor,
- public LightRefBase<VendorTagDescriptor> {
-
+class VendorTagDescriptor : public ::android::hardware::camera2::params::VendorTagDescriptor,
+ public LightRefBase<VendorTagDescriptor> {
public:
-
/**
* Create a VendorTagDescriptor object from the given vendor_tag_ops_t
* struct.
@@ -132,8 +128,8 @@
* Returns OK on success, or a negative error code.
*/
static status_t createDescriptorFromOps(const vendor_tag_ops_t* vOps,
- /*out*/
- sp<VendorTagDescriptor>& descriptor);
+ /*out*/
+ sp<VendorTagDescriptor>& descriptor);
/**
* Sets the global vendor tag descriptor to use for this process.
@@ -154,11 +150,9 @@
* Clears the global vendor tag descriptor used by this process.
*/
static void clearGlobalVendorTagDescriptor();
-
};
} /* namespace helper */
-} /* namespace V1_0 */
} /* namespace common */
} /* namespace camera */
@@ -166,9 +160,8 @@
namespace params {
class VendorTagDescriptorCache {
- public:
- typedef android::hardware::camera::common::V1_0::helper::VendorTagDescriptor
- VendorTagDescriptor;
+ public:
+ typedef android::hardware::camera::common::helper::VendorTagDescriptor VendorTagDescriptor;
VendorTagDescriptorCache(){};
int32_t addVendorDescriptor(metadata_vendor_id_t id, sp<VendorTagDescriptor> desc);
@@ -194,7 +187,7 @@
*/
void dump(int fd, int verbosity, int indentation) const;
- protected:
+ protected:
std::unordered_map<metadata_vendor_id_t, sp<VendorTagDescriptor>> mVendorMap;
struct vendor_tag_cache_ops mVendorCacheOps;
};
@@ -204,13 +197,12 @@
namespace camera {
namespace common {
-namespace V1_0 {
namespace helper {
class VendorTagDescriptorCache
: public ::android::hardware::camera2::params::VendorTagDescriptorCache,
public LightRefBase<VendorTagDescriptorCache> {
- public:
+ public:
/**
* Sets the global vendor tag descriptor cache to use for this process.
* Camera metadata operations that access vendor tags will use the
@@ -232,11 +224,19 @@
static void clearGlobalVendorTagCache();
};
-} // namespace helper
-} // namespace V1_0
-} // namespace common
-} // namespace camera
-} // namespace hardware
-} // namespace android
+} // namespace helper
+
+// NOTE: Deprecated namespace. This namespace should no longer be used for the following symbols
+namespace V1_0::helper {
+// Export symbols to the old namespace to preserve compatibility
+typedef android::hardware::camera::common::helper::VendorTagDescriptor VendorTagDescriptor;
+typedef android::hardware::camera::common::helper::VendorTagDescriptorCache
+ VendorTagDescriptorCache;
+} // namespace V1_0::helper
+
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
#endif /* CAMERA_COMMON_1_0_VENDORTAGDESCRIPTOR_H */
diff --git a/camera/device/default/Android.bp b/camera/device/default/Android.bp
new file mode 100644
index 0000000..b577597
--- /dev/null
+++ b/camera/device/default/Android.bp
@@ -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.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_shared {
+ name: "camera.device-external-impl",
+ defaults: ["hidl_defaults"],
+ proprietary: true,
+ srcs: [
+ "ExternalCameraDevice.cpp",
+ "ExternalCameraDeviceSession.cpp",
+ "ExternalCameraOfflineSession.cpp",
+ "ExternalCameraUtils.cpp",
+ "convert.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.camera.common-V1-ndk",
+ "android.hardware.camera.device-V1-ndk",
+ "android.hardware.graphics.allocator-V1-ndk",
+ "android.hardware.graphics.common-V4-ndk",
+ "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",
+ "libbinder_ndk",
+ "libcamera_metadata",
+ "libcutils",
+ "libexif",
+ "libfmq",
+ "libgralloctypes",
+ "libhardware",
+ "libhidlbase",
+ "libhidlmemory",
+ "libjpeg",
+ "liblog",
+ "libsync",
+ "libtinyxml2",
+ "libutils",
+ "libyuv",
+ ],
+ static_libs: [
+ "android.hardware.camera.common@1.0-helper",
+ "libaidlcommonsupport",
+ ],
+ header_libs: [
+ "media_plugin_headers",
+ ],
+ export_include_dirs: ["."],
+}
diff --git a/camera/device/default/ExternalCameraDevice.cpp b/camera/device/default/ExternalCameraDevice.cpp
new file mode 100644
index 0000000..677fb42
--- /dev/null
+++ b/camera/device/default/ExternalCameraDevice.cpp
@@ -0,0 +1,1001 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ExtCamDev"
+// #define LOG_NDEBUG 0
+#include <log/log.h>
+
+#include "ExternalCameraDevice.h"
+
+#include <aidl/android/hardware/camera/common/Status.h>
+#include <convert.h>
+#include <linux/videodev2.h>
+#include <regex>
+#include <set>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace implementation {
+
+using ::aidl::android::hardware::camera::common::Status;
+
+namespace {
+// Only support MJPEG for now as it seems to be the one supports higher fps
+// Other formats to consider in the future:
+// * V4L2_PIX_FMT_YVU420 (== YV12)
+// * V4L2_PIX_FMT_YVYU (YVYU: can be converted to YV12 or other YUV420_888 formats)
+const std::array<uint32_t, /*size*/ 2> kSupportedFourCCs{
+ {V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_Z16}}; // double braces required in C++11
+
+constexpr int MAX_RETRY = 5; // Allow retry v4l2 open failures a few times.
+constexpr int OPEN_RETRY_SLEEP_US = 100'000; // 100ms * MAX_RETRY = 0.5 seconds
+
+const std::regex kDevicePathRE("/dev/video([0-9]+)");
+} // namespace
+
+std::string ExternalCameraDevice::kDeviceVersion = "1.1";
+
+ExternalCameraDevice::ExternalCameraDevice(const std::string& devicePath,
+ const ExternalCameraConfig& config)
+ : mCameraId("-1"), mDevicePath(devicePath), mCfg(config) {
+ std::smatch sm;
+ if (std::regex_match(mDevicePath, sm, kDevicePathRE)) {
+ mCameraId = std::to_string(mCfg.cameraIdOffset + std::stoi(sm[1]));
+ } else {
+ ALOGE("%s: device path match failed for %s", __FUNCTION__, mDevicePath.c_str());
+ }
+}
+
+ExternalCameraDevice::~ExternalCameraDevice() {}
+
+ndk::ScopedAStatus ExternalCameraDevice::getCameraCharacteristics(CameraMetadata* _aidl_return) {
+ Mutex::Autolock _l(mLock);
+ if (_aidl_return == nullptr) {
+ return fromStatus(Status::ILLEGAL_ARGUMENT);
+ }
+
+ if (isInitFailedLocked()) {
+ return fromStatus(Status::INTERNAL_ERROR);
+ }
+
+ const camera_metadata_t* rawMetadata = mCameraCharacteristics.getAndLock();
+ convertToAidl(rawMetadata, _aidl_return);
+ mCameraCharacteristics.unlock(rawMetadata);
+ return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus ExternalCameraDevice::getPhysicalCameraCharacteristics(const std::string&,
+ CameraMetadata*) {
+ ALOGE("%s: Physical camera functions are not supported for external cameras.", __FUNCTION__);
+ return fromStatus(Status::ILLEGAL_ARGUMENT);
+}
+
+ndk::ScopedAStatus ExternalCameraDevice::getResourceCost(CameraResourceCost* _aidl_return) {
+ if (_aidl_return == nullptr) {
+ return fromStatus(Status::ILLEGAL_ARGUMENT);
+ }
+
+ _aidl_return->resourceCost = 100;
+ return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus ExternalCameraDevice::isStreamCombinationSupported(
+ const StreamConfiguration& in_streams, bool* _aidl_return) {
+ if (isInitFailed()) {
+ ALOGE("%s: camera %s. camera init failed!", __FUNCTION__, mCameraId.c_str());
+ return fromStatus(Status::INTERNAL_ERROR);
+ }
+ Status s = ExternalCameraDeviceSession::isStreamCombinationSupported(in_streams,
+ mSupportedFormats, mCfg);
+ *_aidl_return = s == Status::OK;
+ return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus ExternalCameraDevice::open(
+ const std::shared_ptr<ICameraDeviceCallback>& in_callback,
+ std::shared_ptr<ICameraDeviceSession>* _aidl_return) {
+ if (_aidl_return == nullptr) {
+ ALOGE("%s: cannot open camera %s. return session ptr is null!", __FUNCTION__,
+ mCameraId.c_str());
+ return fromStatus(Status::ILLEGAL_ARGUMENT);
+ }
+
+ Mutex::Autolock _l(mLock);
+ if (isInitFailedLocked()) {
+ ALOGE("%s: cannot open camera %s. camera init failed!", __FUNCTION__, mCameraId.c_str());
+ return fromStatus(Status::INTERNAL_ERROR);
+ }
+
+ std::shared_ptr<ExternalCameraDeviceSession> session;
+ ALOGV("%s: Initializing device for camera %s", __FUNCTION__, mCameraId.c_str());
+ session = mSession.lock();
+
+ if (session != nullptr && !session->isClosed()) {
+ ALOGE("%s: cannot open an already opened camera!", __FUNCTION__);
+ return fromStatus(Status::CAMERA_IN_USE);
+ }
+
+ int numAttempt = 0;
+ unique_fd fd(::open(mDevicePath.c_str(), O_RDWR));
+ while (fd.get() < 0 && numAttempt < MAX_RETRY) {
+ // Previous retry attempts failed. Retry opening the device at most MAX_RETRY times
+ ALOGW("%s: v4l2 device %s open failed, wait 33ms and try again", __FUNCTION__,
+ mDevicePath.c_str());
+ usleep(OPEN_RETRY_SLEEP_US); // sleep and try again
+ fd.reset(::open(mDevicePath.c_str(), O_RDWR));
+ numAttempt++;
+ }
+
+ if (fd.get() < 0) {
+ ALOGE("%s: v4l2 device open %s failed: %s", __FUNCTION__, mDevicePath.c_str(),
+ strerror(errno));
+ return fromStatus(Status::INTERNAL_ERROR);
+ }
+
+ session = createSession(in_callback, mCfg, mSupportedFormats, mCroppingType,
+ mCameraCharacteristics, mCameraId, std::move(fd));
+ if (session == nullptr) {
+ ALOGE("%s: camera device session allocation failed", __FUNCTION__);
+ return fromStatus(Status::INTERNAL_ERROR);
+ }
+
+ if (session->isInitFailed()) {
+ ALOGE("%s: camera device session init failed", __FUNCTION__);
+ return fromStatus(Status::INTERNAL_ERROR);
+ }
+
+ mSession = session;
+ *_aidl_return = session;
+ return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus ExternalCameraDevice::openInjectionSession(
+ const std::shared_ptr<ICameraDeviceCallback>&, std::shared_ptr<ICameraInjectionSession>*) {
+ return fromStatus(Status::OPERATION_NOT_SUPPORTED);
+}
+
+ndk::ScopedAStatus ExternalCameraDevice::setTorchMode(bool) {
+ return fromStatus(Status::OPERATION_NOT_SUPPORTED);
+}
+
+ndk::ScopedAStatus ExternalCameraDevice::turnOnTorchWithStrengthLevel(int32_t) {
+ return fromStatus(Status::OPERATION_NOT_SUPPORTED);
+}
+
+ndk::ScopedAStatus ExternalCameraDevice::getTorchStrengthLevel(int32_t*) {
+ return fromStatus(Status::OPERATION_NOT_SUPPORTED);
+}
+
+std::shared_ptr<ExternalCameraDeviceSession> ExternalCameraDevice::createSession(
+ const std::shared_ptr<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 ndk::SharedRefBase::make<ExternalCameraDeviceSession>(
+ cb, cfg, sortedFormats, croppingType, chars, cameraId, std::move(v4l2Fd));
+}
+
+bool ExternalCameraDevice::isInitFailed() {
+ Mutex::Autolock _l(mLock);
+ return isInitFailedLocked();
+}
+
+bool ExternalCameraDevice::isInitFailedLocked() {
+ if (!mInitialized) {
+ status_t ret = initCameraCharacteristics();
+ if (ret != OK) {
+ ALOGE("%s: init camera characteristics failed: errorno %d", __FUNCTION__, ret);
+ mInitFailed = true;
+ }
+ mInitialized = true;
+ }
+ return mInitFailed;
+}
+
+void ExternalCameraDevice::initSupportedFormatsLocked(int fd) {
+ std::vector<SupportedV4L2Format> horizontalFmts =
+ getCandidateSupportedFormatsLocked(fd, HORIZONTAL, mCfg.fpsLimits, mCfg.depthFpsLimits,
+ mCfg.minStreamSize, mCfg.depthEnabled);
+ std::vector<SupportedV4L2Format> verticalFmts =
+ getCandidateSupportedFormatsLocked(fd, VERTICAL, mCfg.fpsLimits, mCfg.depthFpsLimits,
+ mCfg.minStreamSize, mCfg.depthEnabled);
+
+ size_t horiSize = horizontalFmts.size();
+ size_t vertSize = verticalFmts.size();
+
+ if (horiSize == 0 && vertSize == 0) {
+ ALOGE("%s: cannot find suitable cropping type!", __FUNCTION__);
+ return;
+ }
+
+ if (horiSize == 0) {
+ mSupportedFormats = verticalFmts;
+ mCroppingType = VERTICAL;
+ return;
+ } else if (vertSize == 0) {
+ mSupportedFormats = horizontalFmts;
+ mCroppingType = HORIZONTAL;
+ return;
+ }
+
+ const auto& maxHoriSize = horizontalFmts[horizontalFmts.size() - 1];
+ const auto& maxVertSize = verticalFmts[verticalFmts.size() - 1];
+
+ // Try to keep the largest possible output size
+ // When they are the same or ambiguous, pick the one support more sizes
+ if (maxHoriSize.width == maxVertSize.width && maxHoriSize.height == maxVertSize.height) {
+ if (horiSize > vertSize) {
+ mSupportedFormats = horizontalFmts;
+ mCroppingType = HORIZONTAL;
+ } else {
+ mSupportedFormats = verticalFmts;
+ mCroppingType = VERTICAL;
+ }
+ } else if (maxHoriSize.width >= maxVertSize.width && maxHoriSize.height >= maxVertSize.height) {
+ mSupportedFormats = horizontalFmts;
+ mCroppingType = HORIZONTAL;
+ } else if (maxHoriSize.width <= maxVertSize.width && maxHoriSize.height <= maxVertSize.height) {
+ mSupportedFormats = verticalFmts;
+ mCroppingType = VERTICAL;
+ } else {
+ if (horiSize > vertSize) {
+ mSupportedFormats = horizontalFmts;
+ mCroppingType = HORIZONTAL;
+ } else {
+ mSupportedFormats = verticalFmts;
+ mCroppingType = VERTICAL;
+ }
+ }
+}
+
+status_t ExternalCameraDevice::initCameraCharacteristics() {
+ if (!mCameraCharacteristics.isEmpty()) {
+ // Camera Characteristics previously initialized. Skip.
+ return OK;
+ }
+
+ // init camera characteristics
+ unique_fd fd(::open(mDevicePath.c_str(), O_RDWR));
+ if (fd.get() < 0) {
+ ALOGE("%s: v4l2 device open %s failed", __FUNCTION__, mDevicePath.c_str());
+ return DEAD_OBJECT;
+ }
+
+ status_t ret;
+ ret = initDefaultCharsKeys(&mCameraCharacteristics);
+ if (ret != OK) {
+ ALOGE("%s: init default characteristics key failed: errorno %d", __FUNCTION__, ret);
+ mCameraCharacteristics.clear();
+ return ret;
+ }
+
+ ret = initCameraControlsCharsKeys(fd.get(), &mCameraCharacteristics);
+ if (ret != OK) {
+ ALOGE("%s: init camera control characteristics key failed: errorno %d", __FUNCTION__, ret);
+ mCameraCharacteristics.clear();
+ return ret;
+ }
+
+ ret = initOutputCharsKeys(fd.get(), &mCameraCharacteristics);
+ if (ret != OK) {
+ ALOGE("%s: init output characteristics key failed: errorno %d", __FUNCTION__, ret);
+ mCameraCharacteristics.clear();
+ return ret;
+ }
+
+ ret = initAvailableCapabilities(&mCameraCharacteristics);
+ if (ret != OK) {
+ ALOGE("%s: init available capabilities key failed: errorno %d", __FUNCTION__, ret);
+ mCameraCharacteristics.clear();
+ return ret;
+ }
+
+ return OK;
+}
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#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) {
+ if (mSupportedFormats.empty()) {
+ ALOGE("%s: Supported formats list is empty", __FUNCTION__);
+ return UNKNOWN_ERROR;
+ }
+
+ bool hasDepth = false;
+ bool hasColor = false;
+ for (const auto& fmt : mSupportedFormats) {
+ switch (fmt.fourcc) {
+ case V4L2_PIX_FMT_Z16:
+ hasDepth = true;
+ break;
+ case V4L2_PIX_FMT_MJPEG:
+ hasColor = true;
+ break;
+ default:
+ ALOGW("%s: Unsupported format found", __FUNCTION__);
+ }
+ }
+
+ std::vector<uint8_t> availableCapabilities;
+ if (hasDepth) {
+ availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT);
+ }
+ if (hasColor) {
+ availableCapabilities.push_back(ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
+ }
+ if (!availableCapabilities.empty()) {
+ UPDATE(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, availableCapabilities.data(),
+ availableCapabilities.size());
+ }
+
+ return OK;
+}
+
+status_t ExternalCameraDevice::initDefaultCharsKeys(
+ ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) {
+ const uint8_t hardware_level = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
+ UPDATE(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, &hardware_level, 1);
+
+ // android.colorCorrection
+ const uint8_t availableAberrationModes[] = {ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF};
+ UPDATE(ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES, availableAberrationModes,
+ ARRAY_SIZE(availableAberrationModes));
+
+ // android.control
+ const uint8_t antibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
+ UPDATE(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, &antibandingMode, 1);
+
+ const int32_t controlMaxRegions[] = {/*AE*/ 0, /*AWB*/ 0, /*AF*/ 0};
+ UPDATE(ANDROID_CONTROL_MAX_REGIONS, controlMaxRegions, ARRAY_SIZE(controlMaxRegions));
+
+ const uint8_t videoStabilizationMode = ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF;
+ UPDATE(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, &videoStabilizationMode, 1);
+
+ const uint8_t awbAvailableMode = ANDROID_CONTROL_AWB_MODE_AUTO;
+ UPDATE(ANDROID_CONTROL_AWB_AVAILABLE_MODES, &awbAvailableMode, 1);
+
+ const uint8_t aeAvailableMode = ANDROID_CONTROL_AE_MODE_ON;
+ UPDATE(ANDROID_CONTROL_AE_AVAILABLE_MODES, &aeAvailableMode, 1);
+
+ const uint8_t availableFffect = ANDROID_CONTROL_EFFECT_MODE_OFF;
+ UPDATE(ANDROID_CONTROL_AVAILABLE_EFFECTS, &availableFffect, 1);
+
+ const uint8_t controlAvailableModes[] = {ANDROID_CONTROL_MODE_OFF, ANDROID_CONTROL_MODE_AUTO};
+ UPDATE(ANDROID_CONTROL_AVAILABLE_MODES, controlAvailableModes,
+ ARRAY_SIZE(controlAvailableModes));
+
+ // android.edge
+ const uint8_t edgeMode = ANDROID_EDGE_MODE_OFF;
+ UPDATE(ANDROID_EDGE_AVAILABLE_EDGE_MODES, &edgeMode, 1);
+
+ // android.flash
+ const uint8_t flashInfo = ANDROID_FLASH_INFO_AVAILABLE_FALSE;
+ UPDATE(ANDROID_FLASH_INFO_AVAILABLE, &flashInfo, 1);
+
+ // android.hotPixel
+ const uint8_t hotPixelMode = ANDROID_HOT_PIXEL_MODE_OFF;
+ UPDATE(ANDROID_HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES, &hotPixelMode, 1);
+
+ // android.jpeg
+ const int32_t jpegAvailableThumbnailSizes[] = {0, 0, 176, 144, 240, 144, 256,
+ 144, 240, 160, 256, 154, 240, 180};
+ UPDATE(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, jpegAvailableThumbnailSizes,
+ ARRAY_SIZE(jpegAvailableThumbnailSizes));
+
+ const int32_t jpegMaxSize = mCfg.maxJpegBufSize;
+ UPDATE(ANDROID_JPEG_MAX_SIZE, &jpegMaxSize, 1);
+
+ // android.lens
+ const uint8_t focusDistanceCalibration =
+ ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED;
+ UPDATE(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION, &focusDistanceCalibration, 1);
+
+ const uint8_t opticalStabilizationMode = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
+ UPDATE(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION, &opticalStabilizationMode, 1);
+
+ const uint8_t facing = ANDROID_LENS_FACING_EXTERNAL;
+ UPDATE(ANDROID_LENS_FACING, &facing, 1);
+
+ // android.noiseReduction
+ const uint8_t noiseReductionMode = ANDROID_NOISE_REDUCTION_MODE_OFF;
+ UPDATE(ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES, &noiseReductionMode, 1);
+ UPDATE(ANDROID_NOISE_REDUCTION_MODE, &noiseReductionMode, 1);
+
+ const int32_t partialResultCount = 1;
+ UPDATE(ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &partialResultCount, 1);
+
+ // This means pipeline latency of X frame intervals. The maximum number is 4.
+ const uint8_t requestPipelineMaxDepth = 4;
+ UPDATE(ANDROID_REQUEST_PIPELINE_MAX_DEPTH, &requestPipelineMaxDepth, 1);
+
+ // Three numbers represent the maximum numbers of different types of output
+ // streams simultaneously. The types are raw sensor, processed (but not
+ // stalling), and processed (but stalling). For usb limited mode, raw sensor
+ // is not supported. Stalling stream is JPEG. Non-stalling streams are
+ // YUV_420_888 or YV12.
+ const int32_t requestMaxNumOutputStreams[] = {
+ /*RAW*/ 0,
+ /*Processed*/ ExternalCameraDeviceSession::kMaxProcessedStream,
+ /*Stall*/ ExternalCameraDeviceSession::kMaxStallStream};
+ UPDATE(ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS, requestMaxNumOutputStreams,
+ ARRAY_SIZE(requestMaxNumOutputStreams));
+
+ // Limited mode doesn't support reprocessing.
+ const int32_t requestMaxNumInputStreams = 0;
+ UPDATE(ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS, &requestMaxNumInputStreams, 1);
+
+ // android.scaler
+ // TODO: b/72263447 V4L2_CID_ZOOM_*
+ const float scalerAvailableMaxDigitalZoom[] = {1};
+ UPDATE(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, scalerAvailableMaxDigitalZoom,
+ ARRAY_SIZE(scalerAvailableMaxDigitalZoom));
+
+ const uint8_t croppingType = ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY;
+ UPDATE(ANDROID_SCALER_CROPPING_TYPE, &croppingType, 1);
+
+ const int32_t testPatternModes[] = {ANDROID_SENSOR_TEST_PATTERN_MODE_OFF,
+ ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR};
+ UPDATE(ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES, testPatternModes,
+ ARRAY_SIZE(testPatternModes));
+
+ const uint8_t timestampSource = ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN;
+ UPDATE(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE, ×tampSource, 1);
+
+ // Orientation is a bit odd for external camera, but consider it as the orientation
+ // between the external camera sensor (which is usually landscape) and the device's
+ // natural display orientation. For devices with natural landscape display (ex: tablet/TV), the
+ // orientation should be 0. For devices with natural portrait display (phone), the orientation
+ // should be 270.
+ const int32_t orientation = mCfg.orientation;
+ UPDATE(ANDROID_SENSOR_ORIENTATION, &orientation, 1);
+
+ // android.shading
+ const uint8_t availableMode = ANDROID_SHADING_MODE_OFF;
+ UPDATE(ANDROID_SHADING_AVAILABLE_MODES, &availableMode, 1);
+
+ // android.statistics
+ const uint8_t faceDetectMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+ UPDATE(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, &faceDetectMode, 1);
+
+ const int32_t maxFaceCount = 0;
+ UPDATE(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT, &maxFaceCount, 1);
+
+ const uint8_t availableHotpixelMode = ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF;
+ UPDATE(ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES, &availableHotpixelMode, 1);
+
+ const uint8_t lensShadingMapMode = ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF;
+ UPDATE(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES, &lensShadingMapMode, 1);
+
+ // android.sync
+ const int32_t maxLatency = ANDROID_SYNC_MAX_LATENCY_UNKNOWN;
+ UPDATE(ANDROID_SYNC_MAX_LATENCY, &maxLatency, 1);
+
+ /* Other sensor/RAW related keys:
+ * android.sensor.info.colorFilterArrangement -> no need if we don't do RAW
+ * android.sensor.info.physicalSize -> not available
+ * android.sensor.info.whiteLevel -> not available/not needed
+ * android.sensor.info.lensShadingApplied -> not needed
+ * android.sensor.info.preCorrectionActiveArraySize -> not available/not needed
+ * android.sensor.blackLevelPattern -> not available/not needed
+ */
+
+ const int32_t availableRequestKeys[] = {ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+ ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+ ANDROID_CONTROL_AE_LOCK,
+ ANDROID_CONTROL_AE_MODE,
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+ ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+ ANDROID_CONTROL_AF_MODE,
+ ANDROID_CONTROL_AF_TRIGGER,
+ ANDROID_CONTROL_AWB_LOCK,
+ ANDROID_CONTROL_AWB_MODE,
+ ANDROID_CONTROL_CAPTURE_INTENT,
+ ANDROID_CONTROL_EFFECT_MODE,
+ ANDROID_CONTROL_MODE,
+ ANDROID_CONTROL_SCENE_MODE,
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
+ ANDROID_FLASH_MODE,
+ ANDROID_JPEG_ORIENTATION,
+ ANDROID_JPEG_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_SIZE,
+ ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+ ANDROID_NOISE_REDUCTION_MODE,
+ ANDROID_SCALER_CROP_REGION,
+ ANDROID_SENSOR_TEST_PATTERN_MODE,
+ ANDROID_STATISTICS_FACE_DETECT_MODE,
+ ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE};
+ UPDATE(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, availableRequestKeys,
+ ARRAY_SIZE(availableRequestKeys));
+
+ const int32_t availableResultKeys[] = {ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+ ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+ ANDROID_CONTROL_AE_LOCK,
+ ANDROID_CONTROL_AE_MODE,
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+ ANDROID_CONTROL_AE_STATE,
+ ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+ ANDROID_CONTROL_AF_MODE,
+ ANDROID_CONTROL_AF_STATE,
+ ANDROID_CONTROL_AF_TRIGGER,
+ ANDROID_CONTROL_AWB_LOCK,
+ ANDROID_CONTROL_AWB_MODE,
+ ANDROID_CONTROL_AWB_STATE,
+ ANDROID_CONTROL_CAPTURE_INTENT,
+ ANDROID_CONTROL_EFFECT_MODE,
+ ANDROID_CONTROL_MODE,
+ ANDROID_CONTROL_SCENE_MODE,
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
+ ANDROID_FLASH_MODE,
+ ANDROID_FLASH_STATE,
+ ANDROID_JPEG_ORIENTATION,
+ ANDROID_JPEG_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_SIZE,
+ ANDROID_LENS_OPTICAL_STABILIZATION_MODE,
+ ANDROID_NOISE_REDUCTION_MODE,
+ ANDROID_REQUEST_PIPELINE_DEPTH,
+ ANDROID_SCALER_CROP_REGION,
+ ANDROID_SENSOR_TIMESTAMP,
+ ANDROID_STATISTICS_FACE_DETECT_MODE,
+ ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE,
+ ANDROID_STATISTICS_LENS_SHADING_MAP_MODE,
+ ANDROID_STATISTICS_SCENE_FLICKER};
+ UPDATE(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, availableResultKeys,
+ ARRAY_SIZE(availableResultKeys));
+
+ UPDATE(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, AVAILABLE_CHARACTERISTICS_KEYS.data(),
+ AVAILABLE_CHARACTERISTICS_KEYS.size());
+
+ return OK;
+}
+
+status_t ExternalCameraDevice::initCameraControlsCharsKeys(
+ int, ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) {
+ // android.sensor.info.sensitivityRange -> V4L2_CID_ISO_SENSITIVITY
+ // android.sensor.info.exposureTimeRange -> V4L2_CID_EXPOSURE_ABSOLUTE
+ // android.sensor.info.maxFrameDuration -> TBD
+ // android.lens.info.minimumFocusDistance -> V4L2_CID_FOCUS_ABSOLUTE
+ // android.lens.info.hyperfocalDistance
+ // android.lens.info.availableFocalLengths -> not available?
+
+ // android.control
+ // No AE compensation support for now.
+ // TODO: V4L2_CID_EXPOSURE_BIAS
+ const int32_t controlAeCompensationRange[] = {0, 0};
+ UPDATE(ANDROID_CONTROL_AE_COMPENSATION_RANGE, controlAeCompensationRange,
+ ARRAY_SIZE(controlAeCompensationRange));
+ const camera_metadata_rational_t controlAeCompensationStep[] = {{0, 1}};
+ UPDATE(ANDROID_CONTROL_AE_COMPENSATION_STEP, controlAeCompensationStep,
+ ARRAY_SIZE(controlAeCompensationStep));
+
+ // TODO: Check V4L2_CID_AUTO_FOCUS_*.
+ const uint8_t afAvailableModes[] = {ANDROID_CONTROL_AF_MODE_AUTO, ANDROID_CONTROL_AF_MODE_OFF};
+ UPDATE(ANDROID_CONTROL_AF_AVAILABLE_MODES, afAvailableModes, ARRAY_SIZE(afAvailableModes));
+
+ // TODO: V4L2_CID_SCENE_MODE
+ const uint8_t availableSceneMode = ANDROID_CONTROL_SCENE_MODE_DISABLED;
+ UPDATE(ANDROID_CONTROL_AVAILABLE_SCENE_MODES, &availableSceneMode, 1);
+
+ // TODO: V4L2_CID_3A_LOCK
+ const uint8_t aeLockAvailable = ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE;
+ UPDATE(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &aeLockAvailable, 1);
+ const uint8_t awbLockAvailable = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE;
+ UPDATE(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &awbLockAvailable, 1);
+
+ // TODO: V4L2_CID_ZOOM_*
+ const float scalerAvailableMaxDigitalZoom[] = {1};
+ UPDATE(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, scalerAvailableMaxDigitalZoom,
+ ARRAY_SIZE(scalerAvailableMaxDigitalZoom));
+
+ return OK;
+}
+
+status_t ExternalCameraDevice::initOutputCharsKeys(
+ int fd, ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) {
+ initSupportedFormatsLocked(fd);
+ if (mSupportedFormats.empty()) {
+ ALOGE("%s: Init supported format list failed", __FUNCTION__);
+ return UNKNOWN_ERROR;
+ }
+
+ bool hasDepth = false;
+ bool hasColor = false;
+
+ // For V4L2_PIX_FMT_Z16
+ std::array<int, /*size*/ 1> halDepthFormats{{HAL_PIXEL_FORMAT_Y16}};
+ // For V4L2_PIX_FMT_MJPEG
+ std::array<int, /*size*/ 3> halFormats{{HAL_PIXEL_FORMAT_BLOB, HAL_PIXEL_FORMAT_YCbCr_420_888,
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED}};
+
+ for (const auto& supportedFormat : mSupportedFormats) {
+ switch (supportedFormat.fourcc) {
+ case V4L2_PIX_FMT_Z16:
+ hasDepth = true;
+ break;
+ case V4L2_PIX_FMT_MJPEG:
+ hasColor = true;
+ break;
+ default:
+ ALOGW("%s: format %c%c%c%c is not supported!", __FUNCTION__,
+ supportedFormat.fourcc & 0xFF, (supportedFormat.fourcc >> 8) & 0xFF,
+ (supportedFormat.fourcc >> 16) & 0xFF, (supportedFormat.fourcc >> 24) & 0xFF);
+ }
+ }
+
+ if (hasDepth) {
+ status_t ret = initOutputCharsKeysByFormat(
+ metadata, V4L2_PIX_FMT_Z16, halDepthFormats,
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_OUTPUT,
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS,
+ ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS,
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS);
+ if (ret != OK) {
+ ALOGE("%s: Unable to initialize depth format keys: %s", __FUNCTION__,
+ statusToString(ret).c_str());
+ return ret;
+ }
+ }
+ if (hasColor) {
+ status_t ret =
+ initOutputCharsKeysByFormat(metadata, V4L2_PIX_FMT_MJPEG, halFormats,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+ ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
+ ANDROID_SCALER_AVAILABLE_STALL_DURATIONS);
+ if (ret != OK) {
+ ALOGE("%s: Unable to initialize color format keys: %s", __FUNCTION__,
+ statusToString(ret).c_str());
+ return ret;
+ }
+ }
+
+ status_t ret = calculateMinFps(metadata);
+ if (ret != OK) {
+ ALOGE("%s: Unable to update fps metadata: %s", __FUNCTION__, statusToString(ret).c_str());
+ return ret;
+ }
+
+ SupportedV4L2Format maximumFormat{.width = 0, .height = 0};
+ for (const auto& supportedFormat : mSupportedFormats) {
+ if (supportedFormat.width >= maximumFormat.width &&
+ supportedFormat.height >= maximumFormat.height) {
+ maximumFormat = supportedFormat;
+ }
+ }
+ int32_t activeArraySize[] = {0, 0, static_cast<int32_t>(maximumFormat.width),
+ static_cast<int32_t>(maximumFormat.height)};
+ UPDATE(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, activeArraySize,
+ ARRAY_SIZE(activeArraySize));
+ UPDATE(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, activeArraySize, ARRAY_SIZE(activeArraySize));
+
+ int32_t pixelArraySize[] = {static_cast<int32_t>(maximumFormat.width),
+ static_cast<int32_t>(maximumFormat.height)};
+ UPDATE(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, pixelArraySize, ARRAY_SIZE(pixelArraySize));
+ return OK;
+}
+
+template <size_t SIZE>
+status_t ExternalCameraDevice::initOutputCharsKeysByFormat(
+ ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata,
+ uint32_t fourcc, const std::array<int, SIZE>& halFormats, int streamConfigTag,
+ int streamConfigurationKey, int minFrameDurationKey, int stallDurationKey) {
+ if (mSupportedFormats.empty()) {
+ ALOGE("%s: Init supported format list failed", __FUNCTION__);
+ return UNKNOWN_ERROR;
+ }
+
+ std::vector<int32_t> streamConfigurations;
+ std::vector<int64_t> minFrameDurations;
+ std::vector<int64_t> stallDurations;
+
+ for (const auto& supportedFormat : mSupportedFormats) {
+ if (supportedFormat.fourcc != fourcc) {
+ // Skip 4CCs not meant for the halFormats
+ continue;
+ }
+ for (const auto& format : halFormats) {
+ streamConfigurations.push_back(format);
+ streamConfigurations.push_back(supportedFormat.width);
+ streamConfigurations.push_back(supportedFormat.height);
+ streamConfigurations.push_back(streamConfigTag);
+ }
+
+ int64_t minFrameDuration = std::numeric_limits<int64_t>::max();
+ for (const auto& fr : supportedFormat.frameRates) {
+ // 1000000000LL < (2^32 - 1) and
+ // fr.durationNumerator is uint32_t, so no overflow here
+ int64_t frameDuration = 1000000000LL * fr.durationNumerator / fr.durationDenominator;
+ if (frameDuration < minFrameDuration) {
+ minFrameDuration = frameDuration;
+ }
+ }
+
+ for (const auto& format : halFormats) {
+ minFrameDurations.push_back(format);
+ minFrameDurations.push_back(supportedFormat.width);
+ minFrameDurations.push_back(supportedFormat.height);
+ minFrameDurations.push_back(minFrameDuration);
+ }
+
+ // The stall duration is 0 for non-jpeg formats. For JPEG format, stall
+ // duration can be 0 if JPEG is small. Here we choose 1 sec for JPEG.
+ // TODO: b/72261675. Maybe set this dynamically
+ for (const auto& format : halFormats) {
+ const int64_t NS_TO_SECOND = 1E9;
+ int64_t stall_duration = (format == HAL_PIXEL_FORMAT_BLOB) ? NS_TO_SECOND : 0;
+ stallDurations.push_back(format);
+ stallDurations.push_back(supportedFormat.width);
+ stallDurations.push_back(supportedFormat.height);
+ stallDurations.push_back(stall_duration);
+ }
+ }
+
+ UPDATE(streamConfigurationKey, streamConfigurations.data(), streamConfigurations.size());
+
+ UPDATE(minFrameDurationKey, minFrameDurations.data(), minFrameDurations.size());
+
+ UPDATE(stallDurationKey, stallDurations.data(), stallDurations.size());
+
+ return OK;
+}
+
+status_t ExternalCameraDevice::calculateMinFps(
+ ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) {
+ std::set<int32_t> framerates;
+ int32_t minFps = std::numeric_limits<int32_t>::max();
+
+ for (const auto& supportedFormat : mSupportedFormats) {
+ for (const auto& fr : supportedFormat.frameRates) {
+ int32_t frameRateInt = static_cast<int32_t>(fr.getFramesPerSecond());
+ if (minFps > frameRateInt) {
+ minFps = frameRateInt;
+ }
+ framerates.insert(frameRateInt);
+ }
+ }
+
+ std::vector<int32_t> fpsRanges;
+ // FPS ranges
+ for (const auto& framerate : framerates) {
+ // Empirical: webcams often have close to 2x fps error and cannot support fixed fps range
+ fpsRanges.push_back(framerate / 2);
+ fpsRanges.push_back(framerate);
+ }
+ minFps /= 2;
+ int64_t maxFrameDuration = 1000000000LL / minFps;
+
+ UPDATE(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, fpsRanges.data(), fpsRanges.size());
+
+ UPDATE(ANDROID_SENSOR_INFO_MAX_FRAME_DURATION, &maxFrameDuration, 1);
+
+ return OK;
+}
+
+#undef ARRAY_SIZE
+#undef UPDATE
+
+void ExternalCameraDevice::getFrameRateList(int fd, double fpsUpperBound,
+ SupportedV4L2Format* format) {
+ format->frameRates.clear();
+
+ v4l2_frmivalenum frameInterval{
+ .index = 0,
+ .pixel_format = format->fourcc,
+ .width = static_cast<__u32>(format->width),
+ .height = static_cast<__u32>(format->height),
+ };
+
+ for (frameInterval.index = 0;
+ TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frameInterval)) == 0;
+ ++frameInterval.index) {
+ if (frameInterval.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
+ if (frameInterval.discrete.numerator != 0) {
+ SupportedV4L2Format::FrameRate fr = {frameInterval.discrete.numerator,
+ frameInterval.discrete.denominator};
+ double framerate = fr.getFramesPerSecond();
+ if (framerate > fpsUpperBound) {
+ continue;
+ }
+ ALOGV("index:%d, format:%c%c%c%c, w %d, h %d, framerate %f", frameInterval.index,
+ frameInterval.pixel_format & 0xFF, (frameInterval.pixel_format >> 8) & 0xFF,
+ (frameInterval.pixel_format >> 16) & 0xFF,
+ (frameInterval.pixel_format >> 24) & 0xFF, frameInterval.width,
+ frameInterval.height, framerate);
+ format->frameRates.push_back(fr);
+ }
+ }
+ }
+
+ if (format->frameRates.empty()) {
+ ALOGE("%s: failed to get supported frame rates for format:%c%c%c%c w %d h %d", __FUNCTION__,
+ frameInterval.pixel_format & 0xFF, (frameInterval.pixel_format >> 8) & 0xFF,
+ (frameInterval.pixel_format >> 16) & 0xFF, (frameInterval.pixel_format >> 24) & 0xFF,
+ frameInterval.width, frameInterval.height);
+ }
+}
+
+void ExternalCameraDevice::updateFpsBounds(
+ int fd, CroppingType cropType,
+ const std::vector<ExternalCameraConfig::FpsLimitation>& fpsLimits,
+ SupportedV4L2Format format, std::vector<SupportedV4L2Format>& outFmts) {
+ double fpsUpperBound = -1.0;
+ for (const auto& limit : fpsLimits) {
+ if (cropType == VERTICAL) {
+ if (format.width <= limit.size.width) {
+ fpsUpperBound = limit.fpsUpperBound;
+ break;
+ }
+ } else { // HORIZONTAL
+ if (format.height <= limit.size.height) {
+ fpsUpperBound = limit.fpsUpperBound;
+ break;
+ }
+ }
+ }
+ if (fpsUpperBound < 0.f) {
+ return;
+ }
+
+ getFrameRateList(fd, fpsUpperBound, &format);
+ if (!format.frameRates.empty()) {
+ outFmts.push_back(format);
+ }
+}
+
+std::vector<SupportedV4L2Format> ExternalCameraDevice::getCandidateSupportedFormatsLocked(
+ int fd, CroppingType cropType,
+ const std::vector<ExternalCameraConfig::FpsLimitation>& fpsLimits,
+ const std::vector<ExternalCameraConfig::FpsLimitation>& depthFpsLimits,
+ const Size& minStreamSize, bool depthEnabled) {
+ std::vector<SupportedV4L2Format> outFmts;
+ struct v4l2_fmtdesc fmtdesc {
+ .index = 0, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE
+ };
+ int ret = 0;
+ while (ret == 0) {
+ ret = TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc));
+ ALOGV("index:%d,ret:%d, format:%c%c%c%c", fmtdesc.index, ret, fmtdesc.pixelformat & 0xFF,
+ (fmtdesc.pixelformat >> 8) & 0xFF, (fmtdesc.pixelformat >> 16) & 0xFF,
+ (fmtdesc.pixelformat >> 24) & 0xFF);
+
+ if (ret != 0 || (fmtdesc.flags & V4L2_FMT_FLAG_EMULATED)) {
+ // Skip if IOCTL failed, or if the format is emulated
+ fmtdesc.index++;
+ continue;
+ }
+ auto it =
+ std::find(kSupportedFourCCs.begin(), kSupportedFourCCs.end(), fmtdesc.pixelformat);
+ if (it == kSupportedFourCCs.end()) {
+ fmtdesc.index++;
+ continue;
+ }
+
+ // Found supported format
+ v4l2_frmsizeenum frameSize{.index = 0, .pixel_format = fmtdesc.pixelformat};
+ for (; TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frameSize)) == 0;
+ ++frameSize.index) {
+ if (frameSize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
+ ALOGV("index:%d, format:%c%c%c%c, w %d, h %d", frameSize.index,
+ fmtdesc.pixelformat & 0xFF, (fmtdesc.pixelformat >> 8) & 0xFF,
+ (fmtdesc.pixelformat >> 16) & 0xFF, (fmtdesc.pixelformat >> 24) & 0xFF,
+ frameSize.discrete.width, frameSize.discrete.height);
+
+ // Disregard h > w formats so all aspect ratio (h/w) <= 1.0
+ // This will simplify the crop/scaling logic down the road
+ if (frameSize.discrete.height > frameSize.discrete.width) {
+ continue;
+ }
+
+ // Discard all formats which is smaller than minStreamSize
+ if (frameSize.discrete.width < minStreamSize.width ||
+ frameSize.discrete.height < minStreamSize.height) {
+ continue;
+ }
+
+ SupportedV4L2Format format{
+ .width = static_cast<int32_t>(frameSize.discrete.width),
+ .height = static_cast<int32_t>(frameSize.discrete.height),
+ .fourcc = fmtdesc.pixelformat};
+
+ if (format.fourcc == V4L2_PIX_FMT_Z16 && depthEnabled) {
+ updateFpsBounds(fd, cropType, depthFpsLimits, format, outFmts);
+ } else {
+ updateFpsBounds(fd, cropType, fpsLimits, format, outFmts);
+ }
+ }
+ }
+ fmtdesc.index++;
+ }
+ trimSupportedFormats(cropType, &outFmts);
+ return outFmts;
+}
+
+void ExternalCameraDevice::trimSupportedFormats(CroppingType cropType,
+ std::vector<SupportedV4L2Format>* pFmts) {
+ std::vector<SupportedV4L2Format>& sortedFmts = *pFmts;
+ if (cropType == VERTICAL) {
+ std::sort(sortedFmts.begin(), sortedFmts.end(),
+ [](const SupportedV4L2Format& a, const SupportedV4L2Format& b) -> bool {
+ if (a.width == b.width) {
+ return a.height < b.height;
+ }
+ return a.width < b.width;
+ });
+ } else {
+ std::sort(sortedFmts.begin(), sortedFmts.end(),
+ [](const SupportedV4L2Format& a, const SupportedV4L2Format& b) -> bool {
+ if (a.height == b.height) {
+ return a.width < b.width;
+ }
+ return a.height < b.height;
+ });
+ }
+
+ if (sortedFmts.empty()) {
+ ALOGE("%s: input format list is empty!", __FUNCTION__);
+ return;
+ }
+
+ const auto& maxSize = sortedFmts[sortedFmts.size() - 1];
+ float maxSizeAr = ASPECT_RATIO(maxSize);
+
+ // Remove formats that has aspect ratio not croppable from largest size
+ std::vector<SupportedV4L2Format> out;
+ for (const auto& fmt : sortedFmts) {
+ float ar = ASPECT_RATIO(fmt);
+ if (isAspectRatioClose(ar, maxSizeAr)) {
+ out.push_back(fmt);
+ } else if (cropType == HORIZONTAL && ar < maxSizeAr) {
+ out.push_back(fmt);
+ } else if (cropType == VERTICAL && ar > maxSizeAr) {
+ out.push_back(fmt);
+ } else {
+ ALOGV("%s: size (%d,%d) is removed due to unable to crop %s from (%d,%d)", __FUNCTION__,
+ fmt.width, fmt.height, cropType == VERTICAL ? "vertically" : "horizontally",
+ maxSize.width, maxSize.height);
+ }
+ }
+ sortedFmts = out;
+}
+
+binder_status_t ExternalCameraDevice::dump(int fd, const char** args, uint32_t numArgs) {
+ std::shared_ptr<ExternalCameraDeviceSession> session = mSession.lock();
+ if (session == nullptr) {
+ dprintf(fd, "No active camera device session instance\n");
+ return STATUS_OK;
+ }
+
+ return session->dump(fd, args, numArgs);
+}
+
+} // namespace implementation
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/camera/device/default/ExternalCameraDevice.h b/camera/device/default/ExternalCameraDevice.h
new file mode 100644
index 0000000..bcae194
--- /dev/null
+++ b/camera/device/default/ExternalCameraDevice.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_EXTERNALCAMERADEVICE_H_
+#define HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_EXTERNALCAMERADEVICE_H_
+
+#include <ExternalCameraDeviceSession.h>
+#include <ExternalCameraUtils.h>
+#include <aidl/android/hardware/camera/device/BnCameraDevice.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace implementation {
+
+using ::aidl::android::hardware::camera::common::CameraResourceCost;
+using ::aidl::android::hardware::camera::device::BnCameraDevice;
+using ::aidl::android::hardware::camera::device::CameraMetadata;
+using ::aidl::android::hardware::camera::device::ICameraDeviceCallback;
+using ::aidl::android::hardware::camera::device::ICameraDeviceSession;
+using ::aidl::android::hardware::camera::device::ICameraInjectionSession;
+using ::aidl::android::hardware::camera::device::StreamConfiguration;
+using ::android::hardware::camera::external::common::ExternalCameraConfig;
+
+class ExternalCameraDevice : public BnCameraDevice {
+ public:
+ // 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& devicePath, const ExternalCameraConfig& config);
+ ~ExternalCameraDevice() override;
+
+ ndk::ScopedAStatus getCameraCharacteristics(CameraMetadata* _aidl_return) override;
+ ndk::ScopedAStatus getPhysicalCameraCharacteristics(const std::string& in_physicalCameraId,
+ CameraMetadata* _aidl_return) override;
+ ndk::ScopedAStatus getResourceCost(CameraResourceCost* _aidl_return) override;
+ ndk::ScopedAStatus isStreamCombinationSupported(const StreamConfiguration& in_streams,
+ bool* _aidl_return) override;
+ ndk::ScopedAStatus open(const std::shared_ptr<ICameraDeviceCallback>& in_callback,
+ std::shared_ptr<ICameraDeviceSession>* _aidl_return) override;
+ ndk::ScopedAStatus openInjectionSession(
+ const std::shared_ptr<ICameraDeviceCallback>& in_callback,
+ std::shared_ptr<ICameraInjectionSession>* _aidl_return) override;
+ ndk::ScopedAStatus setTorchMode(bool in_on) override;
+ ndk::ScopedAStatus turnOnTorchWithStrengthLevel(int32_t in_torchStrength) override;
+ ndk::ScopedAStatus getTorchStrengthLevel(int32_t* _aidl_return) override;
+
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+
+ // Caller must use this method to check if CameraDevice ctor failed
+ bool isInitFailed();
+
+ // Device version to be used by the external camera provider.
+ // Should be of the form <major>.<minor>
+ static std::string kDeviceVersion;
+
+ private:
+ virtual std::shared_ptr<ExternalCameraDeviceSession> createSession(
+ const std::shared_ptr<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);
+
+ bool isInitFailedLocked();
+
+ // Init supported w/h/format/fps in mSupportedFormats. Caller still owns fd
+ void initSupportedFormatsLocked(int fd);
+
+ // Calls into virtual member function. Do not use it in constructor
+ status_t initCameraCharacteristics();
+ // Init available capabilities keys
+ virtual status_t initAvailableCapabilities(
+ ::android::hardware::camera::common::V1_0::helper::CameraMetadata*);
+ // Init non-device dependent keys
+ virtual status_t initDefaultCharsKeys(
+ ::android::hardware::camera::common::V1_0::helper::CameraMetadata*);
+ // Init camera control chars keys. Caller still owns fd
+ status_t initCameraControlsCharsKeys(
+ int fd, ::android::hardware::camera::common::V1_0::helper::CameraMetadata*);
+ // Init camera output configuration related keys. Caller still owns fd
+ status_t initOutputCharsKeys(
+ int fd, ::android::hardware::camera::common::V1_0::helper::CameraMetadata*);
+
+ // Helper function for initOutputCharskeys
+ template <size_t SIZE>
+ status_t initOutputCharsKeysByFormat(
+ ::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata,
+ uint32_t fourcc, const std::array<int, SIZE>& halFormats, int streamConfigTag,
+ int streamConfiguration, int minFrameDuration, int stallDuration);
+
+ status_t calculateMinFps(::android::hardware::camera::common::V1_0::helper::CameraMetadata*);
+
+ static void getFrameRateList(int fd, double fpsUpperBound, SupportedV4L2Format* format);
+
+ static void updateFpsBounds(int fd, CroppingType cropType,
+ const std::vector<ExternalCameraConfig::FpsLimitation>& fpsLimits,
+ SupportedV4L2Format format,
+ std::vector<SupportedV4L2Format>& outFmts);
+
+ // Get candidate supported formats list of input cropping type.
+ static std::vector<SupportedV4L2Format> getCandidateSupportedFormatsLocked(
+ int fd, CroppingType cropType,
+ const std::vector<ExternalCameraConfig::FpsLimitation>& fpsLimits,
+ const std::vector<ExternalCameraConfig::FpsLimitation>& depthFpsLimits,
+ const Size& minStreamSize, bool depthEnabled);
+ // Trim supported format list by the cropping type. Also sort output formats by width/height
+ static void trimSupportedFormats(CroppingType cropType,
+ /*inout*/ std::vector<SupportedV4L2Format>* pFmts);
+
+ Mutex mLock;
+ bool mInitialized = false;
+ bool mInitFailed = false;
+ std::string mCameraId;
+ std::string mDevicePath;
+ const ExternalCameraConfig& mCfg;
+ std::vector<SupportedV4L2Format> mSupportedFormats;
+ CroppingType mCroppingType;
+
+ std::weak_ptr<ExternalCameraDeviceSession> mSession =
+ std::weak_ptr<ExternalCameraDeviceSession>();
+
+ ::android::hardware::camera::common::V1_0::helper::CameraMetadata mCameraCharacteristics;
+
+ const std::vector<int32_t> AVAILABLE_CHARACTERISTICS_KEYS = {
+ ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
+ ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
+ ANDROID_CONTROL_AE_AVAILABLE_MODES,
+ ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+ ANDROID_CONTROL_AE_COMPENSATION_RANGE,
+ ANDROID_CONTROL_AE_COMPENSATION_STEP,
+ ANDROID_CONTROL_AE_LOCK_AVAILABLE,
+ ANDROID_CONTROL_AF_AVAILABLE_MODES,
+ ANDROID_CONTROL_AVAILABLE_EFFECTS,
+ ANDROID_CONTROL_AVAILABLE_MODES,
+ ANDROID_CONTROL_AVAILABLE_SCENE_MODES,
+ ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
+ ANDROID_CONTROL_AWB_AVAILABLE_MODES,
+ ANDROID_CONTROL_AWB_LOCK_AVAILABLE,
+ ANDROID_CONTROL_MAX_REGIONS,
+ ANDROID_FLASH_INFO_AVAILABLE,
+ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
+ ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
+ ANDROID_LENS_FACING,
+ ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
+ ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
+ ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+ ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
+ ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS,
+ ANDROID_REQUEST_PARTIAL_RESULT_COUNT,
+ ANDROID_REQUEST_PIPELINE_MAX_DEPTH,
+ ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+ ANDROID_SCALER_CROPPING_TYPE,
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+ ANDROID_SENSOR_INFO_MAX_FRAME_DURATION,
+ ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE,
+ ANDROID_SENSOR_ORIENTATION,
+ ANDROID_SHADING_AVAILABLE_MODES,
+ ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
+ ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES,
+ ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES,
+ ANDROID_STATISTICS_INFO_MAX_FACE_COUNT,
+ ANDROID_SYNC_MAX_LATENCY};
+};
+
+} // namespace implementation
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+#endif // HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_EXTERNALCAMERADEVICE_H_
diff --git a/camera/device/default/ExternalCameraDeviceSession.cpp b/camera/device/default/ExternalCameraDeviceSession.cpp
new file mode 100644
index 0000000..68c6d2e
--- /dev/null
+++ b/camera/device/default/ExternalCameraDeviceSession.cpp
@@ -0,0 +1,2947 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ExtCamDevSsn"
+// #define LOG_NDEBUG 0
+#include <log/log.h>
+
+#include "ExternalCameraDeviceSession.h"
+
+#include <Exif.h>
+#include <ExternalCameraOfflineSession.h>
+#include <aidl/android/hardware/camera/device/CameraBlob.h>
+#include <aidl/android/hardware/camera/device/CameraBlobId.h>
+#include <aidl/android/hardware/camera/device/ErrorMsg.h>
+#include <aidl/android/hardware/camera/device/ShutterMsg.h>
+#include <aidl/android/hardware/camera/device/StreamBufferRet.h>
+#include <aidl/android/hardware/camera/device/StreamBuffersVal.h>
+#include <aidl/android/hardware/camera/device/StreamConfigurationMode.h>
+#include <aidl/android/hardware/camera/device/StreamRotation.h>
+#include <aidl/android/hardware/camera/device/StreamType.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <convert.h>
+#include <linux/videodev2.h>
+#include <sync/sync.h>
+#include <utils/Trace.h>
+#include <deque>
+
+#define HAVE_JPEG // required for libyuv.h to export MJPEG decode APIs
+#include <libyuv.h>
+#include <libyuv/convert.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace implementation {
+
+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 */;
+
+const int kBadFramesAfterStreamOn = 1; // drop x frames after streamOn to get rid of some initial
+ // bad frames. TODO: develop a better bad frame detection
+ // method
+constexpr int MAX_RETRY = 15; // Allow retry some ioctl failures a few times to account for some
+ // webcam showing temporarily ioctl failures.
+constexpr int IOCTL_RETRY_SLEEP_US = 33000; // 33ms * MAX_RETRY = 0.5 seconds
+
+// Constants for tryLock during dumpstate
+static constexpr int kDumpLockRetries = 50;
+static constexpr int kDumpLockSleep = 60000;
+
+bool tryLock(Mutex& mutex) {
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mutex.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleep);
+ }
+ return locked;
+}
+
+bool tryLock(std::mutex& mutex) {
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mutex.try_lock()) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleep);
+ }
+ return locked;
+}
+
+} // anonymous namespace
+
+using ::aidl::android::hardware::camera::device::BufferRequestStatus;
+using ::aidl::android::hardware::camera::device::CameraBlob;
+using ::aidl::android::hardware::camera::device::CameraBlobId;
+using ::aidl::android::hardware::camera::device::ErrorMsg;
+using ::aidl::android::hardware::camera::device::ShutterMsg;
+using ::aidl::android::hardware::camera::device::StreamBuffer;
+using ::aidl::android::hardware::camera::device::StreamBufferRet;
+using ::aidl::android::hardware::camera::device::StreamBuffersVal;
+using ::aidl::android::hardware::camera::device::StreamConfigurationMode;
+using ::aidl::android::hardware::camera::device::StreamRotation;
+using ::aidl::android::hardware::camera::device::StreamType;
+using ::aidl::android::hardware::graphics::common::Dataspace;
+using ::android::hardware::camera::common::V1_0::helper::ExifUtils;
+
+// Static instances
+const int ExternalCameraDeviceSession::kMaxProcessedStream;
+const int ExternalCameraDeviceSession::kMaxStallStream;
+HandleImporter ExternalCameraDeviceSession::sHandleImporter;
+
+ExternalCameraDeviceSession::ExternalCameraDeviceSession(
+ const std::shared_ptr<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)
+ : mCallback(callback),
+ mCfg(cfg),
+ mCameraCharacteristics(chars),
+ mSupportedFormats(sortedFormats),
+ mCroppingType(croppingType),
+ mCameraId(cameraId),
+ mV4l2Fd(std::move(v4l2Fd)),
+ mMaxThumbResolution(getMaxThumbResolution()),
+ mMaxJpegResolution(getMaxJpegResolution()) {}
+
+Size ExternalCameraDeviceSession::getMaxThumbResolution() const {
+ return getMaxThumbnailResolution(mCameraCharacteristics);
+}
+
+Size ExternalCameraDeviceSession::getMaxJpegResolution() const {
+ Size ret{0, 0};
+ for (auto& fmt : mSupportedFormats) {
+ if (fmt.width * fmt.height > ret.width * ret.height) {
+ ret = Size{fmt.width, fmt.height};
+ }
+ }
+ return ret;
+}
+
+bool ExternalCameraDeviceSession::initialize() {
+ if (mV4l2Fd.get() < 0) {
+ ALOGE("%s: invalid v4l2 device fd %d!", __FUNCTION__, mV4l2Fd.get());
+ return true;
+ }
+
+ struct v4l2_capability capability;
+ int ret = ioctl(mV4l2Fd.get(), VIDIOC_QUERYCAP, &capability);
+ std::string make, model;
+ if (ret < 0) {
+ ALOGW("%s v4l2 QUERYCAP failed", __FUNCTION__);
+ mExifMake = "Generic UVC webcam";
+ mExifModel = "Generic UVC webcam";
+ } else {
+ // capability.card is UTF-8 encoded
+ char card[32];
+ int j = 0;
+ for (int i = 0; i < 32; i++) {
+ if (capability.card[i] < 128) {
+ card[j++] = capability.card[i];
+ }
+ if (capability.card[i] == '\0') {
+ break;
+ }
+ }
+ if (j == 0 || card[j - 1] != '\0') {
+ mExifMake = "Generic UVC webcam";
+ mExifModel = "Generic UVC webcam";
+ } else {
+ mExifMake = card;
+ mExifModel = card;
+ }
+ }
+
+ initOutputThread();
+ if (mOutputThread == nullptr) {
+ ALOGE("%s: init OutputThread failed!", __FUNCTION__);
+ return true;
+ }
+ mOutputThread->setExifMakeModel(mExifMake, mExifModel);
+
+ status_t status = initDefaultRequests();
+ if (status != OK) {
+ ALOGE("%s: init default requests failed!", __FUNCTION__);
+ return true;
+ }
+
+ mRequestMetadataQueue =
+ std::make_unique<RequestMetadataQueue>(kMetadataMsgQueueSize, false /* non blocking */);
+ if (!mRequestMetadataQueue->isValid()) {
+ ALOGE("%s: invalid request fmq", __FUNCTION__);
+ return true;
+ }
+
+ mResultMetadataQueue =
+ std::make_shared<ResultMetadataQueue>(kMetadataMsgQueueSize, false /* non blocking */);
+ if (!mResultMetadataQueue->isValid()) {
+ ALOGE("%s: invalid result fmq", __FUNCTION__);
+ return true;
+ }
+
+ mOutputThread->run();
+ return false;
+}
+
+bool ExternalCameraDeviceSession::isInitFailed() {
+ Mutex::Autolock _l(mLock);
+ if (!mInitialized) {
+ mInitFail = initialize();
+ mInitialized = true;
+ }
+ return mInitFail;
+}
+
+void ExternalCameraDeviceSession::initOutputThread() {
+ // Grab a shared_ptr to 'this' from ndk::SharedRefBase::ref()
+ std::shared_ptr<ExternalCameraDeviceSession> thiz = ref<ExternalCameraDeviceSession>();
+
+ if (mSupportBufMgr) {
+ mBufferRequestThread = std::make_shared<BufferRequestThread>(/*parent=*/thiz, mCallback);
+ mBufferRequestThread->run();
+ }
+ mOutputThread = std::make_shared<OutputThread>(/*parent=*/thiz, mCroppingType,
+ mCameraCharacteristics, mBufferRequestThread);
+}
+
+void ExternalCameraDeviceSession::closeOutputThread() {
+ closeOutputThreadImpl();
+}
+
+void ExternalCameraDeviceSession::closeOutputThreadImpl() {
+ if (mOutputThread != nullptr) {
+ mOutputThread->flush();
+ mOutputThread->requestExitAndWait();
+ mOutputThread.reset();
+ }
+}
+
+Status ExternalCameraDeviceSession::initStatus() const {
+ Mutex::Autolock _l(mLock);
+ Status status = Status::OK;
+ if (mInitFail || mClosed) {
+ ALOGI("%s: session initFailed %d closed %d", __FUNCTION__, mInitFail, mClosed);
+ status = Status::INTERNAL_ERROR;
+ }
+ return status;
+}
+
+ExternalCameraDeviceSession::~ExternalCameraDeviceSession() {
+ if (!isClosed()) {
+ ALOGE("ExternalCameraDeviceSession deleted before close!");
+ close(/*callerIsDtor*/ true);
+ }
+}
+
+ScopedAStatus ExternalCameraDeviceSession::constructDefaultRequestSettings(
+ RequestTemplate in_type, CameraMetadata* _aidl_return) {
+ CameraMetadata emptyMetadata;
+ Status status = initStatus();
+ if (status != Status::OK) {
+ return fromStatus(status);
+ }
+ switch (in_type) {
+ case RequestTemplate::PREVIEW:
+ case RequestTemplate::STILL_CAPTURE:
+ case RequestTemplate::VIDEO_RECORD:
+ case RequestTemplate::VIDEO_SNAPSHOT: {
+ *_aidl_return = mDefaultRequests[in_type];
+ break;
+ }
+ case RequestTemplate::MANUAL:
+ case RequestTemplate::ZERO_SHUTTER_LAG:
+ // Don't support MANUAL, ZSL templates
+ status = Status::ILLEGAL_ARGUMENT;
+ break;
+ default:
+ ALOGE("%s: unknown request template type %d", __FUNCTION__, static_cast<int>(in_type));
+ status = Status::ILLEGAL_ARGUMENT;
+ break;
+ }
+ return fromStatus(status);
+}
+
+ScopedAStatus ExternalCameraDeviceSession::configureStreams(
+ const StreamConfiguration& in_requestedConfiguration,
+ std::vector<HalStream>* _aidl_return) {
+ uint32_t blobBufferSize = 0;
+ _aidl_return->clear();
+ Mutex::Autolock _il(mInterfaceLock);
+
+ Status status =
+ isStreamCombinationSupported(in_requestedConfiguration, mSupportedFormats, mCfg);
+ if (status != Status::OK) {
+ return fromStatus(status);
+ }
+
+ status = initStatus();
+ if (status != Status::OK) {
+ return fromStatus(status);
+ }
+
+ {
+ std::lock_guard<std::mutex> lk(mInflightFramesLock);
+ if (!mInflightFrames.empty()) {
+ ALOGE("%s: trying to configureStreams while there are still %zu inflight frames!",
+ __FUNCTION__, mInflightFrames.size());
+ return fromStatus(Status::INTERNAL_ERROR);
+ }
+ }
+
+ Mutex::Autolock _l(mLock);
+ {
+ Mutex::Autolock _cl(mCbsLock);
+ // Add new streams
+ for (const auto& stream : in_requestedConfiguration.streams) {
+ if (mStreamMap.count(stream.id) == 0) {
+ mStreamMap[stream.id] = stream;
+ mCirculatingBuffers.emplace(stream.id, CirculatingBuffers{});
+ }
+ }
+
+ // Cleanup removed streams
+ for (auto it = mStreamMap.begin(); it != mStreamMap.end();) {
+ int id = it->first;
+ bool found = false;
+ for (const auto& stream : in_requestedConfiguration.streams) {
+ if (id == stream.id) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ // Unmap all buffers of deleted stream
+ cleanupBuffersLocked(id);
+ it = mStreamMap.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ // Now select a V4L2 format to produce all output streams
+ float desiredAr = (mCroppingType == VERTICAL) ? kMaxAspectRatio : kMinAspectRatio;
+ uint32_t maxDim = 0;
+ for (const auto& stream : in_requestedConfiguration.streams) {
+ float aspectRatio = ASPECT_RATIO(stream);
+ ALOGI("%s: request stream %dx%d", __FUNCTION__, stream.width, stream.height);
+ if ((mCroppingType == VERTICAL && aspectRatio < desiredAr) ||
+ (mCroppingType == HORIZONTAL && aspectRatio > desiredAr)) {
+ desiredAr = aspectRatio;
+ }
+
+ // The dimension that's not cropped
+ uint32_t dim = (mCroppingType == VERTICAL) ? stream.width : stream.height;
+ if (dim > maxDim) {
+ maxDim = dim;
+ }
+ }
+
+ // Find the smallest format that matches the desired aspect ratio and is wide/high enough
+ SupportedV4L2Format v4l2Fmt{.width = 0, .height = 0};
+ for (const auto& fmt : mSupportedFormats) {
+ uint32_t dim = (mCroppingType == VERTICAL) ? fmt.width : fmt.height;
+ if (dim >= maxDim) {
+ float aspectRatio = ASPECT_RATIO(fmt);
+ if (isAspectRatioClose(aspectRatio, desiredAr)) {
+ v4l2Fmt = fmt;
+ // since mSupportedFormats is sorted by width then height, the first matching fmt
+ // will be the smallest one with matching aspect ratio
+ break;
+ }
+ }
+ }
+
+ if (v4l2Fmt.width == 0) {
+ // Cannot find exact good aspect ratio candidate, try to find a close one
+ for (const auto& fmt : mSupportedFormats) {
+ uint32_t dim = (mCroppingType == VERTICAL) ? fmt.width : fmt.height;
+ if (dim >= maxDim) {
+ float aspectRatio = ASPECT_RATIO(fmt);
+ if ((mCroppingType == VERTICAL && aspectRatio < desiredAr) ||
+ (mCroppingType == HORIZONTAL && aspectRatio > desiredAr)) {
+ v4l2Fmt = fmt;
+ break;
+ }
+ }
+ }
+ }
+
+ if (v4l2Fmt.width == 0) {
+ ALOGE("%s: unable to find a resolution matching (%s at least %d, aspect ratio %f)",
+ __FUNCTION__, (mCroppingType == VERTICAL) ? "width" : "height", maxDim, desiredAr);
+ return fromStatus(Status::ILLEGAL_ARGUMENT);
+ }
+
+ if (configureV4l2StreamLocked(v4l2Fmt) != 0) {
+ ALOGE("V4L configuration failed!, format:%c%c%c%c, w %d, h %d", v4l2Fmt.fourcc & 0xFF,
+ (v4l2Fmt.fourcc >> 8) & 0xFF, (v4l2Fmt.fourcc >> 16) & 0xFF,
+ (v4l2Fmt.fourcc >> 24) & 0xFF, v4l2Fmt.width, v4l2Fmt.height);
+ return fromStatus(Status::INTERNAL_ERROR);
+ }
+
+ Size v4lSize = {v4l2Fmt.width, v4l2Fmt.height};
+ 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{entry.data.i32[i], entry.data.i32[i + 1]};
+ if (sz.width * sz.height > thumbSize.width * thumbSize.height) {
+ thumbSize = sz;
+ }
+ }
+
+ if (thumbSize.width * thumbSize.height == 0) {
+ ALOGE("%s: non-zero thumbnail size not available", __FUNCTION__);
+ return fromStatus(Status::INTERNAL_ERROR);
+ }
+
+ mBlobBufferSize = blobBufferSize;
+ status = mOutputThread->allocateIntermediateBuffers(
+ v4lSize, mMaxThumbResolution, in_requestedConfiguration.streams, blobBufferSize);
+ if (status != Status::OK) {
+ ALOGE("%s: allocating intermediate buffers failed!", __FUNCTION__);
+ return fromStatus(status);
+ }
+
+ std::vector<HalStream>& out = *_aidl_return;
+ out.resize(in_requestedConfiguration.streams.size());
+ for (size_t i = 0; i < in_requestedConfiguration.streams.size(); i++) {
+ out[i].overrideDataSpace = in_requestedConfiguration.streams[i].dataSpace;
+ out[i].id = in_requestedConfiguration.streams[i].id;
+ // TODO: double check should we add those CAMERA flags
+ mStreamMap[in_requestedConfiguration.streams[i].id].usage = out[i].producerUsage =
+ static_cast<BufferUsage>(((int64_t)in_requestedConfiguration.streams[i].usage) |
+ ((int64_t)BufferUsage::CPU_WRITE_OFTEN) |
+ ((int64_t)BufferUsage::CAMERA_OUTPUT));
+ out[i].consumerUsage = static_cast<BufferUsage>(0);
+ out[i].maxBuffers = static_cast<int32_t>(mV4L2BufferCount);
+
+ switch (in_requestedConfiguration.streams[i].format) {
+ case PixelFormat::BLOB:
+ case PixelFormat::YCBCR_420_888:
+ case PixelFormat::YV12: // Used by SurfaceTexture
+ case PixelFormat::Y16:
+ // No override
+ out[i].overrideFormat = in_requestedConfiguration.streams[i].format;
+ break;
+ case PixelFormat::IMPLEMENTATION_DEFINED:
+ // Implementation Defined
+ // This should look at the Stream's dataspace flag to determine the format or leave
+ // it as is if the rest of the system knows how to handle a private format. To keep
+ // this HAL generic, this is being overridden to YUV420
+ out[i].overrideFormat = PixelFormat::YCBCR_420_888;
+ // Save overridden format in mStreamMap
+ mStreamMap[in_requestedConfiguration.streams[i].id].format = out[i].overrideFormat;
+ break;
+ default:
+ ALOGE("%s: unsupported format 0x%x", __FUNCTION__,
+ in_requestedConfiguration.streams[i].format);
+ return fromStatus(Status::ILLEGAL_ARGUMENT);
+ }
+ }
+
+ mFirstRequest = true;
+ mLastStreamConfigCounter = in_requestedConfiguration.streamConfigCounter;
+ return fromStatus(Status::OK);
+}
+
+ScopedAStatus ExternalCameraDeviceSession::flush() {
+ ATRACE_CALL();
+ Mutex::Autolock _il(mInterfaceLock);
+ Status status = initStatus();
+ if (status != Status::OK) {
+ return fromStatus(status);
+ }
+ mOutputThread->flush();
+ return fromStatus(Status::OK);
+}
+
+ScopedAStatus ExternalCameraDeviceSession::getCaptureRequestMetadataQueue(
+ MQDescriptor<int8_t, SynchronizedReadWrite>* _aidl_return) {
+ Mutex::Autolock _il(mInterfaceLock);
+ *_aidl_return = mRequestMetadataQueue->dupeDesc();
+ return fromStatus(Status::OK);
+}
+
+ScopedAStatus ExternalCameraDeviceSession::getCaptureResultMetadataQueue(
+ MQDescriptor<int8_t, SynchronizedReadWrite>* _aidl_return) {
+ Mutex::Autolock _il(mInterfaceLock);
+ *_aidl_return = mResultMetadataQueue->dupeDesc();
+ return fromStatus(Status::OK);
+}
+
+ScopedAStatus ExternalCameraDeviceSession::isReconfigurationRequired(
+ const CameraMetadata& in_oldSessionParams, const CameraMetadata& in_newSessionParams,
+ bool* _aidl_return) {
+ // reconfiguration required if there is any change in the session params
+ *_aidl_return = in_oldSessionParams != in_newSessionParams;
+ return fromStatus(Status::OK);
+}
+
+ScopedAStatus ExternalCameraDeviceSession::processCaptureRequest(
+ const std::vector<CaptureRequest>& in_requests,
+ const std::vector<BufferCache>& in_cachesToRemove, int32_t* _aidl_return) {
+ Mutex::Autolock _il(mInterfaceLock);
+ updateBufferCaches(in_cachesToRemove);
+
+ int32_t& numRequestProcessed = *_aidl_return;
+ numRequestProcessed = 0;
+ Status s = Status::OK;
+ for (size_t i = 0; i < in_requests.size(); i++, numRequestProcessed++) {
+ s = processOneCaptureRequest(in_requests[i]);
+ if (s != Status::OK) {
+ break;
+ }
+ }
+
+ return fromStatus(s);
+}
+
+Status ExternalCameraDeviceSession::processOneCaptureRequest(const CaptureRequest& request) {
+ ATRACE_CALL();
+ Status status = initStatus();
+ if (status != Status::OK) {
+ return status;
+ }
+
+ if (request.inputBuffer.streamId != -1) {
+ ALOGE("%s: external camera does not support reprocessing!", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ Mutex::Autolock _l(mLock);
+ if (!mV4l2Streaming) {
+ ALOGE("%s: cannot process request in streamOff state!", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+
+ const camera_metadata_t* rawSettings = nullptr;
+ bool converted;
+ CameraMetadata settingsFmq; // settings from FMQ
+
+ if (request.fmqSettingsSize > 0) {
+ // non-blocking read; client must write metadata before calling
+ // processOneCaptureRequest
+ settingsFmq.metadata.resize(request.fmqSettingsSize);
+ bool read = mRequestMetadataQueue->read(
+ reinterpret_cast<int8_t*>(settingsFmq.metadata.data()), request.fmqSettingsSize);
+ if (read) {
+ converted = convertFromAidl(settingsFmq, &rawSettings);
+ } else {
+ ALOGE("%s: capture request settings metadata couldn't be read from fmq!", __FUNCTION__);
+ converted = false;
+ }
+ } else {
+ converted = convertFromAidl(request.settings, &rawSettings);
+ }
+
+ if (converted && rawSettings != nullptr) {
+ mLatestReqSetting = rawSettings;
+ }
+
+ if (!converted) {
+ ALOGE("%s: capture request settings metadata is corrupt!", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ if (mFirstRequest && rawSettings == nullptr) {
+ ALOGE("%s: capture request settings must not be null for first request!", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ std::vector<buffer_handle_t*> allBufPtrs;
+ std::vector<int> allFences;
+ size_t numOutputBufs = request.outputBuffers.size();
+
+ if (numOutputBufs == 0) {
+ ALOGE("%s: capture request must have at least one output buffer!", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ camera_metadata_entry fpsRange = mLatestReqSetting.find(ANDROID_CONTROL_AE_TARGET_FPS_RANGE);
+ if (fpsRange.count == 2) {
+ double requestFpsMax = fpsRange.data.i32[1];
+ double closestFps = 0.0;
+ double fpsError = 1000.0;
+ bool fpsSupported = false;
+ for (const auto& fr : mV4l2StreamingFmt.frameRates) {
+ double f = fr.getFramesPerSecond();
+ if (std::fabs(requestFpsMax - f) < 1.0) {
+ fpsSupported = true;
+ break;
+ }
+ if (std::fabs(requestFpsMax - f) < fpsError) {
+ fpsError = std::fabs(requestFpsMax - f);
+ closestFps = f;
+ }
+ }
+ if (!fpsSupported) {
+ /* This can happen in a few scenarios:
+ * 1. The application is sending an FPS range not supported by the configured outputs.
+ * 2. The application is sending a valid FPS range for all configured outputs, but
+ * the selected V4L2 size can only run at slower speed. This should be very rare
+ * though: for this to happen a sensor needs to support at least 3 different aspect
+ * ratio outputs, and when (at least) two outputs are both not the main aspect ratio
+ * of the webcam, a third size that's larger might be picked and runs into this
+ * issue.
+ */
+ ALOGW("%s: cannot reach fps %d! Will do %f instead", __FUNCTION__, fpsRange.data.i32[1],
+ closestFps);
+ requestFpsMax = closestFps;
+ }
+
+ if (requestFpsMax != mV4l2StreamingFps) {
+ {
+ std::unique_lock<std::mutex> lk(mV4l2BufferLock);
+ while (mNumDequeuedV4l2Buffers != 0) {
+ // Wait until pipeline is idle before reconfigure stream
+ int waitRet = waitForV4L2BufferReturnLocked(lk);
+ if (waitRet != 0) {
+ ALOGE("%s: wait for pipeline idle failed!", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+ }
+ }
+ configureV4l2StreamLocked(mV4l2StreamingFmt, requestFpsMax);
+ }
+ }
+
+ status = importRequestLocked(request, allBufPtrs, allFences);
+ if (status != Status::OK) {
+ return status;
+ }
+
+ nsecs_t shutterTs = 0;
+ std::unique_ptr<V4L2Frame> frameIn = dequeueV4l2FrameLocked(&shutterTs);
+ if (frameIn == nullptr) {
+ ALOGE("%s: V4L2 deque frame failed!", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+
+ std::shared_ptr<HalRequest> halReq = std::make_shared<HalRequest>();
+ halReq->frameNumber = request.frameNumber;
+ halReq->setting = mLatestReqSetting;
+ halReq->frameIn = std::move(frameIn);
+ halReq->shutterTs = shutterTs;
+ halReq->buffers.resize(numOutputBufs);
+ for (size_t i = 0; i < numOutputBufs; i++) {
+ HalStreamBuffer& halBuf = halReq->buffers[i];
+ int streamId = halBuf.streamId = request.outputBuffers[i].streamId;
+ halBuf.bufferId = request.outputBuffers[i].bufferId;
+ const Stream& stream = mStreamMap[streamId];
+ halBuf.width = stream.width;
+ halBuf.height = stream.height;
+ halBuf.format = stream.format;
+ halBuf.usage = stream.usage;
+ halBuf.bufPtr = allBufPtrs[i];
+ halBuf.acquireFence = allFences[i];
+ halBuf.fenceTimeout = false;
+ }
+ {
+ std::lock_guard<std::mutex> lk(mInflightFramesLock);
+ mInflightFrames.insert(halReq->frameNumber);
+ }
+ // Send request to OutputThread for the rest of processing
+ mOutputThread->submitRequest(halReq);
+ mFirstRequest = false;
+ return Status::OK;
+}
+
+ScopedAStatus ExternalCameraDeviceSession::signalStreamFlush(
+ const std::vector<int32_t>& /*in_streamIds*/, int32_t in_streamConfigCounter) {
+ {
+ Mutex::Autolock _l(mLock);
+ if (in_streamConfigCounter < mLastStreamConfigCounter) {
+ // stale call. new streams have been configured since this call was issued.
+ // Do nothing.
+ return fromStatus(Status::OK);
+ }
+ }
+
+ // TODO: implement if needed.
+ return fromStatus(Status::OK);
+}
+
+ScopedAStatus ExternalCameraDeviceSession::switchToOffline(
+ const std::vector<int32_t>& in_streamsToKeep,
+ CameraOfflineSessionInfo* out_offlineSessionInfo,
+ std::shared_ptr<ICameraOfflineSession>* _aidl_return) {
+ std::vector<NotifyMsg> msgs;
+ std::vector<CaptureResult> results;
+ CameraOfflineSessionInfo info;
+ std::shared_ptr<ICameraOfflineSession> session;
+ Status st = switchToOffline(in_streamsToKeep, &msgs, &results, &info, &session);
+
+ mCallback->notify(msgs);
+ invokeProcessCaptureResultCallback(results, /* tryWriteFmq= */ true);
+ freeReleaseFences(results);
+
+ // setup return values
+ *out_offlineSessionInfo = info;
+ *_aidl_return = session;
+ return fromStatus(st);
+}
+
+Status ExternalCameraDeviceSession::switchToOffline(
+ const std::vector<int32_t>& offlineStreams, std::vector<NotifyMsg>* msgs,
+ std::vector<CaptureResult>* results, CameraOfflineSessionInfo* info,
+ std::shared_ptr<ICameraOfflineSession>* session) {
+ ATRACE_CALL();
+ if (offlineStreams.size() > 1) {
+ ALOGE("%s: more than one offline stream is not supported", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ if (info == nullptr || results == nullptr || info == nullptr || session == nullptr) {
+ ALOGE("%s, output arguments (%p, %p, %p, %p) much not be null", __FUNCTION__, msgs, results,
+ info, session);
+ }
+
+ 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<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;
+ aidl::android::hardware::camera::device::ShutterMsg shutterMsg = {
+ .frameNumber = static_cast<int32_t>(halReq->frameNumber),
+ .timestamp = halReq->shutterTs};
+ shutter.set<NotifyMsg::Tag::shutter>(shutterMsg);
+ msgs->push_back(shutter);
+
+ std::vector<HalStreamBuffer> offlineBuffers;
+ for (const auto& buffer : halReq->buffers) {
+ bool dropBuffer = true;
+ for (auto offlineStreamId : offlineStreams) {
+ if (buffer.streamId == offlineStreamId) {
+ dropBuffer = false;
+ break;
+ }
+ }
+ if (dropBuffer) {
+ aidl::android::hardware::camera::device::ErrorMsg errorMsg = {
+ .frameNumber = static_cast<int32_t>(halReq->frameNumber),
+ .errorStreamId = buffer.streamId,
+ .errorCode = ErrorCode::ERROR_BUFFER};
+
+ NotifyMsg error;
+ error.set<NotifyMsg::Tag::error>(errorMsg);
+ msgs->push_back(error);
+
+ results->push_back({
+ .frameNumber = static_cast<int32_t>(halReq->frameNumber),
+ .outputBuffers = {},
+ .inputBuffer = {.streamId = -1},
+ .partialResult = 0, // buffer only result
+ });
+
+ CaptureResult& result = results->back();
+ result.outputBuffers.resize(1);
+ StreamBuffer& outputBuffer = result.outputBuffers[0];
+ outputBuffer.streamId = buffer.streamId;
+ outputBuffer.bufferId = buffer.bufferId;
+ outputBuffer.status = BufferStatus::ERROR;
+ if (buffer.acquireFence >= 0) {
+ native_handle_t* handle = native_handle_create(/*numFds*/ 1, /*numInts*/ 0);
+ handle->data[0] = buffer.acquireFence;
+ outputBuffer.releaseFence = android::makeToAidl(handle);
+ }
+ } 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());
+ size_t i = 0;
+ for (auto& v4lReq : halReqs) {
+ offlineReqs[i] = std::make_shared<HalRequest>();
+ offlineReqs[i]->frameNumber = v4lReq->frameNumber;
+ offlineReqs[i]->setting = v4lReq->setting;
+ offlineReqs[i]->shutterTs = v4lReq->shutterTs;
+ offlineReqs[i]->buffers = v4lReq->buffers;
+ std::shared_ptr<V4L2Frame> v4l2Frame(static_cast<V4L2Frame*>(v4lReq->frameIn.get()));
+ offlineReqs[i]->frameIn = std::make_shared<AllocatedV4L2Frame>(v4l2Frame);
+ i++;
+ // enqueue V4L2 frame
+ enqueueV4l2Frame(v4l2Frame);
+ }
+
+ // Collect buffer caches/streams
+ std::vector<Stream> streamInfos(offlineStreams.size());
+ std::map<int, CirculatingBuffers> circulatingBuffers;
+ {
+ Mutex::Autolock _cbsl(mCbsLock);
+ for (auto streamId : offlineStreams) {
+ circulatingBuffers[streamId] = mCirculatingBuffers.at(streamId);
+ mCirculatingBuffers.erase(streamId);
+ streamInfos.push_back(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;
+ }
+
+ std::shared_ptr<ExternalCameraOfflineSession> sessionImpl =
+ ndk::SharedRefBase::make<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 _cbsl(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.empty()) {
+ *session = sessionImpl;
+ } else {
+ *session = nullptr;
+ }
+
+ return Status::OK;
+}
+
+#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 ExternalCameraDeviceSession::initDefaultRequests() {
+ common::V1_0::helper::CameraMetadata md;
+
+ const uint8_t aberrationMode = ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF;
+ UPDATE(md, ANDROID_COLOR_CORRECTION_ABERRATION_MODE, &aberrationMode, 1);
+
+ const int32_t exposureCompensation = 0;
+ UPDATE(md, ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, &exposureCompensation, 1);
+
+ const uint8_t videoStabilizationMode = ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF;
+ UPDATE(md, ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, &videoStabilizationMode, 1);
+
+ const uint8_t awbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
+ UPDATE(md, ANDROID_CONTROL_AWB_MODE, &awbMode, 1);
+
+ const uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
+ UPDATE(md, ANDROID_CONTROL_AE_MODE, &aeMode, 1);
+
+ const uint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
+ UPDATE(md, ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, &aePrecaptureTrigger, 1);
+
+ const uint8_t afMode = ANDROID_CONTROL_AF_MODE_AUTO;
+ UPDATE(md, ANDROID_CONTROL_AF_MODE, &afMode, 1);
+
+ const uint8_t afTrigger = ANDROID_CONTROL_AF_TRIGGER_IDLE;
+ UPDATE(md, ANDROID_CONTROL_AF_TRIGGER, &afTrigger, 1);
+
+ const uint8_t sceneMode = ANDROID_CONTROL_SCENE_MODE_DISABLED;
+ UPDATE(md, ANDROID_CONTROL_SCENE_MODE, &sceneMode, 1);
+
+ const uint8_t effectMode = ANDROID_CONTROL_EFFECT_MODE_OFF;
+ UPDATE(md, ANDROID_CONTROL_EFFECT_MODE, &effectMode, 1);
+
+ const uint8_t flashMode = ANDROID_FLASH_MODE_OFF;
+ UPDATE(md, ANDROID_FLASH_MODE, &flashMode, 1);
+
+ const int32_t thumbnailSize[] = {240, 180};
+ UPDATE(md, ANDROID_JPEG_THUMBNAIL_SIZE, thumbnailSize, 2);
+
+ const uint8_t jpegQuality = 90;
+ UPDATE(md, ANDROID_JPEG_QUALITY, &jpegQuality, 1);
+ UPDATE(md, ANDROID_JPEG_THUMBNAIL_QUALITY, &jpegQuality, 1);
+
+ const int32_t jpegOrientation = 0;
+ UPDATE(md, ANDROID_JPEG_ORIENTATION, &jpegOrientation, 1);
+
+ const uint8_t oisMode = ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF;
+ UPDATE(md, ANDROID_LENS_OPTICAL_STABILIZATION_MODE, &oisMode, 1);
+
+ const uint8_t nrMode = ANDROID_NOISE_REDUCTION_MODE_OFF;
+ UPDATE(md, ANDROID_NOISE_REDUCTION_MODE, &nrMode, 1);
+
+ const int32_t testPatternModes = ANDROID_SENSOR_TEST_PATTERN_MODE_OFF;
+ UPDATE(md, ANDROID_SENSOR_TEST_PATTERN_MODE, &testPatternModes, 1);
+
+ const uint8_t fdMode = ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+ UPDATE(md, ANDROID_STATISTICS_FACE_DETECT_MODE, &fdMode, 1);
+
+ const uint8_t hotpixelMode = ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF;
+ UPDATE(md, ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE, &hotpixelMode, 1);
+
+ bool support30Fps = false;
+ int32_t maxFps = std::numeric_limits<int32_t>::min();
+ for (const auto& supportedFormat : mSupportedFormats) {
+ for (const auto& fr : supportedFormat.frameRates) {
+ int32_t framerateInt = static_cast<int32_t>(fr.getFramesPerSecond());
+ if (maxFps < framerateInt) {
+ maxFps = framerateInt;
+ }
+ if (framerateInt == 30) {
+ support30Fps = true;
+ break;
+ }
+ }
+ if (support30Fps) {
+ break;
+ }
+ }
+
+ int32_t defaultFramerate = support30Fps ? 30 : maxFps;
+ int32_t defaultFpsRange[] = {defaultFramerate / 2, defaultFramerate};
+ UPDATE(md, ANDROID_CONTROL_AE_TARGET_FPS_RANGE, defaultFpsRange, ARRAY_SIZE(defaultFpsRange));
+
+ uint8_t antibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
+ UPDATE(md, ANDROID_CONTROL_AE_ANTIBANDING_MODE, &antibandingMode, 1);
+
+ const uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
+ UPDATE(md, ANDROID_CONTROL_MODE, &controlMode, 1);
+
+ for (const auto& type : ndk::enum_range<RequestTemplate>()) {
+ common::V1_0::helper::CameraMetadata mdCopy = md;
+ uint8_t intent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
+ switch (type) {
+ case RequestTemplate::PREVIEW:
+ intent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
+ break;
+ case RequestTemplate::STILL_CAPTURE:
+ intent = ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
+ break;
+ case RequestTemplate::VIDEO_RECORD:
+ intent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
+ break;
+ case RequestTemplate::VIDEO_SNAPSHOT:
+ intent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
+ break;
+ default:
+ ALOGV("%s: unsupported RequestTemplate type %d", __FUNCTION__, type);
+ continue;
+ }
+ UPDATE(mdCopy, ANDROID_CONTROL_CAPTURE_INTENT, &intent, 1);
+ camera_metadata_t* mdPtr = mdCopy.release();
+ uint8_t* rawMd = reinterpret_cast<uint8_t*>(mdPtr);
+ CameraMetadata aidlMd;
+ aidlMd.metadata.assign(rawMd, rawMd + get_camera_metadata_size(mdPtr));
+ mDefaultRequests[type] = aidlMd;
+ free_camera_metadata(mdPtr);
+ }
+ return OK;
+}
+
+status_t ExternalCameraDeviceSession::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 =
+ mCameraCharacteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+
+ return fillCaptureResultCommon(md, timestamp, activeArraySize);
+}
+
+int ExternalCameraDeviceSession::configureV4l2StreamLocked(const SupportedV4L2Format& v4l2Fmt,
+ double requestFps) {
+ ATRACE_CALL();
+ int ret = v4l2StreamOffLocked();
+ if (ret != OK) {
+ ALOGE("%s: stop v4l2 streaming failed: ret %d", __FUNCTION__, ret);
+ return ret;
+ }
+
+ // VIDIOC_S_FMT w/h/fmt
+ v4l2_format fmt;
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.width = v4l2Fmt.width;
+ fmt.fmt.pix.height = v4l2Fmt.height;
+ fmt.fmt.pix.pixelformat = v4l2Fmt.fourcc;
+
+ {
+ int numAttempt = 0;
+ do {
+ ret = TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_S_FMT, &fmt));
+ if (numAttempt == MAX_RETRY) {
+ break;
+ }
+ numAttempt++;
+ if (ret < 0) {
+ ALOGW("%s: VIDIOC_S_FMT failed, wait 33ms and try again", __FUNCTION__);
+ usleep(IOCTL_RETRY_SLEEP_US); // sleep and try again
+ }
+ } while (ret < 0);
+ if (ret < 0) {
+ ALOGE("%s: S_FMT ioctl failed: %s", __FUNCTION__, strerror(errno));
+ return -errno;
+ }
+ }
+
+ if (v4l2Fmt.width != fmt.fmt.pix.width || v4l2Fmt.height != fmt.fmt.pix.height ||
+ v4l2Fmt.fourcc != fmt.fmt.pix.pixelformat) {
+ ALOGE("%s: S_FMT expect %c%c%c%c %dx%d, got %c%c%c%c %dx%d instead!", __FUNCTION__,
+ v4l2Fmt.fourcc & 0xFF, (v4l2Fmt.fourcc >> 8) & 0xFF, (v4l2Fmt.fourcc >> 16) & 0xFF,
+ (v4l2Fmt.fourcc >> 24) & 0xFF, v4l2Fmt.width, v4l2Fmt.height,
+ fmt.fmt.pix.pixelformat & 0xFF, (fmt.fmt.pix.pixelformat >> 8) & 0xFF,
+ (fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF,
+ fmt.fmt.pix.width, fmt.fmt.pix.height);
+ return -EINVAL;
+ }
+
+ uint32_t bufferSize = fmt.fmt.pix.sizeimage;
+ ALOGI("%s: V4L2 buffer size is %d", __FUNCTION__, bufferSize);
+ uint32_t expectedMaxBufferSize = kMaxBytesPerPixel * fmt.fmt.pix.width * fmt.fmt.pix.height;
+ if ((bufferSize == 0) || (bufferSize > expectedMaxBufferSize)) {
+ ALOGE("%s: V4L2 buffer size: %u looks invalid. Expected maximum size: %u", __FUNCTION__,
+ bufferSize, expectedMaxBufferSize);
+ return -EINVAL;
+ }
+ mMaxV4L2BufferSize = bufferSize;
+
+ const double kDefaultFps = 30.0;
+ double fps = std::numeric_limits<double>::max();
+ if (requestFps != 0.0) {
+ fps = requestFps;
+ } else {
+ double maxFps = -1.0;
+ // Try to pick the slowest fps that is at least 30
+ for (const auto& fr : v4l2Fmt.frameRates) {
+ double f = fr.getFramesPerSecond();
+ if (maxFps < f) {
+ maxFps = f;
+ }
+ if (f >= kDefaultFps && f < fps) {
+ fps = f;
+ }
+ }
+ // No fps > 30 found, use the highest fps available within supported formats.
+ if (fps == std::numeric_limits<double>::max()) {
+ fps = maxFps;
+ }
+ }
+
+ int fpsRet = setV4l2FpsLocked(fps);
+ if (fpsRet != 0 && fpsRet != -EINVAL) {
+ ALOGE("%s: set fps failed: %s", __FUNCTION__, strerror(fpsRet));
+ return fpsRet;
+ }
+
+ uint32_t v4lBufferCount = (fps >= kDefaultFps) ? mCfg.numVideoBuffers : mCfg.numStillBuffers;
+
+ // VIDIOC_REQBUFS: create buffers
+ v4l2_requestbuffers req_buffers{};
+ req_buffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req_buffers.memory = V4L2_MEMORY_MMAP;
+ req_buffers.count = v4lBufferCount;
+ if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_REQBUFS, &req_buffers)) < 0) {
+ ALOGE("%s: VIDIOC_REQBUFS failed: %s", __FUNCTION__, strerror(errno));
+ return -errno;
+ }
+
+ // Driver can indeed return more buffer if it needs more to operate
+ if (req_buffers.count < v4lBufferCount) {
+ ALOGE("%s: VIDIOC_REQBUFS expected %d buffers, got %d instead", __FUNCTION__,
+ v4lBufferCount, req_buffers.count);
+ return NO_MEMORY;
+ }
+
+ // VIDIOC_QUERYBUF: get buffer offset in the V4L2 fd
+ // VIDIOC_QBUF: send buffer to driver
+ mV4L2BufferCount = req_buffers.count;
+ for (uint32_t i = 0; i < req_buffers.count; i++) {
+ v4l2_buffer buffer = {
+ .index = i, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .memory = V4L2_MEMORY_MMAP};
+
+ if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_QUERYBUF, &buffer)) < 0) {
+ ALOGE("%s: QUERYBUF %d failed: %s", __FUNCTION__, i, strerror(errno));
+ return -errno;
+ }
+
+ if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_QBUF, &buffer)) < 0) {
+ ALOGE("%s: QBUF %d failed: %s", __FUNCTION__, i, strerror(errno));
+ return -errno;
+ }
+ }
+
+ {
+ // VIDIOC_STREAMON: start streaming
+ v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ int numAttempt = 0;
+ do {
+ ret = TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_STREAMON, &capture_type));
+ if (numAttempt == MAX_RETRY) {
+ break;
+ }
+ if (ret < 0) {
+ ALOGW("%s: VIDIOC_STREAMON failed, wait 33ms and try again", __FUNCTION__);
+ usleep(IOCTL_RETRY_SLEEP_US); // sleep 100 ms and try again
+ }
+ } while (ret < 0);
+
+ if (ret < 0) {
+ ALOGE("%s: VIDIOC_STREAMON ioctl failed: %s", __FUNCTION__, strerror(errno));
+ return -errno;
+ }
+ }
+
+ // Swallow first few frames after streamOn to account for bad frames from some devices
+ for (int i = 0; i < kBadFramesAfterStreamOn; i++) {
+ v4l2_buffer buffer{};
+ buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buffer.memory = V4L2_MEMORY_MMAP;
+ if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_DQBUF, &buffer)) < 0) {
+ ALOGE("%s: DQBUF fails: %s", __FUNCTION__, strerror(errno));
+ return -errno;
+ }
+
+ if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_QBUF, &buffer)) < 0) {
+ ALOGE("%s: QBUF index %d fails: %s", __FUNCTION__, buffer.index, strerror(errno));
+ return -errno;
+ }
+ }
+
+ ALOGI("%s: start V4L2 streaming %dx%d@%ffps", __FUNCTION__, v4l2Fmt.width, v4l2Fmt.height, fps);
+ mV4l2StreamingFmt = v4l2Fmt;
+ mV4l2Streaming = true;
+ return OK;
+}
+
+std::unique_ptr<V4L2Frame> ExternalCameraDeviceSession::dequeueV4l2FrameLocked(nsecs_t* shutterTs) {
+ ATRACE_CALL();
+ std::unique_ptr<V4L2Frame> ret = nullptr;
+ if (shutterTs == nullptr) {
+ ALOGE("%s: shutterTs must not be null!", __FUNCTION__);
+ return ret;
+ }
+
+ {
+ std::unique_lock<std::mutex> lk(mV4l2BufferLock);
+ if (mNumDequeuedV4l2Buffers == mV4L2BufferCount) {
+ int waitRet = waitForV4L2BufferReturnLocked(lk);
+ if (waitRet != 0) {
+ return ret;
+ }
+ }
+ }
+
+ ATRACE_BEGIN("VIDIOC_DQBUF");
+ v4l2_buffer buffer{};
+ buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buffer.memory = V4L2_MEMORY_MMAP;
+ if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_DQBUF, &buffer)) < 0) {
+ ALOGE("%s: DQBUF fails: %s", __FUNCTION__, strerror(errno));
+ return ret;
+ }
+ ATRACE_END();
+
+ if (buffer.index >= mV4L2BufferCount) {
+ ALOGE("%s: Invalid buffer id: %d", __FUNCTION__, buffer.index);
+ return ret;
+ }
+
+ if (buffer.flags & V4L2_BUF_FLAG_ERROR) {
+ ALOGE("%s: v4l2 buf error! buf flag 0x%x", __FUNCTION__, buffer.flags);
+ // TODO: try to dequeue again
+ }
+
+ if (buffer.bytesused > mMaxV4L2BufferSize) {
+ ALOGE("%s: v4l2 buffer bytes used: %u maximum %u", __FUNCTION__, buffer.bytesused,
+ mMaxV4L2BufferSize);
+ return ret;
+ }
+
+ if (buffer.flags & V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) {
+ // Ideally we should also check for V4L2_BUF_FLAG_TSTAMP_SRC_SOE, but
+ // even V4L2_BUF_FLAG_TSTAMP_SRC_EOF is better than capture a timestamp now
+ *shutterTs = static_cast<nsecs_t>(buffer.timestamp.tv_sec) * 1000000000LL +
+ buffer.timestamp.tv_usec * 1000LL;
+ } else {
+ *shutterTs = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+
+ {
+ std::lock_guard<std::mutex> lk(mV4l2BufferLock);
+ mNumDequeuedV4l2Buffers++;
+ }
+
+ return std::make_unique<V4L2Frame>(mV4l2StreamingFmt.width, mV4l2StreamingFmt.height,
+ mV4l2StreamingFmt.fourcc, buffer.index, mV4l2Fd.get(),
+ buffer.bytesused, buffer.m.offset);
+}
+
+void ExternalCameraDeviceSession::enqueueV4l2Frame(const std::shared_ptr<V4L2Frame>& frame) {
+ ATRACE_CALL();
+ frame->unmap();
+ ATRACE_BEGIN("VIDIOC_QBUF");
+ v4l2_buffer buffer{};
+ buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buffer.memory = V4L2_MEMORY_MMAP;
+ buffer.index = frame->mBufferIndex;
+ if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_QBUF, &buffer)) < 0) {
+ ALOGE("%s: QBUF index %d fails: %s", __FUNCTION__, frame->mBufferIndex, strerror(errno));
+ return;
+ }
+ ATRACE_END();
+
+ {
+ std::lock_guard<std::mutex> lk(mV4l2BufferLock);
+ mNumDequeuedV4l2Buffers--;
+ }
+ mV4L2BufferReturned.notify_one();
+}
+
+bool ExternalCameraDeviceSession::isSupported(
+ const Stream& stream, const std::vector<SupportedV4L2Format>& supportedFormats,
+ const ExternalCameraConfig& devCfg) {
+ Dataspace ds = stream.dataSpace;
+ PixelFormat fmt = stream.format;
+ uint32_t width = stream.width;
+ uint32_t height = stream.height;
+ // TODO: check usage flags
+
+ if (stream.streamType != StreamType::OUTPUT) {
+ ALOGE("%s: does not support non-output stream type", __FUNCTION__);
+ return false;
+ }
+
+ if (stream.rotation != StreamRotation::ROTATION_0) {
+ ALOGE("%s: does not support stream rotation", __FUNCTION__);
+ return false;
+ }
+
+ switch (fmt) {
+ case PixelFormat::BLOB:
+ if (ds != Dataspace::JFIF) {
+ ALOGI("%s: BLOB format does not support dataSpace %x", __FUNCTION__, ds);
+ return false;
+ }
+ break;
+ case PixelFormat::IMPLEMENTATION_DEFINED:
+ case PixelFormat::YCBCR_420_888:
+ case PixelFormat::YV12:
+ // TODO: check what dataspace we can support here.
+ // intentional no-ops.
+ break;
+ case PixelFormat::Y16:
+ if (!devCfg.depthEnabled) {
+ ALOGI("%s: Depth is not Enabled", __FUNCTION__);
+ return false;
+ }
+ if (!(static_cast<int32_t>(ds) & static_cast<int32_t>(Dataspace::DEPTH))) {
+ ALOGI("%s: Y16 supports only dataSpace DEPTH", __FUNCTION__);
+ return false;
+ }
+ break;
+ default:
+ ALOGI("%s: does not support format %x", __FUNCTION__, fmt);
+ return false;
+ }
+
+ // Assume we can convert any V4L2 format to any of supported output format for now, i.e.
+ // ignoring v4l2Fmt.fourcc for now. Might need more subtle check if we support more v4l format
+ // in the futrue.
+ for (const auto& v4l2Fmt : supportedFormats) {
+ if (width == v4l2Fmt.width && height == v4l2Fmt.height) {
+ return true;
+ }
+ }
+ ALOGI("%s: resolution %dx%d is not supported", __FUNCTION__, width, height);
+ return false;
+}
+
+Status ExternalCameraDeviceSession::importRequestLocked(const CaptureRequest& request,
+ std::vector<buffer_handle_t*>& allBufPtrs,
+ std::vector<int>& allFences) {
+ return importRequestLockedImpl(request, allBufPtrs, allFences);
+}
+
+Status ExternalCameraDeviceSession::importRequestLockedImpl(
+ const CaptureRequest& request, std::vector<buffer_handle_t*>& allBufPtrs,
+ std::vector<int>& allFences) {
+ size_t numOutputBufs = request.outputBuffers.size();
+ size_t numBufs = numOutputBufs;
+ // Validate all I/O buffers
+ std::vector<buffer_handle_t> allBufs;
+ std::vector<uint64_t> allBufIds;
+ allBufs.resize(numBufs);
+ allBufIds.resize(numBufs);
+ allBufPtrs.resize(numBufs);
+ allFences.resize(numBufs);
+ std::vector<int32_t> streamIds(numBufs);
+
+ for (size_t i = 0; i < numOutputBufs; i++) {
+ allBufs[i] = ::android::makeFromAidl(request.outputBuffers[i].buffer);
+ allBufIds[i] = request.outputBuffers[i].bufferId;
+ allBufPtrs[i] = &allBufs[i];
+ streamIds[i] = request.outputBuffers[i].streamId;
+ }
+
+ {
+ Mutex::Autolock _l(mCbsLock);
+ for (size_t i = 0; i < numBufs; i++) {
+ Status st = importBufferLocked(streamIds[i], allBufIds[i], allBufs[i], &allBufPtrs[i]);
+ if (st != Status::OK) {
+ // Detailed error logs printed in importBuffer
+ return st;
+ }
+ }
+ }
+
+ // All buffers are imported. Now validate output buffer acquire fences
+ for (size_t i = 0; i < numOutputBufs; i++) {
+ if (!sHandleImporter.importFence(
+ ::android::makeFromAidl(request.outputBuffers[i].acquireFence), allFences[i])) {
+ ALOGE("%s: output buffer %zu acquire fence is invalid", __FUNCTION__, i);
+ cleanupInflightFences(allFences, i);
+ return Status::INTERNAL_ERROR;
+ }
+ }
+ return Status::OK;
+}
+
+Status ExternalCameraDeviceSession::importBuffer(int32_t streamId, uint64_t bufId,
+ buffer_handle_t buf,
+ /*out*/ buffer_handle_t** outBufPtr) {
+ Mutex::Autolock _l(mCbsLock);
+ return importBufferLocked(streamId, bufId, buf, outBufPtr);
+}
+
+Status ExternalCameraDeviceSession::importBufferLocked(int32_t streamId, uint64_t bufId,
+ buffer_handle_t buf,
+ buffer_handle_t** outBufPtr) {
+ return importBufferImpl(mCirculatingBuffers, sHandleImporter, streamId, bufId, buf, outBufPtr);
+}
+
+ScopedAStatus ExternalCameraDeviceSession::close() {
+ close(false);
+ return fromStatus(Status::OK);
+}
+
+void ExternalCameraDeviceSession::close(bool callerIsDtor) {
+ Mutex::Autolock _il(mInterfaceLock);
+ bool closed = isClosed();
+ if (!closed) {
+ if (callerIsDtor) {
+ closeOutputThreadImpl();
+ } else {
+ closeOutputThread();
+ }
+
+ Mutex::Autolock _l(mLock);
+ // free all buffers
+ {
+ Mutex::Autolock _cbsl(mCbsLock);
+ for (auto pair : mStreamMap) {
+ cleanupBuffersLocked(/*Stream ID*/ pair.first);
+ }
+ }
+ v4l2StreamOffLocked();
+ ALOGV("%s: closing V4L2 camera FD %d", __FUNCTION__, mV4l2Fd.get());
+ mV4l2Fd.reset();
+ mClosed = true;
+ }
+}
+
+bool ExternalCameraDeviceSession::isClosed() {
+ Mutex::Autolock _l(mLock);
+ return mClosed;
+}
+
+ScopedAStatus ExternalCameraDeviceSession::repeatingRequestEnd(
+ int32_t /*in_frameNumber*/, const std::vector<int32_t>& /*in_streamIds*/) {
+ // TODO: Figure this one out.
+ return fromStatus(Status::OK);
+}
+
+int ExternalCameraDeviceSession::v4l2StreamOffLocked() {
+ if (!mV4l2Streaming) {
+ return OK;
+ }
+
+ {
+ std::lock_guard<std::mutex> lk(mV4l2BufferLock);
+ if (mNumDequeuedV4l2Buffers != 0) {
+ ALOGE("%s: there are %zu inflight V4L buffers", __FUNCTION__, mNumDequeuedV4l2Buffers);
+ return -1;
+ }
+ }
+ mV4L2BufferCount = 0;
+
+ // VIDIOC_STREAMOFF
+ v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_STREAMOFF, &capture_type)) < 0) {
+ ALOGE("%s: STREAMOFF failed: %s", __FUNCTION__, strerror(errno));
+ return -errno;
+ }
+
+ // VIDIOC_REQBUFS: clear buffers
+ v4l2_requestbuffers req_buffers{};
+ req_buffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req_buffers.memory = V4L2_MEMORY_MMAP;
+ req_buffers.count = 0;
+ if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_REQBUFS, &req_buffers)) < 0) {
+ ALOGE("%s: REQBUFS failed: %s", __FUNCTION__, strerror(errno));
+ return -errno;
+ }
+
+ mV4l2Streaming = false;
+ return OK;
+}
+
+int ExternalCameraDeviceSession::setV4l2FpsLocked(double fps) {
+ // VIDIOC_G_PARM/VIDIOC_S_PARM: set fps
+ v4l2_streamparm streamparm = {.type = V4L2_BUF_TYPE_VIDEO_CAPTURE};
+ // The following line checks that the driver knows about framerate get/set.
+ int ret = TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_G_PARM, &streamparm));
+ if (ret != 0) {
+ if (errno == -EINVAL) {
+ ALOGW("%s: device does not support VIDIOC_G_PARM", __FUNCTION__);
+ }
+ return -errno;
+ }
+ // Now check if the device is able to accept a capture framerate set.
+ if (!(streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)) {
+ ALOGW("%s: device does not support V4L2_CAP_TIMEPERFRAME", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ // fps is float, approximate by a fraction.
+ const int kFrameRatePrecision = 10000;
+ streamparm.parm.capture.timeperframe.numerator = kFrameRatePrecision;
+ streamparm.parm.capture.timeperframe.denominator = (fps * kFrameRatePrecision);
+
+ if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_S_PARM, &streamparm)) < 0) {
+ ALOGE("%s: failed to set framerate to %f: %s", __FUNCTION__, fps, strerror(errno));
+ return -1;
+ }
+
+ double retFps = streamparm.parm.capture.timeperframe.denominator /
+ static_cast<double>(streamparm.parm.capture.timeperframe.numerator);
+ if (std::fabs(fps - retFps) > 1.0) {
+ ALOGE("%s: expect fps %f, got %f instead", __FUNCTION__, fps, retFps);
+ return -1;
+ }
+ mV4l2StreamingFps = fps;
+ return 0;
+}
+
+void ExternalCameraDeviceSession::cleanupInflightFences(std::vector<int>& allFences,
+ size_t numFences) {
+ for (size_t j = 0; j < numFences; j++) {
+ sHandleImporter.closeFence(allFences[j]);
+ }
+}
+
+void ExternalCameraDeviceSession::cleanupBuffersLocked(int id) {
+ for (auto& pair : mCirculatingBuffers.at(id)) {
+ sHandleImporter.freeBuffer(pair.second);
+ }
+ mCirculatingBuffers[id].clear();
+ mCirculatingBuffers.erase(id);
+}
+
+void ExternalCameraDeviceSession::notifyShutter(int32_t frameNumber, nsecs_t shutterTs) {
+ NotifyMsg msg;
+ msg.set<NotifyMsg::Tag::shutter>(ShutterMsg{
+ .frameNumber = frameNumber,
+ .timestamp = shutterTs,
+ });
+ mCallback->notify({msg});
+}
+void ExternalCameraDeviceSession::notifyError(int32_t frameNumber, int32_t streamId, ErrorCode ec) {
+ NotifyMsg msg;
+ msg.set<NotifyMsg::Tag::error>(ErrorMsg{
+ .frameNumber = frameNumber,
+ .errorStreamId = streamId,
+ .errorCode = ec,
+ });
+ mCallback->notify({msg});
+}
+
+void ExternalCameraDeviceSession::invokeProcessCaptureResultCallback(
+ std::vector<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) {
+ CameraMetadata& md = result.result;
+ if (!md.metadata.empty()) {
+ if (mResultMetadataQueue->write(reinterpret_cast<int8_t*>(md.metadata.data()),
+ md.metadata.size())) {
+ result.fmqResultSize = md.metadata.size();
+ md.metadata.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 : %d:%d", __FUNCTION__, status.getExceptionCode(),
+ status.getServiceSpecificError());
+ }
+
+ mProcessCaptureResultLock.unlock();
+}
+
+int ExternalCameraDeviceSession::waitForV4L2BufferReturnLocked(std::unique_lock<std::mutex>& lk) {
+ ATRACE_CALL();
+ auto timeout = std::chrono::seconds(kBufferWaitTimeoutSec);
+ mLock.unlock();
+ auto st = mV4L2BufferReturned.wait_for(lk, timeout);
+ // Here we introduce an order where mV4l2BufferLock is acquired before mLock, while
+ // the normal lock acquisition order is reversed. This is fine because in most of
+ // cases we are protected by mInterfaceLock. The only thread that can cause deadlock
+ // is the OutputThread, where we do need to make sure we don't acquire mLock then
+ // mV4l2BufferLock
+ mLock.lock();
+ if (st == std::cv_status::timeout) {
+ ALOGE("%s: wait for V4L2 buffer return timeout!", __FUNCTION__);
+ return -1;
+ }
+ return 0;
+}
+
+bool ExternalCameraDeviceSession::supportOfflineLocked(int32_t streamId) {
+ const Stream& stream = mStreamMap[streamId];
+ if (stream.format == PixelFormat::BLOB &&
+ static_cast<int32_t>(stream.dataSpace) == static_cast<int32_t>(Dataspace::JFIF)) {
+ return true;
+ }
+ // TODO: support YUV output stream?
+ return false;
+}
+
+bool ExternalCameraDeviceSession::canDropRequest(const std::vector<int32_t>& offlineStreams,
+ std::shared_ptr<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 std::vector<int32_t>& offlineStreams,
+ std::deque<std::shared_ptr<HalRequest>>& offlineReqs,
+ const std::map<int, CirculatingBuffers>& circulatingBuffers,
+ 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::isStreamCombinationSupported(
+ const StreamConfiguration& config, const std::vector<SupportedV4L2Format>& supportedFormats,
+ const ExternalCameraConfig& devCfg) {
+ if (config.operationMode != StreamConfigurationMode::NORMAL_MODE) {
+ ALOGE("%s: unsupported operation mode: %d", __FUNCTION__, config.operationMode);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ if (config.streams.size() == 0) {
+ ALOGE("%s: cannot configure zero stream", __FUNCTION__);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ int numProcessedStream = 0;
+ int numStallStream = 0;
+ for (const auto& stream : config.streams) {
+ // Check if the format/width/height combo is supported
+ if (!isSupported(stream, supportedFormats, devCfg)) {
+ return Status::ILLEGAL_ARGUMENT;
+ }
+ if (stream.format == PixelFormat::BLOB) {
+ numStallStream++;
+ } else {
+ numProcessedStream++;
+ }
+ }
+
+ if (numProcessedStream > kMaxProcessedStream) {
+ ALOGE("%s: too many processed streams (expect <= %d, got %d)", __FUNCTION__,
+ kMaxProcessedStream, numProcessedStream);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ if (numStallStream > kMaxStallStream) {
+ ALOGE("%s: too many stall streams (expect <= %d, got %d)", __FUNCTION__, kMaxStallStream,
+ numStallStream);
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ return Status::OK;
+}
+void ExternalCameraDeviceSession::updateBufferCaches(
+ const std::vector<BufferCache>& cachesToRemove) {
+ Mutex::Autolock _l(mCbsLock);
+ for (auto& cache : cachesToRemove) {
+ auto cbsIt = mCirculatingBuffers.find(cache.streamId);
+ if (cbsIt == mCirculatingBuffers.end()) {
+ // The stream could have been removed
+ continue;
+ }
+ CirculatingBuffers& cbs = cbsIt->second;
+ auto it = cbs.find(cache.bufferId);
+ if (it != cbs.end()) {
+ sHandleImporter.freeBuffer(it->second);
+ cbs.erase(it);
+ } else {
+ ALOGE("%s: stream %d buffer %" PRIu64 " is not cached", __FUNCTION__, cache.streamId,
+ cache.bufferId);
+ }
+ }
+}
+
+Status ExternalCameraDeviceSession::processCaptureRequestError(
+ const std::shared_ptr<HalRequest>& req, std::vector<NotifyMsg>* outMsgs,
+ std::vector<CaptureResult>* outResults) {
+ ATRACE_CALL();
+ // Return V4L2 buffer to V4L2 buffer queue
+ std::shared_ptr<V4L2Frame> v4l2Frame = std::static_pointer_cast<V4L2Frame>(req->frameIn);
+ enqueueV4l2Frame(v4l2Frame);
+
+ if (outMsgs == nullptr) {
+ notifyShutter(req->frameNumber, req->shutterTs);
+ notifyError(/*frameNum*/ req->frameNumber, /*stream*/ -1, ErrorCode::ERROR_REQUEST);
+ } else {
+ NotifyMsg shutter;
+ shutter.set<NotifyMsg::Tag::shutter>(
+ ShutterMsg{.frameNumber = req->frameNumber, .timestamp = req->shutterTs});
+
+ NotifyMsg error;
+ error.set<NotifyMsg::Tag::error>(ErrorMsg{.frameNumber = req->frameNumber,
+ .errorStreamId = -1,
+ .errorCode = ErrorCode::ERROR_REQUEST});
+ outMsgs->push_back(shutter);
+ outMsgs->push_back(error);
+ }
+
+ // Fill output buffers
+ CaptureResult result;
+ 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 = ::android::makeToAidl(handle);
+ }
+ }
+
+ // update inflight records
+ {
+ std::lock_guard<std::mutex> lk(mInflightFramesLock);
+ mInflightFrames.erase(req->frameNumber);
+ }
+
+ if (outResults == nullptr) {
+ // Callback into framework
+ std::vector<CaptureResult> results(1);
+ results[0] = std::move(result);
+ invokeProcessCaptureResultCallback(results, /* tryWriteFmq */ true);
+ freeReleaseFences(results);
+ } else {
+ outResults->push_back(std::move(result));
+ }
+ return Status::OK;
+}
+
+Status ExternalCameraDeviceSession::processCaptureResult(std::shared_ptr<HalRequest>& req) {
+ ATRACE_CALL();
+ // Return V4L2 buffer to V4L2 buffer queue
+ std::shared_ptr<V4L2Frame> v4l2Frame = std::static_pointer_cast<V4L2Frame>(req->frameIn);
+ enqueueV4l2Frame(v4l2Frame);
+
+ // NotifyShutter
+ notifyShutter(req->frameNumber, req->shutterTs);
+
+ // Fill output buffers;
+ std::vector<CaptureResult> results(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 = ::android::makeToAidl(handle);
+ }
+ 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 = ::android::makeToAidl(handle);
+ }
+ }
+ }
+
+ // Fill capture result metadata
+ fillCaptureResult(req->setting, req->shutterTs);
+ const camera_metadata_t* rawResult = req->setting.getAndLock();
+ convertToAidl(rawResult, &result.result);
+ req->setting.unlock(rawResult);
+
+ // update inflight records
+ {
+ std::lock_guard<std::mutex> lk(mInflightFramesLock);
+ mInflightFrames.erase(req->frameNumber);
+ }
+
+ // Callback into framework
+ invokeProcessCaptureResultCallback(results, /* tryWriteFmq */ true);
+ freeReleaseFences(results);
+ return Status::OK;
+}
+
+ssize_t ExternalCameraDeviceSession::getJpegBufferSize(int32_t width, int32_t height) const {
+ // Constant from camera3.h
+ const ssize_t kMinJpegBufferSize = 256 * 1024 + sizeof(CameraBlob);
+ // Get max jpeg size (area-wise).
+ if (mMaxJpegResolution.width == 0) {
+ ALOGE("%s: No supported JPEG stream", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ // Get max jpeg buffer size
+ ssize_t maxJpegBufferSize = 0;
+ camera_metadata_ro_entry jpegBufMaxSize = mCameraCharacteristics.find(ANDROID_JPEG_MAX_SIZE);
+ if (jpegBufMaxSize.count == 0) {
+ ALOGE("%s: Can't find maximum JPEG size in static metadata!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ maxJpegBufferSize = jpegBufMaxSize.data.i32[0];
+
+ if (maxJpegBufferSize <= kMinJpegBufferSize) {
+ ALOGE("%s: ANDROID_JPEG_MAX_SIZE (%zd) <= kMinJpegBufferSize (%zd)", __FUNCTION__,
+ maxJpegBufferSize, kMinJpegBufferSize);
+ return BAD_VALUE;
+ }
+
+ // Calculate final jpeg buffer size for the given resolution.
+ float scaleFactor =
+ ((float)(width * height)) / (mMaxJpegResolution.width * mMaxJpegResolution.height);
+ ssize_t jpegBufferSize =
+ scaleFactor * (maxJpegBufferSize - kMinJpegBufferSize) + kMinJpegBufferSize;
+ if (jpegBufferSize > maxJpegBufferSize) {
+ jpegBufferSize = maxJpegBufferSize;
+ }
+
+ return jpegBufferSize;
+}
+binder_status_t ExternalCameraDeviceSession::dump(int fd, const char** /*args*/,
+ uint32_t /*numArgs*/) {
+ bool intfLocked = tryLock(mInterfaceLock);
+ if (!intfLocked) {
+ dprintf(fd, "!! ExternalCameraDeviceSession interface may be deadlocked !!\n");
+ }
+
+ if (isClosed()) {
+ dprintf(fd, "External camera %s is closed\n", mCameraId.c_str());
+ return STATUS_OK;
+ }
+
+ bool streaming = false;
+ size_t v4L2BufferCount = 0;
+ SupportedV4L2Format streamingFmt;
+ {
+ bool sessionLocked = tryLock(mLock);
+ if (!sessionLocked) {
+ dprintf(fd, "!! ExternalCameraDeviceSession mLock may be deadlocked !!\n");
+ }
+ streaming = mV4l2Streaming;
+ streamingFmt = mV4l2StreamingFmt;
+ v4L2BufferCount = mV4L2BufferCount;
+
+ if (sessionLocked) {
+ mLock.unlock();
+ }
+ }
+
+ std::unordered_set<uint32_t> inflightFrames;
+ {
+ bool iffLocked = tryLock(mInflightFramesLock);
+ if (!iffLocked) {
+ dprintf(fd,
+ "!! ExternalCameraDeviceSession mInflightFramesLock may be deadlocked !!\n");
+ }
+ inflightFrames = mInflightFrames;
+ if (iffLocked) {
+ mInflightFramesLock.unlock();
+ }
+ }
+
+ dprintf(fd, "External camera %s V4L2 FD %d, cropping type %s, %s\n", mCameraId.c_str(),
+ mV4l2Fd.get(), (mCroppingType == VERTICAL) ? "vertical" : "horizontal",
+ streaming ? "streaming" : "not streaming");
+
+ if (streaming) {
+ // TODO: dump fps later
+ dprintf(fd, "Current V4L2 format %c%c%c%c %dx%d @ %ffps\n", streamingFmt.fourcc & 0xFF,
+ (streamingFmt.fourcc >> 8) & 0xFF, (streamingFmt.fourcc >> 16) & 0xFF,
+ (streamingFmt.fourcc >> 24) & 0xFF, streamingFmt.width, streamingFmt.height,
+ mV4l2StreamingFps);
+
+ size_t numDequeuedV4l2Buffers = 0;
+ {
+ std::lock_guard<std::mutex> lk(mV4l2BufferLock);
+ numDequeuedV4l2Buffers = mNumDequeuedV4l2Buffers;
+ }
+ dprintf(fd, "V4L2 buffer queue size %zu, dequeued %zu\n", v4L2BufferCount,
+ numDequeuedV4l2Buffers);
+ }
+
+ dprintf(fd, "In-flight frames (not sorted):");
+ for (const auto& frameNumber : inflightFrames) {
+ dprintf(fd, "%d, ", frameNumber);
+ }
+ dprintf(fd, "\n");
+ mOutputThread->dump(fd);
+ dprintf(fd, "\n");
+
+ if (intfLocked) {
+ mInterfaceLock.unlock();
+ }
+
+ return STATUS_OK;
+}
+
+// Start ExternalCameraDeviceSession::BufferRequestThread functions
+ExternalCameraDeviceSession::BufferRequestThread::BufferRequestThread(
+ std::weak_ptr<OutputThreadInterface> parent,
+ std::shared_ptr<ICameraDeviceCallback> callbacks)
+ : mParent(parent), mCallbacks(callbacks) {}
+
+int ExternalCameraDeviceSession::BufferRequestThread::requestBufferStart(
+ const std::vector<HalStreamBuffer>& bufReqs) {
+ if (bufReqs.empty()) {
+ ALOGE("%s: bufReqs is empty!", __FUNCTION__);
+ return -1;
+ }
+
+ {
+ std::lock_guard<std::mutex> lk(mLock);
+ if (mRequestingBuffer) {
+ ALOGE("%s: BufferRequestThread does not support more than one concurrent request!",
+ __FUNCTION__);
+ return -1;
+ }
+
+ mBufferReqs = bufReqs;
+ mRequestingBuffer = true;
+ }
+ mRequestCond.notify_one();
+ return 0;
+}
+
+int ExternalCameraDeviceSession::BufferRequestThread::waitForBufferRequestDone(
+ std::vector<HalStreamBuffer>* outBufReqs) {
+ std::unique_lock<std::mutex> lk(mLock);
+ if (!mRequestingBuffer) {
+ ALOGE("%s: no pending buffer request!", __FUNCTION__);
+ return -1;
+ }
+
+ if (mPendingReturnBufferReqs.empty()) {
+ std::chrono::milliseconds timeout = std::chrono::milliseconds(kReqProcTimeoutMs);
+ auto st = mRequestDoneCond.wait_for(lk, timeout);
+ if (st == std::cv_status::timeout) {
+ ALOGE("%s: wait for buffer request finish timeout!", __FUNCTION__);
+ return -1;
+ }
+ }
+ mRequestingBuffer = false;
+ *outBufReqs = std::move(mPendingReturnBufferReqs);
+ mPendingReturnBufferReqs.clear();
+ return 0;
+}
+
+void ExternalCameraDeviceSession::BufferRequestThread::waitForNextRequest() {
+ ATRACE_CALL();
+ std::unique_lock<std::mutex> lk(mLock);
+ int waitTimes = 0;
+ while (mBufferReqs.empty()) {
+ if (exitPending()) {
+ return;
+ }
+ auto timeout = std::chrono::milliseconds(kReqWaitTimeoutMs);
+ auto st = mRequestCond.wait_for(lk, timeout);
+ if (st == std::cv_status::timeout) {
+ waitTimes++;
+ if (waitTimes == kReqWaitTimesWarn) {
+ // BufferRequestThread just wait forever for new buffer request
+ // But it will print some periodic warning indicating it's waiting
+ ALOGV("%s: still waiting for new buffer request", __FUNCTION__);
+ waitTimes = 0;
+ }
+ }
+ }
+
+ // Fill in BufferRequest
+ mHalBufferReqs.resize(mBufferReqs.size());
+ for (size_t i = 0; i < mHalBufferReqs.size(); i++) {
+ mHalBufferReqs[i].streamId = mBufferReqs[i].streamId;
+ mHalBufferReqs[i].numBuffersRequested = 1;
+ }
+}
+
+bool ExternalCameraDeviceSession::BufferRequestThread::threadLoop() {
+ waitForNextRequest();
+ if (exitPending()) {
+ return false;
+ }
+
+ ATRACE_BEGIN("AIDL requestStreamBuffers");
+ BufferRequestStatus status;
+ std::vector<StreamBufferRet> bufRets;
+ ScopedAStatus ret = mCallbacks->requestStreamBuffers(mHalBufferReqs, &bufRets, &status);
+ if (!ret.isOk()) {
+ ALOGE("%s: Transaction error: %d:%d", __FUNCTION__, ret.getExceptionCode(),
+ ret.getServiceSpecificError());
+ return false;
+ }
+
+ std::unique_lock<std::mutex> lk(mLock);
+ if (status == BufferRequestStatus::OK || status == BufferRequestStatus::FAILED_PARTIAL) {
+ if (bufRets.size() != mHalBufferReqs.size()) {
+ ALOGE("%s: expect %zu buffer requests returned, only got %zu", __FUNCTION__,
+ mHalBufferReqs.size(), bufRets.size());
+ return false;
+ }
+
+ auto parent = mParent.lock();
+ if (parent == nullptr) {
+ ALOGE("%s: session has been disconnected!", __FUNCTION__);
+ return false;
+ }
+
+ std::vector<int> importedFences;
+ importedFences.resize(bufRets.size());
+ for (size_t i = 0; i < bufRets.size(); i++) {
+ int streamId = bufRets[i].streamId;
+ switch (bufRets[i].val.getTag()) {
+ case StreamBuffersVal::Tag::error:
+ continue;
+ case StreamBuffersVal::Tag::buffers: {
+ const std::vector<StreamBuffer>& hBufs =
+ bufRets[i].val.get<StreamBuffersVal::Tag::buffers>();
+ if (hBufs.size() != 1) {
+ ALOGE("%s: expect 1 buffer returned, got %zu!", __FUNCTION__, hBufs.size());
+ return false;
+ }
+ const StreamBuffer& hBuf = hBufs[0];
+
+ mBufferReqs[i].bufferId = hBuf.bufferId;
+ // TODO: create a batch import API so we don't need to lock/unlock mCbsLock
+ // repeatedly?
+ lk.unlock();
+ Status s =
+ parent->importBuffer(streamId, hBuf.bufferId, makeFromAidl(hBuf.buffer),
+ /*out*/ &mBufferReqs[i].bufPtr);
+ lk.lock();
+
+ if (s != Status::OK) {
+ ALOGE("%s: stream %d import buffer failed!", __FUNCTION__, streamId);
+ cleanupInflightFences(importedFences, i - 1);
+ return false;
+ }
+ if (!sHandleImporter.importFence(makeFromAidl(hBuf.acquireFence),
+ mBufferReqs[i].acquireFence)) {
+ ALOGE("%s: stream %d import fence failed!", __FUNCTION__, streamId);
+ cleanupInflightFences(importedFences, i - 1);
+ return false;
+ }
+ importedFences[i] = mBufferReqs[i].acquireFence;
+ } break;
+ default:
+ ALOGE("%s: Unknown StreamBuffersVal!", __FUNCTION__);
+ return false;
+ }
+ }
+ } else {
+ ALOGE("%s: requestStreamBuffers call failed!", __FUNCTION__);
+ }
+
+ mPendingReturnBufferReqs = std::move(mBufferReqs);
+ mBufferReqs.clear();
+
+ lk.unlock();
+ mRequestDoneCond.notify_one();
+ return true;
+}
+
+// End ExternalCameraDeviceSession::BufferRequestThread functions
+
+// Start ExternalCameraDeviceSession::OutputThread functions
+
+ExternalCameraDeviceSession::OutputThread::OutputThread(
+ std::weak_ptr<OutputThreadInterface> parent, CroppingType ct,
+ const common::V1_0::helper::CameraMetadata& chars,
+ std::shared_ptr<BufferRequestThread> bufReqThread)
+ : mParent(parent),
+ mCroppingType(ct),
+ mCameraCharacteristics(chars),
+ mBufferRequestThread(bufReqThread) {}
+
+ExternalCameraDeviceSession::OutputThread::~OutputThread() {}
+
+Status ExternalCameraDeviceSession::OutputThread::allocateIntermediateBuffers(
+ const Size& v4lSize, const Size& thumbSize, const std::vector<Stream>& streams,
+ uint32_t blobBufferSize) {
+ std::lock_guard<std::mutex> lk(mBufferLock);
+ if (!mScaledYu12Frames.empty()) {
+ ALOGE("%s: intermediate buffer pool has %zu inflight buffers! (expect 0)", __FUNCTION__,
+ mScaledYu12Frames.size());
+ return Status::INTERNAL_ERROR;
+ }
+
+ // Allocating intermediate YU12 frame
+ if (mYu12Frame == nullptr || mYu12Frame->mWidth != v4lSize.width ||
+ mYu12Frame->mHeight != v4lSize.height) {
+ mYu12Frame.reset();
+ mYu12Frame = std::make_shared<AllocatedFrame>(v4lSize.width, v4lSize.height);
+ int ret = mYu12Frame->allocate(&mYu12FrameLayout);
+ if (ret != 0) {
+ ALOGE("%s: allocating YU12 frame failed!", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+ }
+
+ // Allocating intermediate YU12 thumbnail frame
+ if (mYu12ThumbFrame == nullptr || mYu12ThumbFrame->mWidth != thumbSize.width ||
+ mYu12ThumbFrame->mHeight != thumbSize.height) {
+ mYu12ThumbFrame.reset();
+ mYu12ThumbFrame = std::make_shared<AllocatedFrame>(thumbSize.width, thumbSize.height);
+ int ret = mYu12ThumbFrame->allocate(&mYu12ThumbFrameLayout);
+ if (ret != 0) {
+ ALOGE("%s: allocating YU12 thumb frame failed!", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+ }
+
+ // Allocating scaled buffers
+ for (const auto& stream : streams) {
+ Size sz = {stream.width, stream.height};
+ if (sz == v4lSize) {
+ continue; // Don't need an intermediate buffer same size as v4lBuffer
+ }
+ if (mIntermediateBuffers.count(sz) == 0) {
+ // Create new intermediate buffer
+ std::shared_ptr<AllocatedFrame> buf =
+ std::make_shared<AllocatedFrame>(stream.width, stream.height);
+ int ret = buf->allocate();
+ if (ret != 0) {
+ ALOGE("%s: allocating intermediate YU12 frame %dx%d failed!", __FUNCTION__,
+ stream.width, stream.height);
+ return Status::INTERNAL_ERROR;
+ }
+ mIntermediateBuffers[sz] = buf;
+ }
+ }
+
+ // Remove unconfigured buffers
+ auto it = mIntermediateBuffers.begin();
+ while (it != mIntermediateBuffers.end()) {
+ bool configured = false;
+ auto sz = it->first;
+ for (const auto& stream : streams) {
+ if (stream.width == sz.width && stream.height == sz.height) {
+ configured = true;
+ break;
+ }
+ }
+ if (configured) {
+ it++;
+ } else {
+ it = mIntermediateBuffers.erase(it);
+ }
+ }
+
+ // Allocate mute test pattern frame
+ mMuteTestPatternFrame.resize(mYu12Frame->mWidth * mYu12Frame->mHeight * 3);
+
+ mBlobBufferSize = blobBufferSize;
+ return Status::OK;
+}
+
+Status ExternalCameraDeviceSession::OutputThread::submitRequest(
+ const std::shared_ptr<HalRequest>& req) {
+ std::unique_lock<std::mutex> lk(mRequestListLock);
+ mRequestList.push_back(req);
+ lk.unlock();
+ mRequestCond.notify_one();
+ return Status::OK;
+}
+
+void ExternalCameraDeviceSession::OutputThread::flush() {
+ ATRACE_CALL();
+ auto parent = mParent.lock();
+ if (parent == nullptr) {
+ ALOGE("%s: session has been disconnected!", __FUNCTION__);
+ return;
+ }
+
+ std::unique_lock<std::mutex> lk(mRequestListLock);
+ std::list<std::shared_ptr<HalRequest>> reqs = std::move(mRequestList);
+ mRequestList.clear();
+ if (mProcessingRequest) {
+ auto 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__);
+ }
+ }
+
+ ALOGV("%s: flushing inflight requests", __FUNCTION__);
+ lk.unlock();
+ for (const auto& req : reqs) {
+ parent->processCaptureRequestError(req);
+ }
+}
+
+void ExternalCameraDeviceSession::OutputThread::dump(int fd) {
+ std::lock_guard<std::mutex> lk(mRequestListLock);
+ if (mProcessingRequest) {
+ dprintf(fd, "OutputThread processing frame %d\n", mProcessingFrameNumber);
+ } else {
+ dprintf(fd, "OutputThread not processing any frames\n");
+ }
+ dprintf(fd, "OutputThread request list contains frame: ");
+ for (const auto& req : mRequestList) {
+ dprintf(fd, "%d, ", req->frameNumber);
+ }
+ dprintf(fd, "\n");
+}
+
+void ExternalCameraDeviceSession::OutputThread::setExifMakeModel(const std::string& make,
+ const std::string& model) {
+ mExifMake = make;
+ mExifModel = model;
+}
+
+std::list<std::shared_ptr<HalRequest>>
+ExternalCameraDeviceSession::OutputThread::switchToOffline() {
+ ATRACE_CALL();
+ auto parent = mParent.lock();
+ if (parent == nullptr) {
+ ALOGE("%s: session has been disconnected!", __FUNCTION__);
+ return {};
+ }
+
+ std::unique_lock<std::mutex> lk(mRequestListLock);
+ std::list<std::shared_ptr<HalRequest>> reqs = std::move(mRequestList);
+ mRequestList.clear();
+ if (mProcessingRequest) {
+ auto 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;
+}
+
+int ExternalCameraDeviceSession::OutputThread::requestBufferStart(
+ const std::vector<HalStreamBuffer>& bufs) {
+ if (mBufferRequestThread == nullptr) {
+ return 0;
+ }
+ return mBufferRequestThread->requestBufferStart(bufs);
+}
+
+int ExternalCameraDeviceSession::OutputThread::waitForBufferRequestDone(
+ std::vector<HalStreamBuffer>* outBufs) {
+ if (mBufferRequestThread == nullptr) {
+ return 0;
+ }
+ return mBufferRequestThread->waitForBufferRequestDone(outBufs);
+}
+
+void ExternalCameraDeviceSession::OutputThread::waitForNextRequest(
+ std::shared_ptr<HalRequest>* out) {
+ ATRACE_CALL();
+ if (out == nullptr) {
+ ALOGE("%s: out is null", __FUNCTION__);
+ return;
+ }
+
+ std::unique_lock<std::mutex> lk(mRequestListLock);
+ int waitTimes = 0;
+ while (mRequestList.empty()) {
+ if (exitPending()) {
+ return;
+ }
+ auto timeout = std::chrono::milliseconds(kReqWaitTimeoutMs);
+ auto st = mRequestCond.wait_for(lk, timeout);
+ if (st == std::cv_status::timeout) {
+ waitTimes++;
+ if (waitTimes == kReqWaitTimesMax) {
+ // no new request, return
+ return;
+ }
+ }
+ }
+ *out = mRequestList.front();
+ mRequestList.pop_front();
+ mProcessingRequest = true;
+ mProcessingFrameNumber = (*out)->frameNumber;
+}
+
+void ExternalCameraDeviceSession::OutputThread::signalRequestDone() {
+ std::unique_lock<std::mutex> lk(mRequestListLock);
+ mProcessingRequest = false;
+ mProcessingFrameNumber = 0;
+ lk.unlock();
+ mRequestDoneCond.notify_one();
+}
+
+int ExternalCameraDeviceSession::OutputThread::cropAndScaleLocked(
+ std::shared_ptr<AllocatedFrame>& in, const Size& outSz, YCbCrLayout* out) {
+ Size inSz = {in->mWidth, in->mHeight};
+
+ int ret;
+ if (inSz == outSz) {
+ ret = in->getLayout(out);
+ if (ret != 0) {
+ ALOGE("%s: failed to get input image layout", __FUNCTION__);
+ return ret;
+ }
+ return ret;
+ }
+
+ // Cropping to output aspect ratio
+ IMapper::Rect inputCrop;
+ ret = getCropRect(mCroppingType, inSz, outSz, &inputCrop);
+ if (ret != 0) {
+ ALOGE("%s: failed to compute crop rect for output size %dx%d", __FUNCTION__, outSz.width,
+ outSz.height);
+ return ret;
+ }
+
+ YCbCrLayout croppedLayout;
+ ret = in->getCroppedLayout(inputCrop, &croppedLayout);
+ if (ret != 0) {
+ ALOGE("%s: failed to crop input image %dx%d to output size %dx%d", __FUNCTION__, inSz.width,
+ inSz.height, outSz.width, outSz.height);
+ return ret;
+ }
+
+ if ((mCroppingType == VERTICAL && inSz.width == outSz.width) ||
+ (mCroppingType == HORIZONTAL && inSz.height == outSz.height)) {
+ // No scale is needed
+ *out = croppedLayout;
+ return 0;
+ }
+
+ auto it = mScaledYu12Frames.find(outSz);
+ std::shared_ptr<AllocatedFrame> scaledYu12Buf;
+ if (it != mScaledYu12Frames.end()) {
+ scaledYu12Buf = it->second;
+ } else {
+ it = mIntermediateBuffers.find(outSz);
+ if (it == mIntermediateBuffers.end()) {
+ ALOGE("%s: failed to find intermediate buffer size %dx%d", __FUNCTION__, outSz.width,
+ outSz.height);
+ return -1;
+ }
+ scaledYu12Buf = it->second;
+ }
+ // Scale
+ YCbCrLayout outLayout;
+ ret = scaledYu12Buf->getLayout(&outLayout);
+ if (ret != 0) {
+ ALOGE("%s: failed to get output buffer layout", __FUNCTION__);
+ return ret;
+ }
+
+ ret = libyuv::I420Scale(
+ static_cast<uint8_t*>(croppedLayout.y), croppedLayout.yStride,
+ static_cast<uint8_t*>(croppedLayout.cb), croppedLayout.cStride,
+ static_cast<uint8_t*>(croppedLayout.cr), croppedLayout.cStride, inputCrop.width,
+ inputCrop.height, static_cast<uint8_t*>(outLayout.y), outLayout.yStride,
+ static_cast<uint8_t*>(outLayout.cb), outLayout.cStride,
+ static_cast<uint8_t*>(outLayout.cr), outLayout.cStride, outSz.width, outSz.height,
+ // TODO: b/72261744 see if we can use better filter without losing too much perf
+ libyuv::FilterMode::kFilterNone);
+
+ if (ret != 0) {
+ ALOGE("%s: failed to scale buffer from %dx%d to %dx%d. Ret %d", __FUNCTION__,
+ inputCrop.width, inputCrop.height, outSz.width, outSz.height, ret);
+ return ret;
+ }
+
+ *out = outLayout;
+ mScaledYu12Frames.insert({outSz, scaledYu12Buf});
+ return 0;
+}
+
+int ExternalCameraDeviceSession::OutputThread::cropAndScaleThumbLocked(
+ std::shared_ptr<AllocatedFrame>& in, const Size& outSz, YCbCrLayout* out) {
+ Size inSz{in->mWidth, in->mHeight};
+
+ if ((outSz.width * outSz.height) > (mYu12ThumbFrame->mWidth * mYu12ThumbFrame->mHeight)) {
+ ALOGE("%s: Requested thumbnail size too big (%d,%d) > (%d,%d)", __FUNCTION__, outSz.width,
+ outSz.height, mYu12ThumbFrame->mWidth, mYu12ThumbFrame->mHeight);
+ return -1;
+ }
+
+ int ret;
+
+ /* This will crop-and-zoom the input YUV frame to the thumbnail size
+ * Based on the following logic:
+ * 1) Square pixels come in, square pixels come out, therefore single
+ * scale factor is computed to either make input bigger or smaller
+ * depending on if we are upscaling or downscaling
+ * 2) That single scale factor would either make height too tall or width
+ * too wide so we need to crop the input either horizontally or vertically
+ * but not both
+ */
+
+ /* Convert the input and output dimensions into floats for ease of math */
+ float fWin = static_cast<float>(inSz.width);
+ float fHin = static_cast<float>(inSz.height);
+ float fWout = static_cast<float>(outSz.width);
+ float fHout = static_cast<float>(outSz.height);
+
+ /* Compute the one scale factor from (1) above, it will be the smaller of
+ * the two possibilities. */
+ float scaleFactor = std::min(fHin / fHout, fWin / fWout);
+
+ /* Since we are crop-and-zooming (as opposed to letter/pillar boxing) we can
+ * simply multiply the output by our scaleFactor to get the cropped input
+ * size. Note that at least one of {fWcrop, fHcrop} is going to wind up
+ * being {fWin, fHin} respectively because fHout or fWout cancels out the
+ * scaleFactor calculation above.
+ *
+ * Specifically:
+ * if ( fHin / fHout ) < ( fWin / fWout ) we crop the sides off
+ * input, in which case
+ * scaleFactor = fHin / fHout
+ * fWcrop = fHin / fHout * fWout
+ * fHcrop = fHin
+ *
+ * Note that fWcrop <= fWin ( because ( fHin / fHout ) * fWout < fWin, which
+ * is just the inequality above with both sides multiplied by fWout
+ *
+ * on the other hand if ( fWin / fWout ) < ( fHin / fHout) we crop the top
+ * and the bottom off of input, and
+ * scaleFactor = fWin / fWout
+ * fWcrop = fWin
+ * fHCrop = fWin / fWout * fHout
+ */
+ float fWcrop = scaleFactor * fWout;
+ float fHcrop = scaleFactor * fHout;
+
+ /* Convert to integer and truncate to an even number */
+ Size cropSz = {.width = 2 * static_cast<int32_t>(fWcrop / 2.0f),
+ .height = 2 * static_cast<int32_t>(fHcrop / 2.0f)};
+
+ /* Convert to a centered rectange with even top/left */
+ IMapper::Rect inputCrop{.left = 2 * static_cast<int32_t>((inSz.width - cropSz.width) / 4),
+ .top = 2 * static_cast<int32_t>((inSz.height - cropSz.height) / 4),
+ .width = static_cast<int32_t>(cropSz.width),
+ .height = static_cast<int32_t>(cropSz.height)};
+
+ if ((inputCrop.top < 0) || (inputCrop.top >= static_cast<int32_t>(inSz.height)) ||
+ (inputCrop.left < 0) || (inputCrop.left >= static_cast<int32_t>(inSz.width)) ||
+ (inputCrop.width <= 0) ||
+ (inputCrop.width + inputCrop.left > static_cast<int32_t>(inSz.width)) ||
+ (inputCrop.height <= 0) ||
+ (inputCrop.height + inputCrop.top > static_cast<int32_t>(inSz.height))) {
+ ALOGE("%s: came up with really wrong crop rectangle", __FUNCTION__);
+ ALOGE("%s: input layout %dx%d to for output size %dx%d", __FUNCTION__, inSz.width,
+ inSz.height, outSz.width, outSz.height);
+ ALOGE("%s: computed input crop +%d,+%d %dx%d", __FUNCTION__, inputCrop.left, inputCrop.top,
+ inputCrop.width, inputCrop.height);
+ return -1;
+ }
+
+ YCbCrLayout inputLayout;
+ ret = in->getCroppedLayout(inputCrop, &inputLayout);
+ if (ret != 0) {
+ ALOGE("%s: failed to crop input layout %dx%d to for output size %dx%d", __FUNCTION__,
+ inSz.width, inSz.height, outSz.width, outSz.height);
+ ALOGE("%s: computed input crop +%d,+%d %dx%d", __FUNCTION__, inputCrop.left, inputCrop.top,
+ inputCrop.width, inputCrop.height);
+ return ret;
+ }
+ ALOGV("%s: crop input layout %dx%d to for output size %dx%d", __FUNCTION__, inSz.width,
+ inSz.height, outSz.width, outSz.height);
+ ALOGV("%s: computed input crop +%d,+%d %dx%d", __FUNCTION__, inputCrop.left, inputCrop.top,
+ inputCrop.width, inputCrop.height);
+
+ // Scale
+ YCbCrLayout outFullLayout;
+
+ ret = mYu12ThumbFrame->getLayout(&outFullLayout);
+ if (ret != 0) {
+ ALOGE("%s: failed to get output buffer layout", __FUNCTION__);
+ return ret;
+ }
+
+ ret = libyuv::I420Scale(static_cast<uint8_t*>(inputLayout.y), inputLayout.yStride,
+ static_cast<uint8_t*>(inputLayout.cb), inputLayout.cStride,
+ static_cast<uint8_t*>(inputLayout.cr), inputLayout.cStride,
+ inputCrop.width, inputCrop.height,
+ static_cast<uint8_t*>(outFullLayout.y), outFullLayout.yStride,
+ static_cast<uint8_t*>(outFullLayout.cb), outFullLayout.cStride,
+ static_cast<uint8_t*>(outFullLayout.cr), outFullLayout.cStride,
+ outSz.width, outSz.height, libyuv::FilterMode::kFilterNone);
+
+ if (ret != 0) {
+ ALOGE("%s: failed to scale buffer from %dx%d to %dx%d. Ret %d", __FUNCTION__,
+ inputCrop.width, inputCrop.height, outSz.width, outSz.height, ret);
+ return ret;
+ }
+
+ *out = outFullLayout;
+ return 0;
+}
+
+int ExternalCameraDeviceSession::OutputThread::createJpegLocked(
+ HalStreamBuffer& halBuf, const common::V1_0::helper::CameraMetadata& setting) {
+ ATRACE_CALL();
+ int ret;
+ auto lfail = [&](auto... args) {
+ ALOGE(args...);
+
+ return 1;
+ };
+ auto parent = mParent.lock();
+ if (parent == nullptr) {
+ ALOGE("%s: session has been disconnected!", __FUNCTION__);
+ return 1;
+ }
+
+ ALOGV("%s: HAL buffer sid: %d bid: %" PRIu64 " w: %u h: %u", __FUNCTION__, halBuf.streamId,
+ static_cast<uint64_t>(halBuf.bufferId), halBuf.width, halBuf.height);
+ ALOGV("%s: HAL buffer fmt: %x usage: %" PRIx64 " ptr: %p", __FUNCTION__, halBuf.format,
+ static_cast<uint64_t>(halBuf.usage), halBuf.bufPtr);
+ ALOGV("%s: YV12 buffer %d x %d", __FUNCTION__, mYu12Frame->mWidth, mYu12Frame->mHeight);
+
+ int jpegQuality, thumbQuality;
+ Size thumbSize;
+ bool outputThumbnail = true;
+
+ 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 (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("%s: ANDROID_JPEG_THUMBNAIL_QUALITY not set", __FUNCTION__);
+ }
+
+ if (setting.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) {
+ camera_metadata_ro_entry entry = setting.find(ANDROID_JPEG_THUMBNAIL_SIZE);
+ thumbSize = Size{.width = entry.data.i32[0], .height = entry.data.i32[1]};
+ if (thumbSize.width == 0 && thumbSize.height == 0) {
+ outputThumbnail = false;
+ }
+ } else {
+ return lfail("%s: ANDROID_JPEG_THUMBNAIL_SIZE not set", __FUNCTION__);
+ }
+
+ /* Cropped and scaled YU12 buffer for main and thumbnail */
+ YCbCrLayout yu12Main;
+ Size jpegSize{halBuf.width, halBuf.height};
+
+ /* Compute temporary buffer sizes accounting for the following:
+ * thumbnail can't exceed APP1 size of 64K
+ * main image needs to hold APP1, headers, and at most a poorly
+ * compressed image */
+ const ssize_t maxThumbCodeSize = 64 * 1024;
+ const ssize_t maxJpegCodeSize =
+ mBlobBufferSize == 0 ? parent->getJpegBufferSize(jpegSize.width, jpegSize.height)
+ : mBlobBufferSize;
+
+ /* Check that getJpegBufferSize did not return an error */
+ if (maxJpegCodeSize < 0) {
+ return lfail("%s: getJpegBufferSize returned %zd", __FUNCTION__, maxJpegCodeSize);
+ }
+
+ /* Hold actual thumbnail and main image code sizes */
+ size_t thumbCodeSize = 0, jpegCodeSize = 0;
+ /* Temporary thumbnail code buffer */
+ std::vector<uint8_t> thumbCode(outputThumbnail ? maxThumbCodeSize : 0);
+
+ YCbCrLayout yu12Thumb;
+ if (outputThumbnail) {
+ ret = cropAndScaleThumbLocked(mYu12Frame, thumbSize, &yu12Thumb);
+
+ if (ret != 0) {
+ return lfail("%s: crop and scale thumbnail failed!", __FUNCTION__);
+ }
+ }
+
+ /* Scale and crop main jpeg */
+ ret = cropAndScaleLocked(mYu12Frame, jpegSize, &yu12Main);
+
+ if (ret != 0) {
+ return lfail("%s: crop and scale main failed!", __FUNCTION__);
+ }
+
+ /* Encode the thumbnail image */
+ if (outputThumbnail) {
+ ret = encodeJpegYU12(thumbSize, yu12Thumb, thumbQuality, 0, 0, &thumbCode[0],
+ maxThumbCodeSize, thumbCodeSize);
+
+ if (ret != 0) {
+ return lfail("%s: thumbnail encodeJpegYU12 failed with %d", __FUNCTION__, ret);
+ }
+ }
+
+ /* Combine camera characteristics with request settings to form EXIF
+ * metadata */
+ common::V1_0::helper::CameraMetadata meta(mCameraCharacteristics);
+ meta.append(setting);
+
+ /* Generate EXIF object */
+ std::unique_ptr<ExifUtils> utils(ExifUtils::create());
+ /* Make sure it's initialized */
+ utils->initialize();
+
+ utils->setFromMetadata(meta, jpegSize.width, jpegSize.height);
+ utils->setMake(mExifMake);
+ utils->setModel(mExifModel);
+
+ ret = utils->generateApp1(outputThumbnail ? &thumbCode[0] : nullptr, thumbCodeSize);
+
+ if (!ret) {
+ return lfail("%s: generating APP1 failed", __FUNCTION__);
+ }
+
+ /* Get internal buffer */
+ size_t exifDataSize = utils->getApp1Length();
+ const uint8_t* exifData = utils->getApp1Buffer();
+
+ /* Lock the HAL jpeg code buffer */
+ void* bufPtr = sHandleImporter.lock(*(halBuf.bufPtr), static_cast<uint64_t>(halBuf.usage),
+ maxJpegCodeSize);
+
+ if (!bufPtr) {
+ return lfail("%s: could not lock %zu bytes", __FUNCTION__, maxJpegCodeSize);
+ }
+
+ /* Encode the main jpeg image */
+ ret = encodeJpegYU12(jpegSize, yu12Main, jpegQuality, exifData, exifDataSize, bufPtr,
+ maxJpegCodeSize, jpegCodeSize);
+
+ /* TODO: Not sure this belongs here, maybe better to pass jpegCodeSize out
+ * and do this when returning buffer to parent */
+ CameraBlob blob{CameraBlobId::JPEG, static_cast<int32_t>(jpegCodeSize)};
+ void* blobDst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(bufPtr) + maxJpegCodeSize -
+ sizeof(CameraBlob));
+ memcpy(blobDst, &blob, sizeof(CameraBlob));
+
+ /* Unlock the HAL jpeg code buffer */
+ int relFence = sHandleImporter.unlock(*(halBuf.bufPtr));
+ if (relFence >= 0) {
+ halBuf.acquireFence = relFence;
+ }
+
+ /* Check if our JPEG actually succeeded */
+ if (ret != 0) {
+ return lfail("%s: encodeJpegYU12 failed with %d", __FUNCTION__, ret);
+ }
+
+ ALOGV("%s: encoded JPEG (ret:%d) with Q:%d max size: %zu", __FUNCTION__, ret, jpegQuality,
+ maxJpegCodeSize);
+
+ return 0;
+}
+
+void ExternalCameraDeviceSession::OutputThread::clearIntermediateBuffers() {
+ std::lock_guard<std::mutex> lk(mBufferLock);
+ mYu12Frame.reset();
+ mYu12ThumbFrame.reset();
+ mIntermediateBuffers.clear();
+ mMuteTestPatternFrame.clear();
+ mBlobBufferSize = 0;
+}
+
+bool ExternalCameraDeviceSession::OutputThread::threadLoop() {
+ std::shared_ptr<HalRequest> req;
+ auto parent = mParent.lock();
+ if (parent == nullptr) {
+ ALOGE("%s: session has been disconnected!", __FUNCTION__);
+ return false;
+ }
+
+ // TODO: maybe we need to setup a sensor thread to dq/enq v4l frames
+ // regularly to prevent v4l buffer queue filled with stale buffers
+ // when app doesn't program a preview request
+ waitForNextRequest(&req);
+ if (req == nullptr) {
+ // No new request, wait again
+ return true;
+ }
+
+ 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__);
+ }
+
+ // Process camera mute state
+ auto testPatternMode = req->setting.find(ANDROID_SENSOR_TEST_PATTERN_MODE);
+ if (testPatternMode.count == 1) {
+ if (mCameraMuted != (testPatternMode.data.u8[0] != ANDROID_SENSOR_TEST_PATTERN_MODE_OFF)) {
+ mCameraMuted = !mCameraMuted;
+ // Get solid color for test pattern, if any was set
+ if (testPatternMode.data.u8[0] == ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR) {
+ auto entry = req->setting.find(ANDROID_SENSOR_TEST_PATTERN_DATA);
+ if (entry.count == 4) {
+ // Update the mute frame if the pattern color has changed
+ if (memcmp(entry.data.i32, mTestPatternData, sizeof(mTestPatternData)) != 0) {
+ memcpy(mTestPatternData, entry.data.i32, sizeof(mTestPatternData));
+ // Fill the mute frame with the solid color, use only 8 MSB of RGGB as RGB
+ for (int i = 0; i < mMuteTestPatternFrame.size(); i += 3) {
+ mMuteTestPatternFrame[i] = entry.data.i32[0] >> 24;
+ mMuteTestPatternFrame[i + 1] = entry.data.i32[1] >> 24;
+ mMuteTestPatternFrame[i + 2] = entry.data.i32[3] >> 24;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // 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");
+ res = 0;
+ if (mCameraMuted) {
+ res = libyuv::ConvertToI420(
+ mMuteTestPatternFrame.data(), mMuteTestPatternFrame.size(),
+ static_cast<uint8_t*>(mYu12FrameLayout.y), mYu12FrameLayout.yStride,
+ static_cast<uint8_t*>(mYu12FrameLayout.cb), mYu12FrameLayout.cStride,
+ static_cast<uint8_t*>(mYu12FrameLayout.cr), mYu12FrameLayout.cStride, 0, 0,
+ mYu12Frame->mWidth, mYu12Frame->mHeight, mYu12Frame->mWidth,
+ mYu12Frame->mHeight, libyuv::kRotate0, libyuv::FOURCC_RAW);
+ } else {
+ 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), static_cast<uint64_t>(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), static_cast<uint64_t>(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 = 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 = formatConvert(cropAndScaled, outLayout, sz, outputFourcc);
+ ATRACE_END();
+ if (ret != 0) {
+ lk.unlock();
+ return onDeviceError("%s: format conversion 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;
+}
+
+// End ExternalCameraDeviceSession::OutputThread functions
+
+} // namespace implementation
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
diff --git a/camera/device/default/ExternalCameraDeviceSession.h b/camera/device/default/ExternalCameraDeviceSession.h
new file mode 100644
index 0000000..5d42092
--- /dev/null
+++ b/camera/device/default/ExternalCameraDeviceSession.h
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_EXTERNALCAMERADEVICESESSION_H_
+#define HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_EXTERNALCAMERADEVICESESSION_H_
+
+#include <ExternalCameraUtils.h>
+#include <SimpleThread.h>
+#include <aidl/android/hardware/camera/common/Status.h>
+#include <aidl/android/hardware/camera/device/BnCameraDeviceSession.h>
+#include <aidl/android/hardware/camera/device/BufferRequest.h>
+#include <aidl/android/hardware/camera/device/Stream.h>
+#include <android-base/unique_fd.h>
+#include <fmq/AidlMessageQueue.h>
+#include <utils/Thread.h>
+#include <deque>
+#include <list>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace implementation {
+
+using ::aidl::android::hardware::camera::common::Status;
+using ::aidl::android::hardware::camera::device::BnCameraDeviceSession;
+using ::aidl::android::hardware::camera::device::BufferCache;
+using ::aidl::android::hardware::camera::device::BufferRequest;
+using ::aidl::android::hardware::camera::device::CameraMetadata;
+using ::aidl::android::hardware::camera::device::CameraOfflineSessionInfo;
+using ::aidl::android::hardware::camera::device::CaptureRequest;
+using ::aidl::android::hardware::camera::device::HalStream;
+using ::aidl::android::hardware::camera::device::ICameraDeviceCallback;
+using ::aidl::android::hardware::camera::device::ICameraOfflineSession;
+using ::aidl::android::hardware::camera::device::RequestTemplate;
+using ::aidl::android::hardware::camera::device::Stream;
+using ::aidl::android::hardware::camera::device::StreamConfiguration;
+using ::aidl::android::hardware::common::fmq::MQDescriptor;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using ::android::AidlMessageQueue;
+using ::android::base::unique_fd;
+using ::android::hardware::camera::common::helper::SimpleThread;
+using ::android::hardware::camera::external::common::ExternalCameraConfig;
+using ::android::hardware::camera::external::common::SizeHasher;
+using ::ndk::ScopedAStatus;
+
+class ExternalCameraDeviceSession : public BnCameraDeviceSession, public OutputThreadInterface {
+ public:
+ ExternalCameraDeviceSession(const std::shared_ptr<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);
+ ~ExternalCameraDeviceSession() override;
+
+ // Caller must use this method to check if CameraDeviceSession ctor failed
+ bool isInitFailed();
+ bool isClosed();
+
+ ScopedAStatus close() override;
+
+ ScopedAStatus configureStreams(const StreamConfiguration& in_requestedConfiguration,
+ std::vector<HalStream>* _aidl_return) override;
+ ScopedAStatus constructDefaultRequestSettings(RequestTemplate in_type,
+ CameraMetadata* _aidl_return) override;
+ ScopedAStatus flush() override;
+ ScopedAStatus getCaptureRequestMetadataQueue(
+ MQDescriptor<int8_t, SynchronizedReadWrite>* _aidl_return) override;
+ ScopedAStatus getCaptureResultMetadataQueue(
+ MQDescriptor<int8_t, SynchronizedReadWrite>* _aidl_return) override;
+ ScopedAStatus isReconfigurationRequired(const CameraMetadata& in_oldSessionParams,
+ const CameraMetadata& in_newSessionParams,
+ bool* _aidl_return) override;
+ ScopedAStatus processCaptureRequest(const std::vector<CaptureRequest>& in_requests,
+ const std::vector<BufferCache>& in_cachesToRemove,
+ int32_t* _aidl_return) override;
+ ScopedAStatus signalStreamFlush(const std::vector<int32_t>& in_streamIds,
+ int32_t in_streamConfigCounter) override;
+ ScopedAStatus switchToOffline(const std::vector<int32_t>& in_streamsToKeep,
+ CameraOfflineSessionInfo* out_offlineSessionInfo,
+ std::shared_ptr<ICameraOfflineSession>* _aidl_return) override;
+ ScopedAStatus repeatingRequestEnd(int32_t in_frameNumber,
+ const std::vector<int32_t>& in_streamIds) override;
+
+ Status importBuffer(int32_t streamId, uint64_t bufId, buffer_handle_t buf,
+ buffer_handle_t** outBufPtr) override;
+
+ void notifyError(int32_t frameNumber, int32_t streamId, ErrorCode ec) override;
+
+ Status processCaptureRequestError(const std::shared_ptr<HalRequest>& ptr,
+ std::vector<NotifyMsg>* msgs,
+ std::vector<CaptureResult>* results) override;
+
+ Status processCaptureResult(std::shared_ptr<HalRequest>& ptr) override;
+ ssize_t getJpegBufferSize(int32_t width, int32_t height) const override;
+
+ // Called by CameraDevice to dump active device states
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+
+ static Status isStreamCombinationSupported(
+ const StreamConfiguration& config,
+ const std::vector<SupportedV4L2Format>& supportedFormats,
+ const ExternalCameraConfig& devCfg);
+
+ static const int kMaxProcessedStream = 2;
+ static const int kMaxStallStream = 1;
+ static const uint32_t kMaxBytesPerPixel = 2;
+
+ class BufferRequestThread : public SimpleThread {
+ public:
+ BufferRequestThread(std::weak_ptr<OutputThreadInterface> parent,
+ std::shared_ptr<ICameraDeviceCallback> callbacks);
+
+ int requestBufferStart(const std::vector<HalStreamBuffer>&);
+ int waitForBufferRequestDone(
+ /*out*/ std::vector<HalStreamBuffer>*);
+
+ bool threadLoop() override;
+
+ private:
+ void waitForNextRequest();
+
+ const std::weak_ptr<OutputThreadInterface> mParent;
+ const std::shared_ptr<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
+ std::vector<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 SimpleThread {
+ public:
+ OutputThread(std::weak_ptr<OutputThreadInterface> parent, CroppingType,
+ const common::V1_0::helper::CameraMetadata&,
+ std::shared_ptr<BufferRequestThread> bufReqThread);
+ ~OutputThread();
+
+ Status allocateIntermediateBuffers(const Size& v4lSize, const Size& thumbSize,
+ const std::vector<Stream>& streams,
+ uint32_t blobBufferSize);
+ Status submitRequest(const std::shared_ptr<HalRequest>&);
+ void flush();
+ void dump(int fd);
+ 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:
+ static const int kFlushWaitTimeoutSec = 3; // 3 sec
+ static const int kReqWaitTimeoutMs = 33; // 33ms
+ static const int kReqWaitTimesMax = 90; // 33ms * 90 ~= 3 sec
+
+ // Methods to request output buffer in parallel
+ int requestBufferStart(const std::vector<HalStreamBuffer>&);
+ int waitForBufferRequestDone(
+ /*out*/ std::vector<HalStreamBuffer>*);
+
+ void waitForNextRequest(std::shared_ptr<HalRequest>* out);
+ void signalRequestDone();
+
+ int cropAndScaleLocked(std::shared_ptr<AllocatedFrame>& in, const Size& outSize,
+ YCbCrLayout* out);
+
+ int cropAndScaleThumbLocked(std::shared_ptr<AllocatedFrame>& in, const Size& outSize,
+ YCbCrLayout* out);
+
+ int createJpegLocked(HalStreamBuffer& halBuf,
+ const common::V1_0::helper::CameraMetadata& settings);
+
+ void clearIntermediateBuffers();
+
+ const std::weak_ptr<OutputThreadInterface> mParent;
+ const CroppingType mCroppingType;
+ const common::V1_0::helper::CameraMetadata mCameraCharacteristics;
+
+ mutable std::mutex mRequestListLock; // Protect access to mRequestList,
+ // mProcessingRequest and mProcessingFrameNumber
+ 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 mProcessingFrameNumber = 0;
+
+ // V4L2 frameIn
+ // (MJPG decode)-> mYu12Frame
+ // (Scale)-> mScaledYu12Frames
+ // (Format convert) -> output gralloc frames
+ mutable std::mutex mBufferLock; // Protect access to intermediate buffers
+ std::shared_ptr<AllocatedFrame> mYu12Frame;
+ std::shared_ptr<AllocatedFrame> mYu12ThumbFrame;
+ std::unordered_map<Size, std::shared_ptr<AllocatedFrame>, SizeHasher> mIntermediateBuffers;
+ std::unordered_map<Size, std::shared_ptr<AllocatedFrame>, SizeHasher> mScaledYu12Frames;
+ YCbCrLayout mYu12FrameLayout;
+ YCbCrLayout mYu12ThumbFrameLayout;
+ std::vector<uint8_t> mMuteTestPatternFrame;
+ uint32_t mTestPatternData[4] = {0, 0, 0, 0};
+ bool mCameraMuted = false;
+ uint32_t mBlobBufferSize = 0; // 0 -> HAL derive buffer size, else: use given size
+
+ std::string mExifMake;
+ std::string mExifModel;
+
+ const std::shared_ptr<BufferRequestThread> mBufferRequestThread;
+ };
+
+ private:
+ bool initialize();
+ // To init/close different version of output thread
+ void initOutputThread();
+ void closeOutputThread();
+ void closeOutputThreadImpl();
+
+ void close(bool callerIsDtor);
+ Status initStatus() const;
+ status_t initDefaultRequests();
+
+ status_t fillCaptureResult(common::V1_0::helper::CameraMetadata& md, nsecs_t timestamp);
+ int configureV4l2StreamLocked(const SupportedV4L2Format& fmt, double fps = 0.0);
+ int v4l2StreamOffLocked();
+
+ int setV4l2FpsLocked(double fps);
+
+ std::unique_ptr<V4L2Frame> dequeueV4l2FrameLocked(
+ /*out*/ nsecs_t* shutterTs); // Called with mLock held
+
+ void enqueueV4l2Frame(const std::shared_ptr<V4L2Frame>&);
+
+ // Check if input Stream is one of supported stream setting on this device
+ static bool isSupported(const Stream& stream,
+ const std::vector<SupportedV4L2Format>& supportedFormats,
+ const ExternalCameraConfig& cfg);
+
+ // Validate and import request's output buffers and acquire fence
+ Status importRequestLocked(const CaptureRequest& request,
+ std::vector<buffer_handle_t*>& allBufPtrs,
+ std::vector<int>& allFences);
+
+ Status importRequestLockedImpl(const CaptureRequest& request,
+ std::vector<buffer_handle_t*>& allBufPtrs,
+ std::vector<int>& allFences);
+
+ Status importBufferLocked(int32_t streamId, uint64_t bufId, buffer_handle_t buf,
+ /*out*/ buffer_handle_t** outBufPtr);
+ static void cleanupInflightFences(std::vector<int>& allFences, size_t numFences);
+ void cleanupBuffersLocked(int id);
+
+ void updateBufferCaches(const std::vector<BufferCache>& cachesToRemove);
+
+ Status processOneCaptureRequest(const CaptureRequest& request);
+ void notifyShutter(int32_t frameNumber, nsecs_t shutterTs);
+
+ void invokeProcessCaptureResultCallback(std::vector<CaptureResult>& results, bool tryWriteFmq);
+ Size getMaxJpegResolution() const;
+
+ Size getMaxThumbResolution() const;
+
+ int waitForV4L2BufferReturnLocked(std::unique_lock<std::mutex>& lk);
+
+ // 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 std::vector<int32_t>& offlineStreams,
+ /*out*/ std::vector<NotifyMsg>* msgs,
+ /*out*/ std::vector<CaptureResult>* results,
+ /*out*/ CameraOfflineSessionInfo* info,
+ /*out*/ std::shared_ptr<ICameraOfflineSession>* session);
+
+ bool supportOfflineLocked(int32_t streamId);
+
+ // Whether a request can be completely dropped when switching to offline
+ bool canDropRequest(const std::vector<int32_t>& offlineStreams,
+ std::shared_ptr<HalRequest> halReq);
+
+ void fillOfflineSessionInfo(const std::vector<int32_t>& offlineStreams,
+ std::deque<std::shared_ptr<HalRequest>>& offlineReqs,
+ const std::map<int, CirculatingBuffers>& circulatingBuffers,
+ /*out*/ CameraOfflineSessionInfo* info);
+
+ // Protect (most of) HIDL interface methods from synchronized-entering
+ mutable Mutex mInterfaceLock;
+
+ mutable Mutex mLock; // Protect all private members except otherwise noted
+ const std::shared_ptr<ICameraDeviceCallback> mCallback;
+ const ExternalCameraConfig& mCfg;
+ const common::V1_0::helper::CameraMetadata mCameraCharacteristics;
+ const std::vector<SupportedV4L2Format> mSupportedFormats;
+ const CroppingType mCroppingType;
+ const std::string mCameraId;
+
+ // Not protected by mLock, this is almost a const.
+ // Setup in constructor, reset in close() after OutputThread is joined
+ unique_fd mV4l2Fd;
+
+ // device is closed either
+ // - closed by user
+ // - init failed
+ // - camera disconnected
+ bool mClosed = false;
+ bool mInitialized = false;
+ bool mInitFail = false;
+ bool mFirstRequest = false;
+ common::V1_0::helper::CameraMetadata mLatestReqSetting;
+
+ bool mV4l2Streaming = false;
+ SupportedV4L2Format mV4l2StreamingFmt;
+ double mV4l2StreamingFps = 0.0;
+ size_t mV4L2BufferCount = 0;
+
+ static const int kBufferWaitTimeoutSec = 3; // TODO: handle long exposure (or not allowing)
+ std::mutex mV4l2BufferLock; // protect the buffer count and condition below
+ std::condition_variable mV4L2BufferReturned;
+ size_t mNumDequeuedV4l2Buffers = 0;
+ uint32_t mMaxV4L2BufferSize = 0;
+
+ // Not protected by mLock (but might be used when mLock is locked)
+ std::shared_ptr<OutputThread> mOutputThread;
+
+ // Stream ID -> Stream cache
+ std::unordered_map<int, Stream> mStreamMap;
+
+ std::mutex mInflightFramesLock; // protect mInflightFrames
+ std::unordered_set<uint32_t> mInflightFrames;
+
+ // Stream ID -> circulating buffers map
+ std::map<int, CirculatingBuffers> mCirculatingBuffers;
+ // Protect mCirculatingBuffers, must not lock mLock after acquiring this lock
+ mutable Mutex mCbsLock;
+
+ std::mutex mAfTriggerLock; // protect mAfTrigger
+ bool mAfTrigger = false;
+
+ uint32_t mBlobBufferSize = 0;
+
+ static HandleImporter sHandleImporter;
+
+ bool mSupportBufMgr;
+ std::shared_ptr<BufferRequestThread> mBufferRequestThread;
+
+ /* Beginning of members not changed after initialize() */
+ using RequestMetadataQueue = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+ std::unique_ptr<RequestMetadataQueue> mRequestMetadataQueue;
+ using ResultMetadataQueue = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+ std::shared_ptr<ResultMetadataQueue> mResultMetadataQueue;
+
+ // Protect against invokeProcessCaptureResultCallback()
+ Mutex mProcessCaptureResultLock;
+
+ // tracks last seen stream config counter
+ int32_t mLastStreamConfigCounter = -1;
+
+ std::unordered_map<RequestTemplate, CameraMetadata> mDefaultRequests;
+
+ const Size mMaxThumbResolution;
+ const Size mMaxJpegResolution;
+
+ std::string mExifMake;
+ std::string mExifModel;
+ /* End of members not changed after initialize() */
+};
+
+} // namespace implementation
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+#endif // HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_EXTERNALCAMERADEVICESESSION_H_
diff --git a/camera/device/default/ExternalCameraOfflineSession.cpp b/camera/device/default/ExternalCameraOfflineSession.cpp
new file mode 100644
index 0000000..4c7f732
--- /dev/null
+++ b/camera/device/default/ExternalCameraOfflineSession.cpp
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ExtCamOfflnSsn"
+#include <android/log.h>
+
+#include "ExternalCameraOfflineSession.h"
+
+#include <aidl/android/hardware/camera/device/BufferStatus.h>
+#include <aidl/android/hardware/camera/device/ErrorMsg.h>
+#include <aidl/android/hardware/camera/device/ShutterMsg.h>
+#include <aidl/android/hardware/camera/device/StreamBuffer.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <convert.h>
+#include <linux/videodev2.h>
+#include <sync/sync.h>
+#include <utils/Trace.h>
+
+#define HAVE_JPEG // required for libyuv.h to export MJPEG decode APIs
+#include <libyuv.h>
+
+namespace {
+
+// Size of request/result metadata fast message queue. Change to 0 to always use hwbinder buffer.
+constexpr size_t kMetadataMsgQueueSize = 1 << 18 /* 256kB */;
+
+} // anonymous namespace
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace implementation {
+
+using ::aidl::android::hardware::camera::device::BufferStatus;
+using ::aidl::android::hardware::camera::device::ErrorMsg;
+using ::aidl::android::hardware::camera::device::ShutterMsg;
+using ::aidl::android::hardware::camera::device::StreamBuffer;
+
+// Static instance
+HandleImporter ExternalCameraOfflineSession::sHandleImporter;
+
+ExternalCameraOfflineSession::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 std::vector<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;
+}
+
+Status ExternalCameraOfflineSession::importBuffer(int32_t streamId, uint64_t bufId,
+ buffer_handle_t buf,
+ buffer_handle_t** outBufPtr) {
+ Mutex::Autolock _l(mCbsLock);
+ return importBufferImpl(mCirculatingBuffers, sHandleImporter, streamId, bufId, buf, outBufPtr);
+}
+
+Status ExternalCameraOfflineSession::processCaptureResult(std::shared_ptr<HalRequest>& req) {
+ ATRACE_CALL();
+ // Fill output buffers
+ std::vector<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++) {
+ StreamBuffer& outputBuffer = result.outputBuffers[i];
+ outputBuffer.streamId = req->buffers[i].streamId;
+ outputBuffer.bufferId = req->buffers[i].bufferId;
+ if (req->buffers[i].fenceTimeout) {
+ outputBuffer.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 = android::makeToAidl(handle);
+ }
+ 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;
+ outputBuffer.releaseFence = android::makeToAidl(handle);
+ }
+ }
+ }
+
+ // Fill capture result metadata
+ fillCaptureResult(req->setting, req->shutterTs);
+ const camera_metadata_t* rawResult = req->setting.getAndLock();
+ convertToAidl(rawResult, &result.result);
+ req->setting.unlock(rawResult);
+
+ // Callback into framework
+ invokeProcessCaptureResultCallback(results, /* tryWriteFmq */ true);
+ freeReleaseFences(results);
+ 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 fillCaptureResultCommon(md, timestamp, activeArraySize);
+}
+void ExternalCameraOfflineSession::invokeProcessCaptureResultCallback(
+ std::vector<CaptureResult>& results, bool tryWriteFmq) {
+ if (mProcessCaptureResultLock.tryLock() != OK) {
+ const nsecs_t NS_TO_SECOND = 1E9;
+ 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.metadata.empty()) {
+ if (mResultMetadataQueue->write(
+ reinterpret_cast<int8_t*>(result.result.metadata.data()),
+ result.result.metadata.size())) {
+ result.fmqResultSize = result.result.metadata.size();
+ result.result.metadata.clear();
+ } 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 : %d:%d", __FUNCTION__, status.getExceptionCode(),
+ status.getServiceSpecificError());
+ }
+
+ mProcessCaptureResultLock.unlock();
+}
+
+Status ExternalCameraOfflineSession::processCaptureRequestError(
+ const std::shared_ptr<HalRequest>& req, std::vector<NotifyMsg>* outMsgs,
+ std::vector<CaptureResult>* outResults) {
+ ATRACE_CALL();
+
+ if (outMsgs == nullptr) {
+ notifyError(/*frameNum*/ req->frameNumber, /*stream*/ -1, ErrorCode::ERROR_REQUEST);
+ } else {
+ NotifyMsg shutter;
+ shutter.set<NotifyMsg::Tag::shutter>(ShutterMsg{
+ .frameNumber = req->frameNumber,
+ .timestamp = req->shutterTs,
+ });
+
+ NotifyMsg error;
+ error.set<NotifyMsg::Tag::error>(ErrorMsg{.frameNumber = req->frameNumber,
+ .errorStreamId = -1,
+ .errorCode = ErrorCode::ERROR_REQUEST});
+ outMsgs->push_back(shutter);
+ outMsgs->push_back(error);
+ }
+
+ // Fill output buffers
+ CaptureResult result;
+ 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++) {
+ StreamBuffer& outputBuffer = result.outputBuffers[i];
+ outputBuffer.streamId = req->buffers[i].streamId;
+ outputBuffer.bufferId = req->buffers[i].bufferId;
+ outputBuffer.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;
+ outputBuffer.releaseFence = makeToAidl(handle);
+ }
+ }
+
+ if (outResults == nullptr) {
+ // Callback into framework
+ std::vector<CaptureResult> results(1);
+ results[0] = std::move(result);
+ invokeProcessCaptureResultCallback(results, /* tryWriteFmq */ true);
+ freeReleaseFences(results);
+ } else {
+ outResults->push_back(std::move(result));
+ }
+ return Status::OK;
+}
+
+ssize_t ExternalCameraOfflineSession::getJpegBufferSize(int32_t, int32_t) const {
+ // Empty implementation here as the jpeg buffer size is passed in by ctor
+ return 0;
+}
+
+void ExternalCameraOfflineSession::notifyError(int32_t frameNumber, int32_t streamId,
+ ErrorCode ec) {
+ NotifyMsg msg;
+ msg.set<NotifyMsg::Tag::error>(
+ ErrorMsg{.frameNumber = frameNumber, .errorStreamId = streamId, .errorCode = ec});
+ mCallback->notify({msg});
+}
+
+ScopedAStatus ExternalCameraOfflineSession::setCallback(
+ const std::shared_ptr<ICameraDeviceCallback>& in_cb) {
+ Mutex::Autolock _il(mInterfaceLock);
+ if (mCallback != nullptr && in_cb != nullptr) {
+ ALOGE("%s: callback must not be set twice!", __FUNCTION__);
+ return fromStatus(Status::OK);
+ }
+ mCallback = in_cb;
+
+ initOutputThread();
+
+ if (mOutputThread == nullptr) {
+ ALOGE("%s: init OutputThread failed!", __FUNCTION__);
+ }
+ return fromStatus(Status::OK);
+}
+void ExternalCameraOfflineSession::initOutputThread() {
+ if (mOutputThread != nullptr) {
+ ALOGE("%s: OutputThread already exist!", __FUNCTION__);
+ return;
+ }
+
+ // Grab a shared_ptr to 'this' from ndk::SharedRefBase::ref()
+ std::shared_ptr<ExternalCameraOfflineSession> thiz = ref<ExternalCameraOfflineSession>();
+
+ mBufferRequestThread = std::make_shared<ExternalCameraDeviceSession::BufferRequestThread>(
+ /*parent=*/thiz, mCallback);
+ mBufferRequestThread->run();
+
+ mOutputThread = std::make_shared<OutputThread>(/*parent=*/thiz, mCroppingType, mChars,
+ mBufferRequestThread, mOfflineReqs);
+
+ mOutputThread->setExifMakeModel(mExifMake, mExifModel);
+
+ Size inputSize = {mOfflineReqs[0]->frameIn->mWidth, mOfflineReqs[0]->frameIn->mHeight};
+ Size maxThumbSize = getMaxThumbnailResolution(mChars);
+ mOutputThread->allocateIntermediateBuffers(inputSize, maxThumbSize, mOfflineStreams,
+ mBlobBufferSize);
+
+ mOutputThread->run();
+}
+
+ScopedAStatus ExternalCameraOfflineSession::getCaptureResultMetadataQueue(
+ MQDescriptor<int8_t, SynchronizedReadWrite>* _aidl_return) {
+ Mutex::Autolock _il(mInterfaceLock);
+ *_aidl_return = mResultMetadataQueue->dupeDesc();
+ return fromStatus(Status::OK);
+}
+
+ScopedAStatus ExternalCameraOfflineSession::close() {
+ Mutex::Autolock _il(mInterfaceLock);
+ {
+ Mutex::Autolock _l(mLock);
+ if (mClosed) {
+ ALOGW("%s: offline session already closed!", __FUNCTION__);
+ return fromStatus(Status::OK);
+ }
+ }
+ if (mBufferRequestThread != nullptr) {
+ mBufferRequestThread->requestExitAndWait();
+ mBufferRequestThread.reset();
+ }
+ if (mOutputThread) {
+ mOutputThread->flush();
+ mOutputThread->requestExitAndWait();
+ mOutputThread.reset();
+ }
+
+ Mutex::Autolock _l(mLock);
+ // free all buffers
+ {
+ Mutex::Autolock _cbl(mCbsLock);
+ for (auto& stream : mOfflineStreams) {
+ cleanupBuffersLocked(stream.id);
+ }
+ }
+ mCallback.reset();
+ mClosed = true;
+ return fromStatus(Status::OK);
+}
+void ExternalCameraOfflineSession::cleanupBuffersLocked(int32_t id) {
+ for (auto& pair : mCirculatingBuffers.at(id)) {
+ sHandleImporter.freeBuffer(pair.second);
+ }
+ mCirculatingBuffers[id].clear();
+ mCirculatingBuffers.erase(id);
+}
+
+bool ExternalCameraOfflineSession::OutputThread::threadLoop() {
+ auto parent = mParent.lock();
+ 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 convRes = 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 (convRes != 0) {
+ // For some webcam, the first few V4L2 frames might be malformed...
+ ALOGE("%s: Convert V4L2 frame to YU12 failed! res %d", __FUNCTION__, convRes);
+ 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), static_cast<uint64_t>(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), static_cast<uint64_t>(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 = 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 = 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;
+}
+
+} // namespace implementation
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/camera/device/default/ExternalCameraOfflineSession.h b/camera/device/default/ExternalCameraOfflineSession.h
new file mode 100644
index 0000000..5795c95
--- /dev/null
+++ b/camera/device/default/ExternalCameraOfflineSession.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_EXTERNALCAMERAOFFLINESESSION_H_
+#define HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_EXTERNALCAMERAOFFLINESESSION_H_
+
+#include <ExternalCameraDeviceSession.h>
+#include <ExternalCameraUtils.h>
+#include <aidl/android/hardware/camera/common/Status.h>
+#include <aidl/android/hardware/camera/device/BnCameraOfflineSession.h>
+#include <aidl/android/hardware/camera/device/Stream.h>
+#include <fmq/AidlMessageQueue.h>
+#include <utils/RefBase.h>
+#include <deque>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace implementation {
+
+using ::aidl::android::hardware::camera::common::Status;
+using ::aidl::android::hardware::camera::device::BnCameraOfflineSession;
+using ::aidl::android::hardware::camera::device::ICameraDeviceCallback;
+using ::aidl::android::hardware::camera::device::Stream;
+using ::aidl::android::hardware::common::fmq::MQDescriptor;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+
+class ExternalCameraOfflineSession : public BnCameraOfflineSession,
+ public virtual RefBase,
+ public virtual OutputThreadInterface {
+ public:
+ 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 std::vector<Stream>& offlineStreams,
+ std::deque<std::shared_ptr<HalRequest>>& offlineReqs,
+ const std::map<int, CirculatingBuffers>& circulatingBuffers);
+
+ ~ExternalCameraOfflineSession() override;
+
+ bool initialize();
+
+ // Methods from OutputThreadInterface
+ Status importBuffer(int32_t streamId, uint64_t bufId, buffer_handle_t buf,
+ /*out*/ buffer_handle_t** outBufPtr) override;
+
+ Status processCaptureResult(std::shared_ptr<HalRequest>&) override;
+
+ Status processCaptureRequestError(const std::shared_ptr<HalRequest>&,
+ /*out*/ std::vector<NotifyMsg>* msgs,
+ /*out*/ std::vector<CaptureResult>* results) override;
+
+ ssize_t getJpegBufferSize(int32_t width, int32_t height) const override;
+
+ void notifyError(int32_t frameNumber, int32_t streamId, ErrorCode ec) override;
+ // End of OutputThreadInterface methods
+
+ ScopedAStatus setCallback(const std::shared_ptr<ICameraDeviceCallback>& in_cb) override;
+ ScopedAStatus getCaptureResultMetadataQueue(
+ MQDescriptor<int8_t, SynchronizedReadWrite>* _aidl_return) override;
+ ScopedAStatus close() override;
+
+ private:
+ class OutputThread : public ExternalCameraDeviceSession::OutputThread {
+ public:
+ OutputThread(std::weak_ptr<OutputThreadInterface> parent, CroppingType ct,
+ const common::V1_0::helper::CameraMetadata& chars,
+ std::shared_ptr<ExternalCameraDeviceSession::BufferRequestThread> bufReqThread,
+ std::deque<std::shared_ptr<HalRequest>>& offlineReqs)
+ : ExternalCameraDeviceSession::OutputThread(std::move(parent), ct, chars,
+ std::move(bufReqThread)),
+ mOfflineReqs(offlineReqs) {}
+
+ bool threadLoop() override;
+
+ protected:
+ std::deque<std::shared_ptr<HalRequest>> mOfflineReqs;
+ }; // OutputThread
+
+ status_t fillCaptureResult(common::V1_0::helper::CameraMetadata md, nsecs_t timestamp);
+ void invokeProcessCaptureResultCallback(std::vector<CaptureResult>& results, bool tryWriteFmq);
+ void initOutputThread();
+ void cleanupBuffersLocked(int32_t 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 std::vector<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 = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+ std::shared_ptr<ResultMetadataQueue> mResultMetadataQueue;
+
+ // Protect against invokeProcessCaptureResultCallback()
+ Mutex mProcessCaptureResultLock;
+
+ std::shared_ptr<ICameraDeviceCallback> mCallback;
+
+ std::shared_ptr<ExternalCameraDeviceSession::BufferRequestThread> mBufferRequestThread;
+ std::shared_ptr<OutputThread> mOutputThread;
+};
+
+} // namespace implementation
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+#endif // HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_EXTERNALCAMERAOFFLINESESSION_H_
diff --git a/camera/device/default/ExternalCameraUtils.cpp b/camera/device/default/ExternalCameraUtils.cpp
new file mode 100644
index 0000000..cfb95f2
--- /dev/null
+++ b/camera/device/default/ExternalCameraUtils.cpp
@@ -0,0 +1,860 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ExtCamUtils"
+// #define LOG_NDEBUG 0
+
+#include "ExternalCameraUtils.h"
+
+#include <aidlcommonsupport/NativeHandle.h>
+#include <jpeglib.h>
+#include <linux/videodev2.h>
+#include <log/log.h>
+#include <algorithm>
+#include <cinttypes>
+#include <cmath>
+
+#define HAVE_JPEG // required for libyuv.h to export MJPEG decode APIs
+#include <libyuv.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+
+namespace external {
+namespace common {
+
+namespace {
+const int kDefaultCameraIdOffset = 100;
+const int kDefaultJpegBufSize = 5 << 20; // 5MB
+const int kDefaultNumVideoBuffer = 4;
+const int kDefaultNumStillBuffer = 2;
+const int kDefaultOrientation = 0; // suitable for natural landscape displays like tablet/TV
+ // For phone devices 270 is better
+} // anonymous namespace
+
+const char* ExternalCameraConfig::kDefaultCfgPath = "/vendor/etc/external_camera_config.xml";
+
+ExternalCameraConfig ExternalCameraConfig::loadFromCfg(const char* cfgPath) {
+ using namespace tinyxml2;
+ ExternalCameraConfig ret;
+
+ XMLDocument configXml;
+ XMLError err = configXml.LoadFile(cfgPath);
+ if (err != XML_SUCCESS) {
+ ALOGE("%s: Unable to load external camera config file '%s'. Error: %s", __FUNCTION__,
+ cfgPath, XMLDocument::ErrorIDToName(err));
+ return ret;
+ } else {
+ ALOGI("%s: load external camera config succeeded!", __FUNCTION__);
+ }
+
+ XMLElement* extCam = configXml.FirstChildElement("ExternalCamera");
+ if (extCam == nullptr) {
+ ALOGI("%s: no external camera config specified", __FUNCTION__);
+ return ret;
+ }
+
+ XMLElement* providerCfg = extCam->FirstChildElement("Provider");
+ if (providerCfg == nullptr) {
+ ALOGI("%s: no external camera provider config specified", __FUNCTION__);
+ return ret;
+ }
+
+ XMLElement* cameraIdOffset = providerCfg->FirstChildElement("CameraIdOffset");
+ if (cameraIdOffset != nullptr) {
+ ret.cameraIdOffset = std::atoi(cameraIdOffset->GetText());
+ }
+
+ XMLElement* ignore = providerCfg->FirstChildElement("ignore");
+ if (ignore == nullptr) {
+ ALOGI("%s: no internal ignored device specified", __FUNCTION__);
+ return ret;
+ }
+
+ XMLElement* id = ignore->FirstChildElement("id");
+ while (id != nullptr) {
+ const char* text = id->GetText();
+ if (text != nullptr) {
+ ret.mInternalDevices.insert(text);
+ ALOGI("%s: device %s will be ignored by external camera provider", __FUNCTION__, text);
+ }
+ id = id->NextSiblingElement("id");
+ }
+
+ XMLElement* deviceCfg = extCam->FirstChildElement("Device");
+ if (deviceCfg == nullptr) {
+ ALOGI("%s: no external camera device config specified", __FUNCTION__);
+ return ret;
+ }
+
+ XMLElement* jpegBufSz = deviceCfg->FirstChildElement("MaxJpegBufferSize");
+ if (jpegBufSz == nullptr) {
+ ALOGI("%s: no max jpeg buffer size specified", __FUNCTION__);
+ } else {
+ ret.maxJpegBufSize = jpegBufSz->UnsignedAttribute("bytes", /*Default*/ kDefaultJpegBufSize);
+ }
+
+ XMLElement* numVideoBuf = deviceCfg->FirstChildElement("NumVideoBuffers");
+ if (numVideoBuf == nullptr) {
+ ALOGI("%s: no num video buffers specified", __FUNCTION__);
+ } else {
+ ret.numVideoBuffers =
+ numVideoBuf->UnsignedAttribute("count", /*Default*/ kDefaultNumVideoBuffer);
+ }
+
+ XMLElement* numStillBuf = deviceCfg->FirstChildElement("NumStillBuffers");
+ if (numStillBuf == nullptr) {
+ ALOGI("%s: no num still buffers specified", __FUNCTION__);
+ } else {
+ ret.numStillBuffers =
+ numStillBuf->UnsignedAttribute("count", /*Default*/ kDefaultNumStillBuffer);
+ }
+
+ XMLElement* fpsList = deviceCfg->FirstChildElement("FpsList");
+ if (fpsList == nullptr) {
+ ALOGI("%s: no fps list specified", __FUNCTION__);
+ } else {
+ if (!updateFpsList(fpsList, ret.fpsLimits)) {
+ return ret;
+ }
+ }
+
+ XMLElement* depth = deviceCfg->FirstChildElement("Depth16Supported");
+ if (depth == nullptr) {
+ ret.depthEnabled = false;
+ ALOGI("%s: depth output is not enabled", __FUNCTION__);
+ } else {
+ ret.depthEnabled = depth->BoolAttribute("enabled", false);
+ }
+
+ if (ret.depthEnabled) {
+ XMLElement* depthFpsList = deviceCfg->FirstChildElement("DepthFpsList");
+ if (depthFpsList == nullptr) {
+ ALOGW("%s: no depth fps list specified", __FUNCTION__);
+ } else {
+ if (!updateFpsList(depthFpsList, ret.depthFpsLimits)) {
+ return ret;
+ }
+ }
+ }
+
+ XMLElement* minStreamSize = deviceCfg->FirstChildElement("MinimumStreamSize");
+ if (minStreamSize == nullptr) {
+ ALOGI("%s: no minimum stream size specified", __FUNCTION__);
+ } else {
+ ret.minStreamSize = {
+ static_cast<int32_t>(minStreamSize->UnsignedAttribute("width", /*Default*/ 0)),
+ static_cast<int32_t>(minStreamSize->UnsignedAttribute("height", /*Default*/ 0))};
+ }
+
+ XMLElement* orientation = deviceCfg->FirstChildElement("Orientation");
+ if (orientation == nullptr) {
+ ALOGI("%s: no sensor orientation specified", __FUNCTION__);
+ } else {
+ ret.orientation = orientation->IntAttribute("degree", /*Default*/ kDefaultOrientation);
+ }
+
+ ALOGI("%s: external camera cfg loaded: maxJpgBufSize %d,"
+ " num video buffers %d, num still buffers %d, orientation %d",
+ __FUNCTION__, ret.maxJpegBufSize, ret.numVideoBuffers, ret.numStillBuffers,
+ ret.orientation);
+ for (const auto& limit : ret.fpsLimits) {
+ ALOGI("%s: fpsLimitList: %dx%d@%f", __FUNCTION__, limit.size.width, limit.size.height,
+ limit.fpsUpperBound);
+ }
+ for (const auto& limit : ret.depthFpsLimits) {
+ ALOGI("%s: depthFpsLimitList: %dx%d@%f", __FUNCTION__, limit.size.width, limit.size.height,
+ limit.fpsUpperBound);
+ }
+ ALOGI("%s: minStreamSize: %dx%d", __FUNCTION__, ret.minStreamSize.width,
+ ret.minStreamSize.height);
+ return ret;
+}
+
+bool ExternalCameraConfig::updateFpsList(tinyxml2::XMLElement* fpsList,
+ std::vector<FpsLimitation>& fpsLimits) {
+ using namespace tinyxml2;
+ std::vector<FpsLimitation> limits;
+ XMLElement* row = fpsList->FirstChildElement("Limit");
+ while (row != nullptr) {
+ FpsLimitation prevLimit{{0, 0}, 1000.0};
+ FpsLimitation limit = {
+ {/* width */ static_cast<int32_t>(row->UnsignedAttribute("width", /*Default*/ 0)),
+ /* height */ static_cast<int32_t>(
+ row->UnsignedAttribute("height", /*Default*/ 0))},
+ /* fpsUpperBound */ row->DoubleAttribute("fpsBound", /*Default*/ 1000.0)};
+ if (limit.size.width <= prevLimit.size.width ||
+ limit.size.height <= prevLimit.size.height ||
+ limit.fpsUpperBound >= prevLimit.fpsUpperBound) {
+ ALOGE("%s: FPS limit list must have increasing size and decreasing fps!"
+ " Prev %dx%d@%f, Current %dx%d@%f",
+ __FUNCTION__, prevLimit.size.width, prevLimit.size.height,
+ prevLimit.fpsUpperBound, limit.size.width, limit.size.height,
+ limit.fpsUpperBound);
+ return false;
+ }
+ limits.push_back(limit);
+ row = row->NextSiblingElement("Limit");
+ }
+ fpsLimits = limits;
+ return true;
+}
+
+ExternalCameraConfig::ExternalCameraConfig()
+ : cameraIdOffset(kDefaultCameraIdOffset),
+ maxJpegBufSize(kDefaultJpegBufSize),
+ numVideoBuffers(kDefaultNumVideoBuffer),
+ numStillBuffers(kDefaultNumStillBuffer),
+ depthEnabled(false),
+ orientation(kDefaultOrientation) {
+ fpsLimits.push_back({/* size */ {/* width */ 640, /* height */ 480}, /* fpsUpperBound */ 30.0});
+ fpsLimits.push_back({/* size */ {/* width */ 1280, /* height */ 720}, /* fpsUpperBound */ 7.5});
+ fpsLimits.push_back(
+ {/* size */ {/* width */ 1920, /* height */ 1080}, /* fpsUpperBound */ 5.0});
+ minStreamSize = {0, 0};
+}
+
+} // namespace common
+} // namespace external
+
+namespace device {
+namespace implementation {
+
+double SupportedV4L2Format::FrameRate::getFramesPerSecond() const {
+ return static_cast<double>(durationDenominator) / durationNumerator;
+}
+
+Frame::Frame(uint32_t width, uint32_t height, uint32_t fourcc)
+ : mWidth(width), mHeight(height), mFourcc(fourcc) {}
+Frame::~Frame() {}
+
+V4L2Frame::V4L2Frame(uint32_t w, uint32_t h, uint32_t fourcc, int bufIdx, int fd, uint32_t dataSize,
+ uint64_t offset)
+ : Frame(w, h, fourcc), mBufferIndex(bufIdx), mFd(fd), mDataSize(dataSize), mOffset(offset) {}
+
+V4L2Frame::~V4L2Frame() {
+ unmap();
+}
+
+int V4L2Frame::getData(uint8_t** outData, size_t* dataSize) {
+ return map(outData, dataSize);
+}
+
+int V4L2Frame::map(uint8_t** data, size_t* dataSize) {
+ if (data == nullptr || dataSize == nullptr) {
+ ALOGI("%s: V4L2 buffer map bad argument: data %p, dataSize %p", __FUNCTION__, data,
+ dataSize);
+ return -EINVAL;
+ }
+
+ std::lock_guard<std::mutex> lk(mLock);
+ if (!mMapped) {
+ void* addr = mmap(nullptr, mDataSize, PROT_READ, MAP_SHARED, mFd, mOffset);
+ if (addr == MAP_FAILED) {
+ ALOGE("%s: V4L2 buffer map failed: %s", __FUNCTION__, strerror(errno));
+ return -EINVAL;
+ }
+ mData = static_cast<uint8_t*>(addr);
+ mMapped = true;
+ }
+ *data = mData;
+ *dataSize = mDataSize;
+ ALOGV("%s: V4L map FD %d, data %p size %zu", __FUNCTION__, mFd, mData, mDataSize);
+ return 0;
+}
+
+int V4L2Frame::unmap() {
+ std::lock_guard<std::mutex> lk(mLock);
+ if (mMapped) {
+ ALOGV("%s: V4L unmap data %p size %zu", __FUNCTION__, mData, mDataSize);
+ if (munmap(mData, mDataSize) != 0) {
+ ALOGE("%s: V4L2 buffer unmap failed: %s", __FUNCTION__, strerror(errno));
+ return -EINVAL;
+ }
+ mMapped = false;
+ }
+ return 0;
+}
+
+AllocatedFrame::AllocatedFrame(uint32_t w, uint32_t h) : Frame(w, h, V4L2_PIX_FMT_YUV420) {}
+AllocatedFrame::~AllocatedFrame() {}
+
+int AllocatedFrame::getData(uint8_t** outData, size_t* dataSize) {
+ YCbCrLayout layout;
+ int ret = allocate(&layout);
+ if (ret != 0) {
+ return ret;
+ }
+ *outData = mData.data();
+ *dataSize = mBufferSize;
+ return 0;
+}
+
+int AllocatedFrame::allocate(YCbCrLayout* out) {
+ std::lock_guard<std::mutex> lk(mLock);
+ if ((mWidth % 2) || (mHeight % 2)) {
+ ALOGE("%s: bad dimension %dx%d (not multiple of 2)", __FUNCTION__, mWidth, mHeight);
+ return -EINVAL;
+ }
+
+ // This frame might be sent to jpeglib to be encoded. Since AllocatedFrame only contains YUV420,
+ // jpeglib expects height and width of Y component to be an integral multiple of 2*DCTSIZE,
+ // and heights and widths of Cb and Cr components to be an integral multiple of DCTSIZE. If the
+ // image size does not meet this requirement, libjpeg expects its input to be padded to meet the
+ // constraints. This padding is removed from the final encoded image so the content in the
+ // padding doesn't matter. What matters is that the memory is accessible to jpeglib at the time
+ // of encoding.
+ // For example, if the image size is 1500x844 and DCTSIZE is 8, jpeglib expects a YUV 420
+ // frame with components of following sizes:
+ // Y: 1504x848 because 1504 and 848 are the next smallest multiples of 2*8
+ // Cb/Cr: 752x424 which are the next smallest multiples of 8
+
+ // jpeglib takes an array of row pointers which makes vertical padding trivial when setting up
+ // the pointers. Padding horizontally is a bit more complicated. AllocatedFrame holds the data
+ // in a flattened buffer, which means memory accesses past a row will flow into the next logical
+ // row. For any row of a component, we can consider the first few bytes of the next row as
+ // padding for the current one. This is true for Y and Cb components and all but last row of the
+ // Cr component. Reading past the last row of Cr component will lead to undefined behavior as
+ // libjpeg attempts to read memory past the allocated buffer. To prevent undefined behavior,
+ // the buffer allocated here is padded such that libjpeg never accesses unallocated memory when
+ // reading the last row. Effectively, we only need to ensure that the last row of Cr component
+ // has width that is an integral multiple of DCTSIZE.
+
+ size_t dataSize = mWidth * mHeight * 3 / 2; // YUV420
+
+ size_t cbWidth = mWidth / 2;
+ size_t requiredCbWidth = DCTSIZE * ((cbWidth + DCTSIZE - 1) / DCTSIZE);
+ size_t padding = requiredCbWidth - cbWidth;
+ size_t finalSize = dataSize + padding;
+
+ if (mData.size() != finalSize) {
+ mData.resize(finalSize);
+ mBufferSize = dataSize;
+ }
+
+ if (out != nullptr) {
+ out->y = mData.data();
+ out->yStride = mWidth;
+ uint8_t* cbStart = mData.data() + mWidth * mHeight;
+ uint8_t* crStart = cbStart + mWidth * mHeight / 4;
+ out->cb = cbStart;
+ out->cr = crStart;
+ out->cStride = mWidth / 2;
+ out->chromaStep = 1;
+ }
+ return 0;
+}
+
+int AllocatedFrame::getLayout(YCbCrLayout* out) {
+ IMapper::Rect noCrop = {0, 0, static_cast<int32_t>(mWidth), static_cast<int32_t>(mHeight)};
+ return getCroppedLayout(noCrop, out);
+}
+
+int AllocatedFrame::getCroppedLayout(const IMapper::Rect& rect, YCbCrLayout* out) {
+ if (out == nullptr) {
+ ALOGE("%s: null out", __FUNCTION__);
+ return -1;
+ }
+
+ std::lock_guard<std::mutex> lk(mLock);
+ if ((rect.left + rect.width) > static_cast<int>(mWidth) ||
+ (rect.top + rect.height) > static_cast<int>(mHeight) || (rect.left % 2) || (rect.top % 2) ||
+ (rect.width % 2) || (rect.height % 2)) {
+ ALOGE("%s: bad rect left %d top %d w %d h %d", __FUNCTION__, rect.left, rect.top,
+ rect.width, rect.height);
+ return -1;
+ }
+
+ out->y = mData.data() + mWidth * rect.top + rect.left;
+ out->yStride = mWidth;
+ uint8_t* cbStart = mData.data() + mWidth * mHeight;
+ uint8_t* crStart = cbStart + mWidth * mHeight / 4;
+ out->cb = cbStart + mWidth * rect.top / 4 + rect.left / 2;
+ out->cr = crStart + mWidth * rect.top / 4 + rect.left / 2;
+ out->cStride = mWidth / 2;
+ out->chromaStep = 1;
+ return 0;
+}
+
+bool isAspectRatioClose(float ar1, float ar2) {
+ constexpr float kAspectRatioMatchThres = 0.025f; // This threshold is good enough to
+ // distinguish 4:3/16:9/20:9 1.33/1.78/2
+ return std::abs(ar1 - ar2) < kAspectRatioMatchThres;
+}
+
+aidl::android::hardware::camera::common::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) {
+ using ::aidl::android::hardware::camera::common::Status;
+ if (buf == nullptr && bufId == BUFFER_ID_NO_BUFFER) {
+ 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 = static_cast<int32_t>(inW);
+ out->height = static_cast<int32_t>(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 = static_cast<int32_t>((inH - scaledOutH) / 2) & ~0x1;
+ out->width = static_cast<int32_t>(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 = static_cast<int32_t>((inW - scaledOutW) / 2) & ~0x1;
+ out->top = 0;
+ out->width = static_cast<int32_t>(scaledOutW);
+ out->height = static_cast<int32_t>(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), static_cast<int32_t>(in.yStride),
+ static_cast<uint8_t*>(in.cb), static_cast<int32_t>(in.cStride),
+ static_cast<uint8_t*>(in.cr), static_cast<int32_t>(in.cStride),
+ static_cast<uint8_t*>(out.y), static_cast<int32_t>(out.yStride),
+ static_cast<uint8_t*>(out.cr), static_cast<int32_t>(out.cStride),
+ static_cast<int32_t>(sz.width), static_cast<int32_t>(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), static_cast<int32_t>(in.yStride),
+ static_cast<uint8_t*>(in.cb), static_cast<int32_t>(in.cStride),
+ static_cast<uint8_t*>(in.cr), static_cast<int32_t>(in.cStride),
+ static_cast<uint8_t*>(out.y), static_cast<int32_t>(out.yStride),
+ static_cast<uint8_t*>(out.cb), static_cast<int32_t>(out.cStride),
+ static_cast<int32_t>(sz.width), static_cast<int32_t>(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), static_cast<int32_t>(in.yStride),
+ static_cast<uint8_t*>(in.cb), static_cast<int32_t>(in.cStride),
+ static_cast<uint8_t*>(in.cr), static_cast<int32_t>(in.cStride),
+ static_cast<uint8_t*>(out.y), static_cast<int32_t>(out.yStride),
+ static_cast<uint8_t*>(out.cb), static_cast<int32_t>(out.cStride),
+ static_cast<uint8_t*>(out.cr), static_cast<int32_t>(out.cStride),
+ static_cast<int32_t>(sz.width), static_cast<int32_t>(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, 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;
+
+ /* Start the compressor */
+ jpeg_start_compress(&cinfo, TRUE);
+
+ /* Let's not hardcode YUV420 in 6 places... 5 was enough */
+ int maxVSampFactor = cinfo.max_v_samp_factor;
+ int cVSubSampling = cinfo.comp_info[0].v_samp_factor / cinfo.comp_info[1].v_samp_factor;
+
+ /* Compute our macroblock height, so we can pad our input to be vertically
+ * macroblock aligned. No need to for horizontal alignment since AllocatedFrame already
+ * pads horizontally */
+
+ 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* pcb = static_cast<uint8_t*>(inLayout.cb);
+ uint8_t* pcr = static_cast<uint8_t*>(inLayout.cr);
+
+ for (int32_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);
+ cbLines[i] = static_cast<JSAMPROW>(pcb + li * inLayout.cStride);
+ crLines[i] = static_cast<JSAMPROW>(pcr + 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{.width = entry.data.i32[i], .height = 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(std::vector<CaptureResult>& results) {
+ for (auto& result : results) {
+ native_handle_t* inputReleaseFence =
+ ::android::makeFromAidl(result.inputBuffer.releaseFence);
+ if (inputReleaseFence != nullptr) {
+ native_handle_close(inputReleaseFence);
+ native_handle_delete(inputReleaseFence);
+ }
+ for (auto& buf : result.outputBuffers) {
+ native_handle_t* outReleaseFence = ::android::makeFromAidl(buf.releaseFence);
+ if (outReleaseFence != nullptr) {
+ native_handle_close(outReleaseFence);
+ native_handle_delete(outReleaseFence);
+ }
+ }
+ }
+}
+
+#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(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
+
+AllocatedV4L2Frame::AllocatedV4L2Frame(std::shared_ptr<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);
+}
+
+AllocatedV4L2Frame::~AllocatedV4L2Frame() {}
+
+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;
+}
+
+} // namespace implementation
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/camera/device/default/ExternalCameraUtils.h b/camera/device/default/ExternalCameraUtils.h
new file mode 100644
index 0000000..b37933c
--- /dev/null
+++ b/camera/device/default/ExternalCameraUtils.h
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_EXTERNALCAMERAUTILS_H_
+#define HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_EXTERNALCAMERAUTILS_H_
+
+#include <CameraMetadata.h>
+#include <HandleImporter.h>
+#include <aidl/android/hardware/camera/common/Status.h>
+#include <aidl/android/hardware/camera/device/CaptureResult.h>
+#include <aidl/android/hardware/camera/device/ErrorCode.h>
+#include <aidl/android/hardware/camera/device/NotifyMsg.h>
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <tinyxml2.h>
+#include <unordered_map>
+#include <unordered_set>
+
+using ::aidl::android::hardware::camera::common::Status;
+using ::aidl::android::hardware::camera::device::CaptureResult;
+using ::aidl::android::hardware::camera::device::ErrorCode;
+using ::aidl::android::hardware::camera::device::NotifyMsg;
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::aidl::android::hardware::graphics::common::PixelFormat;
+using ::android::hardware::camera::common::V1_0::helper::CameraMetadata;
+using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
+
+namespace android {
+namespace hardware {
+namespace camera {
+
+namespace external {
+namespace common {
+
+struct Size {
+ int32_t width;
+ int32_t height;
+
+ bool operator==(const Size& other) const {
+ return (width == other.width && height == other.height);
+ }
+};
+
+struct SizeHasher {
+ size_t operator()(const Size& sz) const {
+ size_t result = 1;
+ result = 31 * result + sz.width;
+ result = 31 * result + sz.height;
+ return result;
+ }
+};
+
+struct ExternalCameraConfig {
+ static const char* kDefaultCfgPath;
+ static ExternalCameraConfig loadFromCfg(const char* cfgPath = kDefaultCfgPath);
+
+ // CameraId base offset for numerical representation
+ uint32_t cameraIdOffset;
+
+ // List of internal V4L2 video nodes external camera HAL must ignore.
+ std::unordered_set<std::string> mInternalDevices;
+
+ // Maximal size of a JPEG buffer, in bytes
+ int32_t maxJpegBufSize;
+
+ // Maximum Size that can sustain 30fps streaming
+ Size maxVideoSize;
+
+ // Size of v4l2 buffer queue when streaming <= kMaxVideoSize
+ uint32_t numVideoBuffers;
+
+ // Size of v4l2 buffer queue when streaming > kMaxVideoSize
+ uint32_t numStillBuffers;
+
+ // Indication that the device connected supports depth output
+ bool depthEnabled;
+
+ struct FpsLimitation {
+ Size size;
+ double fpsUpperBound;
+ };
+ std::vector<FpsLimitation> fpsLimits;
+ std::vector<FpsLimitation> depthFpsLimits;
+
+ // Minimum output stream size
+ Size minStreamSize;
+
+ // The value of android.sensor.orientation
+ int32_t orientation;
+
+ private:
+ ExternalCameraConfig();
+ static bool updateFpsList(tinyxml2::XMLElement* fpsList, std::vector<FpsLimitation>& fpsLimits);
+};
+
+} // namespace common
+} // namespace external
+
+namespace device {
+namespace implementation {
+
+struct SupportedV4L2Format {
+ int32_t width;
+ int32_t height;
+ uint32_t fourcc;
+ // All supported frame rate for this w/h/fourcc combination
+ struct FrameRate {
+ // Duration (in seconds) of a single frame.
+ // Numerator and denominator of the frame duration are stored separately.
+ // For ex. a frame lasting 1/30 of a second will be stored as {1, 30}
+ uint32_t durationNumerator; // frame duration numerator. Ex: 1
+ uint32_t durationDenominator; // frame duration denominator. Ex: 30
+ double getFramesPerSecond() const; // FPS as double. Ex: 30.0
+ };
+ std::vector<FrameRate> frameRates;
+};
+
+// A Base class with basic information about a frame
+struct Frame : public std::enable_shared_from_this<Frame> {
+ public:
+ Frame(uint32_t width, uint32_t height, uint32_t fourcc);
+ virtual ~Frame();
+ const int32_t mWidth;
+ const int32_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 Frame {
+ public:
+ V4L2Frame(uint32_t w, uint32_t h, uint32_t fourcc, int bufIdx, int fd, uint32_t dataSize,
+ uint64_t offset);
+ virtual ~V4L2Frame();
+
+ 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();
+
+ private:
+ std::mutex mLock;
+ const int mFd; // used for mmap but doesn't claim ownership
+ const size_t mDataSize;
+ const uint64_t mOffset; // used for mmap
+ uint8_t* mData = nullptr;
+ bool mMapped = false;
+};
+
+// A RAII class representing a CPU allocated YUV frame used as intermediate buffers
+// when generating output images.
+class AllocatedFrame : public Frame {
+ public:
+ AllocatedFrame(uint32_t w, uint32_t h); // only support V4L2_PIX_FMT_YUV420 for now
+ ~AllocatedFrame() override;
+
+ 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
+ private:
+ std::mutex mLock;
+ std::vector<uint8_t> mData;
+ size_t mBufferSize; // size of mData before padding. Actual size of mData might be slightly
+ // bigger to horizontally pad the frame for jpeglib.
+};
+
+enum CroppingType { HORIZONTAL = 0, VERTICAL = 1 };
+
+// Aspect ratio is defined as width/height here and ExternalCameraDevice
+// will guarantee all supported sizes has width >= height (so aspect ratio >= 1.0)
+#define ASPECT_RATIO(sz) (static_cast<float>((sz).width) / (sz).height)
+const float kMaxAspectRatio = std::numeric_limits<float>::max();
+const float kMinAspectRatio = 1.f;
+
+bool isAspectRatioClose(float ar1, float ar2);
+
+struct HalStreamBuffer {
+ int32_t streamId;
+ int64_t bufferId;
+ int32_t width;
+ int32_t height;
+ ::aidl::android::hardware::graphics::common::PixelFormat format;
+ ::aidl::android::hardware::graphics::common::BufferUsage usage;
+ buffer_handle_t* bufPtr;
+ int acquireFence;
+ bool fenceTimeout;
+};
+
+struct HalRequest {
+ int32_t frameNumber;
+ common::V1_0::helper::CameraMetadata setting;
+ std::shared_ptr<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;
+
+aidl::android::hardware::camera::common::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);
+
+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(std::vector<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 {
+ virtual ~OutputThreadInterface() {}
+ virtual aidl::android::hardware::camera::common::Status importBuffer(
+ int32_t streamId, uint64_t bufId, buffer_handle_t buf,
+ /*out*/ buffer_handle_t** outBufPtr) = 0;
+
+ virtual void notifyError(int32_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 aidl::android::hardware::camera::common::Status processCaptureRequestError(
+ const std::shared_ptr<HalRequest>&,
+ /*out*/ std::vector<NotifyMsg>* msgs,
+ /*out*/ std::vector<CaptureResult>* results) = 0;
+
+ virtual aidl::android::hardware::camera::common::Status processCaptureRequestError(
+ const std::shared_ptr<HalRequest>& reqs) final {
+ return processCaptureRequestError(reqs, nullptr, nullptr);
+ }
+
+ virtual aidl::android::hardware::camera::common::Status processCaptureResult(
+ std::shared_ptr<HalRequest>&) = 0;
+
+ virtual ssize_t getJpegBufferSize(int32_t width, int32_t height) const = 0;
+};
+
+// A CPU copy of a mapped V4L2Frame. Will map the input V4L2 frame.
+class AllocatedV4L2Frame : public Frame {
+ public:
+ AllocatedV4L2Frame(std::shared_ptr<V4L2Frame> frameIn);
+ ~AllocatedV4L2Frame() override;
+ virtual int getData(uint8_t** outData, size_t* dataSize) override;
+
+ private:
+ std::vector<uint8_t> mData;
+};
+
+} // namespace implementation
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+#endif // HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_EXTERNALCAMERAUTILS_H_
diff --git a/camera/common/1.0/default/OWNERS b/camera/device/default/OWNERS
similarity index 100%
copy from camera/common/1.0/default/OWNERS
copy to camera/device/default/OWNERS
diff --git a/camera/device/default/convert.cpp b/camera/device/default/convert.cpp
new file mode 100644
index 0000000..8134dd5
--- /dev/null
+++ b/camera/device/default/convert.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.camera.device@3.4-convert-impl"
+#include <log/log.h>
+
+#include "convert.h"
+
+#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <hardware/camera_common.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace implementation {
+
+using ::aidl::android::hardware::camera::device::ErrorCode;
+using ::aidl::android::hardware::camera::device::ErrorMsg;
+using ::aidl::android::hardware::camera::device::ShutterMsg;
+using ::aidl::android::hardware::graphics::common::BufferUsage;
+using ::aidl::android::hardware::graphics::common::PixelFormat;
+
+void convertToAidl(const camera_metadata_t* src, CameraMetadata* dest) {
+ if (src == nullptr) {
+ return;
+ }
+
+ size_t size = get_camera_metadata_size(src);
+ auto* src_start = (uint8_t*)src;
+ uint8_t* src_end = src_start + size;
+ dest->metadata.assign(src_start, src_end);
+}
+
+bool convertFromAidl(const CameraMetadata& src, const camera_metadata_t** dst) {
+ const std::vector<uint8_t>& metadata = src.metadata;
+ if (metadata.empty()) {
+ // Special case for null metadata
+ *dst = nullptr;
+ return true;
+ }
+
+ const uint8_t* data = metadata.data();
+ // check that the size of CameraMetadata match underlying camera_metadata_t
+ if (get_camera_metadata_size((camera_metadata_t*)data) != metadata.size()) {
+ ALOGE("%s: input CameraMetadata is corrupt!", __FUNCTION__);
+ return false;
+ }
+ *dst = (camera_metadata_t*)data;
+ return true;
+}
+
+} // namespace implementation
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
diff --git a/camera/device/default/convert.h b/camera/device/default/convert.h
new file mode 100644
index 0000000..5a508fc
--- /dev/null
+++ b/camera/device/default/convert.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_CONVERT_H_
+#define HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_CONVERT_H_
+
+#include <aidl/android/hardware/camera/common/Status.h>
+#include <aidl/android/hardware/camera/device/BufferStatus.h>
+#include <aidl/android/hardware/camera/device/CameraMetadata.h>
+#include <aidl/android/hardware/camera/device/HalStream.h>
+#include <aidl/android/hardware/camera/device/NotifyMsg.h>
+#include <aidl/android/hardware/camera/device/Stream.h>
+#include <hardware/camera3.h>
+#include <system/camera_metadata.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace device {
+namespace implementation {
+
+using ::aidl::android::hardware::camera::common::Status;
+using ::aidl::android::hardware::camera::device::BufferStatus;
+using ::aidl::android::hardware::camera::device::CameraMetadata;
+using ::aidl::android::hardware::camera::device::HalStream;
+using ::aidl::android::hardware::camera::device::NotifyMsg;
+using ::aidl::android::hardware::camera::device::Stream;
+
+void convertToAidl(const camera_metadata_t* src, CameraMetadata* dest);
+
+bool convertFromAidl(const CameraMetadata& src, const camera_metadata_t** dst);
+
+inline ndk::ScopedAStatus fromStatus(Status status) {
+ return status == Status::OK
+ ? ndk::ScopedAStatus::ok()
+ : ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
+}
+
+} // namespace implementation
+} // namespace device
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+#endif // HARDWARE_INTERFACES_CAMERA_DEVICE_DEFAULT_CONVERT_H_
diff --git a/camera/provider/aidl/Android.bp b/camera/provider/aidl/Android.bp
index bb30e43..d7e613e 100644
--- a/camera/provider/aidl/Android.bp
+++ b/camera/provider/aidl/Android.bp
@@ -17,6 +17,7 @@
"android.hardware.camera.device-V2",
"android.hardware.camera.common-V1",
],
+ frozen: false,
stability: "vintf",
backend: {
java: {
diff --git a/camera/provider/aidl/aidl_api/android.hardware.camera.provider/current/android/hardware/camera/provider/ICameraProvider.aidl b/camera/provider/aidl/aidl_api/android.hardware.camera.provider/current/android/hardware/camera/provider/ICameraProvider.aidl
index c6a3b9a..c15bdee 100644
--- a/camera/provider/aidl/aidl_api/android.hardware.camera.provider/current/android/hardware/camera/provider/ICameraProvider.aidl
+++ b/camera/provider/aidl/aidl_api/android.hardware.camera.provider/current/android/hardware/camera/provider/ICameraProvider.aidl
@@ -41,7 +41,6 @@
void notifyDeviceStateChange(long deviceState);
android.hardware.camera.provider.ConcurrentCameraIdCombination[] getConcurrentCameraIds();
boolean isConcurrentStreamCombinationSupported(in android.hardware.camera.provider.CameraIdAndStreamCombination[] configs);
- void placeholder();
const long DEVICE_STATE_NORMAL = 0;
const long DEVICE_STATE_BACK_COVERED = 1;
const long DEVICE_STATE_FRONT_COVERED = 2;
diff --git a/camera/provider/aidl/android/hardware/camera/provider/ICameraProvider.aidl b/camera/provider/aidl/android/hardware/camera/provider/ICameraProvider.aidl
index 5442058..c4eba8d 100644
--- a/camera/provider/aidl/android/hardware/camera/provider/ICameraProvider.aidl
+++ b/camera/provider/aidl/android/hardware/camera/provider/ICameraProvider.aidl
@@ -304,12 +304,4 @@
*
*/
boolean isConcurrentStreamCombinationSupported(in CameraIdAndStreamCombination[] configs);
-
- /*
- * Due to a bug in vintf regarding aidl changes that are contained to fields,
- * we need a placeholder method that will be removed after this patch.
- *
- * TODO(b/237048744): Remove this once fixed.
- */
- void placeholder();
}
diff --git a/camera/provider/default/Android.bp b/camera/provider/default/Android.bp
new file mode 100644
index 0000000..ed45cbe
--- /dev/null
+++ b/camera/provider/default/Android.bp
@@ -0,0 +1,104 @@
+//
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_shared {
+ name: "android.hardware.camera.provider-V1-external-impl",
+ defaults: ["hidl_defaults"],
+ proprietary: true,
+ srcs: [
+ "ExternalCameraProvider.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.camera.common-V1-ndk",
+ "android.hardware.camera.device-V1-ndk",
+ "android.hardware.camera.provider-V1-ndk",
+ "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-external-impl",
+ "libbinder_ndk",
+ "libcamera_metadata",
+ "libcutils",
+ "libfmq",
+ "libhardware",
+ "libhidlbase",
+ "liblog",
+ "libtinyxml2",
+ "libutils",
+ ],
+ static_libs: [
+ "android.hardware.camera.common@1.0-helper",
+ ],
+ export_include_dirs: ["."],
+}
+
+cc_defaults {
+ name: "camera_external_service_defaults",
+ defaults: ["hidl_defaults"],
+ proprietary: true,
+ relative_install_path: "hw",
+ srcs: ["external-service.cpp"],
+ compile_multilib: "first",
+ shared_libs: [
+ "android.hardware.camera.common-V1-ndk",
+ "android.hardware.camera.device-V1-ndk",
+ "android.hardware.camera.provider-V1-ndk",
+ "android.hardware.camera.provider-V1-external-impl",
+ "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-external-impl",
+ "libbinder_ndk",
+ "libcamera_metadata",
+ "libcutils",
+ "libfmq",
+ "libhardware",
+ "libhidlbase",
+ "liblog",
+ "libtinyxml2",
+ "libutils",
+ ],
+ static_libs: [
+ "android.hardware.camera.common@1.0-helper",
+ ],
+}
+
+cc_binary {
+ name: "android.hardware.camera.provider-V1-external-service",
+ defaults: ["camera_external_service_defaults"],
+ init_rc: ["android.hardware.camera.provider-V1-external-service.rc"],
+}
+
+cc_binary {
+ name: "android.hardware.camera.provider-V1-external-service-lazy",
+ overrides: ["android.hardware.camera.provider-V1-external-service"],
+ defaults: ["camera_external_service_defaults"],
+ init_rc: ["android.hardware.camera.provider-V1-external-service-lazy.rc"],
+ cflags: ["-DLAZY_SERVICE"],
+}
diff --git a/camera/provider/default/ExternalCameraProvider.cpp b/camera/provider/default/ExternalCameraProvider.cpp
new file mode 100644
index 0000000..d47ddbf
--- /dev/null
+++ b/camera/provider/default/ExternalCameraProvider.cpp
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ExtCamPrvdr"
+// #define LOG_NDEBUG 0
+
+#include "ExternalCameraProvider.h"
+
+#include <ExternalCameraDevice.h>
+#include <aidl/android/hardware/camera/common/Status.h>
+#include <convert.h>
+#include <cutils/properties.h>
+#include <linux/videodev2.h>
+#include <log/log.h>
+#include <sys/inotify.h>
+#include <regex>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace provider {
+namespace implementation {
+
+using ::aidl::android::hardware::camera::common::Status;
+using ::android::hardware::camera::device::implementation::ExternalCameraDevice;
+using ::android::hardware::camera::device::implementation::fromStatus;
+using ::android::hardware::camera::external::common::ExternalCameraConfig;
+
+namespace {
+// "device@<version>/external/<id>"
+const std::regex kDeviceNameRE("device@([0-9]+\\.[0-9]+)/external/(.+)");
+const int kMaxDevicePathLen = 256;
+constexpr char kDevicePath[] = "/dev/";
+constexpr char kPrefix[] = "video";
+constexpr int kPrefixLen = sizeof(kPrefix) - 1;
+constexpr int kDevicePrefixLen = sizeof(kDevicePath) + kPrefixLen + 1;
+
+bool matchDeviceName(int cameraIdOffset, const std::string& deviceName, std::string* deviceVersion,
+ std::string* cameraDevicePath) {
+ std::smatch sm;
+ if (std::regex_match(deviceName, sm, kDeviceNameRE)) {
+ if (deviceVersion != nullptr) {
+ *deviceVersion = sm[1];
+ }
+ if (cameraDevicePath != nullptr) {
+ *cameraDevicePath = "/dev/video" + std::to_string(std::stoi(sm[2]) - cameraIdOffset);
+ }
+ return true;
+ }
+ return false;
+}
+} // namespace
+
+ExternalCameraProvider::ExternalCameraProvider() : mCfg(ExternalCameraConfig::loadFromCfg()) {
+ mHotPlugThread = std::make_shared<HotplugThread>(this);
+ mHotPlugThread->run();
+}
+
+ExternalCameraProvider::~ExternalCameraProvider() {
+ mHotPlugThread->requestExitAndWait();
+}
+
+ndk::ScopedAStatus ExternalCameraProvider::setCallback(
+ const std::shared_ptr<ICameraProviderCallback>& in_callback) {
+ {
+ Mutex::Autolock _l(mLock);
+ mCallback = in_callback;
+ }
+
+ if (mCallback == nullptr) {
+ return fromStatus(Status::OK);
+ }
+
+ for (const auto& pair : mCameraStatusMap) {
+ mCallback->cameraDeviceStatusChange(pair.first, pair.second);
+ }
+ return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus ExternalCameraProvider::getVendorTags(
+ std::vector<VendorTagSection>* _aidl_return) {
+ if (_aidl_return == nullptr) {
+ return fromStatus(Status::ILLEGAL_ARGUMENT);
+ }
+ // No vendor tag support for USB camera
+ *_aidl_return = {};
+ return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus ExternalCameraProvider::getCameraIdList(std::vector<std::string>* _aidl_return) {
+ if (_aidl_return == nullptr) {
+ return fromStatus(Status::ILLEGAL_ARGUMENT);
+ }
+ // External camera HAL always report 0 camera, and extra cameras
+ // are just reported via cameraDeviceStatusChange callbacks
+ *_aidl_return = {};
+ return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus ExternalCameraProvider::getCameraDeviceInterface(
+ const std::string& in_cameraDeviceName, std::shared_ptr<ICameraDevice>* _aidl_return) {
+ if (_aidl_return == nullptr) {
+ return fromStatus(Status::ILLEGAL_ARGUMENT);
+ }
+ std::string cameraDevicePath, deviceVersion;
+ bool match = matchDeviceName(mCfg.cameraIdOffset, in_cameraDeviceName, &deviceVersion,
+ &cameraDevicePath);
+
+ if (!match) {
+ *_aidl_return = nullptr;
+ return fromStatus(Status::ILLEGAL_ARGUMENT);
+ }
+
+ if (mCameraStatusMap.count(in_cameraDeviceName) == 0 ||
+ mCameraStatusMap[in_cameraDeviceName] != CameraDeviceStatus::PRESENT) {
+ *_aidl_return = nullptr;
+ return fromStatus(Status::ILLEGAL_ARGUMENT);
+ }
+
+ ALOGV("Constructing external camera device");
+ std::shared_ptr<ExternalCameraDevice> deviceImpl =
+ ndk::SharedRefBase::make<ExternalCameraDevice>(cameraDevicePath, mCfg);
+ if (deviceImpl == nullptr || deviceImpl->isInitFailed()) {
+ ALOGE("%s: camera device %s init failed!", __FUNCTION__, cameraDevicePath.c_str());
+ *_aidl_return = nullptr;
+ return fromStatus(Status::INTERNAL_ERROR);
+ }
+
+ IF_ALOGV() {
+ int interfaceVersion;
+ deviceImpl->getInterfaceVersion(&interfaceVersion);
+ ALOGV("%s: device interface version: %d", __FUNCTION__, interfaceVersion);
+ }
+
+ *_aidl_return = deviceImpl;
+ return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus ExternalCameraProvider::notifyDeviceStateChange(int64_t) {
+ return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus ExternalCameraProvider::getConcurrentCameraIds(
+ std::vector<ConcurrentCameraIdCombination>* _aidl_return) {
+ if (_aidl_return == nullptr) {
+ return fromStatus(Status::ILLEGAL_ARGUMENT);
+ }
+ *_aidl_return = {};
+ return fromStatus(Status::OK);
+}
+
+ndk::ScopedAStatus ExternalCameraProvider::isConcurrentStreamCombinationSupported(
+ const std::vector<CameraIdAndStreamCombination>&, bool* _aidl_return) {
+ if (_aidl_return == nullptr) {
+ return fromStatus(Status::ILLEGAL_ARGUMENT);
+ }
+ // No concurrent stream combinations are supported
+ *_aidl_return = false;
+ return fromStatus(Status::OK);
+}
+
+void ExternalCameraProvider::addExternalCamera(const char* devName) {
+ ALOGV("%s: ExtCam: adding %s to External Camera HAL!", __FUNCTION__, devName);
+ Mutex::Autolock _l(mLock);
+ std::string deviceName;
+ std::string cameraId =
+ std::to_string(mCfg.cameraIdOffset + std::atoi(devName + kDevicePrefixLen));
+ deviceName =
+ std::string("device@") + ExternalCameraDevice::kDeviceVersion + "/external/" + cameraId;
+ mCameraStatusMap[deviceName] = CameraDeviceStatus::PRESENT;
+ if (mCallback != nullptr) {
+ mCallback->cameraDeviceStatusChange(deviceName, CameraDeviceStatus::PRESENT);
+ }
+}
+
+void ExternalCameraProvider::deviceAdded(const char* devName) {
+ {
+ base::unique_fd fd(::open(devName, O_RDWR));
+ if (fd.get() < 0) {
+ ALOGE("%s open v4l2 device %s failed:%s", __FUNCTION__, devName, strerror(errno));
+ return;
+ }
+
+ struct v4l2_capability capability;
+ int ret = ioctl(fd.get(), VIDIOC_QUERYCAP, &capability);
+ if (ret < 0) {
+ ALOGE("%s v4l2 QUERYCAP %s failed", __FUNCTION__, devName);
+ return;
+ }
+
+ if (!(capability.device_caps & V4L2_CAP_VIDEO_CAPTURE)) {
+ ALOGW("%s device %s does not support VIDEO_CAPTURE", __FUNCTION__, devName);
+ return;
+ }
+ }
+
+ // See if we can initialize ExternalCameraDevice correctly
+ std::shared_ptr<ExternalCameraDevice> deviceImpl =
+ ndk::SharedRefBase::make<ExternalCameraDevice>(devName, mCfg);
+ if (deviceImpl == nullptr || deviceImpl->isInitFailed()) {
+ ALOGW("%s: Attempt to init camera device %s failed!", __FUNCTION__, devName);
+ return;
+ }
+ deviceImpl.reset();
+ addExternalCamera(devName);
+}
+
+void ExternalCameraProvider::deviceRemoved(const char* devName) {
+ Mutex::Autolock _l(mLock);
+ std::string deviceName;
+ std::string cameraId =
+ std::to_string(mCfg.cameraIdOffset + std::atoi(devName + kDevicePrefixLen));
+
+ deviceName =
+ std::string("device@") + ExternalCameraDevice::kDeviceVersion + "/external/" + cameraId;
+
+ if (mCameraStatusMap.erase(deviceName) == 0) {
+ // Unknown device, do not fire callback
+ ALOGE("%s: cannot find camera device to remove %s", __FUNCTION__, devName);
+ return;
+ }
+
+ if (mCallback != nullptr) {
+ mCallback->cameraDeviceStatusChange(deviceName, CameraDeviceStatus::NOT_PRESENT);
+ }
+}
+
+void ExternalCameraProvider::updateAttachedCameras() {
+ ALOGV("%s start scanning for existing V4L2 devices", __FUNCTION__);
+
+ // Find existing /dev/video* devices
+ DIR* devdir = opendir(kDevicePath);
+ if (devdir == nullptr) {
+ ALOGE("%s: cannot open %s! Exiting threadloop", __FUNCTION__, kDevicePath);
+ return;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(devdir)) != nullptr) {
+ // Find external v4l devices that's existing before we start watching and add them
+ if (!strncmp(kPrefix, de->d_name, kPrefixLen)) {
+ std::string deviceId(de->d_name + kPrefixLen);
+ if (mCfg.mInternalDevices.count(deviceId) == 0) {
+ ALOGV("Non-internal v4l device %s found", de->d_name);
+ char v4l2DevicePath[kMaxDevicePathLen];
+ snprintf(v4l2DevicePath, kMaxDevicePathLen, "%s%s", kDevicePath, de->d_name);
+ deviceAdded(v4l2DevicePath);
+ }
+ }
+ }
+ closedir(devdir);
+}
+
+// Start ExternalCameraProvider::HotplugThread functions
+
+ExternalCameraProvider::HotplugThread::HotplugThread(ExternalCameraProvider* parent)
+ : mParent(parent), mInternalDevices(parent->mCfg.mInternalDevices) {}
+
+ExternalCameraProvider::HotplugThread::~HotplugThread() {
+ // Clean up inotify descriptor if needed.
+ if (mINotifyFD >= 0) {
+ close(mINotifyFD);
+ }
+}
+
+bool ExternalCameraProvider::HotplugThread::initialize() {
+ // Update existing cameras
+ mParent->updateAttachedCameras();
+
+ // Set up non-blocking fd. The threadLoop will be responsible for polling read at the
+ // desired frequency
+ mINotifyFD = inotify_init();
+ if (mINotifyFD < 0) {
+ ALOGE("%s: inotify init failed! Exiting threadloop", __FUNCTION__);
+ return false;
+ }
+
+ // Start watching /dev/ directory for created and deleted files
+ mWd = inotify_add_watch(mINotifyFD, kDevicePath, IN_CREATE | IN_DELETE);
+ if (mWd < 0) {
+ ALOGE("%s: inotify add watch failed! Exiting threadloop", __FUNCTION__);
+ return false;
+ }
+
+ mPollFd = {.fd = mINotifyFD, .events = POLLIN};
+
+ mIsInitialized = true;
+ return true;
+}
+
+bool ExternalCameraProvider::HotplugThread::threadLoop() {
+ // Initialize inotify descriptors if needed.
+ if (!mIsInitialized && !initialize()) {
+ return true;
+ }
+
+ // poll /dev/* and handle timeouts and error
+ int pollRet = poll(&mPollFd, /* fd_count= */ 1, /* timeout= */ 250);
+ if (pollRet == 0) {
+ // no read event in 100ms
+ mPollFd.revents = 0;
+ return true;
+ } else if (pollRet < 0) {
+ ALOGE("%s: error while polling for /dev/*: %d", __FUNCTION__, errno);
+ mPollFd.revents = 0;
+ return true;
+ } else if (mPollFd.revents & POLLERR) {
+ ALOGE("%s: polling /dev/ returned POLLERR", __FUNCTION__);
+ mPollFd.revents = 0;
+ return true;
+ } else if (mPollFd.revents & POLLHUP) {
+ ALOGE("%s: polling /dev/ returned POLLHUP", __FUNCTION__);
+ mPollFd.revents = 0;
+ return true;
+ } else if (mPollFd.revents & POLLNVAL) {
+ ALOGE("%s: polling /dev/ returned POLLNVAL", __FUNCTION__);
+ mPollFd.revents = 0;
+ return true;
+ }
+ // mPollFd.revents must contain POLLIN, so safe to reset it before reading
+ mPollFd.revents = 0;
+
+ uint64_t offset = 0;
+ ssize_t ret = read(mINotifyFD, mEventBuf, sizeof(mEventBuf));
+ if (ret < sizeof(struct inotify_event)) {
+ // invalid event. skip
+ return true;
+ }
+
+ while (offset < ret) {
+ struct inotify_event* event = (struct inotify_event*)&mEventBuf[offset];
+ offset += sizeof(struct inotify_event) + event->len;
+
+ if (event->wd != mWd) {
+ // event for an unrelated descriptor. ignore.
+ continue;
+ }
+
+ ALOGV("%s inotify_event %s", __FUNCTION__, event->name);
+ if (strncmp(kPrefix, event->name, kPrefixLen) != 0) {
+ // event not for /dev/video*. ignore.
+ continue;
+ }
+
+ std::string deviceId = event->name + kPrefixLen;
+ if (mInternalDevices.count(deviceId) != 0) {
+ // update to an internal device. ignore.
+ continue;
+ }
+
+ char v4l2DevicePath[kMaxDevicePathLen];
+ snprintf(v4l2DevicePath, kMaxDevicePathLen, "%s%s", kDevicePath, event->name);
+
+ if (event->mask & IN_CREATE) {
+ mParent->deviceAdded(v4l2DevicePath);
+ } else if (event->mask & IN_DELETE) {
+ mParent->deviceRemoved(v4l2DevicePath);
+ }
+ }
+ return true;
+}
+
+// End ExternalCameraProvider::HotplugThread functions
+
+} // namespace implementation
+} // namespace provider
+} // namespace camera
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/camera/provider/default/ExternalCameraProvider.h b/camera/provider/default/ExternalCameraProvider.h
new file mode 100644
index 0000000..347a53c
--- /dev/null
+++ b/camera/provider/default/ExternalCameraProvider.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HARDWARE_INTERFACES_CAMERA_PROVIDER_DEFAULT_EXTERNALCAMERAPROVIDER_H_
+#define HARDWARE_INTERFACES_CAMERA_PROVIDER_DEFAULT_EXTERNALCAMERAPROVIDER_H_
+
+#include <ExternalCameraUtils.h>
+#include <SimpleThread.h>
+#include <aidl/android/hardware/camera/common/CameraDeviceStatus.h>
+#include <aidl/android/hardware/camera/common/VendorTagSection.h>
+#include <aidl/android/hardware/camera/device/ICameraDevice.h>
+#include <aidl/android/hardware/camera/provider/BnCameraProvider.h>
+#include <aidl/android/hardware/camera/provider/CameraIdAndStreamCombination.h>
+#include <aidl/android/hardware/camera/provider/ConcurrentCameraIdCombination.h>
+#include <aidl/android/hardware/camera/provider/ICameraProviderCallback.h>
+#include <poll.h>
+#include <utils/Mutex.h>
+#include <utils/Thread.h>
+#include <thread>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace provider {
+namespace implementation {
+
+using ::aidl::android::hardware::camera::common::CameraDeviceStatus;
+using ::aidl::android::hardware::camera::common::VendorTagSection;
+using ::aidl::android::hardware::camera::device::ICameraDevice;
+using ::aidl::android::hardware::camera::provider::BnCameraProvider;
+using ::aidl::android::hardware::camera::provider::CameraIdAndStreamCombination;
+using ::aidl::android::hardware::camera::provider::ConcurrentCameraIdCombination;
+using ::aidl::android::hardware::camera::provider::ICameraProviderCallback;
+using ::android::hardware::camera::common::helper::SimpleThread;
+using ::android::hardware::camera::external::common::ExternalCameraConfig;
+
+class ExternalCameraProvider : public BnCameraProvider {
+ public:
+ ExternalCameraProvider();
+ ~ExternalCameraProvider() override;
+ ndk::ScopedAStatus setCallback(
+ const std::shared_ptr<ICameraProviderCallback>& in_callback) override;
+ ndk::ScopedAStatus getVendorTags(std::vector<VendorTagSection>* _aidl_return) override;
+ ndk::ScopedAStatus getCameraIdList(std::vector<std::string>* _aidl_return) override;
+ ndk::ScopedAStatus getCameraDeviceInterface(
+ const std::string& in_cameraDeviceName,
+ std::shared_ptr<ICameraDevice>* _aidl_return) override;
+ ndk::ScopedAStatus notifyDeviceStateChange(int64_t in_deviceState) override;
+ ndk::ScopedAStatus getConcurrentCameraIds(
+ std::vector<ConcurrentCameraIdCombination>* _aidl_return) override;
+ ndk::ScopedAStatus isConcurrentStreamCombinationSupported(
+ const std::vector<CameraIdAndStreamCombination>& in_configs,
+ bool* _aidl_return) override;
+
+ private:
+ void addExternalCamera(const char* devName);
+ void deviceAdded(const char* devName);
+ void deviceRemoved(const char* devName);
+ void updateAttachedCameras();
+
+ // A separate thread to monitor '/dev' directory for '/dev/video*' entries
+ // This thread calls back into ExternalCameraProvider when an actionable change is detected.
+ class HotplugThread : public SimpleThread {
+ public:
+ explicit HotplugThread(ExternalCameraProvider* parent);
+ ~HotplugThread() override;
+
+ protected:
+ bool threadLoop() override;
+
+ private:
+ // Returns true if thread initialization succeeded, and false if thread initialization
+ // failed.
+ bool initialize();
+
+ ExternalCameraProvider* mParent = nullptr;
+ const std::unordered_set<std::string> mInternalDevices;
+
+ bool mIsInitialized = false;
+
+ int mINotifyFD = -1;
+ int mWd = -1;
+
+ // struct to wrap mINotifyFD and poll it with timeout
+ struct pollfd mPollFd = {};
+ char mEventBuf[512] = {0};
+ };
+
+ Mutex mLock;
+ std::shared_ptr<ICameraProviderCallback> mCallback = nullptr;
+ std::unordered_map<std::string, CameraDeviceStatus> mCameraStatusMap; // camera id -> status
+ const ExternalCameraConfig mCfg;
+ std::shared_ptr<HotplugThread> mHotPlugThread;
+};
+
+} // namespace implementation
+} // namespace provider
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+#endif // HARDWARE_INTERFACES_CAMERA_PROVIDER_DEFAULT_EXTERNALCAMERAPROVIDER_H_
diff --git a/camera/common/1.0/default/OWNERS b/camera/provider/default/OWNERS
similarity index 100%
copy from camera/common/1.0/default/OWNERS
copy to camera/provider/default/OWNERS
diff --git a/camera/provider/default/android.hardware.camera.provider-V1-external-service-lazy.rc b/camera/provider/default/android.hardware.camera.provider-V1-external-service-lazy.rc
new file mode 100644
index 0000000..dcdd88c
--- /dev/null
+++ b/camera/provider/default/android.hardware.camera.provider-V1-external-service-lazy.rc
@@ -0,0 +1,10 @@
+service vendor.camera.provider-ext /vendor/bin/hw/android.hardware.camera.provider-V1-external-service-lazy
+ interface aidl android.hardware.camera.provider.ICameraProvider/external/0
+ class hal
+ oneshot
+ disabled
+ user cameraserver
+ group audio camera input drmrpc usb
+ ioprio rt 4
+ capabilities SYS_NICE
+ task_profiles CameraServiceCapacity MaxPerformance
\ No newline at end of file
diff --git a/camera/provider/default/android.hardware.camera.provider-V1-external-service.rc b/camera/provider/default/android.hardware.camera.provider-V1-external-service.rc
new file mode 100644
index 0000000..302c56f
--- /dev/null
+++ b/camera/provider/default/android.hardware.camera.provider-V1-external-service.rc
@@ -0,0 +1,8 @@
+service vendor.camera.provider-ext /vendor/bin/hw/android.hardware.camera.provider-V1-external-service
+ interface aidl android.hardware.camera.provider.ICameraProvider/external/0
+ class hal
+ user cameraserver
+ group audio camera input drmrpc usb
+ ioprio rt 4
+ capabilities SYS_NICE
+ task_profiles CameraServiceCapacity MaxPerformance
\ No newline at end of file
diff --git a/camera/provider/default/external-service.cpp b/camera/provider/default/external-service.cpp
new file mode 100644
index 0000000..b18f182
--- /dev/null
+++ b/camera/provider/default/external-service.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ExternalCameraProvider.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using ::android::hardware::camera::provider::implementation::ExternalCameraProvider;
+
+namespace {
+// Default recommended RPC thread count for camera provider implementations
+const int HWBINDER_THREAD_COUNT = 6;
+} // namespace
+
+int main() {
+ ALOGI("CameraProvider: external webcam service is starting.");
+
+ ABinderProcess_setThreadPoolMaxThreadCount(HWBINDER_THREAD_COUNT);
+
+ std::shared_ptr<ExternalCameraProvider> defaultProvider =
+ ndk::SharedRefBase::make<ExternalCameraProvider>();
+ const std::string serviceName = std::string(ExternalCameraProvider::descriptor) + "/external/0";
+
+#ifdef LAZY_SERVICE
+ binder_exception_t ret = AServiceManager_registerLazyService(defaultProvider->asBinder().get(),
+ serviceName.c_str());
+ LOG_ALWAYS_FATAL_IF(ret != EX_NONE,
+ "Error while registering lazy ext camera provider service: %d", ret);
+#else
+ binder_exception_t ret =
+ AServiceManager_addService(defaultProvider->asBinder().get(), serviceName.c_str());
+ LOG_ALWAYS_FATAL_IF(ret != EX_NONE, "Error while registering ext camera provider service: %d",
+ ret);
+#endif
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
\ No newline at end of file
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 645753f..d0e844d 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -109,14 +109,14 @@
<name>android.hardware.automotive.vehicle</name>
<interface>
<name>IVehicle</name>
- <regex-instance>.*</regex-instance>
+ <instance>default</instance>
</interface>
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.automotive.remoteaccess</name>
<interface>
<name>IRemoteAccess</name>
- <regex-instance>.*</regex-instance>
+ <instance>default</instance>
</interface>
</hal>
<hal format="aidl" optional="true">
@@ -206,6 +206,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.contexthub</name>
+ <version>2</version>
<interface>
<name>IContextHub</name>
<instance>default</instance>
@@ -423,14 +424,6 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.oemlock</name>
- <version>1.0</version>
- <interface>
- <name>IOemLock</name>
- <instance>default</instance>
- </interface>
- </hal>
<hal format="aidl" optional="false">
<name>android.hardware.power</name>
<version>2-4</version>
@@ -556,9 +549,9 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
+ <hal format="aidl" optional="true">
<name>android.hardware.secure_element</name>
- <version>1.0-2</version>
+ <version>1</version>
<interface>
<name>ISecureElement</name>
<regex-instance>eSE[1-9][0-9]*</regex-instance>
@@ -653,6 +646,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.usb</name>
+ <version>1-2</version>
<interface>
<name>IUsb</name>
<instance>default</instance>
@@ -667,6 +661,13 @@
</interface>
</hal>
<hal format="aidl" optional="true">
+ <name>android.hardware.usb.gadget</name>
+ <interface>
+ <name>IUsbGadget</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="aidl" optional="true">
<name>android.hardware.vibrator</name>
<version>1-2</version>
<interface>
diff --git a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl
index f0676be..272d768 100644
--- a/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl
+++ b/contexthub/aidl/aidl_api/android.hardware.contexthub/current/android/hardware/contexthub/IContextHub.aidl
@@ -45,5 +45,6 @@
void sendMessageToHub(in int contextHubId, in android.hardware.contexthub.ContextHubMessage message);
void onHostEndpointConnected(in android.hardware.contexthub.HostEndpointInfo hostEndpointInfo);
void onHostEndpointDisconnected(char hostEndpointId);
+ long[] getPreloadedNanoappIds();
const int EX_CONTEXT_HUB_UNSPECIFIED = -1;
}
diff --git a/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl b/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl
index 16666ef..9fa67a5 100644
--- a/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl
+++ b/contexthub/aidl/android/hardware/contexthub/IContextHub.aidl
@@ -21,6 +21,7 @@
import android.hardware.contexthub.HostEndpointInfo;
import android.hardware.contexthub.IContextHubCallback;
import android.hardware.contexthub.NanoappBinary;
+import android.hardware.contexthub.NanoappInfo;
import android.hardware.contexthub.Setting;
@VintfStability
@@ -195,6 +196,14 @@
void onHostEndpointDisconnected(char hostEndpointId);
/**
+ * Provides the list of preloaded nanoapp IDs on the system. The output of this API must
+ * not change.
+ *
+ * @return The list of preloaded nanoapp IDs
+ */
+ long[] getPreloadedNanoappIds();
+
+ /**
* Error codes that are used as service specific errors with the AIDL return
* value EX_SERVICE_SPECIFIC.
*/
diff --git a/contexthub/aidl/default/Android.bp b/contexthub/aidl/default/Android.bp
index 269057a..6ee7407 100644
--- a/contexthub/aidl/default/Android.bp
+++ b/contexthub/aidl/default/Android.bp
@@ -29,7 +29,7 @@
shared_libs: [
"libbase",
"libbinder_ndk",
- "android.hardware.contexthub-V1-ndk",
+ "android.hardware.contexthub-V2-ndk",
],
export_include_dirs: ["include"],
srcs: [
@@ -50,7 +50,7 @@
shared_libs: [
"libbase",
"libbinder_ndk",
- "android.hardware.contexthub-V1-ndk",
+ "android.hardware.contexthub-V2-ndk",
],
static_libs: [
"libcontexthubexampleimpl",
diff --git a/contexthub/aidl/default/ContextHub.cpp b/contexthub/aidl/default/ContextHub.cpp
index 35e4650..ac1dc46 100644
--- a/contexthub/aidl/default/ContextHub.cpp
+++ b/contexthub/aidl/default/ContextHub.cpp
@@ -76,6 +76,17 @@
}
}
+ScopedAStatus ContextHub::getPreloadedNanoappIds(std::vector<int64_t>* out_preloadedNanoappIds) {
+ if (out_preloadedNanoappIds == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ for (uint64_t i = 0; i < 10; ++i) {
+ out_preloadedNanoappIds->push_back(i);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
ScopedAStatus ContextHub::registerCallback(int32_t in_contextHubId,
const std::shared_ptr<IContextHubCallback>& in_cb) {
if (in_contextHubId == kMockHubId) {
diff --git a/contexthub/aidl/default/contexthub-default.xml b/contexthub/aidl/default/contexthub-default.xml
index e383c50..930f672 100644
--- a/contexthub/aidl/default/contexthub-default.xml
+++ b/contexthub/aidl/default/contexthub-default.xml
@@ -1,7 +1,10 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.contexthub</name>
- <version>1</version>
- <fqname>IContextHub/default</fqname>
+ <version>2</version>
+ <interface>
+ <name>IContextHub</name>
+ <instance>default</instance>
+ </interface>
</hal>
</manifest>
diff --git a/contexthub/aidl/default/include/contexthub-impl/ContextHub.h b/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
index 03d8432..4aeb948 100644
--- a/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
+++ b/contexthub/aidl/default/include/contexthub-impl/ContextHub.h
@@ -19,6 +19,7 @@
#include <aidl/android/hardware/contexthub/BnContextHub.h>
#include <unordered_set>
+#include <vector>
namespace aidl {
namespace android {
@@ -37,6 +38,8 @@
int32_t in_transactionId) override;
::ndk::ScopedAStatus onSettingChanged(Setting in_setting, bool in_enabled) override;
::ndk::ScopedAStatus queryNanoapps(int32_t in_contextHubId) override;
+ ::ndk::ScopedAStatus getPreloadedNanoappIds(
+ std::vector<int64_t>* out_preloadedNanoappIds) override;
::ndk::ScopedAStatus registerCallback(
int32_t in_contextHubId, const std::shared_ptr<IContextHubCallback>& in_cb) override;
::ndk::ScopedAStatus sendMessageToHub(int32_t in_contextHubId,
diff --git a/contexthub/aidl/vts/Android.bp b/contexthub/aidl/vts/Android.bp
index 673eac0..1534b40 100644
--- a/contexthub/aidl/vts/Android.bp
+++ b/contexthub/aidl/vts/Android.bp
@@ -32,7 +32,7 @@
"libbinder",
],
static_libs: [
- "android.hardware.contexthub-V1-cpp",
+ "android.hardware.contexthub-V2-cpp",
"VtsHalContexthubUtils",
],
test_suites: [
diff --git a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
index 3c01c6b..8104f27 100644
--- a/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
+++ b/contexthub/aidl/vts/VtsAidlHalContextHubTargetTest.cpp
@@ -156,6 +156,19 @@
}
}
+// Calls getPreloadedNanoapps() and verifies there are preloaded nanoapps
+TEST_P(ContextHubAidl, TestGetPreloadedNanoapps) {
+ std::vector<int64_t> preloadedNanoappIds;
+ Status status = contextHub->getPreloadedNanoappIds(&preloadedNanoappIds);
+ if (status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == android::UNKNOWN_TRANSACTION) {
+ return; // not supported -> old API; or not implemented
+ }
+
+ ASSERT_TRUE(status.isOk());
+ ASSERT_FALSE(preloadedNanoappIds.empty());
+}
+
// Helper callback that puts the TransactionResult for the expectedTransactionId into a
// promise
class TransactionResultCallback : public android::hardware::contexthub::BnContextHubCallback {
diff --git a/current.txt b/current.txt
index afde7b1..146ded6 100644
--- a/current.txt
+++ b/current.txt
@@ -929,4 +929,7 @@
1bac6a7c8136dfb0414fe5639eec115aa2d12927e64a0642a43fb53225f099b2 android.hardware.wifi@1.6::IWifiStaIface
0a800e010e8eb6eecdfdc96f04fd2ae2f417a79a74a7c0eec3a9f539199bccd4 android.hardware.wifi@1.6::types
+# ABI preserving changes to HALs during Android U
+2aa559cda86c358c6429114ef6bc72c1b43281e98f9eb6b4df5e7073c8d05767 android.hardware.automotive.vehicle@2.0::types
+
# There will be no more HIDL HALs. Use AIDL instead.
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssCallback.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssCallback.aidl
index fd07a6e..0247182 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssCallback.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssCallback.aidl
@@ -61,6 +61,7 @@
const int CAPABILITY_CORRELATION_VECTOR = 4096;
const int CAPABILITY_SATELLITE_PVT = 8192;
const int CAPABILITY_MEASUREMENT_CORRECTIONS_FOR_DRIVING = 16384;
+ const int CAPABILITY_ACCUMULATED_DELTA_RANGE = 32768;
@Backing(type="int") @VintfStability
enum GnssStatusValue {
NONE = 0,
diff --git a/gnss/aidl/android/hardware/gnss/IGnssCallback.aidl b/gnss/aidl/android/hardware/gnss/IGnssCallback.aidl
index 2b2592b..ff9feea 100644
--- a/gnss/aidl/android/hardware/gnss/IGnssCallback.aidl
+++ b/gnss/aidl/android/hardware/gnss/IGnssCallback.aidl
@@ -82,6 +82,9 @@
/** Capability bit mask indicating that GNSS supports measurement corrections for driving */
const int CAPABILITY_MEASUREMENT_CORRECTIONS_FOR_DRIVING = 1 << 14;
+ /** Capability bit mask indicating that GNSS supports accumulated delta range */
+ const int CAPABILITY_ACCUMULATED_DELTA_RANGE = 1 << 15;
+
/**
* Callback to inform framework of the GNSS HAL implementation's capabilities.
*
diff --git a/gnss/aidl/default/Gnss.cpp b/gnss/aidl/default/Gnss.cpp
index 23d7999..ec86d2e 100644
--- a/gnss/aidl/default/Gnss.cpp
+++ b/gnss/aidl/default/Gnss.cpp
@@ -60,7 +60,8 @@
IGnssCallback::CAPABILITY_SATELLITE_BLOCKLIST |
IGnssCallback::CAPABILITY_SATELLITE_PVT |
IGnssCallback::CAPABILITY_CORRELATION_VECTOR |
- IGnssCallback::CAPABILITY_ANTENNA_INFO);
+ IGnssCallback::CAPABILITY_ANTENNA_INFO |
+ IGnssCallback::CAPABILITY_ACCUMULATED_DELTA_RANGE);
auto status = sGnssCallback->gnssSetCapabilitiesCb(capabilities);
if (!status.isOk()) {
ALOGE("%s: Unable to invoke callback.gnssSetCapabilitiesCb", __func__);
diff --git a/gnss/aidl/vts/gnss_hal_test.cpp b/gnss/aidl/vts/gnss_hal_test.cpp
index b4f2b77..7578585 100644
--- a/gnss/aidl/vts/gnss_hal_test.cpp
+++ b/gnss/aidl/vts/gnss_hal_test.cpp
@@ -489,7 +489,9 @@
// Validity check GnssData fields
checkGnssMeasurementClockFields(lastGnssData);
if (aidl_gnss_hal_->getInterfaceVersion() >= 3) {
- EXPECT_EQ(lastGnssData.isFullTracking, isFullTracking);
+ if (isFullTracking) {
+ EXPECT_EQ(lastGnssData.isFullTracking, isFullTracking);
+ }
}
for (const auto& measurement : lastGnssData.measurements) {
checkGnssMeasurementFields(measurement, lastGnssData);
diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp
index 31cef15..48027b6 100644
--- a/gnss/aidl/vts/gnss_hal_test_cases.cpp
+++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp
@@ -1505,10 +1505,11 @@
}
/*
- * TestGnssMeasurementSetCallback
- * 1. Start measurement with 20s interval. Expect the first measurement received in 10s.
+ * TestGnssMeasurementSetCallback:
+ * This test ensures setCallback() can be called consecutively without close().
+ * 1. Start measurement with 20s interval and wait for 1 measurement.
* 2. Start measurement with 1s interval and wait for 5 measurements.
- * 3. Verify the received measurement intervals have expected mean and stddev.
+ * Verify the measurements were received at 1Hz.
*/
TEST_P(GnssHalTest, TestGnssMeasurementSetCallback) {
if (aidl_gnss_hal_->getInterfaceVersion() <= 2) {
@@ -1581,3 +1582,57 @@
status = iGnssMeasurement->close();
ASSERT_TRUE(status.isOk());
}
+
+/*
+ * TestAccumulatedDeltaRange:
+ * 1. Gets the GnssMeasurementExtension and verifies that it returns a non-null extension.
+ * 2. Start measurement with 1s interval and wait for up to 15 measurements.
+ * 3. Verify at least one measurement has a valid AccumulatedDeltaRange state.
+ */
+TEST_P(GnssHalTest, TestAccumulatedDeltaRange) {
+ if (aidl_gnss_hal_->getInterfaceVersion() <= 2) {
+ return;
+ }
+ if ((aidl_gnss_cb_->last_capabilities_ & IGnssCallback::CAPABILITY_ACCUMULATED_DELTA_RANGE) ==
+ 0) {
+ return;
+ }
+
+ ALOGD("TestAccumulatedDeltaRange");
+
+ auto callback = sp<GnssMeasurementCallbackAidl>::make();
+ sp<IGnssMeasurementInterface> iGnssMeasurement;
+ auto status = aidl_gnss_hal_->getExtensionGnssMeasurement(&iGnssMeasurement);
+ ASSERT_TRUE(status.isOk());
+ ASSERT_TRUE(iGnssMeasurement != nullptr);
+
+ IGnssMeasurementInterface::Options options;
+ options.intervalMs = 1000;
+ options.enableFullTracking = true;
+ status = iGnssMeasurement->setCallbackWithOptions(callback, options);
+ ASSERT_TRUE(status.isOk());
+
+ bool accumulatedDeltaRangeFound = false;
+ const int kNumMeasurementEvents = 15;
+
+ // setCallback at 1s interval and wait for 15 measurements
+ for (int i = 0; i < kNumMeasurementEvents; i++) {
+ GnssData lastGnssData;
+ ASSERT_TRUE(callback->gnss_data_cbq_.retrieve(lastGnssData, 10));
+ EXPECT_EQ(callback->gnss_data_cbq_.calledCount(), i + 1);
+ ASSERT_TRUE(lastGnssData.measurements.size() > 0);
+
+ // Validity check GnssData fields
+ checkGnssMeasurementClockFields(lastGnssData);
+ for (const auto& measurement : lastGnssData.measurements) {
+ if ((measurement.accumulatedDeltaRangeState & measurement.ADR_STATE_VALID) > 0) {
+ accumulatedDeltaRangeFound = true;
+ break;
+ }
+ }
+ if (accumulatedDeltaRangeFound) break;
+ }
+ ASSERT_TRUE(accumulatedDeltaRangeFound);
+ status = iGnssMeasurement->close();
+ ASSERT_TRUE(status.isOk());
+}
\ No newline at end of file
diff --git a/gnss/common/utils/default/Utils.cpp b/gnss/common/utils/default/Utils.cpp
index ad351f8..2aed29b 100644
--- a/gnss/common/utils/default/Utils.cpp
+++ b/gnss/common/utils/default/Utils.cpp
@@ -170,7 +170,7 @@
.agcLevelDb = 2.3,
.pseudorangeRateMps = -484.13739013671875,
.pseudorangeRateUncertaintyMps = 1.0379999876022339,
- .accumulatedDeltaRangeState = GnssMeasurement::ADR_STATE_UNKNOWN,
+ .accumulatedDeltaRangeState = GnssMeasurement::ADR_STATE_VALID,
.accumulatedDeltaRangeM = 1.52,
.accumulatedDeltaRangeUncertaintyM = 2.43,
.multipathIndicator = aidl::android::hardware::gnss::GnssMultipathIndicator::UNKNOWN,
diff --git a/graphics/allocator/aidl/Android.bp b/graphics/allocator/aidl/Android.bp
index 098ef17..67c7fb5 100644
--- a/graphics/allocator/aidl/Android.bp
+++ b/graphics/allocator/aidl/Android.bp
@@ -39,6 +39,7 @@
min_sdk_version: "29",
},
},
+ frozen: false,
versions_with_info: [
{
version: "1",
diff --git a/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl b/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl
index 8126143..4bca795 100644
--- a/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl
+++ b/graphics/common/aidl/android/hardware/graphics/common/StandardMetadataType.aidl
@@ -22,9 +22,9 @@
* This is an enum that defines the common types of gralloc 4 buffer metadata. The comments for
* each enum include a description of the metadata that is associated with the type.
*
- * IMapper@4.x must support getting the following standard buffer metadata types, with the exception
- * of SMPTE 2094-10 metadata. IMapper@4.x may support setting these standard buffer metadata types
- * as well.
+ * IMapper@4.x & later must support getting the following standard buffer metadata types, with the
+ * exception of SMPTE 2094-10 and SMPTE 2094-40 metadata. IMapper@4.x & later may support setting
+ * these standard buffer metadata types as well.
*
* When encoding these StandardMetadataTypes into a byte stream, the associated MetadataType is
* is first encoded followed by the StandardMetadataType value. The MetadataType is encoded by
diff --git a/graphics/mapper/stable-c/include/android/hardware/graphics/mapper/IMapper.h b/graphics/mapper/stable-c/include/android/hardware/graphics/mapper/IMapper.h
index f27b0f4..0f6d146 100644
--- a/graphics/mapper/stable-c/include/android/hardware/graphics/mapper/IMapper.h
+++ b/graphics/mapper/stable-c/include/android/hardware/graphics/mapper/IMapper.h
@@ -509,11 +509,12 @@
* particular Metadata field.
*
* The framework will attempt to set the following StandardMetadataType
- * values: DATASPACE, SMPTE2086, CTA861_3, SMPTE2094_40 and BLEND_MODE.
- * We require everyone to support setting those fields. If a device's Composer
- * implementation supports a field, it should be supported here. Over time these
- * metadata fields will be moved out of Composer/BufferQueue/etc. and into the
- * buffer's Metadata fields.
+ * values: DATASPACE, SMPTE2086, CTA861_3, and BLEND_MODE.
+ * We require everyone to support setting those fields. Framework will also attempt to set
+ * SMPTE2094_40 and SMPTE2094_10 if available, and it is required to support setting those
+ * if it is possible to get them. If a device's Composer implementation supports a field,
+ * it should be supported here. Over time these metadata fields will be moved out of
+ * Composer/BufferQueue/etc. and into the buffer's Metadata fields.
*
* @param buffer Buffer receiving desired metadata
* @param metadataType MetadataType for the metadata value being set
@@ -546,11 +547,12 @@
* particular Metadata field.
*
* The framework will attempt to set the following StandardMetadataType
- * values: DATASPACE, SMPTE2086, CTA861_3, SMPTE2094_40 and BLEND_MODE.
- * We require everyone to support setting those fields. If a device's Composer
- * implementation supports a field, it should be supported here. Over time these
- * metadata fields will be moved out of Composer/BufferQueue/etc. and into the
- * buffer's Metadata fields.
+ * values: DATASPACE, SMPTE2086, CTA861_3, and BLEND_MODE.
+ * We require everyone to support setting those fields. Framework will also attempt to set
+ * SMPTE2094_40 and SMPTE2094_10 if available, and it is required to support setting those
+ * if it is possible to get them. If a device's Composer implementation supports a field,
+ * it should be supported here. Over time these metadata fields will be moved out of
+ * Composer/BufferQueue/etc. and into the buffer's Metadata fields.
*
* @param buffer Buffer receiving desired metadata
* @param standardMetadataType StandardMetadataType for the metadata value being set
diff --git a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
index 6ab11a3..326346c 100644
--- a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
+++ b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
@@ -24,6 +24,7 @@
#include <aidl/android/hardware/graphics/common/BufferUsage.h>
#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <aidlcommonsupport/NativeHandle.h>
+#include <android/binder_enums.h>
#include <android/binder_manager.h>
#include <android/dlext.h>
#include <android/hardware/graphics/mapper/IMapper.h>
@@ -66,6 +67,24 @@
int64_t verticalSubSampling;
};
+constexpr const char* STANDARD_METADATA_NAME =
+ "android.hardware.graphics.common.StandardMetadataType";
+
+static bool isStandardMetadata(AIMapper_MetadataType metadataType) {
+ return strcmp(STANDARD_METADATA_NAME, metadataType.name) == 0;
+}
+
+static std::string toString(const std::vector<StandardMetadataType> types) {
+ std::stringstream buf;
+ buf << "[";
+ for (auto type : types) {
+ buf << toString(type) << ", ";
+ }
+ buf.seekp(-2, buf.cur);
+ buf << "]";
+ return buf.str();
+}
+
class BufferHandle {
AIMapper* mIMapper;
buffer_handle_t mHandle = nullptr;
@@ -1533,8 +1552,187 @@
auto bufferHandle = buffer->import();
ASSERT_TRUE(bufferHandle);
auto value = getStandardMetadata<StandardMetadataType::SMPTE2094_40>(*bufferHandle);
- ASSERT_TRUE(value.has_value());
- EXPECT_FALSE(value->has_value());
+ if (value.has_value()) {
+ EXPECT_FALSE(value->has_value());
+ }
+}
+
+TEST_P(GraphicsMapperStableCTests, SupportsRequiredGettersSetters) {
+ auto buffer = allocateGeneric();
+ ASSERT_TRUE(buffer);
+ auto bufferHandle = buffer->import();
+ ASSERT_TRUE(bufferHandle);
+ const AIMapper_MetadataTypeDescription* descriptions = nullptr;
+ size_t descriptionCount = 0;
+ ASSERT_EQ(AIMAPPER_ERROR_NONE,
+ mapper()->v5.listSupportedMetadataTypes(&descriptions, &descriptionCount));
+ std::vector<StandardMetadataType> requiredGetters = {
+ StandardMetadataType::BUFFER_ID,
+ StandardMetadataType::NAME,
+ StandardMetadataType::WIDTH,
+ StandardMetadataType::HEIGHT,
+ StandardMetadataType::LAYER_COUNT,
+ StandardMetadataType::PIXEL_FORMAT_REQUESTED,
+ StandardMetadataType::PIXEL_FORMAT_FOURCC,
+ StandardMetadataType::PIXEL_FORMAT_MODIFIER,
+ StandardMetadataType::USAGE,
+ StandardMetadataType::ALLOCATION_SIZE,
+ StandardMetadataType::PROTECTED_CONTENT,
+ StandardMetadataType::COMPRESSION,
+ StandardMetadataType::INTERLACED,
+ StandardMetadataType::CHROMA_SITING,
+ StandardMetadataType::PLANE_LAYOUTS,
+ StandardMetadataType::CROP,
+ StandardMetadataType::DATASPACE,
+ StandardMetadataType::BLEND_MODE,
+ StandardMetadataType::SMPTE2086,
+ StandardMetadataType::CTA861_3,
+ };
+
+ std::vector<StandardMetadataType> requiredSetters = {
+ StandardMetadataType::DATASPACE,
+ StandardMetadataType::BLEND_MODE,
+ StandardMetadataType::SMPTE2086,
+ StandardMetadataType::CTA861_3,
+ };
+
+ for (int i = 0; i < descriptionCount; i++) {
+ const auto& it = descriptions[i];
+ if (isStandardMetadata(it.metadataType)) {
+ EXPECT_GT(it.metadataType.value, static_cast<int64_t>(StandardMetadataType::INVALID));
+ EXPECT_LT(it.metadataType.value,
+ ndk::internal::enum_values<StandardMetadataType>.size());
+
+ if (it.isGettable) {
+ std::erase(requiredGetters,
+ static_cast<StandardMetadataType>(it.metadataType.value));
+ }
+ if (it.isSettable) {
+ std::erase(requiredSetters,
+ static_cast<StandardMetadataType>(it.metadataType.value));
+ }
+ } else {
+ EXPECT_NE(nullptr, it.description) << "Non-standard metadata must have a description";
+ int len = strlen(it.description);
+ EXPECT_GE(len, 0) << "Non-standard metadata must have a description";
+ }
+ }
+
+ EXPECT_EQ(0, requiredGetters.size()) << "Missing required getters" << toString(requiredGetters);
+ EXPECT_EQ(0, requiredSetters.size()) << "Missing required setters" << toString(requiredSetters);
+}
+
+/*
+ * Test that verifies that if the optional StandardMetadataTypes have getters, they have
+ * the required setters as well
+ */
+TEST_P(GraphicsMapperStableCTests, CheckRequiredSettersIfHasGetters) {
+ auto buffer = allocateGeneric();
+ ASSERT_TRUE(buffer);
+ auto bufferHandle = buffer->import();
+ ASSERT_TRUE(bufferHandle);
+ const AIMapper_MetadataTypeDescription* descriptions = nullptr;
+ size_t descriptionCount = 0;
+ ASSERT_EQ(AIMAPPER_ERROR_NONE,
+ mapper()->v5.listSupportedMetadataTypes(&descriptions, &descriptionCount));
+
+ for (int i = 0; i < descriptionCount; i++) {
+ const auto& it = descriptions[i];
+ if (isStandardMetadata(it.metadataType)) {
+ const auto type = static_cast<StandardMetadataType>(it.metadataType.value);
+ switch (type) {
+ case StandardMetadataType::SMPTE2094_10:
+ case StandardMetadataType::SMPTE2094_40:
+ if (it.isGettable) {
+ EXPECT_TRUE(it.isSettable)
+ << "Type " << toString(type) << " must be settable if gettable";
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+TEST_P(GraphicsMapperStableCTests, ListSupportedWorks) {
+ auto buffer = allocateGeneric();
+ ASSERT_TRUE(buffer);
+ auto bufferHandle = buffer->import();
+ ASSERT_TRUE(bufferHandle);
+ const AIMapper_MetadataTypeDescription* descriptions = nullptr;
+ size_t descriptionCount = 0;
+ ASSERT_EQ(AIMAPPER_ERROR_NONE,
+ mapper()->v5.listSupportedMetadataTypes(&descriptions, &descriptionCount));
+
+ std::vector<uint8_t> metadataBuffer;
+ auto get = [&](AIMapper_MetadataType metadataType) -> int32_t {
+ int32_t size = mapper()->v5.getMetadata(*bufferHandle, metadataType, nullptr, 0);
+ if (size >= 0) {
+ metadataBuffer.resize(size);
+ size = mapper()->v5.getMetadata(*bufferHandle, metadataType, metadataBuffer.data(),
+ metadataBuffer.size());
+ EXPECT_EQ(size, metadataBuffer.size());
+ }
+ return size;
+ };
+
+ for (int i = 0; i < descriptionCount; i++) {
+ const auto& it = descriptions[i];
+ if (!isStandardMetadata(it.metadataType)) {
+ continue;
+ }
+ if (!it.isGettable) {
+ EXPECT_FALSE(it.isSettable)
+ << "StandardMetadata that isn't gettable must not be settable";
+ continue;
+ }
+ EXPECT_GE(get(it.metadataType), 0)
+ << "Get failed for claimed supported getter of "
+ << toString(static_cast<StandardMetadataType>(it.metadataType.value));
+ if (it.isSettable) {
+ EXPECT_EQ(AIMAPPER_ERROR_NONE,
+ mapper()->v5.setMetadata(*bufferHandle, it.metadataType,
+ metadataBuffer.data(), metadataBuffer.size()))
+ << "Failed to set metadata for "
+ << toString(static_cast<StandardMetadataType>(it.metadataType.value));
+ }
+ }
+}
+
+TEST_P(GraphicsMapperStableCTests, GetMetadataBadValue) {
+ auto get = [this](StandardMetadataType type) -> AIMapper_Error {
+ // This is a _Nonnull parameter, but this is enough obfuscation to fool the linter
+ buffer_handle_t buffer = nullptr;
+ int32_t ret =
+ mapper()->v5.getStandardMetadata(buffer, static_cast<int64_t>(type), nullptr, 0);
+ return (ret < 0) ? (AIMapper_Error)-ret : AIMAPPER_ERROR_NONE;
+ };
+
+ for (auto type : ndk::enum_range<StandardMetadataType>()) {
+ if (type == StandardMetadataType::INVALID) {
+ continue;
+ }
+ EXPECT_EQ(AIMAPPER_ERROR_BAD_BUFFER, get(type)) << "Wrong error for " << toString(type);
+ }
+}
+
+TEST_P(GraphicsMapperStableCTests, GetUnsupportedMetadata) {
+ auto buffer = allocateGeneric();
+ ASSERT_TRUE(buffer);
+ auto bufferHandle = buffer->import();
+ ASSERT_TRUE(bufferHandle);
+
+ int result = mapper()->v5.getMetadata(*bufferHandle, {"Fake", 1}, nullptr, 0);
+ EXPECT_EQ(AIMAPPER_ERROR_UNSUPPORTED, -result);
+
+ result = mapper()->v5.getStandardMetadata(
+ *bufferHandle, static_cast<int64_t>(StandardMetadataType::INVALID), nullptr, 0);
+ EXPECT_EQ(AIMAPPER_ERROR_UNSUPPORTED, -result);
+
+ constexpr int64_t unknownStandardType = ndk::internal::enum_values<StandardMetadataType>.size();
+ result = mapper()->v5.getStandardMetadata(*bufferHandle, unknownStandardType, nullptr, 0);
+ EXPECT_EQ(AIMAPPER_ERROR_UNSUPPORTED, -result);
}
std::vector<std::tuple<std::string, std::shared_ptr<IAllocator>>> getIAllocatorsAtLeastVersion(
diff --git a/neuralnetworks/1.0/vts/functional/Android.bp b/neuralnetworks/1.0/vts/functional/Android.bp
index a41f37f..8048e62 100644
--- a/neuralnetworks/1.0/vts/functional/Android.bp
+++ b/neuralnetworks/1.0/vts/functional/Android.bp
@@ -27,7 +27,6 @@
name: "neuralnetworks_vts_functional_defaults",
defaults: [
"VtsHalTargetTestDefaults",
- "neuralnetworks_float16",
],
}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.config/current/android/hardware/radio/config/MultipleEnabledProfilesMode.aidl b/radio/aidl/aidl_api/android.hardware.radio.config/current/android/hardware/radio/config/MultipleEnabledProfilesMode.aidl
new file mode 100644
index 0000000..fad767c
--- /dev/null
+++ b/radio/aidl/aidl_api/android.hardware.radio.config/current/android/hardware/radio/config/MultipleEnabledProfilesMode.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.radio.config;
+@Backing(type="int") @JavaDerive(toString=true) @VintfStability
+enum MultipleEnabledProfilesMode {
+ NONE = 0,
+ MEP_A1 = 1,
+ MEP_A2 = 2,
+ MEP_B = 3,
+}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.config/current/android/hardware/radio/config/SimSlotStatus.aidl b/radio/aidl/aidl_api/android.hardware.radio.config/current/android/hardware/radio/config/SimSlotStatus.aidl
index be4c080..da894cd 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.config/current/android/hardware/radio/config/SimSlotStatus.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.config/current/android/hardware/radio/config/SimSlotStatus.aidl
@@ -38,4 +38,5 @@
String atr;
String eid;
android.hardware.radio.config.SimPortInfo[] portInfo;
+ android.hardware.radio.config.MultipleEnabledProfilesMode supportedMepMode;
}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/NrQos.aidl b/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/NrQos.aidl
index 62f6204..a0792c3 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/NrQos.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/NrQos.aidl
@@ -37,8 +37,12 @@
int fiveQi;
android.hardware.radio.data.QosBandwidth downlink;
android.hardware.radio.data.QosBandwidth uplink;
+ /**
+ * @deprecated use qosFlowIdentifier.
+ */
byte qfi;
char averagingWindowMs;
+ int qosFlowIdentifier;
const byte FLOW_ID_RANGE_MIN = 1;
const byte FLOW_ID_RANGE_MAX = 63;
}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/IRadioModem.aidl b/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/IRadioModem.aidl
index 74dc39d..8546be7 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/IRadioModem.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/IRadioModem.aidl
@@ -36,6 +36,9 @@
interface IRadioModem {
oneway void enableModem(in int serial, in boolean on);
oneway void getBasebandVersion(in int serial);
+ /**
+ * @deprecated use getImei(int serial)
+ */
oneway void getDeviceIdentity(in int serial);
oneway void getHardwareConfig(in int serial);
oneway void getModemActivityInfo(in int serial);
@@ -60,4 +63,5 @@
oneway void setRadioCapability(in int serial, in android.hardware.radio.modem.RadioCapability rc);
oneway void setRadioPower(in int serial, in boolean powerOn, in boolean forEmergencyCall, in boolean preferredForEmergencyCall);
oneway void setResponseFunctions(in android.hardware.radio.modem.IRadioModemResponse radioModemResponse, in android.hardware.radio.modem.IRadioModemIndication radioModemIndication);
+ oneway void getImei(in int serial);
}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/IRadioModemResponse.aidl b/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/IRadioModemResponse.aidl
index af8bbe1..5955439 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/IRadioModemResponse.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/IRadioModemResponse.aidl
@@ -37,6 +37,9 @@
oneway void acknowledgeRequest(in int serial);
oneway void enableModemResponse(in android.hardware.radio.RadioResponseInfo info);
oneway void getBasebandVersionResponse(in android.hardware.radio.RadioResponseInfo info, in String version);
+ /**
+ * @deprecated use getImeiResponse(RadioResponseInfo responseInfo, ImeiInfo imeiInfo)
+ */
oneway void getDeviceIdentityResponse(in android.hardware.radio.RadioResponseInfo info, in String imei, in String imeisv, in String esn, in String meid);
oneway void getHardwareConfigResponse(in android.hardware.radio.RadioResponseInfo info, in android.hardware.radio.modem.HardwareConfig[] config);
oneway void getModemActivityInfoResponse(in android.hardware.radio.RadioResponseInfo info, in android.hardware.radio.modem.ActivityStatsInfo activityInfo);
@@ -59,4 +62,5 @@
oneway void sendDeviceStateResponse(in android.hardware.radio.RadioResponseInfo info);
oneway void setRadioCapabilityResponse(in android.hardware.radio.RadioResponseInfo info, in android.hardware.radio.modem.RadioCapability rc);
oneway void setRadioPowerResponse(in android.hardware.radio.RadioResponseInfo info);
+ oneway void getImeiResponse(in android.hardware.radio.RadioResponseInfo responseInfo, in @nullable android.hardware.radio.modem.ImeiInfo imeiInfo);
}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/ImeiInfo.aidl b/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/ImeiInfo.aidl
new file mode 100644
index 0000000..dda7062
--- /dev/null
+++ b/radio/aidl/aidl_api/android.hardware.radio.modem/current/android/hardware/radio/modem/ImeiInfo.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.radio.modem;
+@JavaDerive(toString=true) @VintfStability
+parcelable ImeiInfo {
+ @Backing(type="int") @VintfStability
+ enum ImeiType {
+ PRIMARY = 1,
+ SECONDARY = 2,
+ }
+ ImeiType type;
+ String imei;
+ String svn;
+}
\ No newline at end of file
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetwork.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetwork.aidl
index 9761900..5aed024 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetwork.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetwork.aidl
@@ -80,4 +80,6 @@
oneway void setNullCipherAndIntegrityEnabled(in int serial, in boolean enabled);
oneway void isN1ModeEnabled(in int serial);
oneway void setN1ModeEnabled(in int serial, boolean enable);
+ oneway void setLocationPrivacySetting(in int serial, in boolean shareLocation);
+ oneway void getLocationPrivacySetting(in int serial);
}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
index 0f017ea..229f3e2 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
@@ -49,4 +49,5 @@
oneway void suppSvcNotify(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.SuppSvcNotification suppSvc);
oneway void voiceRadioTechChanged(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.RadioTechnology rat);
oneway void emergencyNetworkScanResult(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.EmergencyRegResult result);
+ oneway void onNetworkInitiatedLocationResult(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.LocationResponseType locationResponseType);
}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkResponse.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkResponse.aidl
index c228bc1..ccdfde6 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkResponse.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkResponse.aidl
@@ -79,4 +79,6 @@
oneway void setNullCipherAndIntegrityEnabledResponse(in android.hardware.radio.RadioResponseInfo info);
oneway void isN1ModeEnabledResponse(in android.hardware.radio.RadioResponseInfo info, boolean isEnabled);
oneway void setN1ModeEnabledResponse(in android.hardware.radio.RadioResponseInfo info);
+ oneway void setLocationPrivacySettingResponse(in android.hardware.radio.RadioResponseInfo info);
+ oneway void getLocationPrivacySettingResponse(in android.hardware.radio.RadioResponseInfo info, boolean shareLocation);
}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/LocationResponseType.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/LocationResponseType.aidl
new file mode 100644
index 0000000..e89a40f
--- /dev/null
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/LocationResponseType.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.radio.network;
+@Backing(type="int") @JavaDerive(toString=true) @VintfStability
+enum LocationResponseType {
+ REJECTED = 0,
+ ACCEPTED_NO_LOCATION_PROVIDED = 1,
+ ACCEPTED_LOCATION_PROVIDED = 2,
+}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.sim/current/android/hardware/radio/sim/CarrierRestrictions.aidl b/radio/aidl/aidl_api/android.hardware.radio.sim/current/android/hardware/radio/sim/CarrierRestrictions.aidl
index 944f1ca..8a61dca 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.sim/current/android/hardware/radio/sim/CarrierRestrictions.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.sim/current/android/hardware/radio/sim/CarrierRestrictions.aidl
@@ -34,7 +34,15 @@
package android.hardware.radio.sim;
@JavaDerive(toString=true) @VintfStability
parcelable CarrierRestrictions {
- android.hardware.radio.sim.Carrier[] allowedCarriers;
- android.hardware.radio.sim.Carrier[] excludedCarriers;
- boolean allowedCarriersPrioritized;
+ @Backing(type="int") @VintfStability
+ enum CarrierRestrictionStatus {
+ UNKNOWN = 0,
+ NOT_RESTRICTED = 1,
+ RESTRICTED = 2,
+ }
+ android.hardware.radio.sim.Carrier[] allowedCarriers;
+ android.hardware.radio.sim.Carrier[] excludedCarriers;
+ boolean allowedCarriersPrioritized;
+ CarrierRestrictionStatus status;
+ int carrierId;
}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.sim/current/android/hardware/radio/sim/SimApdu.aidl b/radio/aidl/aidl_api/android.hardware.radio.sim/current/android/hardware/radio/sim/SimApdu.aidl
index 2201345..2f2e07b 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.sim/current/android/hardware/radio/sim/SimApdu.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.sim/current/android/hardware/radio/sim/SimApdu.aidl
@@ -41,4 +41,5 @@
int p2;
int p3;
String data;
+ boolean isEs10;
}
diff --git a/radio/aidl/android/hardware/radio/config/MultipleEnabledProfilesMode.aidl b/radio/aidl/android/hardware/radio/config/MultipleEnabledProfilesMode.aidl
new file mode 100644
index 0000000..b18ea0e
--- /dev/null
+++ b/radio/aidl/android/hardware/radio/config/MultipleEnabledProfilesMode.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.config;
+
+/**
+ * Multiple Enabled Profiles(MEP) mode is the jointly supported MEP mode. As per section 3.4.1.1 of
+ * GSMA spec SGP.22 v3.0,there are 3 supported MEP modes: MEP-A1, MEP-A2 and MEP-B.
+ * If there is no jointly supported MEP mode, supported MEP mode is set to NONE.
+ */
+@VintfStability
+@Backing(type="int")
+@JavaDerive(toString=true)
+enum MultipleEnabledProfilesMode {
+ /**
+ * If there is no jointly supported MEP mode, set supported MEP mode to NONE.
+ */
+ NONE,
+ /**
+ * In case of MEP-A1, the ISD-R is selected on eSIM port 0 only and profiles are selected on
+ * eSIM ports 1 and higher, with the eSIM port being assigned by the LPA or platform.
+ */
+ MEP_A1,
+ /**
+ * In case of MEP-A2, the ISD-R is selected on eSIM port 0 only and profiles are selected on
+ * eSIM ports 1 and higher, with the eSIM port being assigned by the eUICC.
+ */
+ MEP_A2,
+ /**
+ * In case of MEP-B, profiles are selected on eSIM ports 0 and higher, with the ISD-R being
+ * selectable on any of these eSIM ports.
+ */
+ MEP_B,
+}
diff --git a/radio/aidl/android/hardware/radio/config/SimSlotStatus.aidl b/radio/aidl/android/hardware/radio/config/SimSlotStatus.aidl
index 748660f..73f2954 100644
--- a/radio/aidl/android/hardware/radio/config/SimSlotStatus.aidl
+++ b/radio/aidl/android/hardware/radio/config/SimSlotStatus.aidl
@@ -16,6 +16,7 @@
package android.hardware.radio.config;
+import android.hardware.radio.config.MultipleEnabledProfilesMode;
import android.hardware.radio.config.SimPortInfo;
@VintfStability
@@ -52,4 +53,8 @@
* active port in the slot mapping.
*/
SimPortInfo[] portInfo;
+ /**
+ * Jointly supported Multiple Enabled Profiles(MEP) mode as per SGP.22 V3.0
+ */
+ MultipleEnabledProfilesMode supportedMepMode;
}
diff --git a/radio/aidl/android/hardware/radio/data/NrQos.aidl b/radio/aidl/android/hardware/radio/data/NrQos.aidl
index af8ab83..28b4a7f 100644
--- a/radio/aidl/android/hardware/radio/data/NrQos.aidl
+++ b/radio/aidl/android/hardware/radio/data/NrQos.aidl
@@ -36,9 +36,13 @@
QosBandwidth downlink;
QosBandwidth uplink;
/**
- * QOS flow identifier of the QOS flow description in the range
- * (FLOW_ID_RANGE_MIN, FLOW_ID_RANGE_MAX)
+ * @deprecated use qosFlowIdentifier.
*/
byte qfi;
char averagingWindowMs;
+ /**
+ * QOS flow identifier of the QOS flow description in the range
+ * (FLOW_ID_RANGE_MIN, FLOW_ID_RANGE_MAX).
+ **/
+ int qosFlowIdentifier;
}
diff --git a/radio/aidl/android/hardware/radio/modem/IRadioModem.aidl b/radio/aidl/android/hardware/radio/modem/IRadioModem.aidl
index a5d98d3..2011c53 100644
--- a/radio/aidl/android/hardware/radio/modem/IRadioModem.aidl
+++ b/radio/aidl/android/hardware/radio/modem/IRadioModem.aidl
@@ -67,6 +67,7 @@
* @param serial Serial number of request.
*
* Response function is IRadioModemResponse.getDeviceIdentityResponse()
+ * @deprecated use getImei(int serial)
*/
void getDeviceIdentity(in int serial);
@@ -235,4 +236,13 @@
*/
void setResponseFunctions(in IRadioModemResponse radioModemResponse,
in IRadioModemIndication radioModemIndication);
+
+ /**
+ * Request the IMEI associated with the radio.
+ *
+ * @param serial : Serial number of request.
+ *
+ * Response function is IRadioModemResponse.getImeiResponse()
+ */
+ void getImei(in int serial);
}
diff --git a/radio/aidl/android/hardware/radio/modem/IRadioModemResponse.aidl b/radio/aidl/android/hardware/radio/modem/IRadioModemResponse.aidl
index eca3192..fd4bffb 100644
--- a/radio/aidl/android/hardware/radio/modem/IRadioModemResponse.aidl
+++ b/radio/aidl/android/hardware/radio/modem/IRadioModemResponse.aidl
@@ -20,6 +20,7 @@
import android.hardware.radio.modem.ActivityStatsInfo;
import android.hardware.radio.modem.HardwareConfig;
import android.hardware.radio.modem.RadioCapability;
+import android.hardware.radio.modem.ImeiInfo;
/**
* Interface declaring response functions to solicited radio requests for modem APIs.
@@ -87,6 +88,8 @@
* RadioError:NOT_PROVISIONED
* RadioError:NO_RESOURCES
* RadioError:CANCELLED
+ * RadioError:REQUEST_NOT_SUPPORTED
+ * @deprecated use getImeiResponse(RadioResponseInfo responseInfo, ImeiInfo imeiInfo)
*/
void getDeviceIdentityResponse(in RadioResponseInfo info, in String imei, in String imeisv,
in String esn, in String meid);
@@ -250,4 +253,20 @@
* RadioError:NO_RF_CALIBRATION_INFO
*/
void setRadioPowerResponse(in RadioResponseInfo info);
+
+ /**
+ * ImeiInfo to encapsulate the IMEI information from modem. When the return error code
+ * is {@code RadioError:NONE}, {@code imeiInfo} must be non-null, and a valid IMEITYPE,
+ * IMEI and SVN must be filled in {@code imeiInfo}. When the error code is not
+ * {@code RadioError:NONE}, {@code imeiInfo} must be {@code null}.
+ *
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ * @param imeiInfo IMEI information
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:MODEM_ERR
+ */
+ void getImeiResponse(in RadioResponseInfo responseInfo, in @nullable ImeiInfo imeiInfo);
}
diff --git a/radio/aidl/android/hardware/radio/modem/ImeiInfo.aidl b/radio/aidl/android/hardware/radio/modem/ImeiInfo.aidl
new file mode 100644
index 0000000..2d25bb7
--- /dev/null
+++ b/radio/aidl/android/hardware/radio/modem/ImeiInfo.aidl
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.modem;
+
+/**
+ * ImeiInfo to encapsulate the IMEI information from modem
+ */
+
+@VintfStability
+@JavaDerive(toString=true)
+parcelable ImeiInfo {
+
+ @VintfStability
+ @Backing(type="int")
+ /**
+ * ImeiType enum is used identify the IMEI as primary or secondary as mentioned in GSMA TS.37
+ */
+ enum ImeiType {
+ /**
+ * This is the primary IMEI of the device as mentioned in the GSMA TS.37. In a multi-SIM
+ * device the modem must set one IMEI with this type as mentioned in GSMA TS37_2.2_REQ_8.
+ * A single SIM with one IMEI must by default set that IMEI with this type.
+ */
+ PRIMARY = 1,
+ /** This is not the primary IMEI of the device */
+ SECONDARY = 2,
+ }
+
+ /** Primary or secondary IMEI as mentioned in GSMA spec TS.37 */
+ ImeiType type;
+ /**
+ * IMEI value, see 3gpp spec 23.003 section 6. Note: This primary IMEI mapping must be
+ * permanent throughout the lifetime of the device irrespective of the factory data reset,
+ * SIM activations or swaps.
+ */
+ String imei;
+ /**
+ * IMEI software version, see 3gpp spec 23.003 section 6.
+ */
+ String svn;
+}
\ No newline at end of file
diff --git a/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl b/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl
index 4d35742..70427f8 100644
--- a/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl
+++ b/radio/aidl/android/hardware/radio/network/IRadioNetwork.aidl
@@ -488,7 +488,11 @@
/**
* Set if null encryption and integrity modes are enabled. If the value of enabled is false
* the modem must not allow any network communications with null ciphering or null integrity
- * modes. In case of an emergency call, the modem must bypass this setting.
+ * modes.
+ *
+ * In the case when enabled is false, integrity protection for user data is optional, but
+ * ciphering for user data is required. In case of an emergency call, the modem must bypass
+ * this setting.
*
* Null ciphering and integrity modes include (but are not limited to):
* 2G: A5/0
@@ -530,4 +534,30 @@
* Response function is IRadioNetworkResponse.setN1ModeEnabledResponse()
*/
void setN1ModeEnabled(in int serial, boolean enable);
+
+ /**
+ * This API updates the current user setting of sharing the location data. This value must be
+ * used by radio before honoring a network initiated location request for non emergency use
+ * cases. The radio shall ignore this setting during emergency call, emergency SMS or emergency
+ * call back modes and continue to provide the location information to the network initiated
+ * location requests.
+ *
+ * @param serial Serial number of request.
+ * @param shareLocation Whether to share location data to the network or not. true means the
+ * radio is allowed to provide location data for any network initiated locations
+ * request. false means the radio must not share location data for any network initiated
+ * location requests for non-emergency use cases.
+ *
+ * Response function is IRadioNetworkResponse.setLocationPrivacySettingResponse()
+ */
+ void setLocationPrivacySetting(in int serial, in boolean shareLocation);
+
+ /**
+ * Request the current setting of sharing the location data.
+ *
+ * @param serial Serial number of request.
+ *
+ * Response function is IRadioNetworkResponse.getLocationPrivacySettingResponse()
+ */
+ void getLocationPrivacySetting(in int serial);
}
diff --git a/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl b/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
index 47d932d..2891496 100644
--- a/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
+++ b/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
@@ -21,13 +21,14 @@
import android.hardware.radio.network.BarringInfo;
import android.hardware.radio.network.CellIdentity;
import android.hardware.radio.network.CellInfo;
+import android.hardware.radio.network.EmergencyRegResult;
import android.hardware.radio.network.LinkCapacityEstimate;
+import android.hardware.radio.network.LocationResponseType;
import android.hardware.radio.network.NetworkScanResult;
import android.hardware.radio.network.PhoneRestrictedState;
import android.hardware.radio.network.PhysicalChannelConfig;
import android.hardware.radio.network.SignalStrength;
import android.hardware.radio.network.SuppSvcNotification;
-import android.hardware.radio.network.EmergencyRegResult;
/**
* Interface declaring unsolicited radio indications for network APIs.
@@ -199,4 +200,13 @@
* @param result the result of the Emergency Network Scan
*/
void emergencyNetworkScanResult(in RadioIndicationType type, in EmergencyRegResult result);
+
+ /**
+ * Reports the result of the network initiated location request.
+ *
+ * @param type Type of radio indication
+ * @param locationResponseType result of the network initiated location request.
+ */
+ void onNetworkInitiatedLocationResult(
+ in RadioIndicationType type, in LocationResponseType locationResponseType);
}
diff --git a/radio/aidl/android/hardware/radio/network/IRadioNetworkResponse.aidl b/radio/aidl/android/hardware/radio/network/IRadioNetworkResponse.aidl
index 3802bd6..3fa6521 100644
--- a/radio/aidl/android/hardware/radio/network/IRadioNetworkResponse.aidl
+++ b/radio/aidl/android/hardware/radio/network/IRadioNetworkResponse.aidl
@@ -654,4 +654,26 @@
* RadioError:INVALID_STATE
*/
void setN1ModeEnabledResponse(in RadioResponseInfo info);
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:INTERNAL_ERR
+ */
+ void setLocationPrivacySettingResponse(in RadioResponseInfo info);
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ * @param shareLocation Indicates whether the location sharing is allowed or not, True if
+ * allowed else false.
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:INTERNAL_ERR
+ */
+ void getLocationPrivacySettingResponse(in RadioResponseInfo info, boolean shareLocation);
}
diff --git a/radio/aidl/android/hardware/radio/network/LocationResponseType.aidl b/radio/aidl/android/hardware/radio/network/LocationResponseType.aidl
new file mode 100644
index 0000000..0c502d0
--- /dev/null
+++ b/radio/aidl/android/hardware/radio/network/LocationResponseType.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.network;
+
+@VintfStability
+@Backing(type="int")
+@JavaDerive(toString=true)
+enum LocationResponseType {
+ /**
+ * Network initiated Location request rejected by modem because the user has not given
+ * permission for this use case
+ */
+ REJECTED = 0,
+ /**
+ * Network initiated Location request is accepted by modem however no location information has
+ * been shared to network due to a failure
+ */
+ ACCEPTED_NO_LOCATION_PROVIDED = 1,
+ /**
+ * Network initiated Location request is accepted and location information is provided to the
+ * network by modem
+ */
+ ACCEPTED_LOCATION_PROVIDED = 2,
+}
diff --git a/radio/aidl/android/hardware/radio/sim/CarrierRestrictions.aidl b/radio/aidl/android/hardware/radio/sim/CarrierRestrictions.aidl
index 3dce907..5a79f0f 100644
--- a/radio/aidl/android/hardware/radio/sim/CarrierRestrictions.aidl
+++ b/radio/aidl/android/hardware/radio/sim/CarrierRestrictions.aidl
@@ -21,6 +21,22 @@
@VintfStability
@JavaDerive(toString=true)
parcelable CarrierRestrictions {
+ @VintfStability
+ @Backing(type="int")
+ /** This enum defines the carrier restriction status values */
+ enum CarrierRestrictionStatus {
+ /**
+ * Carrier restriction status value is unknown, used in cases where modem is dependent on
+ * external module to know about the lock status and the module hasn’t yet provided the lock
+ * status. For example, when the lock status is maintained on a cloud server and device has
+ * just booted after out of box and not yet connected to the internet.
+ */
+ UNKNOWN = 0,
+ /** There is no carrier restriction on the device */
+ NOT_RESTRICTED = 1,
+ /** The device is restricted to a carrier */
+ RESTRICTED = 2,
+ }
/**
* Allowed carriers
*/
@@ -40,4 +56,11 @@
* and not in the allowed list.
*/
boolean allowedCarriersPrioritized;
+ /** Current restriction status as defined in CarrierRestrictionStatus enum */
+ CarrierRestrictionStatus status;
+ /**
+ * Android carrier ID of the locked carrier.
+ * see https://source.android.com/docs/core/connect/carrierid
+ */
+ int carrierId;
}
diff --git a/radio/aidl/android/hardware/radio/sim/SimApdu.aidl b/radio/aidl/android/hardware/radio/sim/SimApdu.aidl
index 43adbbc..4759e2e 100644
--- a/radio/aidl/android/hardware/radio/sim/SimApdu.aidl
+++ b/radio/aidl/android/hardware/radio/sim/SimApdu.aidl
@@ -49,4 +49,9 @@
* In hex string format ([a-fA-F0-9]*)
*/
String data;
+ /**
+ * isEs10 indicates that the current streaming APDU contains an ES10 command or it is a regular
+ * APDU. (As per spec SGP.22 V3.0, ES10 commands needs to be sent over command port of MEP-A1)
+ */
+ boolean isEs10;
}
diff --git a/radio/aidl/compat/libradiocompat/data/structs.cpp b/radio/aidl/compat/libradiocompat/data/structs.cpp
index cc6dcbc..22cde6b 100644
--- a/radio/aidl/compat/libradiocompat/data/structs.cpp
+++ b/radio/aidl/compat/libradiocompat/data/structs.cpp
@@ -136,7 +136,7 @@
.fiveQi = qos.fiveQi,
.downlink = toAidl(qos.downlink),
.uplink = toAidl(qos.uplink),
- .qfi = static_cast<int8_t>(qos.qfi),
+ .qosFlowIdentifier = static_cast<int8_t>(qos.qfi),
.averagingWindowMs = qos.averagingWindowMs,
};
}
diff --git a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioModem.h b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioModem.h
index beb1fb0..54bedd8 100644
--- a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioModem.h
+++ b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioModem.h
@@ -26,6 +26,7 @@
::ndk::ScopedAStatus enableModem(int32_t serial, bool on) override;
::ndk::ScopedAStatus getBasebandVersion(int32_t serial) override;
::ndk::ScopedAStatus getDeviceIdentity(int32_t serial) override;
+ ::ndk::ScopedAStatus getImei(int32_t serial) override;
::ndk::ScopedAStatus getHardwareConfig(int32_t serial) override;
::ndk::ScopedAStatus getModemActivityInfo(int32_t serial) override;
::ndk::ScopedAStatus getModemStackStatus(int32_t serial) override;
diff --git a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioNetwork.h b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioNetwork.h
index f9f3c6c..b446103 100644
--- a/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioNetwork.h
+++ b/radio/aidl/compat/libradiocompat/include/libradiocompat/RadioNetwork.h
@@ -104,6 +104,9 @@
::ndk::ScopedAStatus setNullCipherAndIntegrityEnabled(int32_t serial, bool enabled) override;
+ ::ndk::ScopedAStatus setLocationPrivacySetting(int32_t serial, bool shareLocation) override;
+ ::ndk::ScopedAStatus getLocationPrivacySetting(int32_t serial) override;
+
protected:
std::shared_ptr<::aidl::android::hardware::radio::network::IRadioNetworkResponse> respond();
diff --git a/radio/aidl/compat/libradiocompat/modem/RadioModem.cpp b/radio/aidl/compat/libradiocompat/modem/RadioModem.cpp
index d28b940..f088b10 100644
--- a/radio/aidl/compat/libradiocompat/modem/RadioModem.cpp
+++ b/radio/aidl/compat/libradiocompat/modem/RadioModem.cpp
@@ -15,7 +15,7 @@
*/
#include <libradiocompat/RadioModem.h>
-
+#include "commonStructs.h"
#include "debug.h"
#include "structs.h"
@@ -49,6 +49,13 @@
return ok();
}
+ScopedAStatus RadioModem::getImei(int32_t serial) {
+ LOG_CALL << serial;
+ LOG(ERROR) << " getImei is unsupported by HIDL HALs";
+ respond()->getImeiResponse(notSupported(serial), {});
+ return ok();
+}
+
ScopedAStatus RadioModem::getHardwareConfig(int32_t serial) {
LOG_CALL << serial;
mHal1_5->getHardwareConfig(serial);
diff --git a/radio/aidl/compat/libradiocompat/network/RadioNetwork.cpp b/radio/aidl/compat/libradiocompat/network/RadioNetwork.cpp
index 005deae..730b5dd 100644
--- a/radio/aidl/compat/libradiocompat/network/RadioNetwork.cpp
+++ b/radio/aidl/compat/libradiocompat/network/RadioNetwork.cpp
@@ -360,4 +360,18 @@
respond()->setN1ModeEnabledResponse(notSupported(serial));
return ok();
}
+
+ScopedAStatus RadioNetwork::setLocationPrivacySetting(int32_t serial, bool /*shareLocation*/) {
+ LOG_CALL << serial;
+ LOG(ERROR) << " setLocationPrivacySetting is unsupported by HIDL HALs";
+ respond()->setLocationPrivacySettingResponse(notSupported(serial));
+ return ok();
+}
+
+ScopedAStatus RadioNetwork::getLocationPrivacySetting(int32_t serial) {
+ LOG_CALL << serial;
+ LOG(ERROR) << " getLocationPrivacySetting is unsupported by HIDL HALs";
+ respond()->getLocationPrivacySettingResponse(notSupported(serial), false);
+ return ok();
+}
} // namespace android::hardware::radio::compat
diff --git a/radio/aidl/vts/radio_modem_response.cpp b/radio/aidl/vts/radio_modem_response.cpp
index 20b44c5..050b2c8 100644
--- a/radio/aidl/vts/radio_modem_response.cpp
+++ b/radio/aidl/vts/radio_modem_response.cpp
@@ -46,6 +46,13 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus RadioModemResponse::getImeiResponse(const RadioResponseInfo& info,
+ const std::optional<ImeiInfo>& /*imeiInfo*/) {
+ rspInfo = info;
+ parent_modem.notify(info.serial);
+ return ndk::ScopedAStatus::ok();
+}
+
ndk::ScopedAStatus RadioModemResponse::getHardwareConfigResponse(
const RadioResponseInfo& info, const std::vector<HardwareConfig>& /*config*/) {
rspInfo = info;
diff --git a/radio/aidl/vts/radio_modem_test.cpp b/radio/aidl/vts/radio_modem_test.cpp
index f88da13..c00b238 100644
--- a/radio/aidl/vts/radio_modem_test.cpp
+++ b/radio/aidl/vts/radio_modem_test.cpp
@@ -188,6 +188,25 @@
}
/*
+ * Test IRadioModem.getImei() for the response returned.
+ */
+TEST_P(RadioModemTest, getImei) {
+ LOG(DEBUG) << "getImei";
+ serial = GetRandomSerialNumber();
+
+ radio_modem->getImei(serial);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_modem->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_modem->rspInfo.serial);
+
+ if (cardStatus.cardState == CardStatus::STATE_ABSENT) {
+ ASSERT_TRUE(CheckAnyOfErrors(radioRsp_modem->rspInfo.error,
+ {RadioError::NONE, RadioError::EMPTY_RECORD}));
+ }
+ LOG(DEBUG) << "getImei finished";
+}
+
+/*
* Test IRadioModem.nvReadItem() for the response returned.
*/
TEST_P(RadioModemTest, nvReadItem) {
diff --git a/radio/aidl/vts/radio_modem_utils.h b/radio/aidl/vts/radio_modem_utils.h
index 49e1891..d2f5a10 100644
--- a/radio/aidl/vts/radio_modem_utils.h
+++ b/radio/aidl/vts/radio_modem_utils.h
@@ -19,6 +19,7 @@
#include <aidl/android/hardware/radio/modem/BnRadioModemIndication.h>
#include <aidl/android/hardware/radio/modem/BnRadioModemResponse.h>
#include <aidl/android/hardware/radio/modem/IRadioModem.h>
+#include <aidl/android/hardware/radio/modem/ImeiInfo.h>
#include "radio_aidl_hal_utils.h"
@@ -52,6 +53,9 @@
const std::string& esn,
const std::string& meid) override;
+ virtual ndk::ScopedAStatus getImeiResponse(const RadioResponseInfo& info,
+ const std::optional<ImeiInfo>& config) override;
+
virtual ndk::ScopedAStatus getHardwareConfigResponse(
const RadioResponseInfo& info, const std::vector<HardwareConfig>& config) override;
diff --git a/radio/aidl/vts/radio_network_indication.cpp b/radio/aidl/vts/radio_network_indication.cpp
index ae3bd4b..7178982 100644
--- a/radio/aidl/vts/radio_network_indication.cpp
+++ b/radio/aidl/vts/radio_network_indication.cpp
@@ -97,3 +97,8 @@
RadioIndicationType /*type*/, const EmergencyRegResult& /*result*/) {
return ndk::ScopedAStatus::ok();
}
+
+ndk::ScopedAStatus RadioNetworkIndication::onNetworkInitiatedLocationResult(
+ RadioIndicationType /*type*/, LocationResponseType /*locationResponseType*/) {
+ return ndk::ScopedAStatus::ok();
+}
diff --git a/radio/aidl/vts/radio_network_response.cpp b/radio/aidl/vts/radio_network_response.cpp
index c890df0..4f0e4f3 100644
--- a/radio/aidl/vts/radio_network_response.cpp
+++ b/radio/aidl/vts/radio_network_response.cpp
@@ -313,3 +313,17 @@
parent_network.notify(info.serial);
return ndk::ScopedAStatus::ok();
}
+
+ndk::ScopedAStatus RadioNetworkResponse::setLocationPrivacySettingResponse(
+ const RadioResponseInfo& info) {
+ rspInfo = info;
+ parent_network.notify(info.serial);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus RadioNetworkResponse::getLocationPrivacySettingResponse(
+ const RadioResponseInfo& info, bool /*shareLocation*/) {
+ rspInfo = info;
+ parent_network.notify(info.serial);
+ return ndk::ScopedAStatus::ok();
+}
diff --git a/radio/aidl/vts/radio_network_test.cpp b/radio/aidl/vts/radio_network_test.cpp
index 5872eb0..e7a48bc 100644
--- a/radio/aidl/vts/radio_network_test.cpp
+++ b/radio/aidl/vts/radio_network_test.cpp
@@ -1961,3 +1961,21 @@
RadioError::REQUEST_NOT_SUPPORTED, RadioError::NONE}));
}
}
+
+/*
+ * Test IRadioNetwork.setNullCipherAndIntegrityEnabled() for the response returned.
+ */
+TEST_P(RadioNetworkTest, setNullCipherAndIntegrityEnabled) {
+ LOG(DEBUG) << "setNullCipherAndIntegrityEnabled";
+ serial = GetRandomSerialNumber();
+
+ radio_network->setNullCipherAndIntegrityEnabled(serial, false);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_network->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_network->rspInfo.serial);
+
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_network->rspInfo.error,
+ {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::MODEM_ERR}));
+ LOG(DEBUG) << "setNullCipherAndIntegrityEnabled finished";
+}
diff --git a/radio/aidl/vts/radio_network_utils.h b/radio/aidl/vts/radio_network_utils.h
index e6a320b..92dcb1f 100644
--- a/radio/aidl/vts/radio_network_utils.h
+++ b/radio/aidl/vts/radio_network_utils.h
@@ -166,6 +166,12 @@
const RadioResponseInfo& info, bool isEnabled) override;
virtual ndk::ScopedAStatus setN1ModeEnabledResponse(const RadioResponseInfo& info) override;
+
+ virtual ndk::ScopedAStatus setLocationPrivacySettingResponse(
+ const RadioResponseInfo& info) override;
+
+ virtual ndk::ScopedAStatus getLocationPrivacySettingResponse(const RadioResponseInfo& info,
+ bool shareLocation) override;
};
/* Callback class for radio network indication */
@@ -223,6 +229,9 @@
virtual ndk::ScopedAStatus emergencyNetworkScanResult(
RadioIndicationType type, const EmergencyRegResult& result) override;
+
+ virtual ndk::ScopedAStatus onNetworkInitiatedLocationResult(
+ RadioIndicationType type, LocationResponseType locationResponseType) override;
};
// The main test class for Radio AIDL Network.
diff --git a/secure_element/aidl/Android.bp b/secure_element/aidl/Android.bp
new file mode 100644
index 0000000..6450eb4
--- /dev/null
+++ b/secure_element/aidl/Android.bp
@@ -0,0 +1,44 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+ name: "android.hardware.secure_element",
+ vendor_available: true,
+ host_supported: true,
+ srcs: ["android/hardware/secure_element/*.aidl"],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ sdk_version: "system_current",
+ },
+ },
+}
+
+cc_test {
+ name: "VtsHalSecureElementTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["vts/VtsHalSecureElementTargetTest.cpp"],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "android.hardware.secure_element-V1-ndk",
+ "libgmock",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/ISecureElement.aidl b/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/ISecureElement.aidl
new file mode 100644
index 0000000..fba29ab
--- /dev/null
+++ b/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/ISecureElement.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.secure_element;
+@VintfStability
+interface ISecureElement {
+ void closeChannel(in byte channelNumber);
+ byte[] getAtr();
+ void init(in android.hardware.secure_element.ISecureElementCallback clientCallback);
+ boolean isCardPresent();
+ byte[] openBasicChannel(in byte[] aid, in byte p2);
+ android.hardware.secure_element.LogicalChannelResponse openLogicalChannel(in byte[] aid, in byte p2);
+ void reset();
+ byte[] transmit(in byte[] data);
+ const int FAILED = 1;
+ const int CHANNEL_NOT_AVAILABLE = 2;
+ const int NO_SUCH_ELEMENT_ERROR = 3;
+ const int UNSUPPORTED_OPERATION = 4;
+ const int IOERROR = 5;
+}
diff --git a/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/ISecureElementCallback.aidl b/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/ISecureElementCallback.aidl
new file mode 100644
index 0000000..6c2be2a
--- /dev/null
+++ b/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/ISecureElementCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.secure_element;
+@VintfStability
+interface ISecureElementCallback {
+ void onStateChange(in boolean connected, in String debugReason);
+}
diff --git a/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/LogicalChannelResponse.aidl b/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/LogicalChannelResponse.aidl
new file mode 100644
index 0000000..f2e7f04
--- /dev/null
+++ b/secure_element/aidl/aidl_api/android.hardware.secure_element/current/android/hardware/secure_element/LogicalChannelResponse.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.secure_element;
+@VintfStability
+parcelable LogicalChannelResponse {
+ byte channelNumber;
+ byte[] selectResponse;
+}
diff --git a/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl b/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl
new file mode 100644
index 0000000..7c5a704
--- /dev/null
+++ b/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.secure_element;
+
+import android.hardware.secure_element.ISecureElementCallback;
+import android.hardware.secure_element.LogicalChannelResponse;
+
+@VintfStability
+interface ISecureElement {
+ const int FAILED = 1;
+ const int CHANNEL_NOT_AVAILABLE = 2;
+ const int NO_SUCH_ELEMENT_ERROR = 3;
+ const int UNSUPPORTED_OPERATION = 4;
+ const int IOERROR = 5;
+
+ /**
+ * Closes the channel indicated by the channelNumber.
+ *
+ * @throws ServiceSpecificException Closing a channel must return
+ * FAILED on an error or if a basic channel (i.e. channel 0)
+ * is used.
+ *
+ * @param channelNumber to be closed
+ */
+ void closeChannel(in byte channelNumber);
+
+ /**
+ * Returns Answer to Reset as per ISO/IEC 7816
+ *
+ * @return containing the response. Empty vector if Secure Element
+ * doesn't support ATR.
+ */
+ byte[] getAtr();
+
+ /**
+ * Initializes the Secure Element. This may include updating the applet
+ * and/or vendor-specific initialization.
+ *
+ * HAL service must send onStateChange() with connected equal to true
+ * after all the initialization has been successfully completed.
+ * Clients must wait for a onStateChange(true) before opening channels.
+ *
+ * @param clientCallback callback used to sent status of the SE back to the
+ * client
+ */
+ void init(in ISecureElementCallback clientCallback);
+
+ /**
+ * Returns the current state of the card.
+ *
+ * This is useful for removable Secure Elements like UICC,
+ * Secure Elements on SD cards etc.
+ *
+ * @return true if present, false otherwise
+ */
+ boolean isCardPresent();
+
+ /**
+ * Opens a basic channel with the Secure Element, selecting the applet
+ * represented by the Application ID (AID). A basic channel has channel
+ * number 0.
+ *
+ * @throws ServiceSpecificException with codes
+ * - CHANNEL_NOT_AVAILABLE if secure element has reached the maximum
+ * limit on the number of channels it can support.
+ * - NO_SUCH_ELEMENT_ERROR if AID provided doesn't match any applet
+ * on the secure element.
+ * - UNSUPPORTED_OPERATION if operation provided by the P2 parameter
+ * is not permitted by the applet.
+ * - IOERROR if there was an error communicating with the Secure Element.
+ *
+ * @param aid AID to uniquely identify the applet on the Secure Element
+ * @param p2 P2 parameter of SELECT APDU as per ISO 7816-4
+ *
+ * @return On success, response to SELECT command.
+ */
+ byte[] openBasicChannel(in byte[] aid, in byte p2);
+
+ /**
+ * Opens a logical channel with the Secure Element, selecting the applet
+ * represented by the Application ID (AID).
+ *
+ * @param aid AID to uniquely identify the applet on the Secure Element
+ * @param p2 P2 parameter of SELECT APDU as per ISO 7816-4
+ * @throws ServiceSpecificException on error with the following code:
+ * - CHANNEL_NOT_AVAILABLE if secure element has reached the maximum
+ * limit on the number of channels it can support.
+ * - NO_SUCH_ELEMENT_ERROR if AID provided doesn't match any applet
+ * on the secure element.
+ * - UNSUPPORTED_OPERATION if operation provided by the P2 parameter
+ * is not permitted by the applet.
+ * - IOERROR if there was an error communicating with the Secure Element.
+ *
+ * @return On success, response to SELECT command
+ */
+ LogicalChannelResponse openLogicalChannel(in byte[] aid, in byte p2);
+
+ /**
+ * Reset the Secure Element.
+ *
+ * HAL should trigger reset to the secure element. It could hardware power cycle or
+ * a soft reset depends on the hardware design.
+ * HAL service must send onStateChange() with connected equal to true
+ * after resetting and all the re-initialization has been successfully completed.
+ */
+ void reset();
+
+ /**
+ * Transmits an APDU command (as per ISO/IEC 7816) to the SE.
+ *
+ * @param data APDU command to be sent
+ * @return response to the command
+ */
+ byte[] transmit(in byte[] data);
+}
diff --git a/secure_element/aidl/android/hardware/secure_element/ISecureElementCallback.aidl b/secure_element/aidl/android/hardware/secure_element/ISecureElementCallback.aidl
new file mode 100644
index 0000000..d15a7fb
--- /dev/null
+++ b/secure_element/aidl/android/hardware/secure_element/ISecureElementCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.secure_element;
+
+@VintfStability
+interface ISecureElementCallback {
+ /**
+ * Used to inform the client about changes in the state of the Secure
+ * Element.
+ *
+ * @param connected indicates the current state of the SE
+ * @param reason provides additional data why there was a change in state
+ * ex. initialization error, SE removed etc
+ * This is used only for debugging purpose to understand
+ * in-field issues.
+ */
+ void onStateChange(in boolean connected, in String debugReason);
+}
diff --git a/secure_element/aidl/android/hardware/secure_element/LogicalChannelResponse.aidl b/secure_element/aidl/android/hardware/secure_element/LogicalChannelResponse.aidl
new file mode 100644
index 0000000..65ea71e
--- /dev/null
+++ b/secure_element/aidl/android/hardware/secure_element/LogicalChannelResponse.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.secure_element;
+
+@VintfStability
+parcelable LogicalChannelResponse {
+ /**
+ * Channel number to uniquely identify the channel
+ */
+ byte channelNumber;
+ /**
+ * Response to SELECT command as per ISO/IEC 7816
+ */
+ byte[] selectResponse;
+}
diff --git a/secure_element/aidl/default/Android.bp b/secure_element/aidl/default/Android.bp
new file mode 100644
index 0000000..d1bb393
--- /dev/null
+++ b/secure_element/aidl/default/Android.bp
@@ -0,0 +1,24 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+ name: "android.hardware.secure_element-service.example",
+ relative_install_path: "hw",
+ vendor: true,
+ init_rc: ["secure_element.rc"],
+ vintf_fragments: ["secure_element.xml"],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "android.hardware.secure_element-V1-ndk",
+ ],
+ srcs: [
+ "main.cpp",
+ ],
+}
diff --git a/secure_element/aidl/default/main.cpp b/secure_element/aidl/default/main.cpp
new file mode 100644
index 0000000..16b8236
--- /dev/null
+++ b/secure_element/aidl/default/main.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/android/hardware/secure_element/BnSecureElement.h>
+
+#include <android-base/hex.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using aidl::android::hardware::secure_element::BnSecureElement;
+using aidl::android::hardware::secure_element::ISecureElementCallback;
+using aidl::android::hardware::secure_element::LogicalChannelResponse;
+using android::base::HexString;
+using ndk::ScopedAStatus;
+
+static const std::vector<uint8_t> kAndroidTestAid = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41,
+ 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64,
+ 0x43, 0x54, 0x53, 0x31};
+static const std::vector<uint8_t> kLongAndroidTestAid = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41,
+ 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64,
+ 0x43, 0x54, 0x53, 0x32};
+
+class MySecureElement : public BnSecureElement {
+ public:
+ ScopedAStatus closeChannel(int8_t channelNumber) override {
+ LOG(INFO) << __func__ << " channel number: " << channelNumber;
+ return ScopedAStatus::ok();
+ }
+ ScopedAStatus getAtr(std::vector<uint8_t>* _aidl_return) override {
+ LOG(INFO) << __func__;
+ _aidl_return->clear();
+ return ScopedAStatus::ok();
+ }
+ ScopedAStatus init(const std::shared_ptr<ISecureElementCallback>& clientCallback) override {
+ LOG(INFO) << __func__ << " callback: " << clientCallback.get();
+ if (!clientCallback) {
+ return ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
+ }
+ mCb = clientCallback;
+ mCb->onStateChange(true, "");
+ return ScopedAStatus::ok();
+ }
+ ScopedAStatus isCardPresent(bool* _aidl_return) override {
+ LOG(INFO) << __func__;
+ *_aidl_return = true;
+ return ScopedAStatus::ok();
+ }
+ ScopedAStatus openBasicChannel(const std::vector<uint8_t>& aid, int8_t p2,
+ std::vector<uint8_t>* _aidl_return) override {
+ LOG(INFO) << __func__ << " aid: " << HexString(aid.data(), aid.size()) << " (" << aid.size()
+ << ") p2 " << p2;
+
+ // TODO(b/123254068) - this is not an implementation of the OMAPI protocol or APDU.
+ // The functionality here is enough to exercise the framework, but actual
+ // calls to the secure element will fail. This implementation does not model
+ // channel isolation or any other aspects important to implementing secure element.
+ *_aidl_return = {0x90, 0x00, 0x00}; // DO NOT COPY
+ return ScopedAStatus::ok();
+ }
+ ScopedAStatus openLogicalChannel(
+ const std::vector<uint8_t>& aid, int8_t p2,
+ ::aidl::android::hardware::secure_element::LogicalChannelResponse* _aidl_return)
+ override {
+ LOG(INFO) << __func__ << " aid: " << HexString(aid.data(), aid.size()) << " (" << aid.size()
+ << ") p2 " << p2;
+
+ if (aid != kAndroidTestAid && aid != kLongAndroidTestAid) {
+ return ScopedAStatus::fromServiceSpecificError(NO_SUCH_ELEMENT_ERROR);
+ }
+
+ *_aidl_return = LogicalChannelResponse{.channelNumber = 1, .selectResponse = {}};
+
+ // TODO(b/123254068) - this is not an implementation of the OMAPI protocol or APDU.
+ // The functionality here is enough to exercise the framework, but actual
+ // calls to the secure element will fail. This implementation does not model
+ // channel isolation or any other aspects important to implementing secure element.
+ if (aid == kAndroidTestAid) { // DO NOT COPY
+ size_t size = 2050; // DO NOT COPY
+ _aidl_return->selectResponse.resize(size); // DO NOT COPY
+ _aidl_return->selectResponse[size - 1] = 0x00; // DO NOT COPY
+ _aidl_return->selectResponse[size - 2] = 0x90; // DO NOT COPY
+ } else { // DO NOT COPY
+ _aidl_return->selectResponse = {0x00, 0x00, 0x90, 0x00}; // DO NOT COPY
+ } // DO NOT COPY
+
+ LOG(INFO) << __func__ << " sending response: "
+ << HexString(_aidl_return->selectResponse.data(),
+ _aidl_return->selectResponse.size());
+
+ return ScopedAStatus::ok();
+ }
+ ScopedAStatus reset() override {
+ LOG(INFO) << __func__;
+ mCb->onStateChange(false, "reset");
+ mCb->onStateChange(true, "reset");
+ return ScopedAStatus::ok();
+ }
+ ScopedAStatus transmit(const std::vector<uint8_t>& data,
+ std::vector<uint8_t>* _aidl_return) override {
+ LOG(INFO) << __func__ << " data: " << HexString(data.data(), data.size()) << " ("
+ << data.size() << ")";
+
+ // TODO(b/123254068) - this is not an implementation of the OMAPI protocol or APDU.
+ // The functionality here is enough to exercise the framework, but actual
+ // calls to the secure element will fail. This implementation does not model
+ // channel isolation or any other aspects important to implementing secure element.
+
+ std::string hex = HexString(data.data(), data.size()); // DO NOT COPY
+ if (hex == "01a4040210a000000476416e64726f696443545331") { // DO NOT COPY
+ *_aidl_return = {0x00, 0x6A, 0x00}; // DO NOT COPY
+ } else if (data == std::vector<uint8_t>{0x00, 0xF4, 0x00, 0x00, 0x00}) { // DO NOT COPY
+ // CHECK_SELECT_P2_APDU w/ channel 1 // DO NOT COPY
+ *_aidl_return = {0x00, 0x90, 0x00}; // DO NOT COPY
+ } else if (data == std::vector<uint8_t>{0x01, 0xF4, 0x00, 0x00, 0x00}) { // DO NOT COPY
+ // CHECK_SELECT_P2_APDU w/ channel 1 // DO NOT COPY
+ *_aidl_return = {0x00, 0x90, 0x00}; // DO NOT COPY
+ } else if (data.size() == 5 || data.size() == 8) { // DO NOT COPY
+ // SEGMENTED_RESP_APDU - happens to use length 5 and 8 // DO NOT COPY
+ size_t size = (data[2] << 8 | data[3]) + 2; // DO NOT COPY
+ _aidl_return->resize(size); // DO NOT COPY
+ (*_aidl_return)[size - 1] = 0x00; // DO NOT COPY
+ (*_aidl_return)[size - 2] = 0x90; // DO NOT COPY
+ if (size >= 3) (*_aidl_return)[size - 3] = 0xFF; // DO NOT COPY
+ } else { // DO NOT COPY
+ *_aidl_return = {0x90, 0x00, 0x00}; // DO NOT COPY
+ } // DO NOT COPY
+
+ return ScopedAStatus::ok();
+ }
+
+ private:
+ std::shared_ptr<ISecureElementCallback> mCb;
+};
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+ auto se = ndk::SharedRefBase::make<MySecureElement>();
+ const std::string name = std::string() + BnSecureElement::descriptor + "/eSE1";
+ binder_status_t status = AServiceManager_addService(se->asBinder().get(), name.c_str());
+ CHECK_EQ(status, STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/secure_element/aidl/default/secure_element.rc b/secure_element/aidl/default/secure_element.rc
new file mode 100644
index 0000000..7d21666
--- /dev/null
+++ b/secure_element/aidl/default/secure_element.rc
@@ -0,0 +1,4 @@
+service vendor.secure_element /vendor/bin/hw/android.hardware.secure_element-service.example
+ class hal
+ user nobody
+ group nobody
diff --git a/secure_element/aidl/default/secure_element.xml b/secure_element/aidl/default/secure_element.xml
new file mode 100644
index 0000000..96ab2e7
--- /dev/null
+++ b/secure_element/aidl/default/secure_element.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.secure_element</name>
+ <version>1</version>
+ <fqname>ISecureElement/eSE1</fqname>
+ </hal>
+</manifest>
diff --git a/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
new file mode 100644
index 0000000..a85a8bc
--- /dev/null
+++ b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/secure_element/BnSecureElementCallback.h>
+#include <aidl/android/hardware/secure_element/ISecureElement.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+using namespace std::chrono_literals;
+
+using aidl::android::hardware::secure_element::BnSecureElementCallback;
+using aidl::android::hardware::secure_element::ISecureElement;
+using aidl::android::hardware::secure_element::LogicalChannelResponse;
+using ndk::ScopedAStatus;
+using ndk::SharedRefBase;
+using ndk::SpAIBinder;
+using testing::ElementsAre;
+using testing::ElementsAreArray;
+
+#define EXPECT_OK(status) \
+ do { \
+ auto status_impl = (status); \
+ EXPECT_TRUE(status_impl.isOk()) << status_impl.getDescription(); \
+ } while (false)
+
+static const std::vector<uint8_t> kDataApdu = {0x00, 0x08, 0x00, 0x00, 0x00};
+static const std::vector<uint8_t> kAndroidTestAid = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41,
+ 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64,
+ 0x43, 0x54, 0x53, 0x31};
+
+class MySecureElementCallback : public BnSecureElementCallback {
+ public:
+ ScopedAStatus onStateChange(bool state, const std::string& debugReason) override {
+ {
+ std::unique_lock<std::mutex> l(m);
+ (void)debugReason;
+ history.push_back(state);
+ }
+ cv.notify_one();
+ return ScopedAStatus::ok();
+ };
+
+ void expectCallbackHistory(std::vector<bool>&& want) {
+ std::unique_lock<std::mutex> l(m);
+ cv.wait_for(l, 2s, [&]() { return history.size() >= want.size(); });
+ EXPECT_THAT(history, ElementsAreArray(want));
+ }
+
+ private:
+ std::mutex m; // guards history
+ std::condition_variable cv;
+ std::vector<bool> history;
+};
+
+class SecureElementAidl : public ::testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ SpAIBinder binder = SpAIBinder(AServiceManager_waitForService(GetParam().c_str()));
+ se = ISecureElement::fromBinder(binder);
+ ASSERT_NE(se, nullptr);
+
+ cb = SharedRefBase::make<MySecureElementCallback>();
+ EXPECT_OK(se->init(cb));
+
+ cb->expectCallbackHistory({true});
+ }
+
+ std::shared_ptr<ISecureElement> se;
+ std::shared_ptr<MySecureElementCallback> cb;
+};
+
+TEST_P(SecureElementAidl, isCardPresent) {
+ bool res = false;
+ EXPECT_OK(se->isCardPresent(&res));
+ EXPECT_TRUE(res);
+}
+
+TEST_P(SecureElementAidl, transmit) {
+ LogicalChannelResponse response;
+ EXPECT_OK(se->openLogicalChannel(kAndroidTestAid, 0x00, &response));
+
+ EXPECT_GE(response.selectResponse.size(), 2u);
+ EXPECT_GE(response.channelNumber, 1);
+
+ std::vector<uint8_t> command = kDataApdu;
+ command[0] |= response.channelNumber;
+
+ std::vector<uint8_t> transmitResponse;
+ EXPECT_OK(se->transmit(command, &transmitResponse));
+
+ EXPECT_LE(transmitResponse.size(), 3);
+ EXPECT_GE(transmitResponse.size(), 2);
+ EXPECT_EQ(transmitResponse[transmitResponse.size() - 1], 0x00);
+ EXPECT_EQ(transmitResponse[transmitResponse.size() - 2], 0x90);
+
+ EXPECT_OK(se->closeChannel(response.channelNumber));
+}
+
+TEST_P(SecureElementAidl, openBasicChannel) {
+ std::vector<uint8_t> response;
+ auto status = se->openBasicChannel(kAndroidTestAid, 0x00, &response);
+
+ if (!status.isOk()) {
+ EXPECT_EQ(status.getServiceSpecificError(), ISecureElement::CHANNEL_NOT_AVAILABLE)
+ << status.getDescription();
+ return;
+ }
+
+ EXPECT_GE(response.size(), 2u);
+ EXPECT_OK(se->closeChannel(0));
+}
+
+TEST_P(SecureElementAidl, getAtr) {
+ std::vector<uint8_t> atr;
+ EXPECT_OK(se->getAtr(&atr));
+ if (atr.size() == 0) {
+ return;
+ }
+ EXPECT_LE(atr.size(), 32u);
+ EXPECT_GE(atr.size(), 1u);
+}
+
+TEST_P(SecureElementAidl, openCloseLogicalChannel) {
+ LogicalChannelResponse response;
+ EXPECT_OK(se->openLogicalChannel(kAndroidTestAid, 0x00, &response));
+ EXPECT_GE(response.selectResponse.size(), 2u);
+ EXPECT_GE(response.channelNumber, 1);
+ EXPECT_OK(se->closeChannel(response.channelNumber));
+}
+
+TEST_P(SecureElementAidl, openInvalidAid) {
+ LogicalChannelResponse response;
+ auto status = se->openLogicalChannel({0x42}, 0x00, &response);
+ EXPECT_EQ(status.getServiceSpecificError(), ISecureElement::NO_SUCH_ELEMENT_ERROR)
+ << status.getDescription();
+}
+
+TEST_P(SecureElementAidl, Reset) {
+ cb->expectCallbackHistory({true});
+ EXPECT_OK(se->reset());
+ cb->expectCallbackHistory({true, false, true});
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SecureElementAidl);
+INSTANTIATE_TEST_SUITE_P(
+ SecureElement, SecureElementAidl,
+ testing::ValuesIn(android::getAidlHalInstanceNames(ISecureElement::descriptor)),
+ android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
index 4c2be89..294c205 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
@@ -125,9 +125,9 @@
* straightforward translation of the KeyMint tag/value parameter lists to ASN.1.
*
* KeyDescription ::= SEQUENCE {
- * attestationVersion INTEGER, # Value 200
+ * attestationVersion INTEGER, # Value 300
* attestationSecurityLevel SecurityLevel, # See below
- * keyMintVersion INTEGER, # Value 200
+ * keyMintVersion INTEGER, # Value 300
* keymintSecurityLevel SecurityLevel, # See below
* attestationChallenge OCTET_STRING, # Tag::ATTESTATION_CHALLENGE from attestParams
* uniqueId OCTET_STRING, # Empty unless key has Tag::INCLUDE_UNIQUE_ID
@@ -209,6 +209,7 @@
* vendorPatchLevel [718] EXPLICIT INTEGER OPTIONAL,
* bootPatchLevel [719] EXPLICIT INTEGER OPTIONAL,
* deviceUniqueAttestation [720] EXPLICIT NULL OPTIONAL,
+ * attestationIdSecondImei [723] EXPLICIT OCTET_STRING OPTIONAL,
* }
*/
Certificate[] certificateChain;
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
index f4c0095..ea4ba18 100644
--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -892,6 +892,7 @@
ASSERT_TRUE(result == ErrorCode::CANNOT_ATTEST_IDS || result == ErrorCode::INVALID_TAG)
<< "result = " << result;
+ device_id_attestation_vsr_check(result);
}
CheckedDeleteKey(&attest_key.keyBlob);
}
diff --git a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
index cd140c8..26dc3f5 100644
--- a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
+++ b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -348,8 +348,8 @@
// Add the tag that doesn't match the local device's real ID.
builder.push_back(invalid_tag);
auto result = GenerateKey(builder, &key_blob, &key_characteristics);
-
ASSERT_TRUE(result == ErrorCode::CANNOT_ATTEST_IDS || result == ErrorCode::INVALID_TAG);
+ device_id_attestation_vsr_check(result);
}
}
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 80abd92..43ad30a 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -2031,6 +2031,16 @@
*signingKey = std::move(pubKey);
}
+void device_id_attestation_vsr_check(const ErrorCode& result) {
+ if (get_vsr_api_level() >= 34) {
+ ASSERT_FALSE(result == ErrorCode::INVALID_TAG)
+ << "It is a specification violation for INVALID_TAG to be returned due to ID "
+ << "mismatch in a Device ID Attestation call. INVALID_TAG is only intended to "
+ << "be used for a case where updateAad() is called after update(). As of "
+ << "VSR-14, this is now enforced as an error.";
+ }
+}
+
} // namespace test
} // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 67e8b21..5b09ca5 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -395,6 +395,7 @@
void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
vector<uint8_t>* payload_value);
void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey);
+void device_id_attestation_vsr_check(const ErrorCode& result);
AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index 97fe08a..6d9c8c9 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -181,15 +181,6 @@
return params;
}
- void checkMacedPubkeyVersioned(const MacedPublicKey& macedPubKey, bool testMode,
- vector<uint8_t>* payload_value) {
- if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) {
- check_maced_pubkey(macedPubKey, false, payload_value);
- } else {
- check_maced_pubkey(macedPubKey, testMode, payload_value);
- }
- }
-
protected:
std::shared_ptr<IRemotelyProvisionedComponent> provisionable_;
RpcHardwareInfo rpcHardwareInfo;
@@ -279,7 +270,7 @@
auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
ASSERT_TRUE(status.isOk());
vector<uint8_t> coseKeyData;
- checkMacedPubkeyVersioned(macedPubKey, testMode, &coseKeyData);
+ check_maced_pubkey(macedPubKey, testMode, &coseKeyData);
}
/**
@@ -302,7 +293,7 @@
auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
ASSERT_TRUE(status.isOk());
vector<uint8_t> coseKeyData;
- checkMacedPubkeyVersioned(macedPubKey, testMode, &coseKeyData);
+ check_maced_pubkey(macedPubKey, testMode, &coseKeyData);
AttestationKey attestKey;
attestKey.keyBlob = std::move(privateKeyBlob);
@@ -357,7 +348,7 @@
bool testMode = true;
auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
ASSERT_TRUE(status.isOk());
- checkMacedPubkeyVersioned(macedPubKey, testMode, nullptr);
+ check_maced_pubkey(macedPubKey, testMode, nullptr);
}
class CertificateRequestTestBase : public VtsRemotelyProvisionedComponentTests {
@@ -382,7 +373,7 @@
ASSERT_TRUE(status.isOk()) << status.getMessage();
vector<uint8_t> payload_value;
- checkMacedPubkeyVersioned(key, testMode, &payload_value);
+ check_maced_pubkey(key, testMode, &payload_value);
cborKeysToSign_.add(cppbor::EncodedItem(payload_value));
}
}
@@ -401,8 +392,16 @@
CertificateRequestTestBase::SetUp();
if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) {
- GTEST_SKIP() << "This test case only applies to RKP v1 and v2. "
- << "RKP version discovered: " << rpcHardwareInfo.versionNumber;
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ false, {}, {}, {}, &deviceInfo, &protectedData, &keysToSignMac);
+ if (!status.isOk() && (status.getServiceSpecificError() ==
+ BnRemotelyProvisionedComponent::STATUS_REMOVED)) {
+ GTEST_SKIP() << "This test case applies to RKP v3+ only if "
+ << "generateCertificateRequest() is implemented.";
+ }
}
}
};
@@ -769,30 +768,17 @@
}
/**
- * Generate a non-empty certificate request in prod mode, with test keys. Test mode must be
- * ignored, i.e. test must pass.
+ * Generate a non-empty certificate request in prod mode, with test keys. Must fail with
+ * STATUS_TEST_KEY_IN_PRODUCTION_REQUEST.
*/
TEST_P(CertificateRequestV2Test, NonEmptyRequest_testKeyInProdCert) {
generateKeys(true /* testMode */, 1 /* numKeys */);
bytevec csr;
auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
- ASSERT_TRUE(status.isOk()) << status.getMessage();
-}
-
-/**
- * Call generateCertificateRequest(). Make sure it's removed.
- */
-TEST_P(CertificateRequestV2Test, CertificateRequestV1Removed) {
- generateTestEekChain(2);
- bytevec keysToSignMac;
- DeviceInfo deviceInfo;
- ProtectedData protectedData;
- auto status = provisionable_->generateCertificateRequest(
- true /* testMode */, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo,
- &protectedData, &keysToSignMac);
ASSERT_FALSE(status.isOk()) << status.getMessage();
- EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_REMOVED);
+ ASSERT_EQ(status.getServiceSpecificError(),
+ BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
}
INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestV2Test);
diff --git a/security/rkp/CHANGELOG.md b/security/rkp/CHANGELOG.md
index 29b1a1c..715cf28 100644
--- a/security/rkp/CHANGELOG.md
+++ b/security/rkp/CHANGELOG.md
@@ -27,14 +27,20 @@
`"android.hardward.security.keymint"`).
* ProtectedData has been removed.
* DeviceInfo
- * `version` has moved to a top-level field within the CSR generated by the HAL
+ * `version` has moved to a top-level field within the CSR generated by the HAL.
* IRemotelyProvisionedComponent
* The need for an EEK has been removed. There is no longer an encrypted portion of the CSR.
- * Test mode has been removed.
+ * Keys for new CSR format must be generated with test mode set to false, effectively removing test
+ mode in the new CSR flow. Old behavior is kept unchanged for backwards compatibility.
* The schema for the CSR itself has been significantly simplified, please see
IRemotelyProvisionedComponent.aidl for more details. Notably,
* the chain of signing, MACing, and encryption operations has been replaced with a single
COSE_Sign1 object.
* CertificateType has been added to identify the type of certificate being requested.
+ * The structure has been composed to enable a clear split between what is required to validate a
+ payload and the implementation-defined payload itself. This is done by creating a typed
+ `AuthenticatedRequest<T>` object representing the top level data required to authenticate
+ the data provided in the payload, `T`.
* RpcHardwareInfo
* `supportedNumKeysInCsr` added to report the maximum number of keys supported in a CSR.
+ * `supportedEekCurve` is no longer used, due to the removal of the EEK from the scheme.
diff --git a/security/rkp/aidl/Android.bp b/security/rkp/aidl/Android.bp
index 4c479f4..5285477 100644
--- a/security/rkp/aidl/Android.bp
+++ b/security/rkp/aidl/Android.bp
@@ -21,6 +21,10 @@
backend: {
java: {
min_sdk_version: "33",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.rkpd",
+ ],
},
rust: {
enabled: true,
diff --git a/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index 78969d1..5485db3 100644
--- a/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -132,11 +132,7 @@
* generateKeyPair generates a new ECDSA P-256 key pair that can be attested by the remote
* server.
*
- * @param in boolean testMode this field is now deprecated. It is ignored by the implementation
- * in v3, but retained to simplify backwards compatibility support. V1 and V2
- * implementations must still respect the testMode flag.
- *
- * testMode indicates whether the generated key is for testing only. Test keys
+ * @param in boolean testMode indicates whether the generated key is for testing only. Test keys
* are marked (see the definition of PublicKey in the MacedPublicKey structure) to
* prevent them from being confused with production keys.
*
@@ -150,8 +146,8 @@
byte[] generateEcdsaP256KeyPair(in boolean testMode, out MacedPublicKey macedPublicKey);
/**
- * This method has been removed in version 3 of the HAL. The header is kept around for
- * backwards compatibility purposes. From v3, this method should raise a
+ * This method can be removed in version 3 of the HAL. The header is kept around for
+ * backwards compatibility purposes. From v3, this method is allowed to raise a
* ServiceSpecificException with an error code of STATUS_REMOVED.
*
* For v1 and v2 implementations:
@@ -306,7 +302,9 @@
*
* @param in MacedPublicKey[] keysToSign contains the set of keys to certify. The
* IRemotelyProvisionedComponent must validate the MACs on each key. If any entry in the
- * array lacks a valid MAC, the method must return STATUS_INVALID_MAC.
+ * array lacks a valid MAC, the method must return STATUS_INVALID_MAC. This method must
+ * not accept test keys. If any entry in the array is a test key, the method must return
+ * STATUS_TEST_KEY_IN_PRODUCTION_REQUEST.
*
* @param in challenge contains a byte string from the provisioning server which will be
* included in the signed data of the CSR structure. Different provisioned backends may
@@ -345,20 +343,20 @@
* ]
*
* ; COSE_Sign1 (untagged)
- * SignedData<T> = [
+ * SignedData<Data> = [
* protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
* unprotected: {},
- * payload: bstr .cbor T / nil,
- * signature: bstr ; PureEd25519(CDI_Leaf_Priv, bstr .cbor SignedDataSigStruct<T>) /
- * ; ECDSA(CDI_Leaf_Priv, bstr .cbor SignedDataSigStruct<T>)
+ * payload: bstr .cbor Data / nil,
+ * signature: bstr ; PureEd25519(CDI_Leaf_Priv, bstr .cbor SignedDataSigStruct<Data>) /
+ * ; ECDSA(CDI_Leaf_Priv, bstr .cbor SignedDataSigStruct<Data>)
* ]
*
* ; Sig_structure for SignedData
- * SignedDataSigStruct<T> = [
+ * SignedDataSigStruct<Data> = [
* context: "Signature1",
* protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 },
* external_aad: bstr .size 0,
- * payload: bstr .cbor T
+ * payload: bstr .cbor Data / nil,
* ]
*
* ; UdsCerts allows the platform to provide additional certifications for the UDS_Pub. For
diff --git a/security/rkp/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl b/security/rkp/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl
index 5fe5b00..d0b059d 100644
--- a/security/rkp/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl
+++ b/security/rkp/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl
@@ -29,9 +29,9 @@
const int CURVE_25519 = 2;
/**
- * Implementation version of the remotely provisioned component hardware. The version number is
- * implementation defined, and not necessarily globally meaningful. The version is used to
- * distinguish between different versions of a given implementation.
+ * Implementation version of the remotely provisioned component hardware. The version provided
+ * here must match the version reported in the CsrPayload produced by the HAL interface. This
+ * field primarily acts as a convenience for the system components interacting with the HALs.
*/
int versionNumber;
@@ -43,6 +43,9 @@
@utf8InCpp String rpcAuthorName;
/**
+ * NOTE: This field is no longer used as of version 3 of the HAL interface. This is because the
+ * Endpoint Encryption Key is no longer used in the provisioning scheme.
+ *
* supportedEekCurve returns an int representing which curve is supported for validating
* signatures over the Endpoint Encryption Key certificate chain and for using the corresponding
* signed encryption key in ECDH. Only one curve should be supported, with preference for 25519
diff --git a/sensors/aidl/convert/Android.bp b/sensors/aidl/convert/Android.bp
index 8e2146d..d47de8e 100644
--- a/sensors/aidl/convert/Android.bp
+++ b/sensors/aidl/convert/Android.bp
@@ -26,6 +26,7 @@
cc_library_static {
name: "android.hardware.sensors-V1-convert",
vendor_available: true,
+ host_supported: true,
srcs: ["convert.cpp"],
export_include_dirs: ["include"],
shared_libs: [
diff --git a/sensors/aidl/default/Sensor.cpp b/sensors/aidl/default/Sensor.cpp
index 62193d6..3bdd8b6 100644
--- a/sensors/aidl/default/Sensor.cpp
+++ b/sensors/aidl/default/Sensor.cpp
@@ -223,7 +223,7 @@
EventPayload::Vec3 vec3 = {
.x = 0,
.y = 0,
- .z = -9.8,
+ .z = 9.8,
.status = SensorStatus::ACCURACY_HIGH,
};
payload.set<EventPayload::Tag::vec3>(vec3);
diff --git a/sensors/common/default/2.X/Sensor.cpp b/sensors/common/default/2.X/Sensor.cpp
index fd701fd..2c1cdfb 100644
--- a/sensors/common/default/2.X/Sensor.cpp
+++ b/sensors/common/default/2.X/Sensor.cpp
@@ -218,7 +218,7 @@
void AccelSensor::readEventPayload(EventPayload& payload) {
payload.vec3.x = 0;
payload.vec3.y = 0;
- payload.vec3.z = -9.8;
+ payload.vec3.z = 9.8;
payload.vec3.status = SensorStatus::ACCURACY_HIGH;
}
diff --git a/sensors/common/default/2.X/multihal/HalProxy.cpp b/sensors/common/default/2.X/multihal/HalProxy.cpp
index f44f5c4..31a17db 100644
--- a/sensors/common/default/2.X/multihal/HalProxy.cpp
+++ b/sensors/common/default/2.X/multihal/HalProxy.cpp
@@ -82,8 +82,11 @@
}
HalProxy::HalProxy() {
- const char* kMultiHalConfigFile = "/vendor/etc/sensors/hals.conf";
- initializeSubHalListFromConfigFile(kMultiHalConfigFile);
+ static const std::string kMultiHalConfigFiles[] = {"/vendor/etc/sensors/hals.conf",
+ "/odm/etc/sensors/hals.conf"};
+ for (const std::string& configFile : kMultiHalConfigFiles) {
+ initializeSubHalListFromConfigFile(configFile.c_str());
+ }
init();
}
diff --git a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp
index f5745c5..a0bb67a 100644
--- a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp
+++ b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp
@@ -237,7 +237,7 @@
event.timestamp = ::android::elapsedRealtimeNano();
event.u.vec3.x = 0;
event.u.vec3.y = 0;
- event.u.vec3.z = -9.815;
+ event.u.vec3.z = 9.815;
event.u.vec3.status = SensorStatus::ACCURACY_HIGH;
events.push_back(event);
return events;
diff --git a/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortInfo.aidl b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortInfo.aidl
index a5e3a2a..25c3be1 100644
--- a/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortInfo.aidl
+++ b/tv/hdmi/aidl/aidl_api/android.hardware.tv.hdmi/current/android/hardware/tv/hdmi/HdmiPortInfo.aidl
@@ -38,5 +38,6 @@
int portId;
boolean cecSupported;
boolean arcSupported;
+ boolean eArcSupported;
int physicalAddress;
}
diff --git a/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortInfo.aidl b/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortInfo.aidl
index 1d6f27d..2e2c858 100644
--- a/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortInfo.aidl
+++ b/tv/hdmi/aidl/android/hardware/tv/hdmi/HdmiPortInfo.aidl
@@ -27,6 +27,7 @@
int portId; // Should start from 1 which corresponds to HDMI "port 1".
boolean cecSupported;
boolean arcSupported;
+ boolean eArcSupported;
// The physical address of the device connected to this port, valid range is 0x0000 to 0xFFFF
// (ref Sec 8.7.2 of HDMI 1.4b).
int physicalAddress;
diff --git a/tv/hdmi/aidl/default/HdmiMock.cpp b/tv/hdmi/aidl/default/HdmiMock.cpp
index 0cf5118..bbc4705 100644
--- a/tv/hdmi/aidl/default/HdmiMock.cpp
+++ b/tv/hdmi/aidl/default/HdmiMock.cpp
@@ -166,6 +166,7 @@
.portId = static_cast<uint32_t>(1),
.cecSupported = true,
.arcSupported = false,
+ .eArcSupported = false,
.physicalAddress = mPhysicalAddress};
mPortConnectionStatus[0] = false;
mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(serviceDied));
diff --git a/tv/tuner/OWNERS b/tv/tuner/OWNERS
new file mode 100644
index 0000000..83e090d
--- /dev/null
+++ b/tv/tuner/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 136752
+
+quxiangfang@google.com
+shubang@google.com
+hgchen@google.com
+raychin@google.com
diff --git a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/DemuxFilterScIndexMask.aidl b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/DemuxFilterScIndexMask.aidl
index 371e075..8dfc60e 100644
--- a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/DemuxFilterScIndexMask.aidl
+++ b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/DemuxFilterScIndexMask.aidl
@@ -38,4 +38,5 @@
int scIndex;
int scAvc;
int scHevc;
+ int scVvc;
}
diff --git a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/DemuxRecordScIndexType.aidl b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/DemuxRecordScIndexType.aidl
index 30ec29a..fb4430b 100644
--- a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/DemuxRecordScIndexType.aidl
+++ b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/DemuxRecordScIndexType.aidl
@@ -39,4 +39,5 @@
SC = 1,
SC_HEVC = 2,
SC_AVC = 3,
+ SC_VVC = 4,
}
diff --git a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/DemuxScVvcIndex.aidl b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/DemuxScVvcIndex.aidl
new file mode 100644
index 0000000..3e08d26
--- /dev/null
+++ b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/DemuxScVvcIndex.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.tv.tuner;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum DemuxScVvcIndex {
+ UNDEFINED = 0,
+ SLICE_IDR_W_RADL = 1,
+ SLICE_IDR_N_LP = 2,
+ SLICE_CRA = 4,
+ SLICE_GDR = 8,
+ VPS = 16,
+ SPS = 32,
+ AUD = 64,
+}
diff --git a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/ITuner.aidl b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/ITuner.aidl
index 0ff2da9..5d1d215 100644
--- a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/ITuner.aidl
+++ b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/ITuner.aidl
@@ -47,4 +47,5 @@
void setLna(in boolean bEnable);
void setMaxNumberOfFrontends(in android.hardware.tv.tuner.FrontendType frontendType, in int maxNumber);
int getMaxNumberOfFrontends(in android.hardware.tv.tuner.FrontendType frontendType);
+ boolean isLnaSupported();
}
diff --git a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/VideoStreamType.aidl b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/VideoStreamType.aidl
index 9dfd686..dbb6033 100644
--- a/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/VideoStreamType.aidl
+++ b/tv/tuner/aidl/aidl_api/android.hardware.tv.tuner/current/android/hardware/tv/tuner/VideoStreamType.aidl
@@ -48,4 +48,5 @@
AV1 = 10,
AVS = 11,
AVS2 = 12,
+ VVC = 13,
}
diff --git a/tv/tuner/aidl/android/hardware/tv/tuner/DemuxFilterScIndexMask.aidl b/tv/tuner/aidl/android/hardware/tv/tuner/DemuxFilterScIndexMask.aidl
index 4036b06..d53bc5b 100644
--- a/tv/tuner/aidl/android/hardware/tv/tuner/DemuxFilterScIndexMask.aidl
+++ b/tv/tuner/aidl/android/hardware/tv/tuner/DemuxFilterScIndexMask.aidl
@@ -39,4 +39,9 @@
* Indexes defined by DemuxScHevcIndex.
*/
int scHevc;
+
+ /**
+ * Indexes defined by DemuxScVvcIndex.
+ */
+ int scVvc;
}
diff --git a/tv/tuner/aidl/android/hardware/tv/tuner/DemuxRecordScIndexType.aidl b/tv/tuner/aidl/android/hardware/tv/tuner/DemuxRecordScIndexType.aidl
index 98427f7..265b978 100644
--- a/tv/tuner/aidl/android/hardware/tv/tuner/DemuxRecordScIndexType.aidl
+++ b/tv/tuner/aidl/android/hardware/tv/tuner/DemuxRecordScIndexType.aidl
@@ -42,4 +42,9 @@
* Use Start Code index for AVC
*/
SC_AVC,
+
+ /**
+ * Use Start Code index for VVC
+ */
+ SC_VVC,
}
diff --git a/tv/tuner/aidl/android/hardware/tv/tuner/DemuxScVvcIndex.aidl b/tv/tuner/aidl/android/hardware/tv/tuner/DemuxScVvcIndex.aidl
new file mode 100644
index 0000000..8b47f62
--- /dev/null
+++ b/tv/tuner/aidl/android/hardware/tv/tuner/DemuxScVvcIndex.aidl
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tv.tuner;
+
+/**
+ * Indexes can be tagged by start point of slice groups according to ISO/IEC 23090-3.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum DemuxScVvcIndex {
+ UNDEFINED = 0,
+
+ /**
+ * Coded slice of an IDR picture or subpicture with RADL pictures.
+ */
+ SLICE_IDR_W_RADL = 1 << 0,
+
+ /**
+ * Coded slice of an IDR picture or subpicture without leading pictures.
+ */
+ SLICE_IDR_N_LP = 1 << 1,
+
+ /**
+ * Coded slice of a CRA (clean random access) picture or subpicture.
+ */
+ SLICE_CRA = 1 << 2,
+
+ /**
+ * Coded slice of a GDR (gradual decoder refresh) picture or subpicture.
+ */
+ SLICE_GDR = 1 << 3,
+
+ /**
+ * Video parameter set (non-VCL NALU).
+ */
+ VPS = 1 << 4,
+
+ /**
+ * Sequence parameter set (non-VCL NALU).
+ */
+ SPS = 1 << 5,
+
+ /**
+ * Access unit delimiter (non-VCL NALU).
+ */
+ AUD = 1 << 6,
+}
diff --git a/tv/tuner/aidl/android/hardware/tv/tuner/ITuner.aidl b/tv/tuner/aidl/android/hardware/tv/tuner/ITuner.aidl
index 03def33..4a0b7a2 100644
--- a/tv/tuner/aidl/android/hardware/tv/tuner/ITuner.aidl
+++ b/tv/tuner/aidl/android/hardware/tv/tuner/ITuner.aidl
@@ -125,6 +125,9 @@
/**
* Enable or Disable Low Noise Amplifier (LNA).
*
+ * If the device doesn't support LNA, the HAL implement should set {@link Result#UNAVAILABLE}
+ * to EX_SERVICE_SPECIFIC as the service specific error.
+ *
* @param bEnable true if activate LNA module; false if deactivate LNA
*/
void setLna(in boolean bEnable);
@@ -148,4 +151,11 @@
* @return the maximum usable number of the queried frontend type.
*/
int getMaxNumberOfFrontends(in FrontendType frontendType);
+
+ /**
+ * Is Low Noise Amplifier (LNA) supported.
+ *
+ * @return true if supported, otherwise false
+ */
+ boolean isLnaSupported();
}
diff --git a/tv/tuner/aidl/android/hardware/tv/tuner/VideoStreamType.aidl b/tv/tuner/aidl/android/hardware/tv/tuner/VideoStreamType.aidl
index 108d986..bd000d2 100644
--- a/tv/tuner/aidl/android/hardware/tv/tuner/VideoStreamType.aidl
+++ b/tv/tuner/aidl/android/hardware/tv/tuner/VideoStreamType.aidl
@@ -84,4 +84,9 @@
* New Chinese Standard
*/
AVS2,
+
+ /*
+ * ITU-T Rec. H.266 and ISO/IEC 23090-3
+ */
+ VVC,
}
diff --git a/tv/tuner/aidl/default/Tuner.cpp b/tv/tuner/aidl/default/Tuner.cpp
index 591f936..8c715a0 100644
--- a/tv/tuner/aidl/default/Tuner.cpp
+++ b/tv/tuner/aidl/default/Tuner.cpp
@@ -204,6 +204,13 @@
return ::ndk::ScopedAStatus::ok();
}
+::ndk::ScopedAStatus Tuner::isLnaSupported(bool* _aidl_return) {
+ ALOGV("%s", __FUNCTION__);
+
+ *_aidl_return = true;
+ return ::ndk::ScopedAStatus::ok();
+}
+
binder_status_t Tuner::dump(int fd, const char** args, uint32_t numArgs) {
ALOGV("%s", __FUNCTION__);
{
diff --git a/tv/tuner/aidl/default/Tuner.h b/tv/tuner/aidl/default/Tuner.h
index ad73003..a77c930 100644
--- a/tv/tuner/aidl/default/Tuner.h
+++ b/tv/tuner/aidl/default/Tuner.h
@@ -61,6 +61,7 @@
int32_t in_maxNumber) override;
::ndk::ScopedAStatus getMaxNumberOfFrontends(FrontendType in_frontendType,
int32_t* _aidl_return) override;
+ ::ndk::ScopedAStatus isLnaSupported(bool* _aidl_return) override;
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ComplianceWarning.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ComplianceWarning.aidl
new file mode 100644
index 0000000..8b67070
--- /dev/null
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ComplianceWarning.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.usb;
+@Backing(type="int") @VintfStability
+enum ComplianceWarning {
+ OTHER = 1,
+ DEBUG_ACCESSORY = 2,
+ BC_1_2 = 3,
+ MISSING_RP = 4,
+}
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsb.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsb.aidl
index f866c1e..859f526 100644
--- a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsb.aidl
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsb.aidl
@@ -41,5 +41,5 @@
oneway void setCallback(in android.hardware.usb.IUsbCallback callback);
oneway void switchRole(in String portName, in android.hardware.usb.PortRole role, long transactionId);
oneway void limitPowerTransfer(in String portName, boolean limit, long transactionId);
- oneway void resetUsbPort(in String portName,long transactionId);
+ oneway void resetUsbPort(in String portName, long transactionId);
}
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortStatus.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortStatus.aidl
index dfd99fb..b0b7552 100644
--- a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortStatus.aidl
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortStatus.aidl
@@ -50,4 +50,6 @@
android.hardware.usb.UsbDataStatus[] usbDataStatus;
boolean powerTransferLimited;
android.hardware.usb.PowerBrickStatus powerBrickStatus;
+ boolean supportsComplianceWarnings = false;
+ android.hardware.usb.ComplianceWarning[] complianceWarnings = {};
}
diff --git a/usb/aidl/android/hardware/usb/ComplianceWarning.aidl b/usb/aidl/android/hardware/usb/ComplianceWarning.aidl
new file mode 100644
index 0000000..4c18a31
--- /dev/null
+++ b/usb/aidl/android/hardware/usb/ComplianceWarning.aidl
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+@VintfStability
+@Backing(type="int")
+/**
+ * Indicates the potential non-compliance reasons for the
+ * connected USB Type-C port partner which could be a power
+ * source, accessory or cable. Applicable for USB-C receptacles
+ * in Android devices.
+ */
+enum ComplianceWarning {
+ /**
+ * Used to indicate Type-C sources/cables/accessories/ports
+ * whose issue is not listed below but do not meet
+ * specification requirements from including but not limited to
+ * USB Type-C Cable and Connector Specification, Universal Serial Bus
+ * Power Delivery Specification, and Universal Serial Bus
+ * 1.x/2.0/3.x/4.0.
+ */
+ OTHER = 1,
+ /**
+ * Used to indicate Type-C port partner
+ * (cable/accessory/source) that identifies itself as debug
+ * accessory source as defined in USB Type-C Cable and
+ * Connector Specification. However, the specification
+ * states that this is meant for debug only and shall not
+ * be used for with commercial products.
+ */
+ DEBUG_ACCESSORY = 2,
+ /**
+ * Used to indicate Type-C port partner that does not
+ * identify itself as one of the charging port types
+ * (SDP/CDP/DCP etc) as defined by Battery Charging v1.2
+ * Specification.
+ */
+ BC_1_2 = 3,
+ /**
+ * Used to indicate Type-C sources/cables that are missing
+ * pull up resistors on the CC pins as required by USB
+ * Type-C Cable and Connector Specification.
+ */
+ MISSING_RP = 4,
+}
diff --git a/usb/aidl/android/hardware/usb/PortStatus.aidl b/usb/aidl/android/hardware/usb/PortStatus.aidl
index 51bee71..d7e61ec 100644
--- a/usb/aidl/android/hardware/usb/PortStatus.aidl
+++ b/usb/aidl/android/hardware/usb/PortStatus.aidl
@@ -19,6 +19,7 @@
import android.hardware.usb.ContaminantDetectionStatus;
import android.hardware.usb.ContaminantProtectionMode;
import android.hardware.usb.ContaminantProtectionStatus;
+import android.hardware.usb.ComplianceWarning;
import android.hardware.usb.PortDataRole;
import android.hardware.usb.PortMode;
import android.hardware.usb.PortPowerRole;
@@ -115,4 +116,15 @@
* Denotes whether Power brick is connected.
*/
PowerBrickStatus powerBrickStatus;
+ /**
+ * True if the hal implementation can support identifying
+ * non compliant USB power source/cable/accessory. False other
+ * otherwise.
+ */
+ boolean supportsComplianceWarnings = false;
+ /**
+ * List of reasons as to why the attached USB
+ * power source/cable/accessory is non compliant.
+ */
+ ComplianceWarning[] complianceWarnings = {};
}
diff --git a/usb/aidl/default/Android.bp b/usb/aidl/default/Android.bp
index ad331f9..472e732 100644
--- a/usb/aidl/default/Android.bp
+++ b/usb/aidl/default/Android.bp
@@ -34,7 +34,7 @@
"Usb.cpp",
],
shared_libs: [
- "android.hardware.usb-V1-ndk",
+ "android.hardware.usb-V2-ndk",
"libbase",
"libbinder_ndk",
"libcutils",
diff --git a/usb/aidl/default/Usb.cpp b/usb/aidl/default/Usb.cpp
index 7e738c4..dd12da0 100644
--- a/usb/aidl/default/Usb.cpp
+++ b/usb/aidl/default/Usb.cpp
@@ -123,6 +123,15 @@
return Status::SUCCESS;
}
+Status queryNonCompliantChargerStatus(std::vector<PortStatus> *currentPortStatus) {
+ string reasons, path;
+
+ for (int i = 0; i < currentPortStatus->size(); i++) {
+ (*currentPortStatus)[i].supportsComplianceWarnings = false;
+ }
+ return Status::SUCCESS;
+}
+
string appendRoleNodeHelper(const string &portName, PortRole::Tag tag) {
string node(kTypecPath + portName);
@@ -527,6 +536,7 @@
pthread_mutex_lock(&usb->mLock);
status = getPortStatusHelper(currentPortStatus);
queryMoistureDetectionStatus(currentPortStatus);
+ queryNonCompliantChargerStatus(currentPortStatus);
if (usb->mCallback != NULL) {
ScopedAStatus ret = usb->mCallback->notifyPortStatusChange(*currentPortStatus,
status);
diff --git a/usb/aidl/default/android.hardware.usb-service.example.xml b/usb/aidl/default/android.hardware.usb-service.example.xml
index 6088194..c3f07f5 100644
--- a/usb/aidl/default/android.hardware.usb-service.example.xml
+++ b/usb/aidl/default/android.hardware.usb-service.example.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.usb</name>
- <version>1</version>
+ <version>2</version>
<interface>
<name>IUsb</name>
<instance>default</instance>
diff --git a/usb/aidl/vts/Android.bp b/usb/aidl/vts/Android.bp
index 00a7c93..d0e0eec 100644
--- a/usb/aidl/vts/Android.bp
+++ b/usb/aidl/vts/Android.bp
@@ -34,7 +34,7 @@
"libbinder_ndk",
],
static_libs: [
- "android.hardware.usb-V1-ndk",
+ "android.hardware.usb-V2-ndk",
],
test_suites: [
"general-tests",
diff --git a/usb/aidl/vts/VtsAidlUsbTargetTest.cpp b/usb/aidl/vts/VtsAidlUsbTargetTest.cpp
index ea2f985..c4ec8a4 100644
--- a/usb/aidl/vts/VtsAidlUsbTargetTest.cpp
+++ b/usb/aidl/vts/VtsAidlUsbTargetTest.cpp
@@ -43,6 +43,7 @@
#define TIMEOUT_PERIOD 10
using ::aidl::android::hardware::usb::BnUsbCallback;
+using ::aidl::android::hardware::usb::ComplianceWarning;
using ::aidl::android::hardware::usb::IUsb;
using ::aidl::android::hardware::usb::IUsbCallback;
using ::aidl::android::hardware::usb::PortDataRole;
@@ -560,6 +561,53 @@
ALOGI("UsbAidlTest resetUsbPort end");
}
+/*
+ * Test charger compliance warning
+ * The test asserts that complianceWarnings is
+ * empty when the feature is not supported. i.e.
+ * supportsComplianceWarning is false.
+ */
+TEST_P(UsbAidlTest, nonCompliantChargerStatus) {
+ ALOGI("UsbAidlTest nonCompliantChargerStatus start");
+ int64_t transactionId = rand() % 10000;
+ const auto& ret = usb->queryPortStatus(transactionId);
+ ASSERT_TRUE(ret.isOk());
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(2, usb_last_cookie);
+ EXPECT_EQ(transactionId, last_transactionId);
+
+ if (!usb_last_port_status.supportsComplianceWarnings) {
+ EXPECT_TRUE(usb_last_port_status.complianceWarnings.empty());
+ }
+
+ ALOGI("UsbAidlTest nonCompliantChargerStatus end");
+}
+
+/*
+ * Test charger compliance warning values
+ * The test asserts that complianceWarning values
+ * are valid.
+ */
+TEST_P(UsbAidlTest, nonCompliantChargerValues) {
+ ALOGI("UsbAidlTest nonCompliantChargerValues start");
+ int64_t transactionId = rand() % 10000;
+ const auto& ret = usb->queryPortStatus(transactionId);
+ ASSERT_TRUE(ret.isOk());
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(2, usb_last_cookie);
+ EXPECT_EQ(transactionId, last_transactionId);
+
+ // Current compliance values range from [1, 4]
+ if (usb_last_port_status.supportsComplianceWarnings) {
+ for (auto warning : usb_last_port_status.complianceWarnings) {
+ EXPECT_TRUE((int)warning >= (int)ComplianceWarning::OTHER);
+ EXPECT_TRUE((int)warning <= (int)ComplianceWarning::MISSING_RP);
+ }
+ }
+
+ ALOGI("UsbAidlTest nonCompliantChargerValues end");
+}
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UsbAidlTest);
INSTANTIATE_TEST_SUITE_P(
PerInstance, UsbAidlTest,
diff --git a/usb/gadget/aidl/Android.bp b/usb/gadget/aidl/Android.bp
new file mode 100644
index 0000000..cb8560a
--- /dev/null
+++ b/usb/gadget/aidl/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+ name: "android.hardware.usb.gadget",
+ vendor_available: true,
+ srcs: ["android/hardware/usb/gadget/*.aidl"],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ sdk_version: "module_current",
+ },
+ },
+}
diff --git a/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/GadgetFunction.aidl b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/GadgetFunction.aidl
new file mode 100644
index 0000000..c3f26d5
--- /dev/null
+++ b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/GadgetFunction.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.usb.gadget;
+@VintfStability
+parcelable GadgetFunction {
+ const long NONE = 0;
+ const long ADB = 1;
+ const long ACCESSORY = 2;
+ const long MTP = 4;
+ const long MIDI = 8;
+ const long PTP = 16;
+ const long RNDIS = 32;
+ const long AUDIO_SOURCE = 64;
+ const long NCM = 1024;
+}
diff --git a/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/IUsbGadget.aidl b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/IUsbGadget.aidl
new file mode 100644
index 0000000..ef45f8b
--- /dev/null
+++ b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/IUsbGadget.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.usb.gadget;
+@VintfStability
+interface IUsbGadget {
+ oneway void setCurrentUsbFunctions(in long functions, in android.hardware.usb.gadget.IUsbGadgetCallback callback, in long timeoutMs, long transactionId);
+ oneway void getCurrentUsbFunctions(in android.hardware.usb.gadget.IUsbGadgetCallback callback, long transactionId);
+ oneway void getUsbSpeed(in android.hardware.usb.gadget.IUsbGadgetCallback callback, long transactionId);
+ oneway void reset();
+}
diff --git a/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/IUsbGadgetCallback.aidl b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/IUsbGadgetCallback.aidl
new file mode 100644
index 0000000..8672a0c
--- /dev/null
+++ b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/IUsbGadgetCallback.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.usb.gadget;
+@VintfStability
+interface IUsbGadgetCallback {
+ oneway void getCurrentUsbFunctionsCb(in long functions, in android.hardware.usb.gadget.Status status, long transactionId);
+ oneway void getUsbSpeedCb(in android.hardware.usb.gadget.UsbSpeed speed, long transactionId);
+ oneway void setCurrentUsbFunctionsCb(in long functions, in android.hardware.usb.gadget.Status status, long transactionId);
+}
diff --git a/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/Status.aidl b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/Status.aidl
new file mode 100644
index 0000000..bdcf685
--- /dev/null
+++ b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/Status.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.usb.gadget;
+@Backing(type="int") @VintfStability
+enum Status {
+ SUCCESS = 0,
+ ERROR = 1,
+ FUNCTIONS_APPLIED = 2,
+ FUNCTIONS_NOT_APPLIED = 3,
+ CONFIGURATION_NOT_SUPPORTED = 4,
+}
diff --git a/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/UsbSpeed.aidl b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/UsbSpeed.aidl
new file mode 100644
index 0000000..0f54ee5
--- /dev/null
+++ b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/UsbSpeed.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.usb.gadget;
+@Backing(type="int") @VintfStability
+enum UsbSpeed {
+ UNKNOWN = 0,
+ LOWSPEED = 1,
+ FULLSPEED = 2,
+ HIGHSPEED = 3,
+ SUPERSPEED = 4,
+ SUPERSPEED_10Gb = 5,
+ SUPERSPEED_20Gb = 6,
+}
diff --git a/usb/gadget/aidl/android/hardware/usb/gadget/GadgetFunction.aidl b/usb/gadget/aidl/android/hardware/usb/gadget/GadgetFunction.aidl
new file mode 100644
index 0000000..18b31b8
--- /dev/null
+++ b/usb/gadget/aidl/android/hardware/usb/gadget/GadgetFunction.aidl
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb.gadget;
+
+@VintfStability
+parcelable GadgetFunction {
+ /**
+ * Removes all the functions and pulls down the gadget.
+ */
+ const long NONE = 0;
+ /**
+ * Android Debug Bridge function.
+ */
+ const long ADB = 1;
+ /**
+ * Android open accessory protocol function.
+ */
+ const long ACCESSORY = 2;
+ /**
+ * Media Transfer protocol function.
+ */
+ const long MTP = 4;
+ /**
+ * Peripheral mode USB Midi function.
+ */
+ const long MIDI = 8;
+ /**
+ * Picture transfer protocol function.
+ */
+ const long PTP = 16;
+ /**
+ * Tethering function.
+ */
+ const long RNDIS = 32;
+ /**
+ * AOAv2.0 - Audio Source function.
+ */
+ const long AUDIO_SOURCE = 64;
+ /**
+ * NCM - NCM function.
+ */
+ const long NCM = 1024;
+}
diff --git a/usb/gadget/aidl/android/hardware/usb/gadget/IUsbGadget.aidl b/usb/gadget/aidl/android/hardware/usb/gadget/IUsbGadget.aidl
new file mode 100644
index 0000000..d187993
--- /dev/null
+++ b/usb/gadget/aidl/android/hardware/usb/gadget/IUsbGadget.aidl
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb.gadget;
+
+import android.hardware.usb.gadget.GadgetFunction;
+import android.hardware.usb.gadget.IUsbGadgetCallback;
+
+@VintfStability
+oneway interface IUsbGadget {
+ /**
+ * This function is used to set the current USB gadget configuration.
+ * Usb gadget needs to teared down if an USB configuration is already
+ * active.
+ *
+ * @param functions The GadgetFunction bitmap. See GadgetFunction for
+ * the value of each bit.
+ * @param callback IUsbGadgetCallback::setCurrentUsbFunctionsCb used to
+ * propagate back the status.
+ * @param timeoutMs The maximum time (in milliseconds) within which the
+ * IUsbGadgetCallback needs to be returned.
+ * @param transactionId ID to be used when invoking the callback.
+ *
+ */
+ void setCurrentUsbFunctions(in long functions, in IUsbGadgetCallback callback,
+ in long timeoutMs, long transactionId);
+
+ /**
+ * This function is used to query the USB functions included in the
+ * current USB configuration.
+ *
+ * @param callback IUsbGadgetCallback::getCurrentUsbFunctionsCb used to
+ * propagate the current functions list.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void getCurrentUsbFunctions(in IUsbGadgetCallback callback, long transactionId);
+
+ /**
+ * The function is used to query current USB speed.
+ *
+ * @param callback IUsbGadgetCallback::getUsbSpeedCb used to propagate
+ * current USB speed.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void getUsbSpeed(in IUsbGadgetCallback callback, long transactionId);
+
+ /**
+ * This function is used to reset USB gadget driver.
+ * Performs USB data connection reset. The connection will disconnect and
+ * reconnect.
+ */
+ void reset();
+}
diff --git a/usb/gadget/aidl/android/hardware/usb/gadget/IUsbGadgetCallback.aidl b/usb/gadget/aidl/android/hardware/usb/gadget/IUsbGadgetCallback.aidl
new file mode 100644
index 0000000..75ff02b
--- /dev/null
+++ b/usb/gadget/aidl/android/hardware/usb/gadget/IUsbGadgetCallback.aidl
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb.gadget;
+
+import android.hardware.usb.gadget.GadgetFunction;
+import android.hardware.usb.gadget.Status;
+import android.hardware.usb.gadget.UsbSpeed;
+
+@VintfStability
+oneway interface IUsbGadgetCallback {
+ /**
+ * Callback function used to propagate the current USB gadget
+ * configuration.
+ * @param functions The GadgetFunction bitmap. See GadgetFunction for
+ * the value of each bit.
+ * @param status FUNCTIONS_APPLIED when list of functions have been
+ * applied.
+ * FUNCTIONS_NOT_APPLIED when the functions have not
+ * been applied.
+ * ERROR otherwise.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void getCurrentUsbFunctionsCb(in long functions, in Status status, long transactionId);
+
+ /**
+ * Used to convey the current USB speed to the caller.
+ * Must be called either when USB state changes due to USB enumeration or
+ * when caller requested for USB speed through getUsbSpeed.
+ *
+ * @param speed USB Speed defined by UsbSpeed showed current USB
+ * connection speed.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void getUsbSpeedCb(in UsbSpeed speed, long transactionId);
+
+ /**
+ * Callback function used to propagate the status of configuration
+ * switch to the caller.
+ *
+ * @param functions list of functions defined by GadgetFunction
+ * included in the current USB gadget composition.
+ * @param status SUCCESS when the functions are applied.
+ * FUNCTIONS_NOT_SUPPORTED when the configuration is
+ * not supported.
+ * ERROR otherwise.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void setCurrentUsbFunctionsCb(in long functions, in Status status, long transactionId);
+}
diff --git a/usb/gadget/aidl/android/hardware/usb/gadget/Status.aidl b/usb/gadget/aidl/android/hardware/usb/gadget/Status.aidl
new file mode 100644
index 0000000..8d8c3e3
--- /dev/null
+++ b/usb/gadget/aidl/android/hardware/usb/gadget/Status.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb.gadget;
+
+@VintfStability
+@Backing(type="int")
+enum Status {
+ SUCCESS = 0,
+ /**
+ * Error value when the HAL operation fails for reasons not listed here.
+ */
+ ERROR = 1,
+ /**
+ * USB configuration applied successfully.
+ */
+ FUNCTIONS_APPLIED = 2,
+ /**
+ * USB confgiuration failed to apply.
+ */
+ FUNCTIONS_NOT_APPLIED = 3,
+ /**
+ * USB configuration not supported.
+ */
+ CONFIGURATION_NOT_SUPPORTED = 4,
+}
diff --git a/usb/gadget/aidl/android/hardware/usb/gadget/UsbSpeed.aidl b/usb/gadget/aidl/android/hardware/usb/gadget/UsbSpeed.aidl
new file mode 100644
index 0000000..0492757
--- /dev/null
+++ b/usb/gadget/aidl/android/hardware/usb/gadget/UsbSpeed.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb.gadget;
+
+@VintfStability
+@Backing(type="int")
+enum UsbSpeed {
+ /**
+ * UNKNOWN - Not Connected or Unsupported Speed
+ */
+ UNKNOWN = 0,
+ /**
+ * USB Low Speed
+ */
+ LOWSPEED = 1,
+ /**
+ * USB Full Speed
+ */
+ FULLSPEED = 2,
+ /**
+ * USB High Speed
+ */
+ HIGHSPEED = 3,
+ /**
+ * USB Super Speed
+ */
+ SUPERSPEED = 4,
+ /**
+ * USB Super Speed 10Gbps
+ */
+ SUPERSPEED_10Gb = 5,
+ /**
+ * USB Super Speed 20Gbps
+ */
+ SUPERSPEED_20Gb = 6,
+}
diff --git a/usb/gadget/aidl/default/Android.bp b/usb/gadget/aidl/default/Android.bp
new file mode 100644
index 0000000..2ea0e12
--- /dev/null
+++ b/usb/gadget/aidl/default/Android.bp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: [
+ "hardware_interfaces_license",
+ ],
+}
+cc_binary {
+ name: "android.hardware.usb.gadget-service.example",
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.usb.gadget-service.example.rc"],
+ vintf_fragments: [
+ "android.hardware.usb.gadget-service.example.xml",
+ ],
+ vendor: true,
+ srcs: ["service_gadget.cpp", "UsbGadget.cpp"],
+ cflags: ["-Wall", "-Werror"],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "libhardware",
+ "android.hardware.usb.gadget-V1-ndk",
+ "android.frameworks.stats-V1-ndk",
+ "libcutils",
+ "libbinder_ndk",
+ ],
+}
diff --git a/usb/gadget/aidl/default/UsbGadget.cpp b/usb/gadget/aidl/default/UsbGadget.cpp
new file mode 100644
index 0000000..c4986e8
--- /dev/null
+++ b/usb/gadget/aidl/default/UsbGadget.cpp
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.usb.gadget.aidl-service"
+
+#include "UsbGadget.h"
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <aidl/android/frameworks/stats/IStats.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+
+string enabledPath;
+constexpr char kHsi2cPath[] = "/sys/devices/platform/10d50000.hsi2c";
+constexpr char kI2CPath[] = "/sys/devices/platform/10d50000.hsi2c/i2c-";
+constexpr char kAccessoryLimitCurrent[] = "i2c-max77759tcpc/usb_limit_accessory_current";
+constexpr char kAccessoryLimitCurrentEnable[] = "i2c-max77759tcpc/usb_limit_accessory_enable";
+
+UsbGadget::UsbGadget() : mGadgetIrqPath("") {
+}
+
+Status UsbGadget::getUsbGadgetIrqPath() {
+ std::string irqs;
+ size_t read_pos = 0;
+ size_t found_pos = 0;
+
+ if (!ReadFileToString(kProcInterruptsPath, &irqs)) {
+ ALOGE("cannot read all interrupts");
+ return Status::ERROR;
+ }
+
+ while (true) {
+ found_pos = irqs.find_first_of("\n", read_pos);
+ if (found_pos == std::string::npos) {
+ ALOGI("the string of all interrupts is unexpected");
+ return Status::ERROR;
+ }
+
+ std::string single_irq = irqs.substr(read_pos, found_pos - read_pos);
+
+ if (single_irq.find("dwc3", 0) != std::string::npos) {
+ unsigned int dwc3_irq_number;
+ size_t dwc3_pos = single_irq.find_first_of(":");
+ if (!ParseUint(single_irq.substr(0, dwc3_pos), &dwc3_irq_number)) {
+ ALOGI("unknown IRQ strings");
+ return Status::ERROR;
+ }
+
+ mGadgetIrqPath = kProcIrqPath + single_irq.substr(0, dwc3_pos) + kSmpAffinityList;
+ break;
+ }
+
+ if (found_pos == irqs.npos) {
+ ALOGI("USB gadget doesn't start");
+ return Status::ERROR;
+ }
+
+ read_pos = found_pos + 1;
+ }
+
+ return Status::SUCCESS;
+}
+
+void currentFunctionsAppliedCallback(bool functionsApplied, void *payload) {
+ UsbGadget *gadget = (UsbGadget *)payload;
+ gadget->mCurrentUsbFunctionsApplied = functionsApplied;
+}
+
+ScopedAStatus UsbGadget::getCurrentUsbFunctions(const shared_ptr<IUsbGadgetCallback> &callback,
+ int64_t in_transactionId) {
+ ScopedAStatus ret = callback->getCurrentUsbFunctionsCb(
+ mCurrentUsbFunctions,
+ mCurrentUsbFunctionsApplied ? Status::FUNCTIONS_APPLIED : Status::FUNCTIONS_NOT_APPLIED,
+ in_transactionId);
+ if (!ret.isOk())
+ ALOGE("Call to getCurrentUsbFunctionsCb failed %s", ret.getDescription().c_str());
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus UsbGadget::getUsbSpeed(const shared_ptr<IUsbGadgetCallback> &callback,
+ int64_t in_transactionId) {
+ std::string current_speed;
+ if (ReadFileToString(SPEED_PATH, ¤t_speed)) {
+ current_speed = Trim(current_speed);
+ ALOGI("current USB speed is %s", current_speed.c_str());
+ if (current_speed == "low-speed")
+ mUsbSpeed = UsbSpeed::LOWSPEED;
+ else if (current_speed == "full-speed")
+ mUsbSpeed = UsbSpeed::FULLSPEED;
+ else if (current_speed == "high-speed")
+ mUsbSpeed = UsbSpeed::HIGHSPEED;
+ else if (current_speed == "super-speed")
+ mUsbSpeed = UsbSpeed::SUPERSPEED;
+ else if (current_speed == "super-speed-plus")
+ mUsbSpeed = UsbSpeed::SUPERSPEED_10Gb;
+ else if (current_speed == "UNKNOWN")
+ mUsbSpeed = UsbSpeed::UNKNOWN;
+ else
+ mUsbSpeed = UsbSpeed::UNKNOWN;
+ } else {
+ ALOGE("Fail to read current speed");
+ mUsbSpeed = UsbSpeed::UNKNOWN;
+ }
+
+ if (callback) {
+ ScopedAStatus ret = callback->getUsbSpeedCb(mUsbSpeed, in_transactionId);
+
+ if (!ret.isOk())
+ ALOGE("Call to getUsbSpeedCb failed %s", ret.getDescription().c_str());
+ }
+
+ return ScopedAStatus::ok();
+}
+
+Status UsbGadget::tearDownGadget() {
+ return Status::SUCCESS;
+}
+
+ScopedAStatus UsbGadget::reset() {
+ return ScopedAStatus::ok();
+}
+
+Status UsbGadget::setupFunctions(long functions,
+ const shared_ptr<IUsbGadgetCallback> &callback, uint64_t timeout,
+ int64_t in_transactionId) {
+ bool ffsEnabled = false;
+ if (timeout == 0) {
+ ALOGI("timeout not setup");
+ }
+
+ if ((functions & GadgetFunction::ADB) != 0) {
+ ffsEnabled = true;
+ }
+
+ if ((functions & GadgetFunction::NCM) != 0) {
+ ALOGI("setCurrentUsbFunctions ncm");
+ }
+
+ // Pull up the gadget right away when there are no ffs functions.
+ if (!ffsEnabled) {
+ mCurrentUsbFunctionsApplied = true;
+ if (callback)
+ callback->setCurrentUsbFunctionsCb(functions, Status::SUCCESS, in_transactionId);
+ return Status::SUCCESS;
+ }
+
+ return Status::SUCCESS;
+}
+
+Status getI2cBusHelper(string *name) {
+ DIR *dp;
+
+ dp = opendir(kHsi2cPath);
+ if (dp != NULL) {
+ struct dirent *ep;
+
+ while ((ep = readdir(dp))) {
+ if (ep->d_type == DT_DIR) {
+ if (string::npos != string(ep->d_name).find("i2c-")) {
+ std::strtok(ep->d_name, "-");
+ *name = std::strtok(NULL, "-");
+ }
+ }
+ }
+ closedir(dp);
+ return Status::SUCCESS;
+ }
+
+ ALOGE("Failed to open %s", kHsi2cPath);
+ return Status::ERROR;
+}
+
+ScopedAStatus UsbGadget::setCurrentUsbFunctions(int64_t functions,
+ const shared_ptr<IUsbGadgetCallback> &callback,
+ int64_t timeoutMs,
+ int64_t in_transactionId) {
+ std::unique_lock<std::mutex> lk(mLockSetCurrentFunction);
+ std::string current_usb_power_operation_mode, current_usb_type;
+ std::string usb_limit_sink_enable;
+
+ string accessoryCurrentLimitEnablePath, accessoryCurrentLimitPath, path;
+
+ mCurrentUsbFunctions = functions;
+ mCurrentUsbFunctionsApplied = false;
+
+ getI2cBusHelper(&path);
+ accessoryCurrentLimitPath = kI2CPath + path + "/" + kAccessoryLimitCurrent;
+ accessoryCurrentLimitEnablePath = kI2CPath + path + "/" + kAccessoryLimitCurrentEnable;
+
+ // Get the gadget IRQ number before tearDownGadget()
+ if (mGadgetIrqPath.empty())
+ getUsbGadgetIrqPath();
+
+ // Unlink the gadget and stop the monitor if running.
+ Status status = tearDownGadget();
+ if (status != Status::SUCCESS) {
+ goto error;
+ }
+
+ ALOGI("Returned from tearDown gadget");
+
+ // Leave the gadget pulled down to give time for the host to sense disconnect.
+ //usleep(kDisconnectWaitUs);
+
+ if (functions == GadgetFunction::NONE) {
+ if (callback == NULL)
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ -1, "callback == NULL");
+ ScopedAStatus ret = callback->setCurrentUsbFunctionsCb(functions, Status::SUCCESS, in_transactionId);
+ if (!ret.isOk())
+ ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.getDescription().c_str());
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ -1, "Error while calling setCurrentUsbFunctionsCb");
+ }
+
+ status = setupFunctions(functions, callback, timeoutMs, in_transactionId);
+ if (status != Status::SUCCESS) {
+ goto error;
+ }
+
+ if (functions & GadgetFunction::NCM) {
+ if (!mGadgetIrqPath.empty()) {
+ if (!WriteStringToFile(BIG_CORE, mGadgetIrqPath))
+ ALOGI("Cannot move gadget IRQ to big core, path:%s", mGadgetIrqPath.c_str());
+ }
+ } else {
+ if (!mGadgetIrqPath.empty()) {
+ if (!WriteStringToFile(MEDIUM_CORE, mGadgetIrqPath))
+ ALOGI("Cannot move gadget IRQ to medium core, path:%s", mGadgetIrqPath.c_str());
+ }
+ }
+
+ if (ReadFileToString(CURRENT_USB_TYPE_PATH, ¤t_usb_type))
+ current_usb_type = Trim(current_usb_type);
+
+ if (ReadFileToString(CURRENT_USB_POWER_OPERATION_MODE_PATH, ¤t_usb_power_operation_mode))
+ current_usb_power_operation_mode = Trim(current_usb_power_operation_mode);
+
+ if (functions & GadgetFunction::ACCESSORY &&
+ current_usb_type == "Unknown SDP [CDP] DCP" &&
+ (current_usb_power_operation_mode == "default" ||
+ current_usb_power_operation_mode == "1.5A")) {
+ if (!WriteStringToFile("1300000", accessoryCurrentLimitPath)) {
+ ALOGI("Write 1.3A to limit current fail");
+ } else {
+ if (!WriteStringToFile("1", accessoryCurrentLimitEnablePath)) {
+ ALOGI("Enable limit current fail");
+ }
+ }
+ } else {
+ if (!WriteStringToFile("0", accessoryCurrentLimitEnablePath))
+ ALOGI("unvote accessory limit current failed");
+ }
+
+ ALOGI("Usb Gadget setcurrent functions called successfully");
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ -1, "Usb Gadget setcurrent functions called successfully");
+
+
+error:
+ ALOGI("Usb Gadget setcurrent functions failed");
+ if (callback == NULL)
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ -1, "Usb Gadget setcurrent functions failed");
+ ScopedAStatus ret = callback->setCurrentUsbFunctionsCb(functions, status, in_transactionId);
+ if (!ret.isOk())
+ ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.getDescription().c_str());
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ -1, "Error while calling setCurrentUsbFunctionsCb");
+}
+} // namespace gadget
+} // namespace usb
+} // namespace hardware
+} // namespace android
+} // aidl
diff --git a/usb/gadget/aidl/default/UsbGadget.h b/usb/gadget/aidl/default/UsbGadget.h
new file mode 100644
index 0000000..adcfcfa
--- /dev/null
+++ b/usb/gadget/aidl/default/UsbGadget.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <aidl/android/hardware/usb/gadget/BnUsbGadget.h>
+#include <aidl/android/hardware/usb/gadget/BnUsbGadgetCallback.h>
+#include <aidl/android/hardware/usb/gadget/GadgetFunction.h>
+#include <aidl/android/hardware/usb/gadget/IUsbGadget.h>
+#include <aidl/android/hardware/usb/gadget/IUsbGadgetCallback.h>
+#include <sched.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <utils/Log.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+
+using ::aidl::android::hardware::usb::gadget::GadgetFunction;
+using ::aidl::android::hardware::usb::gadget::IUsbGadgetCallback;
+using ::aidl::android::hardware::usb::gadget::IUsbGadget;
+using ::aidl::android::hardware::usb::gadget::Status;
+using ::aidl::android::hardware::usb::gadget::UsbSpeed;
+using ::android::base::GetProperty;
+using ::android::base::SetProperty;
+using ::android::base::ParseUint;
+using ::android::base::unique_fd;
+using ::android::base::ReadFileToString;
+using ::android::base::Trim;
+using ::android::base::WriteStringToFile;
+using ::ndk::ScopedAStatus;
+using ::std::shared_ptr;
+using ::std::string;
+
+constexpr char kGadgetName[] = "11110000.dwc3";
+constexpr char kProcInterruptsPath[] = "/proc/interrupts";
+constexpr char kProcIrqPath[] = "/proc/irq/";
+constexpr char kSmpAffinityList[] = "/smp_affinity_list";
+#ifndef UDC_PATH
+#define UDC_PATH "/sys/class/udc/11110000.dwc3/"
+#endif
+//static MonitorFfs monitorFfs(kGadgetName);
+
+#define SPEED_PATH UDC_PATH "current_speed"
+
+#define BIG_CORE "6"
+#define MEDIUM_CORE "4"
+
+#define POWER_SUPPLY_PATH "/sys/class/power_supply/usb/"
+#define USB_PORT0_PATH "/sys/class/typec/port0/"
+
+#define CURRENT_MAX_PATH POWER_SUPPLY_PATH "current_max"
+#define CURRENT_USB_TYPE_PATH POWER_SUPPLY_PATH "usb_type"
+#define CURRENT_USB_POWER_OPERATION_MODE_PATH USB_PORT0_PATH "power_operation_mode"
+
+struct UsbGadget : public BnUsbGadget {
+ UsbGadget();
+
+ // Makes sure that only one request is processed at a time.
+ std::mutex mLockSetCurrentFunction;
+ std::string mGadgetIrqPath;
+ long mCurrentUsbFunctions;
+ bool mCurrentUsbFunctionsApplied;
+ UsbSpeed mUsbSpeed;
+
+ ScopedAStatus setCurrentUsbFunctions(int64_t functions,
+ const shared_ptr<IUsbGadgetCallback> &callback,
+ int64_t timeoutMs, int64_t in_transactionId) override;
+
+ ScopedAStatus getCurrentUsbFunctions(const shared_ptr<IUsbGadgetCallback> &callback,
+ int64_t in_transactionId) override;
+
+ ScopedAStatus reset() override;
+
+ ScopedAStatus getUsbSpeed(const shared_ptr<IUsbGadgetCallback> &callback,
+ int64_t in_transactionId) override;
+
+ private:
+ Status tearDownGadget();
+ Status getUsbGadgetIrqPath();
+ Status setupFunctions(long functions, const shared_ptr<IUsbGadgetCallback> &callback,
+ uint64_t timeout, int64_t in_transactionId);
+};
+
+} // namespace gadget
+} // namespace usb
+} // namespace hardware
+} // namespace android
+} // aidl
diff --git a/usb/gadget/aidl/default/android.hardware.usb.gadget-service.example.rc b/usb/gadget/aidl/default/android.hardware.usb.gadget-service.example.rc
new file mode 100644
index 0000000..b2a8cc0
--- /dev/null
+++ b/usb/gadget/aidl/default/android.hardware.usb.gadget-service.example.rc
@@ -0,0 +1,4 @@
+service vendor.usb_gadget_default /vendor/bin/hw/android.hardware.usb.gadget-service.example
+ class hal
+ user system
+ group system
diff --git a/usb/gadget/aidl/default/android.hardware.usb.gadget-service.example.xml b/usb/gadget/aidl/default/android.hardware.usb.gadget-service.example.xml
new file mode 100644
index 0000000..e7eebc3
--- /dev/null
+++ b/usb/gadget/aidl/default/android.hardware.usb.gadget-service.example.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.usb.gadget</name>
+ <version>1</version>
+ <interface>
+ <name>IUsbGadget</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/usb/gadget/aidl/default/service_gadget.cpp b/usb/gadget/aidl/default/service_gadget.cpp
new file mode 100644
index 0000000..7efbadd
--- /dev/null
+++ b/usb/gadget/aidl/default/service_gadget.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include "UsbGadget.h"
+using ::aidl::android::hardware::usb::gadget::UsbGadget;
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<UsbGadget> usbgadget = ndk::SharedRefBase::make<UsbGadget>();
+ const std::string instance = std::string() + UsbGadget::descriptor + "/default";
+ binder_status_t status = AServiceManager_addService(usbgadget->asBinder().get(), instance.c_str());
+ CHECK(status == STATUS_OK);
+ ABinderProcess_joinThreadPool();
+ return -1;
+}
diff --git a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
index 6ec8d57..b9ac7b9 100644
--- a/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
+++ b/uwb/aidl/aidl_api/android.hardware.uwb.fira_android/current/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
@@ -43,10 +43,12 @@
CCC_SUPPORTED_UWB_CONFIGS = 165,
CCC_SUPPORTED_PULSE_SHAPE_COMBOS = 166,
CCC_SUPPORTED_RAN_MULTIPLIER = 167,
+ CCC_SUPPORTED_MAX_RANGING_SESSION_NUMBER = 168,
SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 227,
SUPPORTED_MIN_RANGING_INTERVAL_MS = 228,
SUPPORTED_RANGE_DATA_NTF_CONFIG = 229,
SUPPORTED_RSSI_REPORTING = 230,
SUPPORTED_DIAGNOSTICS = 231,
SUPPORTED_MIN_SLOT_DURATION = 232,
+ SUPPORTED_MAX_RANGING_SESSION_NUMBER = 233,
}
diff --git a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
index b182f9d..a3bb7a6 100644
--- a/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
+++ b/uwb/aidl/android/hardware/uwb/fira_android/UwbVendorCapabilityTlvTypes.aidl
@@ -139,6 +139,11 @@
/** Int value for indicating supported ran multiplier */
CCC_SUPPORTED_RAN_MULTIPLIER = 0xA7,
+ /**
+ * Int value to indicate supported max number of ccc ranging sessions
+ */
+ CCC_SUPPORTED_MAX_RANGING_SESSION_NUMBER = 0xA8,
+
/*********************************************
* FIRA specific
********************************************/
@@ -184,4 +189,9 @@
* 4 byte value to indicate supported min slot duration in ms.
*/
SUPPORTED_MIN_SLOT_DURATION = 0xE8,
+
+ /**
+ * Int value to indicate supported max number of fira ranging sessions
+ */
+ SUPPORTED_MAX_RANGING_SESSION_NUMBER = 0xE9,
}
diff --git a/vibrator/aidl/default/main.cpp b/vibrator/aidl/default/main.cpp
index feba2c7..7375889 100644
--- a/vibrator/aidl/default/main.cpp
+++ b/vibrator/aidl/default/main.cpp
@@ -29,15 +29,15 @@
// make a default vibrator service
auto vib = ndk::SharedRefBase::make<Vibrator>();
- const std::string vibName = std::string() + Vibrator::descriptor + "/default";
- binder_status_t status = AServiceManager_addService(vib->asBinder().get(), vibName.c_str());
+ binder_status_t status = AServiceManager_addService(
+ vib->asBinder().get(), Vibrator::makeServiceName("default").c_str());
CHECK_EQ(status, STATUS_OK);
// make the vibrator manager service with a different vibrator
auto managedVib = ndk::SharedRefBase::make<Vibrator>();
auto vibManager = ndk::SharedRefBase::make<VibratorManager>(std::move(managedVib));
- const std::string vibManagerName = std::string() + VibratorManager::descriptor + "/default";
- status = AServiceManager_addService(vibManager->asBinder().get(), vibManagerName.c_str());
+ status = AServiceManager_addService(vibManager->asBinder().get(),
+ VibratorManager::makeServiceName("default").c_str());
CHECK_EQ(status, STATUS_OK);
ABinderProcess_joinThreadPool();
diff --git a/wifi/aidl/default/Android.bp b/wifi/aidl/default/Android.bp
new file mode 100644
index 0000000..441d461
--- /dev/null
+++ b/wifi/aidl/default/Android.bp
@@ -0,0 +1,214 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+soong_config_module_type {
+ name: "wifi_hal_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "wifi",
+ bool_variables: [
+ "hidl_feature_aware", // WIFI_HIDL_FEATURE_AWARE
+ "hidl_feature_dual_interface", // WIFI_HIDL_FEATURE_DUAL_INTERFACE
+ "hidl_feature_disable_ap", // WIFI_HIDL_FEATURE_DISABLE_AP
+ "hidl_feature_disable_ap_mac_randomization", // WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION
+ "avoid_iface_reset_mac_change", // WIFI_AVOID_IFACE_RESET_MAC_CHANGE
+ ],
+ value_variables: [
+ "hal_interface_combinations", // WIFI_HAL_INTERFACE_COMBINATIONS
+ ],
+ properties: [
+ "cppflags",
+ ],
+}
+
+wifi_hal_cc_defaults {
+ name: "android.hardware.wifi-service-cppflags-defaults",
+ soong_config_variables: {
+ hidl_feature_aware: {
+ cppflags: ["-DWIFI_HIDL_FEATURE_AWARE"],
+ },
+ hidl_feature_dual_interface: {
+ cppflags: ["-DWIFI_HIDL_FEATURE_DUAL_INTERFACE"],
+ },
+ hidl_feature_disable_ap: {
+ cppflags: ["-DWIFI_HIDL_FEATURE_DISABLE_AP"],
+ },
+ hidl_feature_disable_ap_mac_randomization: {
+ cppflags: ["-DWIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION"],
+ },
+ avoid_iface_reset_mac_change: {
+ cppflags: ["-DWIFI_AVOID_IFACE_RESET_MAC_CHANGE"],
+ },
+ hal_interface_combinations: {
+ cppflags: ["-DWIFI_HAL_INTERFACE_COMBINATIONS=%s"],
+ },
+ },
+}
+
+cc_library_static {
+ name: "android.hardware.wifi-service-lib",
+ defaults: ["android.hardware.wifi-service-cppflags-defaults"],
+ proprietary: true,
+ compile_multilib: "first",
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ // Allow implicit fallthroughs in wifi_legacy_hal.cpp until they are fixed.
+ cflags: ["-Wno-error=implicit-fallthrough"],
+ srcs: [
+ "aidl_struct_util.cpp",
+ "aidl_sync_util.cpp",
+ "ringbuffer.cpp",
+ "wifi.cpp",
+ "wifi_ap_iface.cpp",
+ "wifi_chip.cpp",
+ "wifi_feature_flags.cpp",
+ "wifi_iface_util.cpp",
+ "wifi_legacy_hal.cpp",
+ "wifi_legacy_hal_factory.cpp",
+ "wifi_legacy_hal_stubs.cpp",
+ "wifi_mode_controller.cpp",
+ "wifi_nan_iface.cpp",
+ "wifi_p2p_iface.cpp",
+ "wifi_rtt_controller.cpp",
+ "wifi_sta_iface.cpp",
+ "wifi_status_util.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libnl",
+ "libutils",
+ "libwifi-hal",
+ "libwifi-system-iface",
+ "libxml2",
+ "android.hardware.wifi-V1-ndk",
+ ],
+
+ export_include_dirs: ["."],
+}
+
+cc_binary {
+ name: "android.hardware.wifi-service",
+ vintf_fragments: ["android.hardware.wifi-service.xml"],
+ relative_install_path: "hw",
+ proprietary: true,
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ srcs: ["service.cpp"],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libnl",
+ "libutils",
+ "libwifi-hal",
+ "libwifi-system-iface",
+ "libxml2",
+ "android.hardware.wifi-V1-ndk",
+ ],
+ static_libs: ["android.hardware.wifi-service-lib"],
+ init_rc: ["android.hardware.wifi-service.rc"],
+}
+
+cc_binary {
+ name: "android.hardware.wifi-service-lazy",
+ vintf_fragments: ["android.hardware.wifi-service.xml"],
+ overrides: ["android.hardware.wifi-service"],
+ cflags: ["-DLAZY_SERVICE"],
+ relative_install_path: "hw",
+ proprietary: true,
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ srcs: ["service.cpp"],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libnl",
+ "libutils",
+ "libwifi-hal",
+ "libwifi-system-iface",
+ "libxml2",
+ "android.hardware.wifi-V1-ndk",
+ ],
+ static_libs: ["android.hardware.wifi-service-lib"],
+ init_rc: ["android.hardware.wifi-service-lazy.rc"],
+}
+
+cc_test {
+ name: "android.hardware.wifi-service-tests",
+ proprietary: true,
+ compile_multilib: "first",
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ srcs: [
+ "tests/aidl_struct_util_unit_tests.cpp",
+ "tests/main.cpp",
+ "tests/mock_interface_tool.cpp",
+ "tests/mock_wifi_feature_flags.cpp",
+ "tests/mock_wifi_iface_util.cpp",
+ "tests/mock_wifi_legacy_hal.cpp",
+ "tests/mock_wifi_mode_controller.cpp",
+ "tests/ringbuffer_unit_tests.cpp",
+ "tests/wifi_nan_iface_unit_tests.cpp",
+ "tests/wifi_chip_unit_tests.cpp",
+ "tests/wifi_iface_util_unit_tests.cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ "libgtest",
+ "android.hardware.wifi-V1-ndk",
+ "android.hardware.wifi-service-lib",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libnl",
+ "libutils",
+ "libwifi-hal",
+ "libwifi-system-iface",
+ ],
+}
+
+filegroup {
+ name: "default-android.hardware.wifi-service.rc",
+ srcs: ["android.hardware.wifi-service.rc"],
+}
+
+filegroup {
+ name: "default-android.hardware.wifi-service.xml",
+ srcs: ["android.hardware.wifi-service.xml"],
+}
diff --git a/wifi/aidl/default/THREADING.README b/wifi/aidl/default/THREADING.README
new file mode 100644
index 0000000..45679da
--- /dev/null
+++ b/wifi/aidl/default/THREADING.README
@@ -0,0 +1,35 @@
+Vendor HAL Threading Model
+==========================
+The vendor HAL service has two threads:
+1. AIDL thread: This is the main thread which processes all the incoming AIDL
+RPC's.
+2. Legacy HAL event loop thread: This is the thread forked off for processing
+the legacy HAL event loop (wifi_event_loop()). This thread is used to process
+any asynchronous netlink events posted by the driver. Any asynchronous
+callbacks passed to the legacy HAL API's are invoked on this thread.
+
+Synchronization Concerns
+========================
+wifi_legacy_hal.cpp has a bunch of global "C" style functions to handle the
+legacy callbacks. Each of these "C" style functions invokes a corresponding
+"std::function" version of the callback which does the actual processing.
+The variables holding these "std::function" callbacks are reset from the AIDL
+thread when they are no longer used. For example: stopGscan() will reset the
+corresponding "on_gscan_*" callback variables which were set when startGscan()
+was invoked. This is not thread safe since these callback variables are
+accesed from the legacy hal event loop thread as well.
+
+Synchronization Solution
+========================
+Adding a global lock seems to be the most trivial solution to the problem.
+a) All of the asynchronous "C" style callbacks will acquire the global lock
+before invoking the corresponding "std::function" callback variables.
+b) All of the AIDL methods will also acquire the global lock before processing
+(in aidl_return_util::validateAndCall()).
+
+Note: It's important that we only acquire the global lock for asynchronous
+callbacks, because there is no guarantee (or documentation to clarify) that the
+synchronous callbacks are invoked on the same invocation thread. If that is not
+the case in some implementation, we will end up deadlocking the system since the
+AIDL thread would have acquired the global lock which is needed by the
+synchronous callback executed on the legacy hal event loop thread.
diff --git a/wifi/aidl/default/aidl_callback_util.h b/wifi/aidl/default/aidl_callback_util.h
new file mode 100644
index 0000000..41d70a5
--- /dev/null
+++ b/wifi/aidl/default/aidl_callback_util.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AIDL_CALLBACK_UTIL_H_
+#define AIDL_CALLBACK_UTIL_H_
+
+#include <android-base/logging.h>
+
+#include <set>
+#include <unordered_map>
+
+namespace {
+std::unordered_map<void* /* callback */, void* /* handler */> callback_handler_map_;
+}
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace aidl_callback_util {
+
+// Provides a class to manage callbacks for the various AIDL interfaces and
+// handle the death of the process hosting each callback.
+template <typename CallbackType>
+class AidlCallbackHandler {
+ public:
+ AidlCallbackHandler() {
+ death_handler_ = AIBinder_DeathRecipient_new(AidlCallbackHandler::onCallbackDeath);
+ }
+ ~AidlCallbackHandler() { invalidate(); }
+
+ bool addCallback(const std::shared_ptr<CallbackType>& cb) {
+ void* cbPtr = reinterpret_cast<void*>(cb->asBinder().get());
+ const auto& cbPosition = findCbInSet(cbPtr);
+ if (cbPosition != cb_set_.end()) {
+ LOG(WARNING) << "Duplicate death notification registration";
+ return true;
+ }
+
+ if (AIBinder_linkToDeath(cb->asBinder().get(), death_handler_, cbPtr /* cookie */) !=
+ STATUS_OK) {
+ LOG(ERROR) << "Failed to register death notification";
+ return false;
+ }
+
+ callback_handler_map_[cbPtr] = reinterpret_cast<void*>(this);
+ cb_set_.insert(cb);
+ return true;
+ }
+
+ const std::set<std::shared_ptr<CallbackType>>& getCallbacks() { return cb_set_; }
+
+ void invalidate() {
+ for (auto cb : cb_set_) {
+ void* cookie = reinterpret_cast<void*>(cb->asBinder().get());
+ if (AIBinder_unlinkToDeath(cb->asBinder().get(), death_handler_, cookie) != STATUS_OK) {
+ LOG(ERROR) << "Failed to deregister death notification";
+ }
+ if (!removeCbFromHandlerMap(cookie)) {
+ LOG(ERROR) << "Failed to remove callback from handler map";
+ }
+ }
+ cb_set_.clear();
+ }
+
+ // Entry point for the death handling logic. AIBinder_DeathRecipient
+ // can only call a static function, so use the cookie to find the
+ // proper handler and route the request there.
+ static void onCallbackDeath(void* cookie) {
+ auto cbQuery = callback_handler_map_.find(cookie);
+ if (cbQuery == callback_handler_map_.end()) {
+ LOG(ERROR) << "Invalid death cookie received";
+ return;
+ }
+
+ AidlCallbackHandler* cbHandler = reinterpret_cast<AidlCallbackHandler*>(cbQuery->second);
+ if (cbHandler == nullptr) {
+ LOG(ERROR) << "Handler mapping contained an invalid handler";
+ return;
+ }
+ cbHandler->handleCallbackDeath(cbQuery->first);
+ }
+
+ private:
+ std::set<std::shared_ptr<CallbackType>> cb_set_;
+ AIBinder_DeathRecipient* death_handler_;
+
+ typename std::set<std::shared_ptr<CallbackType>>::iterator findCbInSet(void* cbPtr) {
+ const auto& cbPosition = std::find_if(
+ cb_set_.begin(), cb_set_.end(), [cbPtr](const std::shared_ptr<CallbackType>& p) {
+ return cbPtr == reinterpret_cast<void*>(p->asBinder().get());
+ });
+ return cbPosition;
+ }
+
+ bool removeCbFromHandlerMap(void* cbPtr) {
+ auto cbQuery = callback_handler_map_.find(cbPtr);
+ if (cbQuery != callback_handler_map_.end()) {
+ callback_handler_map_.erase(cbQuery);
+ return true;
+ }
+ return false;
+ }
+
+ void handleCallbackDeath(void* cbPtr) {
+ const auto& cbPosition = findCbInSet(cbPtr);
+ if (cbPosition == cb_set_.end()) {
+ LOG(ERROR) << "Unknown callback death notification received";
+ return;
+ }
+ cb_set_.erase(cbPosition);
+
+ if (!removeCbFromHandlerMap(cbPtr)) {
+ LOG(ERROR) << "Callback was not in callback handler map";
+ }
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(AidlCallbackHandler);
+};
+
+} // namespace aidl_callback_util
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // AIDL_CALLBACK_UTIL_H_
diff --git a/wifi/aidl/default/aidl_return_util.h b/wifi/aidl/default/aidl_return_util.h
new file mode 100644
index 0000000..9a49a22
--- /dev/null
+++ b/wifi/aidl/default/aidl_return_util.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AIDL_RETURN_UTIL_H_
+#define AIDL_RETURN_UTIL_H_
+
+#include "aidl_sync_util.h"
+#include "wifi_status_util.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace aidl_return_util {
+using aidl::android::hardware::wifi::WifiStatusCode;
+using aidl::android::hardware::wifi::aidl_sync_util::acquireGlobalLock;
+
+/**
+ * These utility functions are used to invoke a method on the provided
+ * AIDL interface object.
+ * These functions checks if the provided AIDL interface object is valid.
+ * a) If valid, Invokes the corresponding internal implementation function of
+ * the AIDL method.
+ * b) If invalid, return without calling the internal implementation function.
+ */
+
+// Use for AIDL methods which return only an AIDL status.
+template <typename ObjT, typename WorkFuncT, typename... Args>
+::ndk::ScopedAStatus validateAndCall(ObjT* obj, WifiStatusCode status_code_if_invalid,
+ WorkFuncT&& work, Args&&... args) {
+ const auto lock = acquireGlobalLock();
+ if (obj->isValid()) {
+ return (obj->*work)(std::forward<Args>(args)...);
+ } else {
+ return createWifiStatus(status_code_if_invalid);
+ }
+}
+
+// Use for AIDL methods which return only an AIDL status.
+// This version passes the global lock acquired to the body of the method.
+template <typename ObjT, typename WorkFuncT, typename... Args>
+::ndk::ScopedAStatus validateAndCallWithLock(ObjT* obj, WifiStatusCode status_code_if_invalid,
+ WorkFuncT&& work, Args&&... args) {
+ auto lock = acquireGlobalLock();
+ if (obj->isValid()) {
+ return (obj->*work)(&lock, std::forward<Args>(args)...);
+ } else {
+ return createWifiStatus(status_code_if_invalid);
+ }
+}
+
+// Use for AIDL methods which have a return value along with the AIDL status
+template <typename ObjT, typename WorkFuncT, typename ReturnT, typename... Args>
+::ndk::ScopedAStatus validateAndCall(ObjT* obj, WifiStatusCode status_code_if_invalid,
+ WorkFuncT&& work, ReturnT* ret_val, Args&&... args) {
+ const auto lock = acquireGlobalLock();
+ if (obj->isValid()) {
+ auto call_pair = (obj->*work)(std::forward<Args>(args)...);
+ *ret_val = call_pair.first;
+ return std::forward<::ndk::ScopedAStatus>(call_pair.second);
+ } else {
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(status_code_if_invalid));
+ }
+}
+
+} // namespace aidl_return_util
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+#endif // AIDL_RETURN_UTIL_H_
diff --git a/wifi/aidl/default/aidl_struct_util.cpp b/wifi/aidl/default/aidl_struct_util.cpp
new file mode 100644
index 0000000..07612b6
--- /dev/null
+++ b/wifi/aidl/default/aidl_struct_util.cpp
@@ -0,0 +1,2775 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <utils/SystemClock.h>
+
+#include "aidl_struct_util.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace aidl_struct_util {
+
+WifiChannelWidthInMhz convertLegacyWifiChannelWidthToAidl(legacy_hal::wifi_channel_width type);
+
+std::string safeConvertChar(const char* str, size_t max_len) {
+ const char* c = str;
+ size_t size = 0;
+ while (*c && (unsigned char)*c < 128 && size < max_len) {
+ ++size;
+ ++c;
+ }
+ return std::string(str, size);
+}
+
+inline std::vector<int32_t> uintToIntVec(const std::vector<uint32_t>& in) {
+ return std::vector<int32_t>(in.begin(), in.end());
+}
+
+IWifiChip::ChipCapabilityMask convertLegacyLoggerFeatureToAidlChipCapability(uint32_t feature) {
+ switch (feature) {
+ case legacy_hal::WIFI_LOGGER_MEMORY_DUMP_SUPPORTED:
+ return IWifiChip::ChipCapabilityMask::DEBUG_MEMORY_FIRMWARE_DUMP;
+ case legacy_hal::WIFI_LOGGER_DRIVER_DUMP_SUPPORTED:
+ return IWifiChip::ChipCapabilityMask::DEBUG_MEMORY_DRIVER_DUMP;
+ case legacy_hal::WIFI_LOGGER_CONNECT_EVENT_SUPPORTED:
+ return IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_CONNECT_EVENT;
+ case legacy_hal::WIFI_LOGGER_POWER_EVENT_SUPPORTED:
+ return IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_POWER_EVENT;
+ case legacy_hal::WIFI_LOGGER_WAKE_LOCK_SUPPORTED:
+ return IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_WAKELOCK_EVENT;
+ };
+ CHECK(false) << "Unknown legacy feature: " << feature;
+ return {};
+}
+
+IWifiStaIface::StaIfaceCapabilityMask convertLegacyLoggerFeatureToAidlStaIfaceCapability(
+ uint32_t feature) {
+ switch (feature) {
+ case legacy_hal::WIFI_LOGGER_PACKET_FATE_SUPPORTED:
+ return IWifiStaIface::StaIfaceCapabilityMask::DEBUG_PACKET_FATE;
+ };
+ CHECK(false) << "Unknown legacy feature: " << feature;
+ return {};
+}
+
+IWifiChip::ChipCapabilityMask convertLegacyFeatureToAidlChipCapability(uint64_t feature) {
+ switch (feature) {
+ case WIFI_FEATURE_SET_TX_POWER_LIMIT:
+ return IWifiChip::ChipCapabilityMask::SET_TX_POWER_LIMIT;
+ case WIFI_FEATURE_USE_BODY_HEAD_SAR:
+ return IWifiChip::ChipCapabilityMask::USE_BODY_HEAD_SAR;
+ case WIFI_FEATURE_D2D_RTT:
+ return IWifiChip::ChipCapabilityMask::D2D_RTT;
+ case WIFI_FEATURE_D2AP_RTT:
+ return IWifiChip::ChipCapabilityMask::D2AP_RTT;
+ case WIFI_FEATURE_INFRA_60G:
+ return IWifiChip::ChipCapabilityMask::WIGIG;
+ case WIFI_FEATURE_SET_LATENCY_MODE:
+ return IWifiChip::ChipCapabilityMask::SET_LATENCY_MODE;
+ case WIFI_FEATURE_P2P_RAND_MAC:
+ return IWifiChip::ChipCapabilityMask::P2P_RAND_MAC;
+ };
+ CHECK(false) << "Unknown legacy feature: " << feature;
+ return {};
+}
+
+IWifiStaIface::StaIfaceCapabilityMask convertLegacyFeatureToAidlStaIfaceCapability(
+ uint64_t feature) {
+ switch (feature) {
+ case WIFI_FEATURE_GSCAN:
+ return IWifiStaIface::StaIfaceCapabilityMask::BACKGROUND_SCAN;
+ case WIFI_FEATURE_LINK_LAYER_STATS:
+ return IWifiStaIface::StaIfaceCapabilityMask::LINK_LAYER_STATS;
+ case WIFI_FEATURE_RSSI_MONITOR:
+ return IWifiStaIface::StaIfaceCapabilityMask::RSSI_MONITOR;
+ case WIFI_FEATURE_CONTROL_ROAMING:
+ return IWifiStaIface::StaIfaceCapabilityMask::CONTROL_ROAMING;
+ case WIFI_FEATURE_IE_WHITELIST:
+ return IWifiStaIface::StaIfaceCapabilityMask::PROBE_IE_ALLOWLIST;
+ case WIFI_FEATURE_SCAN_RAND:
+ return IWifiStaIface::StaIfaceCapabilityMask::SCAN_RAND;
+ case WIFI_FEATURE_INFRA_5G:
+ return IWifiStaIface::StaIfaceCapabilityMask::STA_5G;
+ case WIFI_FEATURE_HOTSPOT:
+ return IWifiStaIface::StaIfaceCapabilityMask::HOTSPOT;
+ case WIFI_FEATURE_PNO:
+ return IWifiStaIface::StaIfaceCapabilityMask::PNO;
+ case WIFI_FEATURE_TDLS:
+ return IWifiStaIface::StaIfaceCapabilityMask::TDLS;
+ case WIFI_FEATURE_TDLS_OFFCHANNEL:
+ return IWifiStaIface::StaIfaceCapabilityMask::TDLS_OFFCHANNEL;
+ case WIFI_FEATURE_CONFIG_NDO:
+ return IWifiStaIface::StaIfaceCapabilityMask::ND_OFFLOAD;
+ case WIFI_FEATURE_MKEEP_ALIVE:
+ return IWifiStaIface::StaIfaceCapabilityMask::KEEP_ALIVE;
+ };
+ CHECK(false) << "Unknown legacy feature: " << feature;
+ return {};
+}
+
+bool convertLegacyFeaturesToAidlChipCapabilities(uint64_t legacy_feature_set,
+ uint32_t legacy_logger_feature_set,
+ uint32_t* aidl_caps) {
+ if (!aidl_caps) {
+ return false;
+ }
+ *aidl_caps = {};
+ for (const auto feature : {legacy_hal::WIFI_LOGGER_MEMORY_DUMP_SUPPORTED,
+ legacy_hal::WIFI_LOGGER_DRIVER_DUMP_SUPPORTED,
+ legacy_hal::WIFI_LOGGER_CONNECT_EVENT_SUPPORTED,
+ legacy_hal::WIFI_LOGGER_POWER_EVENT_SUPPORTED,
+ legacy_hal::WIFI_LOGGER_WAKE_LOCK_SUPPORTED}) {
+ if (feature & legacy_logger_feature_set) {
+ *aidl_caps |=
+ static_cast<uint32_t>(convertLegacyLoggerFeatureToAidlChipCapability(feature));
+ }
+ }
+ std::vector<uint64_t> features = {WIFI_FEATURE_SET_TX_POWER_LIMIT,
+ WIFI_FEATURE_USE_BODY_HEAD_SAR,
+ WIFI_FEATURE_D2D_RTT,
+ WIFI_FEATURE_D2AP_RTT,
+ WIFI_FEATURE_INFRA_60G,
+ WIFI_FEATURE_SET_LATENCY_MODE,
+ WIFI_FEATURE_P2P_RAND_MAC};
+ for (const auto feature : features) {
+ if (feature & legacy_feature_set) {
+ *aidl_caps |= static_cast<uint32_t>(convertLegacyFeatureToAidlChipCapability(feature));
+ }
+ }
+
+ // There are no flags for these 3 in the legacy feature set. Adding them to
+ // the set because all the current devices support it.
+ *aidl_caps |=
+ static_cast<uint32_t>(IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_VENDOR_DATA);
+ *aidl_caps |=
+ static_cast<uint32_t>(IWifiChip::ChipCapabilityMask::DEBUG_HOST_WAKE_REASON_STATS);
+ *aidl_caps |= static_cast<uint32_t>(IWifiChip::ChipCapabilityMask::DEBUG_ERROR_ALERTS);
+ return true;
+}
+
+WifiDebugRingBufferFlags convertLegacyDebugRingBufferFlagsToAidl(uint32_t flag) {
+ switch (flag) {
+ case WIFI_RING_BUFFER_FLAG_HAS_BINARY_ENTRIES:
+ return WifiDebugRingBufferFlags::HAS_BINARY_ENTRIES;
+ case WIFI_RING_BUFFER_FLAG_HAS_ASCII_ENTRIES:
+ return WifiDebugRingBufferFlags::HAS_ASCII_ENTRIES;
+ };
+ CHECK(false) << "Unknown legacy flag: " << flag;
+ return {};
+}
+
+bool convertLegacyDebugRingBufferStatusToAidl(
+ const legacy_hal::wifi_ring_buffer_status& legacy_status,
+ WifiDebugRingBufferStatus* aidl_status) {
+ if (!aidl_status) {
+ return false;
+ }
+ *aidl_status = {};
+ aidl_status->ringName = safeConvertChar(reinterpret_cast<const char*>(legacy_status.name),
+ sizeof(legacy_status.name));
+ aidl_status->flags = 0;
+ for (const auto flag :
+ {WIFI_RING_BUFFER_FLAG_HAS_BINARY_ENTRIES, WIFI_RING_BUFFER_FLAG_HAS_ASCII_ENTRIES}) {
+ if (flag & legacy_status.flags) {
+ aidl_status->flags |= static_cast<std::underlying_type<WifiDebugRingBufferFlags>::type>(
+ convertLegacyDebugRingBufferFlagsToAidl(flag));
+ }
+ }
+ aidl_status->ringId = legacy_status.ring_id;
+ aidl_status->sizeInBytes = legacy_status.ring_buffer_byte_size;
+ // Calculate free size of the ring the buffer. We don't need to send the
+ // exact read/write pointers that were there in the legacy HAL interface.
+ if (legacy_status.written_bytes >= legacy_status.read_bytes) {
+ aidl_status->freeSizeInBytes = legacy_status.ring_buffer_byte_size -
+ (legacy_status.written_bytes - legacy_status.read_bytes);
+ } else {
+ aidl_status->freeSizeInBytes = legacy_status.read_bytes - legacy_status.written_bytes;
+ }
+ aidl_status->verboseLevel = legacy_status.verbose_level;
+ return true;
+}
+
+bool convertLegacyVectorOfDebugRingBufferStatusToAidl(
+ const std::vector<legacy_hal::wifi_ring_buffer_status>& legacy_status_vec,
+ std::vector<WifiDebugRingBufferStatus>* aidl_status_vec) {
+ if (!aidl_status_vec) {
+ return false;
+ }
+ *aidl_status_vec = {};
+ for (const auto& legacy_status : legacy_status_vec) {
+ WifiDebugRingBufferStatus aidl_status;
+ if (!convertLegacyDebugRingBufferStatusToAidl(legacy_status, &aidl_status)) {
+ return false;
+ }
+ aidl_status_vec->push_back(aidl_status);
+ }
+ return true;
+}
+
+bool convertLegacyWakeReasonStatsToAidl(const legacy_hal::WakeReasonStats& legacy_stats,
+ WifiDebugHostWakeReasonStats* aidl_stats) {
+ if (!aidl_stats) {
+ return false;
+ }
+ *aidl_stats = {};
+ aidl_stats->totalCmdEventWakeCnt = legacy_stats.wake_reason_cnt.total_cmd_event_wake;
+ aidl_stats->cmdEventWakeCntPerType = uintToIntVec(legacy_stats.cmd_event_wake_cnt);
+ aidl_stats->totalDriverFwLocalWakeCnt = legacy_stats.wake_reason_cnt.total_driver_fw_local_wake;
+ aidl_stats->driverFwLocalWakeCntPerType = uintToIntVec(legacy_stats.driver_fw_local_wake_cnt);
+ aidl_stats->totalRxPacketWakeCnt = legacy_stats.wake_reason_cnt.total_rx_data_wake;
+ aidl_stats->rxPktWakeDetails.rxUnicastCnt =
+ legacy_stats.wake_reason_cnt.rx_wake_details.rx_unicast_cnt;
+ aidl_stats->rxPktWakeDetails.rxMulticastCnt =
+ legacy_stats.wake_reason_cnt.rx_wake_details.rx_multicast_cnt;
+ aidl_stats->rxPktWakeDetails.rxBroadcastCnt =
+ legacy_stats.wake_reason_cnt.rx_wake_details.rx_broadcast_cnt;
+ aidl_stats->rxMulticastPkWakeDetails.ipv4RxMulticastAddrCnt =
+ legacy_stats.wake_reason_cnt.rx_multicast_wake_pkt_info.ipv4_rx_multicast_addr_cnt;
+ aidl_stats->rxMulticastPkWakeDetails.ipv6RxMulticastAddrCnt =
+ legacy_stats.wake_reason_cnt.rx_multicast_wake_pkt_info.ipv6_rx_multicast_addr_cnt;
+ aidl_stats->rxMulticastPkWakeDetails.otherRxMulticastAddrCnt =
+ legacy_stats.wake_reason_cnt.rx_multicast_wake_pkt_info.other_rx_multicast_addr_cnt;
+ aidl_stats->rxIcmpPkWakeDetails.icmpPkt =
+ legacy_stats.wake_reason_cnt.rx_wake_pkt_classification_info.icmp_pkt;
+ aidl_stats->rxIcmpPkWakeDetails.icmp6Pkt =
+ legacy_stats.wake_reason_cnt.rx_wake_pkt_classification_info.icmp6_pkt;
+ aidl_stats->rxIcmpPkWakeDetails.icmp6Ra =
+ legacy_stats.wake_reason_cnt.rx_wake_pkt_classification_info.icmp6_ra;
+ aidl_stats->rxIcmpPkWakeDetails.icmp6Na =
+ legacy_stats.wake_reason_cnt.rx_wake_pkt_classification_info.icmp6_na;
+ aidl_stats->rxIcmpPkWakeDetails.icmp6Ns =
+ legacy_stats.wake_reason_cnt.rx_wake_pkt_classification_info.icmp6_ns;
+ return true;
+}
+
+legacy_hal::wifi_power_scenario convertAidlTxPowerScenarioToLegacy(
+ IWifiChip::TxPowerScenario aidl_scenario) {
+ switch (aidl_scenario) {
+ case IWifiChip::TxPowerScenario::VOICE_CALL:
+ return legacy_hal::WIFI_POWER_SCENARIO_VOICE_CALL;
+ case IWifiChip::TxPowerScenario::ON_HEAD_CELL_OFF:
+ return legacy_hal::WIFI_POWER_SCENARIO_ON_HEAD_CELL_OFF;
+ case IWifiChip::TxPowerScenario::ON_HEAD_CELL_ON:
+ return legacy_hal::WIFI_POWER_SCENARIO_ON_HEAD_CELL_ON;
+ case IWifiChip::TxPowerScenario::ON_BODY_CELL_OFF:
+ return legacy_hal::WIFI_POWER_SCENARIO_ON_BODY_CELL_OFF;
+ case IWifiChip::TxPowerScenario::ON_BODY_CELL_ON:
+ return legacy_hal::WIFI_POWER_SCENARIO_ON_BODY_CELL_ON;
+ };
+ CHECK(false);
+}
+
+legacy_hal::wifi_latency_mode convertAidlLatencyModeToLegacy(
+ IWifiChip::LatencyMode aidl_latency_mode) {
+ switch (aidl_latency_mode) {
+ case IWifiChip::LatencyMode::NORMAL:
+ return legacy_hal::WIFI_LATENCY_MODE_NORMAL;
+ case IWifiChip::LatencyMode::LOW:
+ return legacy_hal::WIFI_LATENCY_MODE_LOW;
+ }
+ CHECK(false);
+}
+
+bool convertLegacyWifiMacInfoToAidl(const legacy_hal::WifiMacInfo& legacy_mac_info,
+ IWifiChipEventCallback::RadioModeInfo* aidl_radio_mode_info) {
+ if (!aidl_radio_mode_info) {
+ return false;
+ }
+ *aidl_radio_mode_info = {};
+
+ aidl_radio_mode_info->radioId = legacy_mac_info.wlan_mac_id;
+ // Convert from bitmask of bands in the legacy HAL to enum value in
+ // the AIDL interface.
+ if (legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_6_0_BAND &&
+ legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_5_0_BAND &&
+ legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_2_4_BAND) {
+ aidl_radio_mode_info->bandInfo = WifiBand::BAND_24GHZ_5GHZ_6GHZ;
+ } else if (legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_6_0_BAND &&
+ legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_5_0_BAND) {
+ aidl_radio_mode_info->bandInfo = WifiBand::BAND_5GHZ_6GHZ;
+ } else if (legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_6_0_BAND) {
+ aidl_radio_mode_info->bandInfo = WifiBand::BAND_6GHZ;
+ } else if (legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_2_4_BAND &&
+ legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_5_0_BAND) {
+ aidl_radio_mode_info->bandInfo = WifiBand::BAND_24GHZ_5GHZ;
+ } else if (legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_2_4_BAND) {
+ aidl_radio_mode_info->bandInfo = WifiBand::BAND_24GHZ;
+ } else if (legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_5_0_BAND) {
+ aidl_radio_mode_info->bandInfo = WifiBand::BAND_5GHZ;
+ } else {
+ aidl_radio_mode_info->bandInfo = WifiBand::BAND_UNSPECIFIED;
+ }
+ std::vector<IWifiChipEventCallback::IfaceInfo> iface_info_vec;
+ for (const auto& legacy_iface_info : legacy_mac_info.iface_infos) {
+ IWifiChipEventCallback::IfaceInfo iface_info;
+ iface_info.name = legacy_iface_info.name;
+ iface_info.channel = legacy_iface_info.channel;
+ iface_info_vec.push_back(iface_info);
+ }
+ aidl_radio_mode_info->ifaceInfos = iface_info_vec;
+ return true;
+}
+
+uint32_t convertAidlWifiBandToLegacyMacBand(WifiBand aidl_band) {
+ switch (aidl_band) {
+ case WifiBand::BAND_24GHZ:
+ return legacy_hal::WLAN_MAC_2_4_BAND;
+ case WifiBand::BAND_5GHZ:
+ case WifiBand::BAND_5GHZ_DFS:
+ case WifiBand::BAND_5GHZ_WITH_DFS:
+ return legacy_hal::WLAN_MAC_5_0_BAND;
+ case WifiBand::BAND_24GHZ_5GHZ:
+ case WifiBand::BAND_24GHZ_5GHZ_WITH_DFS:
+ return (legacy_hal::WLAN_MAC_2_4_BAND | legacy_hal::WLAN_MAC_5_0_BAND);
+ case WifiBand::BAND_6GHZ:
+ return legacy_hal::WLAN_MAC_6_0_BAND;
+ case WifiBand::BAND_5GHZ_6GHZ:
+ return (legacy_hal::WLAN_MAC_5_0_BAND | legacy_hal::WLAN_MAC_6_0_BAND);
+ case WifiBand::BAND_24GHZ_5GHZ_6GHZ:
+ case WifiBand::BAND_24GHZ_5GHZ_WITH_DFS_6GHZ:
+ return (legacy_hal::WLAN_MAC_2_4_BAND | legacy_hal::WLAN_MAC_5_0_BAND |
+ legacy_hal::WLAN_MAC_6_0_BAND);
+ case WifiBand::BAND_60GHZ:
+ return legacy_hal::WLAN_MAC_60_0_BAND;
+ default:
+ return (legacy_hal::WLAN_MAC_2_4_BAND | legacy_hal::WLAN_MAC_5_0_BAND |
+ legacy_hal::WLAN_MAC_6_0_BAND | legacy_hal::WLAN_MAC_60_0_BAND);
+ }
+}
+
+WifiBand convertLegacyMacBandToAidlWifiBand(uint32_t band) {
+ switch (band) {
+ case legacy_hal::WLAN_MAC_2_4_BAND:
+ return WifiBand::BAND_24GHZ;
+ case legacy_hal::WLAN_MAC_5_0_BAND:
+ return WifiBand::BAND_5GHZ;
+ case legacy_hal::WLAN_MAC_6_0_BAND:
+ return WifiBand::BAND_6GHZ;
+ case legacy_hal::WLAN_MAC_60_0_BAND:
+ return WifiBand::BAND_60GHZ;
+ default:
+ return WifiBand::BAND_UNSPECIFIED;
+ }
+}
+
+uint32_t convertAidlWifiIfaceModeToLegacy(uint32_t aidl_iface_mask) {
+ uint32_t legacy_iface_mask = 0;
+ if (aidl_iface_mask & static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_STA)) {
+ legacy_iface_mask |= (1 << legacy_hal::WIFI_INTERFACE_STA);
+ }
+ if (aidl_iface_mask & static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_SOFTAP)) {
+ legacy_iface_mask |= (1 << legacy_hal::WIFI_INTERFACE_SOFTAP);
+ }
+ if (aidl_iface_mask & static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_P2P_CLIENT)) {
+ legacy_iface_mask |= (1 << legacy_hal::WIFI_INTERFACE_P2P_CLIENT);
+ }
+ if (aidl_iface_mask & static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_P2P_GO)) {
+ legacy_iface_mask |= (1 << legacy_hal::WIFI_INTERFACE_P2P_GO);
+ }
+ if (aidl_iface_mask & static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_NAN)) {
+ legacy_iface_mask |= (1 << legacy_hal::WIFI_INTERFACE_NAN);
+ }
+ if (aidl_iface_mask & static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_TDLS)) {
+ legacy_iface_mask |= (1 << legacy_hal::WIFI_INTERFACE_TDLS);
+ }
+ if (aidl_iface_mask & static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_MESH)) {
+ legacy_iface_mask |= (1 << legacy_hal::WIFI_INTERFACE_MESH);
+ }
+ if (aidl_iface_mask & static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_IBSS)) {
+ legacy_iface_mask |= (1 << legacy_hal::WIFI_INTERFACE_IBSS);
+ }
+ return legacy_iface_mask;
+}
+
+uint32_t convertLegacyWifiInterfaceModeToAidl(uint32_t legacy_iface_mask) {
+ uint32_t aidl_iface_mask = 0;
+ if (legacy_iface_mask & (1 << legacy_hal::WIFI_INTERFACE_STA)) {
+ aidl_iface_mask |= static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_STA);
+ }
+ if (legacy_iface_mask & (1 << legacy_hal::WIFI_INTERFACE_SOFTAP)) {
+ aidl_iface_mask |= static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_SOFTAP);
+ }
+ if (legacy_iface_mask & (1 << legacy_hal::WIFI_INTERFACE_P2P_CLIENT)) {
+ aidl_iface_mask |= static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_P2P_CLIENT);
+ }
+ if (legacy_iface_mask & (1 << legacy_hal::WIFI_INTERFACE_P2P_GO)) {
+ aidl_iface_mask |= static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_P2P_GO);
+ }
+ if (legacy_iface_mask & (1 << legacy_hal::WIFI_INTERFACE_NAN)) {
+ aidl_iface_mask |= static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_NAN);
+ }
+ if (legacy_iface_mask & (1 << legacy_hal::WIFI_INTERFACE_TDLS)) {
+ aidl_iface_mask |= static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_TDLS);
+ }
+ if (legacy_iface_mask & (1 << legacy_hal::WIFI_INTERFACE_MESH)) {
+ aidl_iface_mask |= static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_MESH);
+ }
+ if (legacy_iface_mask & (1 << legacy_hal::WIFI_INTERFACE_IBSS)) {
+ aidl_iface_mask |= static_cast<int32_t>(WifiIfaceMode::IFACE_MODE_IBSS);
+ }
+ return aidl_iface_mask;
+}
+
+uint32_t convertAidlUsableChannelFilterToLegacy(uint32_t aidl_filter_mask) {
+ uint32_t legacy_filter_mask = 0;
+ if (aidl_filter_mask &
+ static_cast<int32_t>(IWifiChip::UsableChannelFilter::CELLULAR_COEXISTENCE)) {
+ legacy_filter_mask |= legacy_hal::WIFI_USABLE_CHANNEL_FILTER_CELLULAR_COEXISTENCE;
+ }
+ if (aidl_filter_mask & static_cast<int32_t>(IWifiChip::UsableChannelFilter::CONCURRENCY)) {
+ legacy_filter_mask |= legacy_hal::WIFI_USABLE_CHANNEL_FILTER_CONCURRENCY;
+ }
+ if (aidl_filter_mask & static_cast<int32_t>(IWifiChip::UsableChannelFilter::NAN_INSTANT_MODE)) {
+ legacy_filter_mask |= WIFI_USABLE_CHANNEL_FILTER_NAN_INSTANT_MODE;
+ }
+ return legacy_filter_mask;
+}
+
+bool convertLegacyWifiUsableChannelToAidl(
+ const legacy_hal::wifi_usable_channel& legacy_usable_channel,
+ WifiUsableChannel* aidl_usable_channel) {
+ if (!aidl_usable_channel) {
+ return false;
+ }
+ *aidl_usable_channel = {};
+ aidl_usable_channel->channel = legacy_usable_channel.freq;
+ aidl_usable_channel->channelBandwidth =
+ convertLegacyWifiChannelWidthToAidl(legacy_usable_channel.width);
+ aidl_usable_channel->ifaceModeMask = static_cast<WifiIfaceMode>(
+ convertLegacyWifiInterfaceModeToAidl(legacy_usable_channel.iface_mode_mask));
+
+ return true;
+}
+
+bool convertLegacyWifiUsableChannelsToAidl(
+ const std::vector<legacy_hal::wifi_usable_channel>& legacy_usable_channels,
+ std::vector<WifiUsableChannel>* aidl_usable_channels) {
+ if (!aidl_usable_channels) {
+ return false;
+ }
+ *aidl_usable_channels = {};
+ for (const auto& legacy_usable_channel : legacy_usable_channels) {
+ WifiUsableChannel aidl_usable_channel;
+ if (!convertLegacyWifiUsableChannelToAidl(legacy_usable_channel, &aidl_usable_channel)) {
+ return false;
+ }
+ aidl_usable_channels->push_back(aidl_usable_channel);
+ }
+ return true;
+}
+
+bool convertLegacyWifiMacInfosToAidl(
+ const std::vector<legacy_hal::WifiMacInfo>& legacy_mac_infos,
+ std::vector<IWifiChipEventCallback::RadioModeInfo>* aidl_radio_mode_infos) {
+ if (!aidl_radio_mode_infos) {
+ return false;
+ }
+ *aidl_radio_mode_infos = {};
+
+ for (const auto& legacy_mac_info : legacy_mac_infos) {
+ IWifiChipEventCallback::RadioModeInfo aidl_radio_mode_info;
+ if (!convertLegacyWifiMacInfoToAidl(legacy_mac_info, &aidl_radio_mode_info)) {
+ return false;
+ }
+ aidl_radio_mode_infos->push_back(aidl_radio_mode_info);
+ }
+ return true;
+}
+
+bool convertLegacyFeaturesToAidlStaCapabilities(uint64_t legacy_feature_set,
+ uint32_t legacy_logger_feature_set,
+ uint32_t* aidl_caps) {
+ if (!aidl_caps) {
+ return false;
+ }
+ *aidl_caps = {};
+ for (const auto feature : {legacy_hal::WIFI_LOGGER_PACKET_FATE_SUPPORTED}) {
+ if (feature & legacy_logger_feature_set) {
+ *aidl_caps |= static_cast<uint32_t>(
+ convertLegacyLoggerFeatureToAidlStaIfaceCapability(feature));
+ }
+ }
+ for (const auto feature :
+ {WIFI_FEATURE_GSCAN, WIFI_FEATURE_LINK_LAYER_STATS, WIFI_FEATURE_RSSI_MONITOR,
+ WIFI_FEATURE_CONTROL_ROAMING, WIFI_FEATURE_IE_WHITELIST, WIFI_FEATURE_SCAN_RAND,
+ WIFI_FEATURE_INFRA_5G, WIFI_FEATURE_HOTSPOT, WIFI_FEATURE_PNO, WIFI_FEATURE_TDLS,
+ WIFI_FEATURE_TDLS_OFFCHANNEL, WIFI_FEATURE_CONFIG_NDO, WIFI_FEATURE_MKEEP_ALIVE}) {
+ if (feature & legacy_feature_set) {
+ *aidl_caps |=
+ static_cast<uint32_t>(convertLegacyFeatureToAidlStaIfaceCapability(feature));
+ }
+ }
+ // There is no flag for this one in the legacy feature set. Adding it to the
+ // set because all the current devices support it.
+ *aidl_caps |= static_cast<uint32_t>(IWifiStaIface::StaIfaceCapabilityMask::APF);
+ return true;
+}
+
+bool convertLegacyApfCapabilitiesToAidl(const legacy_hal::PacketFilterCapabilities& legacy_caps,
+ StaApfPacketFilterCapabilities* aidl_caps) {
+ if (!aidl_caps) {
+ return false;
+ }
+ *aidl_caps = {};
+ aidl_caps->version = legacy_caps.version;
+ aidl_caps->maxLength = legacy_caps.max_len;
+ return true;
+}
+
+uint8_t convertAidlGscanReportEventFlagToLegacy(
+ StaBackgroundScanBucketEventReportSchemeMask aidl_flag) {
+ using AidlFlag = StaBackgroundScanBucketEventReportSchemeMask;
+ switch (aidl_flag) {
+ case AidlFlag::EACH_SCAN:
+ return REPORT_EVENTS_EACH_SCAN;
+ case AidlFlag::FULL_RESULTS:
+ return REPORT_EVENTS_FULL_RESULTS;
+ case AidlFlag::NO_BATCH:
+ return REPORT_EVENTS_NO_BATCH;
+ };
+ CHECK(false);
+}
+
+StaScanDataFlagMask convertLegacyGscanDataFlagToAidl(uint8_t legacy_flag) {
+ switch (legacy_flag) {
+ case legacy_hal::WIFI_SCAN_FLAG_INTERRUPTED:
+ return StaScanDataFlagMask::INTERRUPTED;
+ };
+ CHECK(false) << "Unknown legacy flag: " << legacy_flag;
+ // To silence the compiler warning about reaching the end of non-void
+ // function.
+ return {};
+}
+
+bool convertLegacyGscanCapabilitiesToAidl(const legacy_hal::wifi_gscan_capabilities& legacy_caps,
+ StaBackgroundScanCapabilities* aidl_caps) {
+ if (!aidl_caps) {
+ return false;
+ }
+ *aidl_caps = {};
+ aidl_caps->maxCacheSize = legacy_caps.max_scan_cache_size;
+ aidl_caps->maxBuckets = legacy_caps.max_scan_buckets;
+ aidl_caps->maxApCachePerScan = legacy_caps.max_ap_cache_per_scan;
+ aidl_caps->maxReportingThreshold = legacy_caps.max_scan_reporting_threshold;
+ return true;
+}
+
+legacy_hal::wifi_band convertAidlWifiBandToLegacy(WifiBand band) {
+ switch (band) {
+ case WifiBand::BAND_UNSPECIFIED:
+ return legacy_hal::WIFI_BAND_UNSPECIFIED;
+ case WifiBand::BAND_24GHZ:
+ return legacy_hal::WIFI_BAND_BG;
+ case WifiBand::BAND_5GHZ:
+ return legacy_hal::WIFI_BAND_A;
+ case WifiBand::BAND_5GHZ_DFS:
+ return legacy_hal::WIFI_BAND_A_DFS;
+ case WifiBand::BAND_5GHZ_WITH_DFS:
+ return legacy_hal::WIFI_BAND_A_WITH_DFS;
+ case WifiBand::BAND_24GHZ_5GHZ:
+ return legacy_hal::WIFI_BAND_ABG;
+ case WifiBand::BAND_24GHZ_5GHZ_WITH_DFS:
+ return legacy_hal::WIFI_BAND_ABG_WITH_DFS;
+ default:
+ CHECK(false);
+ return {};
+ };
+}
+
+bool convertAidlGscanParamsToLegacy(const StaBackgroundScanParameters& aidl_scan_params,
+ legacy_hal::wifi_scan_cmd_params* legacy_scan_params) {
+ if (!legacy_scan_params) {
+ return false;
+ }
+ *legacy_scan_params = {};
+ legacy_scan_params->base_period = aidl_scan_params.basePeriodInMs;
+ legacy_scan_params->max_ap_per_scan = aidl_scan_params.maxApPerScan;
+ legacy_scan_params->report_threshold_percent = aidl_scan_params.reportThresholdPercent;
+ legacy_scan_params->report_threshold_num_scans = aidl_scan_params.reportThresholdNumScans;
+ if (aidl_scan_params.buckets.size() > MAX_BUCKETS) {
+ return false;
+ }
+ legacy_scan_params->num_buckets = aidl_scan_params.buckets.size();
+ for (uint32_t bucket_idx = 0; bucket_idx < aidl_scan_params.buckets.size(); bucket_idx++) {
+ const StaBackgroundScanBucketParameters& aidl_bucket_spec =
+ aidl_scan_params.buckets[bucket_idx];
+ legacy_hal::wifi_scan_bucket_spec& legacy_bucket_spec =
+ legacy_scan_params->buckets[bucket_idx];
+ if (aidl_bucket_spec.bucketIdx >= MAX_BUCKETS) {
+ return false;
+ }
+ legacy_bucket_spec.bucket = aidl_bucket_spec.bucketIdx;
+ legacy_bucket_spec.band = convertAidlWifiBandToLegacy(aidl_bucket_spec.band);
+ legacy_bucket_spec.period = aidl_bucket_spec.periodInMs;
+ legacy_bucket_spec.max_period = aidl_bucket_spec.exponentialMaxPeriodInMs;
+ legacy_bucket_spec.base = aidl_bucket_spec.exponentialBase;
+ legacy_bucket_spec.step_count = aidl_bucket_spec.exponentialStepCount;
+ legacy_bucket_spec.report_events = 0;
+ using AidlFlag = StaBackgroundScanBucketEventReportSchemeMask;
+ for (const auto flag : {AidlFlag::EACH_SCAN, AidlFlag::FULL_RESULTS, AidlFlag::NO_BATCH}) {
+ if (static_cast<int32_t>(aidl_bucket_spec.eventReportScheme) &
+ static_cast<std::underlying_type<AidlFlag>::type>(flag)) {
+ legacy_bucket_spec.report_events |= convertAidlGscanReportEventFlagToLegacy(flag);
+ }
+ }
+ if (aidl_bucket_spec.frequencies.size() > MAX_CHANNELS) {
+ return false;
+ }
+ legacy_bucket_spec.num_channels = aidl_bucket_spec.frequencies.size();
+ for (uint32_t freq_idx = 0; freq_idx < aidl_bucket_spec.frequencies.size(); freq_idx++) {
+ legacy_bucket_spec.channels[freq_idx].channel = aidl_bucket_spec.frequencies[freq_idx];
+ }
+ }
+ return true;
+}
+
+bool convertLegacyIeToAidl(const legacy_hal::wifi_information_element& legacy_ie,
+ WifiInformationElement* aidl_ie) {
+ if (!aidl_ie) {
+ return false;
+ }
+ *aidl_ie = {};
+ aidl_ie->id = legacy_ie.id;
+ aidl_ie->data = std::vector<uint8_t>(legacy_ie.data, legacy_ie.data + legacy_ie.len);
+ return true;
+}
+
+bool convertLegacyIeBlobToAidl(const uint8_t* ie_blob, uint32_t ie_blob_len,
+ std::vector<WifiInformationElement>* aidl_ies) {
+ if (!ie_blob || !aidl_ies) {
+ return false;
+ }
+ *aidl_ies = {};
+ const uint8_t* ies_begin = ie_blob;
+ const uint8_t* ies_end = ie_blob + ie_blob_len;
+ const uint8_t* next_ie = ies_begin;
+ using wifi_ie = legacy_hal::wifi_information_element;
+ constexpr size_t kIeHeaderLen = sizeof(wifi_ie);
+ // Each IE should at least have the header (i.e |id| & |len| fields).
+ while (next_ie + kIeHeaderLen <= ies_end) {
+ const wifi_ie& legacy_ie = (*reinterpret_cast<const wifi_ie*>(next_ie));
+ uint32_t curr_ie_len = kIeHeaderLen + legacy_ie.len;
+ if (next_ie + curr_ie_len > ies_end) {
+ LOG(ERROR) << "Error parsing IE blob. Next IE: " << (void*)next_ie
+ << ", Curr IE len: " << curr_ie_len << ", IEs End: " << (void*)ies_end;
+ break;
+ }
+ WifiInformationElement aidl_ie;
+ if (!convertLegacyIeToAidl(legacy_ie, &aidl_ie)) {
+ LOG(ERROR) << "Error converting IE. Id: " << legacy_ie.id << ", len: " << legacy_ie.len;
+ break;
+ }
+ aidl_ies->push_back(std::move(aidl_ie));
+ next_ie += curr_ie_len;
+ }
+ // Check if the blob has been fully consumed.
+ if (next_ie != ies_end) {
+ LOG(ERROR) << "Failed to fully parse IE blob. Next IE: " << (void*)next_ie
+ << ", IEs End: " << (void*)ies_end;
+ }
+ return true;
+}
+
+bool convertLegacyGscanResultToAidl(const legacy_hal::wifi_scan_result& legacy_scan_result,
+ bool has_ie_data, StaScanResult* aidl_scan_result) {
+ if (!aidl_scan_result) {
+ return false;
+ }
+ *aidl_scan_result = {};
+ aidl_scan_result->timeStampInUs = legacy_scan_result.ts;
+ aidl_scan_result->ssid = std::vector<uint8_t>(
+ legacy_scan_result.ssid,
+ legacy_scan_result.ssid +
+ strnlen(legacy_scan_result.ssid, sizeof(legacy_scan_result.ssid) - 1));
+ aidl_scan_result->bssid = std::array<uint8_t, 6>();
+ std::copy(legacy_scan_result.bssid, legacy_scan_result.bssid + 6,
+ std::begin(aidl_scan_result->bssid));
+ aidl_scan_result->frequency = legacy_scan_result.channel;
+ aidl_scan_result->rssi = legacy_scan_result.rssi;
+ aidl_scan_result->beaconPeriodInMs = legacy_scan_result.beacon_period;
+ aidl_scan_result->capability = legacy_scan_result.capability;
+ if (has_ie_data) {
+ std::vector<WifiInformationElement> ies;
+ if (!convertLegacyIeBlobToAidl(reinterpret_cast<const uint8_t*>(legacy_scan_result.ie_data),
+ legacy_scan_result.ie_length, &ies)) {
+ return false;
+ }
+ aidl_scan_result->informationElements = std::move(ies);
+ }
+ return true;
+}
+
+bool convertLegacyCachedGscanResultsToAidl(
+ const legacy_hal::wifi_cached_scan_results& legacy_cached_scan_result,
+ StaScanData* aidl_scan_data) {
+ if (!aidl_scan_data) {
+ return false;
+ }
+ *aidl_scan_data = {};
+ int32_t flags = 0;
+ for (const auto flag : {legacy_hal::WIFI_SCAN_FLAG_INTERRUPTED}) {
+ if (legacy_cached_scan_result.flags & flag) {
+ flags |= static_cast<std::underlying_type<StaScanDataFlagMask>::type>(
+ convertLegacyGscanDataFlagToAidl(flag));
+ }
+ }
+ aidl_scan_data->flags = static_cast<StaScanDataFlagMask>(flags);
+ aidl_scan_data->bucketsScanned = legacy_cached_scan_result.buckets_scanned;
+
+ CHECK(legacy_cached_scan_result.num_results >= 0 &&
+ legacy_cached_scan_result.num_results <= MAX_AP_CACHE_PER_SCAN);
+ std::vector<StaScanResult> aidl_scan_results;
+ for (int32_t result_idx = 0; result_idx < legacy_cached_scan_result.num_results; result_idx++) {
+ StaScanResult aidl_scan_result;
+ if (!convertLegacyGscanResultToAidl(legacy_cached_scan_result.results[result_idx], false,
+ &aidl_scan_result)) {
+ return false;
+ }
+ aidl_scan_results.push_back(aidl_scan_result);
+ }
+ aidl_scan_data->results = std::move(aidl_scan_results);
+ return true;
+}
+
+bool convertLegacyVectorOfCachedGscanResultsToAidl(
+ const std::vector<legacy_hal::wifi_cached_scan_results>& legacy_cached_scan_results,
+ std::vector<StaScanData>* aidl_scan_datas) {
+ if (!aidl_scan_datas) {
+ return false;
+ }
+ *aidl_scan_datas = {};
+ for (const auto& legacy_cached_scan_result : legacy_cached_scan_results) {
+ StaScanData aidl_scan_data;
+ if (!convertLegacyCachedGscanResultsToAidl(legacy_cached_scan_result, &aidl_scan_data)) {
+ return false;
+ }
+ aidl_scan_datas->push_back(aidl_scan_data);
+ }
+ return true;
+}
+
+WifiDebugTxPacketFate convertLegacyDebugTxPacketFateToAidl(legacy_hal::wifi_tx_packet_fate fate) {
+ switch (fate) {
+ case legacy_hal::TX_PKT_FATE_ACKED:
+ return WifiDebugTxPacketFate::ACKED;
+ case legacy_hal::TX_PKT_FATE_SENT:
+ return WifiDebugTxPacketFate::SENT;
+ case legacy_hal::TX_PKT_FATE_FW_QUEUED:
+ return WifiDebugTxPacketFate::FW_QUEUED;
+ case legacy_hal::TX_PKT_FATE_FW_DROP_INVALID:
+ return WifiDebugTxPacketFate::FW_DROP_INVALID;
+ case legacy_hal::TX_PKT_FATE_FW_DROP_NOBUFS:
+ return WifiDebugTxPacketFate::FW_DROP_NOBUFS;
+ case legacy_hal::TX_PKT_FATE_FW_DROP_OTHER:
+ return WifiDebugTxPacketFate::FW_DROP_OTHER;
+ case legacy_hal::TX_PKT_FATE_DRV_QUEUED:
+ return WifiDebugTxPacketFate::DRV_QUEUED;
+ case legacy_hal::TX_PKT_FATE_DRV_DROP_INVALID:
+ return WifiDebugTxPacketFate::DRV_DROP_INVALID;
+ case legacy_hal::TX_PKT_FATE_DRV_DROP_NOBUFS:
+ return WifiDebugTxPacketFate::DRV_DROP_NOBUFS;
+ case legacy_hal::TX_PKT_FATE_DRV_DROP_OTHER:
+ return WifiDebugTxPacketFate::DRV_DROP_OTHER;
+ };
+ CHECK(false) << "Unknown legacy fate type: " << fate;
+}
+
+WifiDebugRxPacketFate convertLegacyDebugRxPacketFateToAidl(legacy_hal::wifi_rx_packet_fate fate) {
+ switch (fate) {
+ case legacy_hal::RX_PKT_FATE_SUCCESS:
+ return WifiDebugRxPacketFate::SUCCESS;
+ case legacy_hal::RX_PKT_FATE_FW_QUEUED:
+ return WifiDebugRxPacketFate::FW_QUEUED;
+ case legacy_hal::RX_PKT_FATE_FW_DROP_FILTER:
+ return WifiDebugRxPacketFate::FW_DROP_FILTER;
+ case legacy_hal::RX_PKT_FATE_FW_DROP_INVALID:
+ return WifiDebugRxPacketFate::FW_DROP_INVALID;
+ case legacy_hal::RX_PKT_FATE_FW_DROP_NOBUFS:
+ return WifiDebugRxPacketFate::FW_DROP_NOBUFS;
+ case legacy_hal::RX_PKT_FATE_FW_DROP_OTHER:
+ return WifiDebugRxPacketFate::FW_DROP_OTHER;
+ case legacy_hal::RX_PKT_FATE_DRV_QUEUED:
+ return WifiDebugRxPacketFate::DRV_QUEUED;
+ case legacy_hal::RX_PKT_FATE_DRV_DROP_FILTER:
+ return WifiDebugRxPacketFate::DRV_DROP_FILTER;
+ case legacy_hal::RX_PKT_FATE_DRV_DROP_INVALID:
+ return WifiDebugRxPacketFate::DRV_DROP_INVALID;
+ case legacy_hal::RX_PKT_FATE_DRV_DROP_NOBUFS:
+ return WifiDebugRxPacketFate::DRV_DROP_NOBUFS;
+ case legacy_hal::RX_PKT_FATE_DRV_DROP_OTHER:
+ return WifiDebugRxPacketFate::DRV_DROP_OTHER;
+ };
+ CHECK(false) << "Unknown legacy fate type: " << fate;
+}
+
+WifiDebugPacketFateFrameType convertLegacyDebugPacketFateFrameTypeToAidl(
+ legacy_hal::frame_type type) {
+ switch (type) {
+ case legacy_hal::FRAME_TYPE_UNKNOWN:
+ return WifiDebugPacketFateFrameType::UNKNOWN;
+ case legacy_hal::FRAME_TYPE_ETHERNET_II:
+ return WifiDebugPacketFateFrameType::ETHERNET_II;
+ case legacy_hal::FRAME_TYPE_80211_MGMT:
+ return WifiDebugPacketFateFrameType::MGMT_80211;
+ };
+ CHECK(false) << "Unknown legacy frame type: " << type;
+}
+
+bool convertLegacyDebugPacketFateFrameToAidl(const legacy_hal::frame_info& legacy_frame,
+ WifiDebugPacketFateFrameInfo* aidl_frame) {
+ if (!aidl_frame) {
+ return false;
+ }
+ *aidl_frame = {};
+ aidl_frame->frameType = convertLegacyDebugPacketFateFrameTypeToAidl(legacy_frame.payload_type);
+ aidl_frame->frameLen = legacy_frame.frame_len;
+ aidl_frame->driverTimestampUsec = legacy_frame.driver_timestamp_usec;
+ aidl_frame->firmwareTimestampUsec = legacy_frame.firmware_timestamp_usec;
+ const uint8_t* frame_begin =
+ reinterpret_cast<const uint8_t*>(legacy_frame.frame_content.ethernet_ii_bytes);
+ aidl_frame->frameContent =
+ std::vector<uint8_t>(frame_begin, frame_begin + legacy_frame.frame_len);
+ return true;
+}
+
+bool convertLegacyDebugTxPacketFateToAidl(const legacy_hal::wifi_tx_report& legacy_fate,
+ WifiDebugTxPacketFateReport* aidl_fate) {
+ if (!aidl_fate) {
+ return false;
+ }
+ *aidl_fate = {};
+ aidl_fate->fate = convertLegacyDebugTxPacketFateToAidl(legacy_fate.fate);
+ return convertLegacyDebugPacketFateFrameToAidl(legacy_fate.frame_inf, &aidl_fate->frameInfo);
+}
+
+bool convertLegacyVectorOfDebugTxPacketFateToAidl(
+ const std::vector<legacy_hal::wifi_tx_report>& legacy_fates,
+ std::vector<WifiDebugTxPacketFateReport>* aidl_fates) {
+ if (!aidl_fates) {
+ return false;
+ }
+ *aidl_fates = {};
+ for (const auto& legacy_fate : legacy_fates) {
+ WifiDebugTxPacketFateReport aidl_fate;
+ if (!convertLegacyDebugTxPacketFateToAidl(legacy_fate, &aidl_fate)) {
+ return false;
+ }
+ aidl_fates->push_back(aidl_fate);
+ }
+ return true;
+}
+
+bool convertLegacyDebugRxPacketFateToAidl(const legacy_hal::wifi_rx_report& legacy_fate,
+ WifiDebugRxPacketFateReport* aidl_fate) {
+ if (!aidl_fate) {
+ return false;
+ }
+ *aidl_fate = {};
+ aidl_fate->fate = convertLegacyDebugRxPacketFateToAidl(legacy_fate.fate);
+ return convertLegacyDebugPacketFateFrameToAidl(legacy_fate.frame_inf, &aidl_fate->frameInfo);
+}
+
+bool convertLegacyVectorOfDebugRxPacketFateToAidl(
+ const std::vector<legacy_hal::wifi_rx_report>& legacy_fates,
+ std::vector<WifiDebugRxPacketFateReport>* aidl_fates) {
+ if (!aidl_fates) {
+ return false;
+ }
+ *aidl_fates = {};
+ for (const auto& legacy_fate : legacy_fates) {
+ WifiDebugRxPacketFateReport aidl_fate;
+ if (!convertLegacyDebugRxPacketFateToAidl(legacy_fate, &aidl_fate)) {
+ return false;
+ }
+ aidl_fates->push_back(aidl_fate);
+ }
+ return true;
+}
+
+bool convertLegacyLinkLayerRadioStatsToAidl(
+ const legacy_hal::LinkLayerRadioStats& legacy_radio_stat,
+ StaLinkLayerRadioStats* aidl_radio_stat) {
+ if (!aidl_radio_stat) {
+ return false;
+ }
+ *aidl_radio_stat = {};
+
+ aidl_radio_stat->radioId = legacy_radio_stat.stats.radio;
+ aidl_radio_stat->onTimeInMs = legacy_radio_stat.stats.on_time;
+ aidl_radio_stat->txTimeInMs = legacy_radio_stat.stats.tx_time;
+ aidl_radio_stat->rxTimeInMs = legacy_radio_stat.stats.rx_time;
+ aidl_radio_stat->onTimeInMsForScan = legacy_radio_stat.stats.on_time_scan;
+ aidl_radio_stat->txTimeInMsPerLevel = uintToIntVec(legacy_radio_stat.tx_time_per_levels);
+ aidl_radio_stat->onTimeInMsForNanScan = legacy_radio_stat.stats.on_time_nbd;
+ aidl_radio_stat->onTimeInMsForBgScan = legacy_radio_stat.stats.on_time_gscan;
+ aidl_radio_stat->onTimeInMsForRoamScan = legacy_radio_stat.stats.on_time_roam_scan;
+ aidl_radio_stat->onTimeInMsForPnoScan = legacy_radio_stat.stats.on_time_pno_scan;
+ aidl_radio_stat->onTimeInMsForHs20Scan = legacy_radio_stat.stats.on_time_hs20;
+
+ std::vector<WifiChannelStats> aidl_channel_stats;
+
+ for (const auto& channel_stat : legacy_radio_stat.channel_stats) {
+ WifiChannelStats aidl_channel_stat;
+ aidl_channel_stat.onTimeInMs = channel_stat.on_time;
+ aidl_channel_stat.ccaBusyTimeInMs = channel_stat.cca_busy_time;
+ aidl_channel_stat.channel.width = WifiChannelWidthInMhz::WIDTH_20;
+ aidl_channel_stat.channel.centerFreq = channel_stat.channel.center_freq;
+ aidl_channel_stat.channel.centerFreq0 = channel_stat.channel.center_freq0;
+ aidl_channel_stat.channel.centerFreq1 = channel_stat.channel.center_freq1;
+ aidl_channel_stats.push_back(aidl_channel_stat);
+ }
+
+ aidl_radio_stat->channelStats = aidl_channel_stats;
+
+ return true;
+}
+
+bool convertLegacyLinkLayerStatsToAidl(const legacy_hal::LinkLayerStats& legacy_stats,
+ StaLinkLayerStats* aidl_stats) {
+ if (!aidl_stats) {
+ return false;
+ }
+ *aidl_stats = {};
+ // iface legacy_stats conversion.
+ aidl_stats->iface.beaconRx = legacy_stats.iface.beacon_rx;
+ aidl_stats->iface.avgRssiMgmt = legacy_stats.iface.rssi_mgmt;
+ aidl_stats->iface.wmeBePktStats.rxMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].rx_mpdu;
+ aidl_stats->iface.wmeBePktStats.txMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].tx_mpdu;
+ aidl_stats->iface.wmeBePktStats.lostMpdu =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].mpdu_lost;
+ aidl_stats->iface.wmeBePktStats.retries = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].retries;
+ aidl_stats->iface.wmeBeContentionTimeStats.contentionTimeMinInUsec =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_min;
+ aidl_stats->iface.wmeBeContentionTimeStats.contentionTimeMaxInUsec =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_max;
+ aidl_stats->iface.wmeBeContentionTimeStats.contentionTimeAvgInUsec =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_avg;
+ aidl_stats->iface.wmeBeContentionTimeStats.contentionNumSamples =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_num_samples;
+ aidl_stats->iface.wmeBkPktStats.rxMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].rx_mpdu;
+ aidl_stats->iface.wmeBkPktStats.txMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].tx_mpdu;
+ aidl_stats->iface.wmeBkPktStats.lostMpdu =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].mpdu_lost;
+ aidl_stats->iface.wmeBkPktStats.retries = legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].retries;
+ aidl_stats->iface.wmeBkContentionTimeStats.contentionTimeMinInUsec =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_min;
+ aidl_stats->iface.wmeBkContentionTimeStats.contentionTimeMaxInUsec =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_max;
+ aidl_stats->iface.wmeBkContentionTimeStats.contentionTimeAvgInUsec =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_avg;
+ aidl_stats->iface.wmeBkContentionTimeStats.contentionNumSamples =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_num_samples;
+ aidl_stats->iface.wmeViPktStats.rxMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].rx_mpdu;
+ aidl_stats->iface.wmeViPktStats.txMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].tx_mpdu;
+ aidl_stats->iface.wmeViPktStats.lostMpdu =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].mpdu_lost;
+ aidl_stats->iface.wmeViPktStats.retries = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].retries;
+ aidl_stats->iface.wmeViContentionTimeStats.contentionTimeMinInUsec =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_min;
+ aidl_stats->iface.wmeViContentionTimeStats.contentionTimeMaxInUsec =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_max;
+ aidl_stats->iface.wmeViContentionTimeStats.contentionTimeAvgInUsec =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_avg;
+ aidl_stats->iface.wmeViContentionTimeStats.contentionNumSamples =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_num_samples;
+ aidl_stats->iface.wmeVoPktStats.rxMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].rx_mpdu;
+ aidl_stats->iface.wmeVoPktStats.txMpdu = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].tx_mpdu;
+ aidl_stats->iface.wmeVoPktStats.lostMpdu =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].mpdu_lost;
+ aidl_stats->iface.wmeVoPktStats.retries = legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].retries;
+ aidl_stats->iface.wmeVoContentionTimeStats.contentionTimeMinInUsec =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_min;
+ aidl_stats->iface.wmeVoContentionTimeStats.contentionTimeMaxInUsec =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_max;
+ aidl_stats->iface.wmeVoContentionTimeStats.contentionTimeAvgInUsec =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_avg;
+ aidl_stats->iface.wmeVoContentionTimeStats.contentionNumSamples =
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_num_samples;
+ aidl_stats->iface.timeSliceDutyCycleInPercent =
+ legacy_stats.iface.info.time_slicing_duty_cycle_percent;
+ // peer info legacy_stats conversion.
+ std::vector<StaPeerInfo> aidl_peers_info_stats;
+ for (const auto& legacy_peer_info_stats : legacy_stats.peers) {
+ StaPeerInfo aidl_peer_info_stats;
+ if (!convertLegacyPeerInfoStatsToAidl(legacy_peer_info_stats, &aidl_peer_info_stats)) {
+ return false;
+ }
+ aidl_peers_info_stats.push_back(aidl_peer_info_stats);
+ }
+ aidl_stats->iface.peers = aidl_peers_info_stats;
+ // radio legacy_stats conversion.
+ std::vector<StaLinkLayerRadioStats> aidl_radios_stats;
+ for (const auto& legacy_radio_stats : legacy_stats.radios) {
+ StaLinkLayerRadioStats aidl_radio_stats;
+ if (!convertLegacyLinkLayerRadioStatsToAidl(legacy_radio_stats, &aidl_radio_stats)) {
+ return false;
+ }
+ aidl_radios_stats.push_back(aidl_radio_stats);
+ }
+ aidl_stats->radios = aidl_radios_stats;
+ aidl_stats->timeStampInMs = ::android::uptimeMillis();
+ return true;
+}
+
+bool convertLegacyPeerInfoStatsToAidl(const legacy_hal::WifiPeerInfo& legacy_peer_info_stats,
+ StaPeerInfo* aidl_peer_info_stats) {
+ if (!aidl_peer_info_stats) {
+ return false;
+ }
+ *aidl_peer_info_stats = {};
+ aidl_peer_info_stats->staCount = legacy_peer_info_stats.peer_info.bssload.sta_count;
+ aidl_peer_info_stats->chanUtil = legacy_peer_info_stats.peer_info.bssload.chan_util;
+
+ std::vector<StaRateStat> aidlRateStats;
+ for (const auto& legacy_rate_stats : legacy_peer_info_stats.rate_stats) {
+ StaRateStat rateStat;
+ if (!convertLegacyWifiRateInfoToAidl(legacy_rate_stats.rate, &rateStat.rateInfo)) {
+ return false;
+ }
+ rateStat.txMpdu = legacy_rate_stats.tx_mpdu;
+ rateStat.rxMpdu = legacy_rate_stats.rx_mpdu;
+ rateStat.mpduLost = legacy_rate_stats.mpdu_lost;
+ rateStat.retries = legacy_rate_stats.retries;
+ aidlRateStats.push_back(rateStat);
+ }
+ aidl_peer_info_stats->rateStats = aidlRateStats;
+ return true;
+}
+
+bool convertLegacyRoamingCapabilitiesToAidl(
+ const legacy_hal::wifi_roaming_capabilities& legacy_caps,
+ StaRoamingCapabilities* aidl_caps) {
+ if (!aidl_caps) {
+ return false;
+ }
+ *aidl_caps = {};
+ aidl_caps->maxBlocklistSize = legacy_caps.max_blacklist_size;
+ aidl_caps->maxAllowlistSize = legacy_caps.max_whitelist_size;
+ return true;
+}
+
+bool convertAidlRoamingConfigToLegacy(const StaRoamingConfig& aidl_config,
+ legacy_hal::wifi_roaming_config* legacy_config) {
+ if (!legacy_config) {
+ return false;
+ }
+ *legacy_config = {};
+ if (aidl_config.bssidBlocklist.size() > MAX_BLACKLIST_BSSID ||
+ aidl_config.ssidAllowlist.size() > MAX_WHITELIST_SSID) {
+ return false;
+ }
+ legacy_config->num_blacklist_bssid = aidl_config.bssidBlocklist.size();
+ uint32_t i = 0;
+ for (const auto& bssid : aidl_config.bssidBlocklist) {
+ CHECK(bssid.data.size() == sizeof(legacy_hal::mac_addr));
+ memcpy(legacy_config->blacklist_bssid[i++], bssid.data.data(), bssid.data.size());
+ }
+ legacy_config->num_whitelist_ssid = aidl_config.ssidAllowlist.size();
+ i = 0;
+ for (const auto& ssid : aidl_config.ssidAllowlist) {
+ CHECK(ssid.data.size() <= sizeof(legacy_hal::ssid_t::ssid_str));
+ legacy_config->whitelist_ssid[i].length = ssid.data.size();
+ memcpy(legacy_config->whitelist_ssid[i].ssid_str, ssid.data.data(), ssid.data.size());
+ i++;
+ }
+ return true;
+}
+
+legacy_hal::fw_roaming_state_t convertAidlRoamingStateToLegacy(StaRoamingState state) {
+ switch (state) {
+ case StaRoamingState::ENABLED:
+ return legacy_hal::ROAMING_ENABLE;
+ case StaRoamingState::DISABLED:
+ return legacy_hal::ROAMING_DISABLE;
+ };
+ CHECK(false);
+}
+
+legacy_hal::NanMatchAlg convertAidlNanMatchAlgToLegacy(NanMatchAlg type) {
+ switch (type) {
+ case NanMatchAlg::MATCH_ONCE:
+ return legacy_hal::NAN_MATCH_ALG_MATCH_ONCE;
+ case NanMatchAlg::MATCH_CONTINUOUS:
+ return legacy_hal::NAN_MATCH_ALG_MATCH_CONTINUOUS;
+ case NanMatchAlg::MATCH_NEVER:
+ return legacy_hal::NAN_MATCH_ALG_MATCH_NEVER;
+ }
+ CHECK(false);
+}
+
+legacy_hal::NanPublishType convertAidlNanPublishTypeToLegacy(NanPublishType type) {
+ switch (type) {
+ case NanPublishType::UNSOLICITED:
+ return legacy_hal::NAN_PUBLISH_TYPE_UNSOLICITED;
+ case NanPublishType::SOLICITED:
+ return legacy_hal::NAN_PUBLISH_TYPE_SOLICITED;
+ case NanPublishType::UNSOLICITED_SOLICITED:
+ return legacy_hal::NAN_PUBLISH_TYPE_UNSOLICITED_SOLICITED;
+ }
+ CHECK(false);
+}
+
+legacy_hal::NanTxType convertAidlNanTxTypeToLegacy(NanTxType type) {
+ switch (type) {
+ case NanTxType::BROADCAST:
+ return legacy_hal::NAN_TX_TYPE_BROADCAST;
+ case NanTxType::UNICAST:
+ return legacy_hal::NAN_TX_TYPE_UNICAST;
+ }
+ CHECK(false);
+}
+
+legacy_hal::NanSubscribeType convertAidlNanSubscribeTypeToLegacy(NanSubscribeType type) {
+ switch (type) {
+ case NanSubscribeType::PASSIVE:
+ return legacy_hal::NAN_SUBSCRIBE_TYPE_PASSIVE;
+ case NanSubscribeType::ACTIVE:
+ return legacy_hal::NAN_SUBSCRIBE_TYPE_ACTIVE;
+ }
+ CHECK(false);
+}
+
+legacy_hal::NanSRFType convertAidlNanSrfTypeToLegacy(NanSrfType type) {
+ switch (type) {
+ case NanSrfType::BLOOM_FILTER:
+ return legacy_hal::NAN_SRF_ATTR_BLOOM_FILTER;
+ case NanSrfType::PARTIAL_MAC_ADDR:
+ return legacy_hal::NAN_SRF_ATTR_PARTIAL_MAC_ADDR;
+ }
+ CHECK(false);
+}
+
+legacy_hal::NanDataPathChannelCfg convertAidlNanDataPathChannelCfgToLegacy(
+ NanDataPathChannelCfg type) {
+ switch (type) {
+ case NanDataPathChannelCfg::CHANNEL_NOT_REQUESTED:
+ return legacy_hal::NAN_DP_CHANNEL_NOT_REQUESTED;
+ case NanDataPathChannelCfg::REQUEST_CHANNEL_SETUP:
+ return legacy_hal::NAN_DP_REQUEST_CHANNEL_SETUP;
+ case NanDataPathChannelCfg::FORCE_CHANNEL_SETUP:
+ return legacy_hal::NAN_DP_FORCE_CHANNEL_SETUP;
+ }
+ CHECK(false);
+}
+
+NanStatusCode convertLegacyNanStatusTypeToAidl(legacy_hal::NanStatusType type) {
+ switch (type) {
+ case legacy_hal::NAN_STATUS_SUCCESS:
+ return NanStatusCode::SUCCESS;
+ case legacy_hal::NAN_STATUS_INTERNAL_FAILURE:
+ return NanStatusCode::INTERNAL_FAILURE;
+ case legacy_hal::NAN_STATUS_PROTOCOL_FAILURE:
+ return NanStatusCode::PROTOCOL_FAILURE;
+ case legacy_hal::NAN_STATUS_INVALID_PUBLISH_SUBSCRIBE_ID:
+ return NanStatusCode::INVALID_SESSION_ID;
+ case legacy_hal::NAN_STATUS_NO_RESOURCE_AVAILABLE:
+ return NanStatusCode::NO_RESOURCES_AVAILABLE;
+ case legacy_hal::NAN_STATUS_INVALID_PARAM:
+ return NanStatusCode::INVALID_ARGS;
+ case legacy_hal::NAN_STATUS_INVALID_REQUESTOR_INSTANCE_ID:
+ return NanStatusCode::INVALID_PEER_ID;
+ case legacy_hal::NAN_STATUS_INVALID_NDP_ID:
+ return NanStatusCode::INVALID_NDP_ID;
+ case legacy_hal::NAN_STATUS_NAN_NOT_ALLOWED:
+ return NanStatusCode::NAN_NOT_ALLOWED;
+ case legacy_hal::NAN_STATUS_NO_OTA_ACK:
+ return NanStatusCode::NO_OTA_ACK;
+ case legacy_hal::NAN_STATUS_ALREADY_ENABLED:
+ return NanStatusCode::ALREADY_ENABLED;
+ case legacy_hal::NAN_STATUS_FOLLOWUP_QUEUE_FULL:
+ return NanStatusCode::FOLLOWUP_TX_QUEUE_FULL;
+ case legacy_hal::NAN_STATUS_UNSUPPORTED_CONCURRENCY_NAN_DISABLED:
+ return NanStatusCode::UNSUPPORTED_CONCURRENCY_NAN_DISABLED;
+ }
+ CHECK(false);
+}
+
+void convertToNanStatus(legacy_hal::NanStatusType type, const char* str, size_t max_len,
+ NanStatus* nanStatus) {
+ nanStatus->status = convertLegacyNanStatusTypeToAidl(type);
+ nanStatus->description = safeConvertChar(str, max_len);
+}
+
+bool convertAidlNanEnableRequestToLegacy(const NanEnableRequest& aidl_request1,
+ const NanConfigRequestSupplemental& aidl_request2,
+ legacy_hal::NanEnableRequest* legacy_request) {
+ if (!legacy_request) {
+ LOG(ERROR) << "convertAidlNanEnableRequestToLegacy: null legacy_request";
+ return false;
+ }
+ *legacy_request = {};
+
+ legacy_request->config_2dot4g_support = 1;
+ legacy_request->support_2dot4g_val =
+ aidl_request1.operateInBand[(size_t)NanBandIndex::NAN_BAND_24GHZ];
+ legacy_request->config_support_5g = 1;
+ legacy_request->support_5g_val =
+ aidl_request1.operateInBand[(size_t)NanBandIndex::NAN_BAND_5GHZ];
+ legacy_request->config_hop_count_limit = 1;
+ legacy_request->hop_count_limit_val = aidl_request1.hopCountMax;
+ legacy_request->master_pref = aidl_request1.configParams.masterPref;
+ legacy_request->discovery_indication_cfg = 0;
+ legacy_request->discovery_indication_cfg |=
+ aidl_request1.configParams.disableDiscoveryAddressChangeIndication ? 0x1 : 0x0;
+ legacy_request->discovery_indication_cfg |=
+ aidl_request1.configParams.disableStartedClusterIndication ? 0x2 : 0x0;
+ legacy_request->discovery_indication_cfg |=
+ aidl_request1.configParams.disableJoinedClusterIndication ? 0x4 : 0x0;
+ legacy_request->config_sid_beacon = 1;
+ if (aidl_request1.configParams.numberOfPublishServiceIdsInBeacon < 0) {
+ LOG(ERROR) << "convertAidlNanEnableRequestToLegacy: "
+ "numberOfPublishServiceIdsInBeacon < 0";
+ return false;
+ }
+ legacy_request->sid_beacon_val =
+ (aidl_request1.configParams.includePublishServiceIdsInBeacon ? 0x1 : 0x0) |
+ (aidl_request1.configParams.numberOfPublishServiceIdsInBeacon << 1);
+ legacy_request->config_subscribe_sid_beacon = 1;
+ if (aidl_request1.configParams.numberOfSubscribeServiceIdsInBeacon < 0) {
+ LOG(ERROR) << "convertAidlNanEnableRequestToLegacy: "
+ "numberOfSubscribeServiceIdsInBeacon < 0";
+ return false;
+ }
+ legacy_request->subscribe_sid_beacon_val =
+ (aidl_request1.configParams.includeSubscribeServiceIdsInBeacon ? 0x1 : 0x0) |
+ (aidl_request1.configParams.numberOfSubscribeServiceIdsInBeacon << 1);
+ legacy_request->config_rssi_window_size = 1;
+ legacy_request->rssi_window_size_val = aidl_request1.configParams.rssiWindowSize;
+ legacy_request->config_disc_mac_addr_randomization = 1;
+ legacy_request->disc_mac_addr_rand_interval_sec =
+ aidl_request1.configParams.macAddressRandomizationIntervalSec;
+ legacy_request->config_2dot4g_rssi_close = 1;
+ if (aidl_request1.configParams.bandSpecificConfig.size() != 3) {
+ LOG(ERROR) << "convertAidlNanEnableRequestToLegacy: "
+ "bandSpecificConfig.size() != 3";
+ return false;
+ }
+ legacy_request->rssi_close_2dot4g_val =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_24GHZ]
+ .rssiClose;
+ legacy_request->config_2dot4g_rssi_middle = 1;
+ legacy_request->rssi_middle_2dot4g_val =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_24GHZ]
+ .rssiMiddle;
+ legacy_request->config_2dot4g_rssi_proximity = 1;
+ legacy_request->rssi_proximity_2dot4g_val =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_24GHZ]
+ .rssiCloseProximity;
+ legacy_request->config_scan_params = 1;
+ legacy_request->scan_params_val.dwell_time[legacy_hal::NAN_CHANNEL_24G_BAND] =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_24GHZ]
+ .dwellTimeMs;
+ legacy_request->scan_params_val.scan_period[legacy_hal::NAN_CHANNEL_24G_BAND] =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_24GHZ]
+ .scanPeriodSec;
+ legacy_request->config_dw.config_2dot4g_dw_band =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_24GHZ]
+ .validDiscoveryWindowIntervalVal;
+ legacy_request->config_dw.dw_2dot4g_interval_val =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_24GHZ]
+ .discoveryWindowIntervalVal;
+ legacy_request->config_5g_rssi_close = 1;
+ legacy_request->rssi_close_5g_val =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ]
+ .rssiClose;
+ legacy_request->config_5g_rssi_middle = 1;
+ legacy_request->rssi_middle_5g_val =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ]
+ .rssiMiddle;
+ legacy_request->config_5g_rssi_close_proximity = 1;
+ legacy_request->rssi_close_proximity_5g_val =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ]
+ .rssiCloseProximity;
+ legacy_request->scan_params_val.dwell_time[legacy_hal::NAN_CHANNEL_5G_BAND_LOW] =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ]
+ .dwellTimeMs;
+ legacy_request->scan_params_val.scan_period[legacy_hal::NAN_CHANNEL_5G_BAND_LOW] =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ]
+ .scanPeriodSec;
+ legacy_request->scan_params_val.dwell_time[legacy_hal::NAN_CHANNEL_5G_BAND_HIGH] =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ]
+ .dwellTimeMs;
+ legacy_request->scan_params_val.scan_period[legacy_hal::NAN_CHANNEL_5G_BAND_HIGH] =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ]
+ .scanPeriodSec;
+ legacy_request->config_dw.config_5g_dw_band =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ]
+ .validDiscoveryWindowIntervalVal;
+ legacy_request->config_dw.dw_5g_interval_val =
+ aidl_request1.configParams.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ]
+ .discoveryWindowIntervalVal;
+ if (aidl_request1.debugConfigs.validClusterIdVals) {
+ legacy_request->cluster_low = aidl_request1.debugConfigs.clusterIdBottomRangeVal;
+ legacy_request->cluster_high = aidl_request1.debugConfigs.clusterIdTopRangeVal;
+ } else { // need 'else' since not configurable in legacy HAL
+ legacy_request->cluster_low = 0x0000;
+ legacy_request->cluster_high = 0xFFFF;
+ }
+ legacy_request->config_intf_addr = aidl_request1.debugConfigs.validIntfAddrVal;
+ memcpy(legacy_request->intf_addr_val, aidl_request1.debugConfigs.intfAddrVal.data(), 6);
+ legacy_request->config_oui = aidl_request1.debugConfigs.validOuiVal;
+ legacy_request->oui_val = aidl_request1.debugConfigs.ouiVal;
+ legacy_request->config_random_factor_force =
+ aidl_request1.debugConfigs.validRandomFactorForceVal;
+ legacy_request->random_factor_force_val = aidl_request1.debugConfigs.randomFactorForceVal;
+ legacy_request->config_hop_count_force = aidl_request1.debugConfigs.validHopCountForceVal;
+ legacy_request->hop_count_force_val = aidl_request1.debugConfigs.hopCountForceVal;
+ legacy_request->config_24g_channel = aidl_request1.debugConfigs.validDiscoveryChannelVal;
+ legacy_request->channel_24g_val =
+ aidl_request1.debugConfigs.discoveryChannelMhzVal[(size_t)NanBandIndex::NAN_BAND_24GHZ];
+ legacy_request->config_5g_channel = aidl_request1.debugConfigs.validDiscoveryChannelVal;
+ legacy_request->channel_5g_val =
+ aidl_request1.debugConfigs.discoveryChannelMhzVal[(size_t)NanBandIndex::NAN_BAND_5GHZ];
+ legacy_request->config_2dot4g_beacons = aidl_request1.debugConfigs.validUseBeaconsInBandVal;
+ legacy_request->beacon_2dot4g_val =
+ aidl_request1.debugConfigs.useBeaconsInBandVal[(size_t)NanBandIndex::NAN_BAND_24GHZ];
+ legacy_request->config_5g_beacons = aidl_request1.debugConfigs.validUseBeaconsInBandVal;
+ legacy_request->beacon_5g_val =
+ aidl_request1.debugConfigs.useBeaconsInBandVal[(size_t)NanBandIndex::NAN_BAND_5GHZ];
+ legacy_request->config_2dot4g_sdf = aidl_request1.debugConfigs.validUseSdfInBandVal;
+ legacy_request->sdf_2dot4g_val =
+ aidl_request1.debugConfigs.useSdfInBandVal[(size_t)NanBandIndex::NAN_BAND_24GHZ];
+ legacy_request->config_5g_sdf = aidl_request1.debugConfigs.validUseSdfInBandVal;
+ legacy_request->sdf_5g_val =
+ aidl_request1.debugConfigs.useSdfInBandVal[(size_t)NanBandIndex::NAN_BAND_5GHZ];
+
+ legacy_request->config_discovery_beacon_int = 1;
+ legacy_request->discovery_beacon_interval = aidl_request2.discoveryBeaconIntervalMs;
+ legacy_request->config_nss = 1;
+ legacy_request->nss = aidl_request2.numberOfSpatialStreamsInDiscovery;
+ legacy_request->config_dw_early_termination = 1;
+ legacy_request->enable_dw_termination = aidl_request2.enableDiscoveryWindowEarlyTermination;
+ legacy_request->config_enable_ranging = 1;
+ legacy_request->enable_ranging = aidl_request2.enableRanging;
+
+ legacy_request->config_enable_instant_mode = 1;
+ legacy_request->enable_instant_mode = aidl_request2.enableInstantCommunicationMode;
+ legacy_request->config_instant_mode_channel = 1;
+ legacy_request->instant_mode_channel = aidl_request2.instantModeChannel;
+
+ return true;
+}
+
+bool convertAidlNanConfigRequestToLegacy(const NanConfigRequest& aidl_request1,
+ const NanConfigRequestSupplemental& aidl_request2,
+ legacy_hal::NanConfigRequest* legacy_request) {
+ if (!legacy_request) {
+ LOG(ERROR) << "convertAidlNanConfigRequestToLegacy: null legacy_request";
+ return false;
+ }
+ *legacy_request = {};
+
+ legacy_request->master_pref = aidl_request1.masterPref;
+ legacy_request->discovery_indication_cfg = 0;
+ legacy_request->discovery_indication_cfg |=
+ aidl_request1.disableDiscoveryAddressChangeIndication ? 0x1 : 0x0;
+ legacy_request->discovery_indication_cfg |=
+ aidl_request1.disableStartedClusterIndication ? 0x2 : 0x0;
+ legacy_request->discovery_indication_cfg |=
+ aidl_request1.disableJoinedClusterIndication ? 0x4 : 0x0;
+ legacy_request->config_sid_beacon = 1;
+ if (aidl_request1.numberOfPublishServiceIdsInBeacon < 0) {
+ LOG(ERROR) << "convertAidlNanConfigRequestToLegacy: "
+ "numberOfPublishServiceIdsInBeacon < 0";
+ return false;
+ }
+ legacy_request->sid_beacon = (aidl_request1.includePublishServiceIdsInBeacon ? 0x1 : 0x0) |
+ (aidl_request1.numberOfPublishServiceIdsInBeacon << 1);
+ legacy_request->config_subscribe_sid_beacon = 1;
+ if (aidl_request1.numberOfSubscribeServiceIdsInBeacon < 0) {
+ LOG(ERROR) << "convertAidlNanConfigRequestToLegacy: "
+ "numberOfSubscribeServiceIdsInBeacon < 0";
+ return false;
+ }
+ legacy_request->subscribe_sid_beacon_val =
+ (aidl_request1.includeSubscribeServiceIdsInBeacon ? 0x1 : 0x0) |
+ (aidl_request1.numberOfSubscribeServiceIdsInBeacon << 1);
+ legacy_request->config_rssi_window_size = 1;
+ legacy_request->rssi_window_size_val = aidl_request1.rssiWindowSize;
+ legacy_request->config_disc_mac_addr_randomization = 1;
+ legacy_request->disc_mac_addr_rand_interval_sec =
+ aidl_request1.macAddressRandomizationIntervalSec;
+
+ legacy_request->config_scan_params = 1;
+ legacy_request->scan_params_val.dwell_time[legacy_hal::NAN_CHANNEL_24G_BAND] =
+ aidl_request1.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_24GHZ].dwellTimeMs;
+ legacy_request->scan_params_val.scan_period[legacy_hal::NAN_CHANNEL_24G_BAND] =
+ aidl_request1.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_24GHZ].scanPeriodSec;
+ legacy_request->config_dw.config_2dot4g_dw_band =
+ aidl_request1.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_24GHZ]
+ .validDiscoveryWindowIntervalVal;
+ legacy_request->config_dw.dw_2dot4g_interval_val =
+ aidl_request1.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_24GHZ]
+ .discoveryWindowIntervalVal;
+
+ legacy_request->config_5g_rssi_close_proximity = 1;
+ legacy_request->rssi_close_proximity_5g_val =
+ aidl_request1.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ]
+ .rssiCloseProximity;
+ legacy_request->scan_params_val.dwell_time[legacy_hal::NAN_CHANNEL_5G_BAND_LOW] =
+ aidl_request1.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ].dwellTimeMs;
+ legacy_request->scan_params_val.scan_period[legacy_hal::NAN_CHANNEL_5G_BAND_LOW] =
+ aidl_request1.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ].scanPeriodSec;
+ legacy_request->scan_params_val.dwell_time[legacy_hal::NAN_CHANNEL_5G_BAND_HIGH] =
+ aidl_request1.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ].dwellTimeMs;
+ legacy_request->scan_params_val.scan_period[legacy_hal::NAN_CHANNEL_5G_BAND_HIGH] =
+ aidl_request1.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ].scanPeriodSec;
+ legacy_request->config_dw.config_5g_dw_band =
+ aidl_request1.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ]
+ .validDiscoveryWindowIntervalVal;
+ legacy_request->config_dw.dw_5g_interval_val =
+ aidl_request1.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ]
+ .discoveryWindowIntervalVal;
+
+ legacy_request->config_discovery_beacon_int = 1;
+ legacy_request->discovery_beacon_interval = aidl_request2.discoveryBeaconIntervalMs;
+ legacy_request->config_nss = 1;
+ legacy_request->nss = aidl_request2.numberOfSpatialStreamsInDiscovery;
+ legacy_request->config_dw_early_termination = 1;
+ legacy_request->enable_dw_termination = aidl_request2.enableDiscoveryWindowEarlyTermination;
+ legacy_request->config_enable_ranging = 1;
+ legacy_request->enable_ranging = aidl_request2.enableRanging;
+
+ legacy_request->config_enable_instant_mode = 1;
+ legacy_request->enable_instant_mode = aidl_request2.enableInstantCommunicationMode;
+ legacy_request->config_instant_mode_channel = 1;
+ legacy_request->instant_mode_channel = aidl_request2.instantModeChannel;
+
+ return true;
+}
+
+bool convertAidlNanPublishRequestToLegacy(const NanPublishRequest& aidl_request,
+ legacy_hal::NanPublishRequest* legacy_request) {
+ if (!legacy_request) {
+ LOG(ERROR) << "convertAidlNanPublishRequestToLegacy: null legacy_request";
+ return false;
+ }
+ *legacy_request = {};
+
+ legacy_request->publish_id = aidl_request.baseConfigs.sessionId;
+ legacy_request->ttl = aidl_request.baseConfigs.ttlSec;
+ legacy_request->period = aidl_request.baseConfigs.discoveryWindowPeriod;
+ legacy_request->publish_count = aidl_request.baseConfigs.discoveryCount;
+ legacy_request->service_name_len = aidl_request.baseConfigs.serviceName.size();
+ if (legacy_request->service_name_len > NAN_MAX_SERVICE_NAME_LEN) {
+ LOG(ERROR) << "convertAidlNanPublishRequestToLegacy: service_name_len "
+ "too large";
+ return false;
+ }
+ memcpy(legacy_request->service_name, aidl_request.baseConfigs.serviceName.data(),
+ legacy_request->service_name_len);
+ legacy_request->publish_match_indicator =
+ convertAidlNanMatchAlgToLegacy(aidl_request.baseConfigs.discoveryMatchIndicator);
+ legacy_request->service_specific_info_len = aidl_request.baseConfigs.serviceSpecificInfo.size();
+ if (legacy_request->service_specific_info_len > NAN_MAX_SERVICE_SPECIFIC_INFO_LEN) {
+ LOG(ERROR) << "convertAidlNanPublishRequestToLegacy: "
+ "service_specific_info_len too large";
+ return false;
+ }
+ memcpy(legacy_request->service_specific_info,
+ aidl_request.baseConfigs.serviceSpecificInfo.data(),
+ legacy_request->service_specific_info_len);
+ legacy_request->sdea_service_specific_info_len =
+ aidl_request.baseConfigs.extendedServiceSpecificInfo.size();
+ if (legacy_request->sdea_service_specific_info_len > NAN_MAX_SDEA_SERVICE_SPECIFIC_INFO_LEN) {
+ LOG(ERROR) << "convertAidlNanPublishRequestToLegacy: "
+ "sdea_service_specific_info_len too large";
+ return false;
+ }
+ memcpy(legacy_request->sdea_service_specific_info,
+ aidl_request.baseConfigs.extendedServiceSpecificInfo.data(),
+ legacy_request->sdea_service_specific_info_len);
+ legacy_request->rx_match_filter_len = aidl_request.baseConfigs.rxMatchFilter.size();
+ if (legacy_request->rx_match_filter_len > NAN_MAX_MATCH_FILTER_LEN) {
+ LOG(ERROR) << "convertAidlNanPublishRequestToLegacy: "
+ "rx_match_filter_len too large";
+ return false;
+ }
+ memcpy(legacy_request->rx_match_filter, aidl_request.baseConfigs.rxMatchFilter.data(),
+ legacy_request->rx_match_filter_len);
+ legacy_request->tx_match_filter_len = aidl_request.baseConfigs.txMatchFilter.size();
+ if (legacy_request->tx_match_filter_len > NAN_MAX_MATCH_FILTER_LEN) {
+ LOG(ERROR) << "convertAidlNanPublishRequestToLegacy: "
+ "tx_match_filter_len too large";
+ return false;
+ }
+ memcpy(legacy_request->tx_match_filter, aidl_request.baseConfigs.txMatchFilter.data(),
+ legacy_request->tx_match_filter_len);
+ legacy_request->rssi_threshold_flag = aidl_request.baseConfigs.useRssiThreshold;
+ legacy_request->recv_indication_cfg = 0;
+ legacy_request->recv_indication_cfg |=
+ aidl_request.baseConfigs.disableDiscoveryTerminationIndication ? 0x1 : 0x0;
+ legacy_request->recv_indication_cfg |=
+ aidl_request.baseConfigs.disableMatchExpirationIndication ? 0x2 : 0x0;
+ legacy_request->recv_indication_cfg |=
+ aidl_request.baseConfigs.disableFollowupReceivedIndication ? 0x4 : 0x0;
+ legacy_request->recv_indication_cfg |= 0x8;
+ legacy_request->cipher_type = (unsigned int)aidl_request.baseConfigs.securityConfig.cipherType;
+
+ legacy_request->scid_len = aidl_request.baseConfigs.securityConfig.scid.size();
+ if (legacy_request->scid_len > NAN_MAX_SCID_BUF_LEN) {
+ LOG(ERROR) << "convertAidlNanPublishRequestToLegacy: scid_len too large";
+ return false;
+ }
+ memcpy(legacy_request->scid, aidl_request.baseConfigs.securityConfig.scid.data(),
+ legacy_request->scid_len);
+
+ if (aidl_request.baseConfigs.securityConfig.securityType == NanDataPathSecurityType::PMK) {
+ legacy_request->key_info.key_type = legacy_hal::NAN_SECURITY_KEY_INPUT_PMK;
+ legacy_request->key_info.body.pmk_info.pmk_len =
+ aidl_request.baseConfigs.securityConfig.pmk.size();
+ if (legacy_request->key_info.body.pmk_info.pmk_len != NAN_PMK_INFO_LEN) {
+ LOG(ERROR) << "convertAidlNanPublishRequestToLegacy: invalid pmk_len";
+ return false;
+ }
+ memcpy(legacy_request->key_info.body.pmk_info.pmk,
+ aidl_request.baseConfigs.securityConfig.pmk.data(),
+ legacy_request->key_info.body.pmk_info.pmk_len);
+ }
+ if (aidl_request.baseConfigs.securityConfig.securityType ==
+ NanDataPathSecurityType::PASSPHRASE) {
+ legacy_request->key_info.key_type = legacy_hal::NAN_SECURITY_KEY_INPUT_PASSPHRASE;
+ legacy_request->key_info.body.passphrase_info.passphrase_len =
+ aidl_request.baseConfigs.securityConfig.passphrase.size();
+ if (legacy_request->key_info.body.passphrase_info.passphrase_len <
+ NAN_SECURITY_MIN_PASSPHRASE_LEN) {
+ LOG(ERROR) << "convertAidlNanPublishRequestToLegacy: "
+ "passphrase_len too small";
+ return false;
+ }
+ if (legacy_request->key_info.body.passphrase_info.passphrase_len >
+ NAN_SECURITY_MAX_PASSPHRASE_LEN) {
+ LOG(ERROR) << "convertAidlNanPublishRequestToLegacy: "
+ "passphrase_len too large";
+ return false;
+ }
+ memcpy(legacy_request->key_info.body.passphrase_info.passphrase,
+ aidl_request.baseConfigs.securityConfig.passphrase.data(),
+ legacy_request->key_info.body.passphrase_info.passphrase_len);
+ }
+ legacy_request->sdea_params.security_cfg =
+ (aidl_request.baseConfigs.securityConfig.securityType != NanDataPathSecurityType::OPEN)
+ ? legacy_hal::NAN_DP_CONFIG_SECURITY
+ : legacy_hal::NAN_DP_CONFIG_NO_SECURITY;
+
+ legacy_request->sdea_params.ranging_state = aidl_request.baseConfigs.rangingRequired
+ ? legacy_hal::NAN_RANGING_ENABLE
+ : legacy_hal::NAN_RANGING_DISABLE;
+ legacy_request->ranging_cfg.ranging_interval_msec = aidl_request.baseConfigs.rangingIntervalMs;
+ legacy_request->ranging_cfg.config_ranging_indications =
+ static_cast<uint32_t>(aidl_request.baseConfigs.configRangingIndications);
+ legacy_request->ranging_cfg.distance_ingress_mm =
+ aidl_request.baseConfigs.distanceIngressCm * 10;
+ legacy_request->ranging_cfg.distance_egress_mm = aidl_request.baseConfigs.distanceEgressCm * 10;
+ legacy_request->ranging_auto_response = aidl_request.baseConfigs.rangingRequired
+ ? legacy_hal::NAN_RANGING_AUTO_RESPONSE_ENABLE
+ : legacy_hal::NAN_RANGING_AUTO_RESPONSE_DISABLE;
+ legacy_request->sdea_params.range_report = legacy_hal::NAN_DISABLE_RANGE_REPORT;
+ legacy_request->publish_type = convertAidlNanPublishTypeToLegacy(aidl_request.publishType);
+ legacy_request->tx_type = convertAidlNanTxTypeToLegacy(aidl_request.txType);
+ legacy_request->service_responder_policy = aidl_request.autoAcceptDataPathRequests
+ ? legacy_hal::NAN_SERVICE_ACCEPT_POLICY_ALL
+ : legacy_hal::NAN_SERVICE_ACCEPT_POLICY_NONE;
+
+ return true;
+}
+
+bool convertAidlNanSubscribeRequestToLegacy(const NanSubscribeRequest& aidl_request,
+ legacy_hal::NanSubscribeRequest* legacy_request) {
+ if (!legacy_request) {
+ LOG(ERROR) << "convertAidlNanSubscribeRequestToLegacy: legacy_request is null";
+ return false;
+ }
+ *legacy_request = {};
+
+ legacy_request->subscribe_id = aidl_request.baseConfigs.sessionId;
+ legacy_request->ttl = aidl_request.baseConfigs.ttlSec;
+ legacy_request->period = aidl_request.baseConfigs.discoveryWindowPeriod;
+ legacy_request->subscribe_count = aidl_request.baseConfigs.discoveryCount;
+ legacy_request->service_name_len = aidl_request.baseConfigs.serviceName.size();
+ if (legacy_request->service_name_len > NAN_MAX_SERVICE_NAME_LEN) {
+ LOG(ERROR) << "convertAidlNanSubscribeRequestToLegacy: "
+ "service_name_len too large";
+ return false;
+ }
+ memcpy(legacy_request->service_name, aidl_request.baseConfigs.serviceName.data(),
+ legacy_request->service_name_len);
+ legacy_request->subscribe_match_indicator =
+ convertAidlNanMatchAlgToLegacy(aidl_request.baseConfigs.discoveryMatchIndicator);
+ legacy_request->service_specific_info_len = aidl_request.baseConfigs.serviceSpecificInfo.size();
+ if (legacy_request->service_specific_info_len > NAN_MAX_SERVICE_SPECIFIC_INFO_LEN) {
+ LOG(ERROR) << "convertAidlNanSubscribeRequestToLegacy: "
+ "service_specific_info_len too large";
+ return false;
+ }
+ memcpy(legacy_request->service_specific_info,
+ aidl_request.baseConfigs.serviceSpecificInfo.data(),
+ legacy_request->service_specific_info_len);
+ legacy_request->sdea_service_specific_info_len =
+ aidl_request.baseConfigs.extendedServiceSpecificInfo.size();
+ if (legacy_request->sdea_service_specific_info_len > NAN_MAX_SDEA_SERVICE_SPECIFIC_INFO_LEN) {
+ LOG(ERROR) << "convertAidlNanSubscribeRequestToLegacy: "
+ "sdea_service_specific_info_len too large";
+ return false;
+ }
+ memcpy(legacy_request->sdea_service_specific_info,
+ aidl_request.baseConfigs.extendedServiceSpecificInfo.data(),
+ legacy_request->sdea_service_specific_info_len);
+ legacy_request->rx_match_filter_len = aidl_request.baseConfigs.rxMatchFilter.size();
+ if (legacy_request->rx_match_filter_len > NAN_MAX_MATCH_FILTER_LEN) {
+ LOG(ERROR) << "convertAidlNanSubscribeRequestToLegacy: "
+ "rx_match_filter_len too large";
+ return false;
+ }
+ memcpy(legacy_request->rx_match_filter, aidl_request.baseConfigs.rxMatchFilter.data(),
+ legacy_request->rx_match_filter_len);
+ legacy_request->tx_match_filter_len = aidl_request.baseConfigs.txMatchFilter.size();
+ if (legacy_request->tx_match_filter_len > NAN_MAX_MATCH_FILTER_LEN) {
+ LOG(ERROR) << "convertAidlNanSubscribeRequestToLegacy: "
+ "tx_match_filter_len too large";
+ return false;
+ }
+ memcpy(legacy_request->tx_match_filter, aidl_request.baseConfigs.txMatchFilter.data(),
+ legacy_request->tx_match_filter_len);
+ legacy_request->rssi_threshold_flag = aidl_request.baseConfigs.useRssiThreshold;
+ legacy_request->recv_indication_cfg = 0;
+ legacy_request->recv_indication_cfg |=
+ aidl_request.baseConfigs.disableDiscoveryTerminationIndication ? 0x1 : 0x0;
+ legacy_request->recv_indication_cfg |=
+ aidl_request.baseConfigs.disableMatchExpirationIndication ? 0x2 : 0x0;
+ legacy_request->recv_indication_cfg |=
+ aidl_request.baseConfigs.disableFollowupReceivedIndication ? 0x4 : 0x0;
+ legacy_request->cipher_type = (unsigned int)aidl_request.baseConfigs.securityConfig.cipherType;
+ if (aidl_request.baseConfigs.securityConfig.securityType == NanDataPathSecurityType::PMK) {
+ legacy_request->key_info.key_type = legacy_hal::NAN_SECURITY_KEY_INPUT_PMK;
+ legacy_request->key_info.body.pmk_info.pmk_len =
+ aidl_request.baseConfigs.securityConfig.pmk.size();
+ if (legacy_request->key_info.body.pmk_info.pmk_len != NAN_PMK_INFO_LEN) {
+ LOG(ERROR) << "convertAidlNanSubscribeRequestToLegacy: invalid pmk_len";
+ return false;
+ }
+ memcpy(legacy_request->key_info.body.pmk_info.pmk,
+ aidl_request.baseConfigs.securityConfig.pmk.data(),
+ legacy_request->key_info.body.pmk_info.pmk_len);
+ }
+ if (aidl_request.baseConfigs.securityConfig.securityType ==
+ NanDataPathSecurityType::PASSPHRASE) {
+ legacy_request->key_info.key_type = legacy_hal::NAN_SECURITY_KEY_INPUT_PASSPHRASE;
+ legacy_request->key_info.body.passphrase_info.passphrase_len =
+ aidl_request.baseConfigs.securityConfig.passphrase.size();
+ if (legacy_request->key_info.body.passphrase_info.passphrase_len <
+ NAN_SECURITY_MIN_PASSPHRASE_LEN) {
+ LOG(ERROR) << "convertAidlNanSubscribeRequestToLegacy: "
+ "passphrase_len too small";
+ return false;
+ }
+ if (legacy_request->key_info.body.passphrase_info.passphrase_len >
+ NAN_SECURITY_MAX_PASSPHRASE_LEN) {
+ LOG(ERROR) << "convertAidlNanSubscribeRequestToLegacy: "
+ "passphrase_len too large";
+ return false;
+ }
+ memcpy(legacy_request->key_info.body.passphrase_info.passphrase,
+ aidl_request.baseConfigs.securityConfig.passphrase.data(),
+ legacy_request->key_info.body.passphrase_info.passphrase_len);
+ }
+ legacy_request->sdea_params.security_cfg =
+ (aidl_request.baseConfigs.securityConfig.securityType != NanDataPathSecurityType::OPEN)
+ ? legacy_hal::NAN_DP_CONFIG_SECURITY
+ : legacy_hal::NAN_DP_CONFIG_NO_SECURITY;
+ legacy_request->sdea_params.ranging_state = aidl_request.baseConfigs.rangingRequired
+ ? legacy_hal::NAN_RANGING_ENABLE
+ : legacy_hal::NAN_RANGING_DISABLE;
+ legacy_request->ranging_cfg.ranging_interval_msec = aidl_request.baseConfigs.rangingIntervalMs;
+ legacy_request->ranging_cfg.config_ranging_indications =
+ static_cast<uint32_t>(aidl_request.baseConfigs.configRangingIndications);
+ legacy_request->ranging_cfg.distance_ingress_mm =
+ aidl_request.baseConfigs.distanceIngressCm * 10;
+ legacy_request->ranging_cfg.distance_egress_mm = aidl_request.baseConfigs.distanceEgressCm * 10;
+ legacy_request->ranging_auto_response = aidl_request.baseConfigs.rangingRequired
+ ? legacy_hal::NAN_RANGING_AUTO_RESPONSE_ENABLE
+ : legacy_hal::NAN_RANGING_AUTO_RESPONSE_DISABLE;
+ legacy_request->sdea_params.range_report = legacy_hal::NAN_DISABLE_RANGE_REPORT;
+ legacy_request->subscribe_type =
+ convertAidlNanSubscribeTypeToLegacy(aidl_request.subscribeType);
+ legacy_request->serviceResponseFilter = convertAidlNanSrfTypeToLegacy(aidl_request.srfType);
+ legacy_request->serviceResponseInclude = aidl_request.srfRespondIfInAddressSet
+ ? legacy_hal::NAN_SRF_INCLUDE_RESPOND
+ : legacy_hal::NAN_SRF_INCLUDE_DO_NOT_RESPOND;
+ legacy_request->useServiceResponseFilter =
+ aidl_request.shouldUseSrf ? legacy_hal::NAN_USE_SRF : legacy_hal::NAN_DO_NOT_USE_SRF;
+ legacy_request->ssiRequiredForMatchIndication =
+ aidl_request.isSsiRequiredForMatch ? legacy_hal::NAN_SSI_REQUIRED_IN_MATCH_IND
+ : legacy_hal::NAN_SSI_NOT_REQUIRED_IN_MATCH_IND;
+ legacy_request->num_intf_addr_present = aidl_request.intfAddr.size();
+ if (legacy_request->num_intf_addr_present > NAN_MAX_SUBSCRIBE_MAX_ADDRESS) {
+ LOG(ERROR) << "convertAidlNanSubscribeRequestToLegacy: "
+ "num_intf_addr_present - too many";
+ return false;
+ }
+ for (int i = 0; i < legacy_request->num_intf_addr_present; i++) {
+ memcpy(legacy_request->intf_addr[i], aidl_request.intfAddr[i].data.data(), 6);
+ }
+
+ return true;
+}
+
+bool convertAidlNanTransmitFollowupRequestToLegacy(
+ const NanTransmitFollowupRequest& aidl_request,
+ legacy_hal::NanTransmitFollowupRequest* legacy_request) {
+ if (!legacy_request) {
+ LOG(ERROR) << "convertAidlNanTransmitFollowupRequestToLegacy: "
+ "legacy_request is null";
+ return false;
+ }
+ *legacy_request = {};
+
+ legacy_request->publish_subscribe_id = aidl_request.discoverySessionId;
+ legacy_request->requestor_instance_id = aidl_request.peerId;
+ memcpy(legacy_request->addr, aidl_request.addr.data(), 6);
+ legacy_request->priority = aidl_request.isHighPriority ? legacy_hal::NAN_TX_PRIORITY_HIGH
+ : legacy_hal::NAN_TX_PRIORITY_NORMAL;
+ legacy_request->dw_or_faw = aidl_request.shouldUseDiscoveryWindow
+ ? legacy_hal::NAN_TRANSMIT_IN_DW
+ : legacy_hal::NAN_TRANSMIT_IN_FAW;
+ legacy_request->service_specific_info_len = aidl_request.serviceSpecificInfo.size();
+ if (legacy_request->service_specific_info_len > NAN_MAX_SERVICE_SPECIFIC_INFO_LEN) {
+ LOG(ERROR) << "convertAidlNanTransmitFollowupRequestToLegacy: "
+ "service_specific_info_len too large";
+ return false;
+ }
+ memcpy(legacy_request->service_specific_info, aidl_request.serviceSpecificInfo.data(),
+ legacy_request->service_specific_info_len);
+ legacy_request->sdea_service_specific_info_len =
+ aidl_request.extendedServiceSpecificInfo.size();
+ if (legacy_request->sdea_service_specific_info_len > NAN_MAX_SDEA_SERVICE_SPECIFIC_INFO_LEN) {
+ LOG(ERROR) << "convertAidlNanTransmitFollowupRequestToLegacy: "
+ "sdea_service_specific_info_len too large";
+ return false;
+ }
+ memcpy(legacy_request->sdea_service_specific_info,
+ aidl_request.extendedServiceSpecificInfo.data(),
+ legacy_request->sdea_service_specific_info_len);
+ legacy_request->recv_indication_cfg = aidl_request.disableFollowupResultIndication ? 0x1 : 0x0;
+
+ return true;
+}
+
+bool convertAidlNanDataPathInitiatorRequestToLegacy(
+ const NanInitiateDataPathRequest& aidl_request,
+ legacy_hal::NanDataPathInitiatorRequest* legacy_request) {
+ if (!legacy_request) {
+ LOG(ERROR) << "convertAidlNanDataPathInitiatorRequestToLegacy: "
+ "legacy_request is null";
+ return false;
+ }
+ *legacy_request = {};
+
+ legacy_request->requestor_instance_id = aidl_request.peerId;
+ memcpy(legacy_request->peer_disc_mac_addr, aidl_request.peerDiscMacAddr.data(), 6);
+ legacy_request->channel_request_type =
+ convertAidlNanDataPathChannelCfgToLegacy(aidl_request.channelRequestType);
+ legacy_request->channel = aidl_request.channel;
+ if (strnlen(aidl_request.ifaceName.c_str(), IFNAMSIZ + 1) == IFNAMSIZ + 1) {
+ LOG(ERROR) << "convertAidlNanDataPathInitiatorRequestToLegacy: "
+ "ifaceName too long";
+ return false;
+ }
+ strlcpy(legacy_request->ndp_iface, aidl_request.ifaceName.c_str(), IFNAMSIZ + 1);
+ legacy_request->ndp_cfg.security_cfg =
+ (aidl_request.securityConfig.securityType != NanDataPathSecurityType::OPEN)
+ ? legacy_hal::NAN_DP_CONFIG_SECURITY
+ : legacy_hal::NAN_DP_CONFIG_NO_SECURITY;
+ legacy_request->app_info.ndp_app_info_len = aidl_request.appInfo.size();
+ if (legacy_request->app_info.ndp_app_info_len > NAN_DP_MAX_APP_INFO_LEN) {
+ LOG(ERROR) << "convertAidlNanDataPathInitiatorRequestToLegacy: "
+ "ndp_app_info_len too large";
+ return false;
+ }
+ memcpy(legacy_request->app_info.ndp_app_info, aidl_request.appInfo.data(),
+ legacy_request->app_info.ndp_app_info_len);
+ legacy_request->cipher_type = (unsigned int)aidl_request.securityConfig.cipherType;
+ if (aidl_request.securityConfig.securityType == NanDataPathSecurityType::PMK) {
+ legacy_request->key_info.key_type = legacy_hal::NAN_SECURITY_KEY_INPUT_PMK;
+ legacy_request->key_info.body.pmk_info.pmk_len = aidl_request.securityConfig.pmk.size();
+ if (legacy_request->key_info.body.pmk_info.pmk_len != NAN_PMK_INFO_LEN) {
+ LOG(ERROR) << "convertAidlNanDataPathInitiatorRequestToLegacy: "
+ "invalid pmk_len";
+ return false;
+ }
+ memcpy(legacy_request->key_info.body.pmk_info.pmk, aidl_request.securityConfig.pmk.data(),
+ legacy_request->key_info.body.pmk_info.pmk_len);
+ }
+ if (aidl_request.securityConfig.securityType == NanDataPathSecurityType::PASSPHRASE) {
+ legacy_request->key_info.key_type = legacy_hal::NAN_SECURITY_KEY_INPUT_PASSPHRASE;
+ legacy_request->key_info.body.passphrase_info.passphrase_len =
+ aidl_request.securityConfig.passphrase.size();
+ if (legacy_request->key_info.body.passphrase_info.passphrase_len <
+ NAN_SECURITY_MIN_PASSPHRASE_LEN) {
+ LOG(ERROR) << "convertAidlNanDataPathInitiatorRequestToLegacy: "
+ "passphrase_len too small";
+ return false;
+ }
+ if (legacy_request->key_info.body.passphrase_info.passphrase_len >
+ NAN_SECURITY_MAX_PASSPHRASE_LEN) {
+ LOG(ERROR) << "convertAidlNanDataPathInitiatorRequestToLegacy: "
+ "passphrase_len too large";
+ return false;
+ }
+ memcpy(legacy_request->key_info.body.passphrase_info.passphrase,
+ aidl_request.securityConfig.passphrase.data(),
+ legacy_request->key_info.body.passphrase_info.passphrase_len);
+ }
+ legacy_request->service_name_len = aidl_request.serviceNameOutOfBand.size();
+ if (legacy_request->service_name_len > NAN_MAX_SERVICE_NAME_LEN) {
+ LOG(ERROR) << "convertAidlNanDataPathInitiatorRequestToLegacy: "
+ "service_name_len too large";
+ return false;
+ }
+ memcpy(legacy_request->service_name, aidl_request.serviceNameOutOfBand.data(),
+ legacy_request->service_name_len);
+ legacy_request->scid_len = aidl_request.securityConfig.scid.size();
+ if (legacy_request->scid_len > NAN_MAX_SCID_BUF_LEN) {
+ LOG(ERROR) << "convertAidlNanDataPathInitiatorRequestToLegacy: scid_len too large";
+ return false;
+ }
+ memcpy(legacy_request->scid, aidl_request.securityConfig.scid.data(), legacy_request->scid_len);
+
+ return true;
+}
+
+bool convertAidlNanDataPathIndicationResponseToLegacy(
+ const NanRespondToDataPathIndicationRequest& aidl_request,
+ legacy_hal::NanDataPathIndicationResponse* legacy_request) {
+ if (!legacy_request) {
+ LOG(ERROR) << "convertAidlNanDataPathIndicationResponseToLegacy: "
+ "legacy_request is null";
+ return false;
+ }
+ *legacy_request = {};
+
+ legacy_request->rsp_code = aidl_request.acceptRequest ? legacy_hal::NAN_DP_REQUEST_ACCEPT
+ : legacy_hal::NAN_DP_REQUEST_REJECT;
+ legacy_request->ndp_instance_id = aidl_request.ndpInstanceId;
+ if (strnlen(aidl_request.ifaceName.c_str(), IFNAMSIZ + 1) == IFNAMSIZ + 1) {
+ LOG(ERROR) << "convertAidlNanDataPathIndicationResponseToLegacy: "
+ "ifaceName too long";
+ return false;
+ }
+ strlcpy(legacy_request->ndp_iface, aidl_request.ifaceName.c_str(), IFNAMSIZ + 1);
+ legacy_request->ndp_cfg.security_cfg =
+ (aidl_request.securityConfig.securityType != NanDataPathSecurityType::OPEN)
+ ? legacy_hal::NAN_DP_CONFIG_SECURITY
+ : legacy_hal::NAN_DP_CONFIG_NO_SECURITY;
+ legacy_request->app_info.ndp_app_info_len = aidl_request.appInfo.size();
+ if (legacy_request->app_info.ndp_app_info_len > NAN_DP_MAX_APP_INFO_LEN) {
+ LOG(ERROR) << "convertAidlNanDataPathIndicationResponseToLegacy: "
+ "ndp_app_info_len too large";
+ return false;
+ }
+ memcpy(legacy_request->app_info.ndp_app_info, aidl_request.appInfo.data(),
+ legacy_request->app_info.ndp_app_info_len);
+ legacy_request->cipher_type = (unsigned int)aidl_request.securityConfig.cipherType;
+ if (aidl_request.securityConfig.securityType == NanDataPathSecurityType::PMK) {
+ legacy_request->key_info.key_type = legacy_hal::NAN_SECURITY_KEY_INPUT_PMK;
+ legacy_request->key_info.body.pmk_info.pmk_len = aidl_request.securityConfig.pmk.size();
+ if (legacy_request->key_info.body.pmk_info.pmk_len != NAN_PMK_INFO_LEN) {
+ LOG(ERROR) << "convertAidlNanDataPathIndicationResponseToLegacy: "
+ "invalid pmk_len";
+ return false;
+ }
+ memcpy(legacy_request->key_info.body.pmk_info.pmk, aidl_request.securityConfig.pmk.data(),
+ legacy_request->key_info.body.pmk_info.pmk_len);
+ }
+ if (aidl_request.securityConfig.securityType == NanDataPathSecurityType::PASSPHRASE) {
+ legacy_request->key_info.key_type = legacy_hal::NAN_SECURITY_KEY_INPUT_PASSPHRASE;
+ legacy_request->key_info.body.passphrase_info.passphrase_len =
+ aidl_request.securityConfig.passphrase.size();
+ if (legacy_request->key_info.body.passphrase_info.passphrase_len <
+ NAN_SECURITY_MIN_PASSPHRASE_LEN) {
+ LOG(ERROR) << "convertAidlNanDataPathIndicationResponseToLegacy: "
+ "passphrase_len too small";
+ return false;
+ }
+ if (legacy_request->key_info.body.passphrase_info.passphrase_len >
+ NAN_SECURITY_MAX_PASSPHRASE_LEN) {
+ LOG(ERROR) << "convertAidlNanDataPathIndicationResponseToLegacy: "
+ "passphrase_len too large";
+ return false;
+ }
+ memcpy(legacy_request->key_info.body.passphrase_info.passphrase,
+ aidl_request.securityConfig.passphrase.data(),
+ legacy_request->key_info.body.passphrase_info.passphrase_len);
+ }
+ legacy_request->service_name_len = aidl_request.serviceNameOutOfBand.size();
+ if (legacy_request->service_name_len > NAN_MAX_SERVICE_NAME_LEN) {
+ LOG(ERROR) << "convertAidlNanDataPathIndicationResponseToLegacy: "
+ "service_name_len too large";
+ return false;
+ }
+ memcpy(legacy_request->service_name, aidl_request.serviceNameOutOfBand.data(),
+ legacy_request->service_name_len);
+ legacy_request->scid_len = aidl_request.securityConfig.scid.size();
+ if (legacy_request->scid_len > NAN_MAX_SCID_BUF_LEN) {
+ LOG(ERROR) << "convertAidlNanDataPathIndicationResponseToLegacy: scid_len too large";
+ return false;
+ }
+ memcpy(legacy_request->scid, aidl_request.securityConfig.scid.data(), legacy_request->scid_len);
+
+ return true;
+}
+
+bool convertLegacyNanResponseHeaderToAidl(const legacy_hal::NanResponseMsg& legacy_response,
+ NanStatus* nanStatus) {
+ if (!nanStatus) {
+ LOG(ERROR) << "convertLegacyNanResponseHeaderToAidl: nanStatus is null";
+ return false;
+ }
+ *nanStatus = {};
+
+ convertToNanStatus(legacy_response.status, legacy_response.nan_error,
+ sizeof(legacy_response.nan_error), nanStatus);
+ return true;
+}
+
+bool convertLegacyNanCapabilitiesResponseToAidl(const legacy_hal::NanCapabilities& legacy_response,
+ NanCapabilities* aidl_response) {
+ if (!aidl_response) {
+ LOG(ERROR) << "convertLegacyNanCapabilitiesResponseToAidl: "
+ "aidl_response is null";
+ return false;
+ }
+ *aidl_response = {};
+
+ aidl_response->maxConcurrentClusters = legacy_response.max_concurrent_nan_clusters;
+ aidl_response->maxPublishes = legacy_response.max_publishes;
+ aidl_response->maxSubscribes = legacy_response.max_subscribes;
+ aidl_response->maxServiceNameLen = legacy_response.max_service_name_len;
+ aidl_response->maxMatchFilterLen = legacy_response.max_match_filter_len;
+ aidl_response->maxTotalMatchFilterLen = legacy_response.max_total_match_filter_len;
+ aidl_response->maxServiceSpecificInfoLen = legacy_response.max_service_specific_info_len;
+ aidl_response->maxExtendedServiceSpecificInfoLen =
+ legacy_response.max_sdea_service_specific_info_len;
+ aidl_response->maxNdiInterfaces = legacy_response.max_ndi_interfaces;
+ aidl_response->maxNdpSessions = legacy_response.max_ndp_sessions;
+ aidl_response->maxAppInfoLen = legacy_response.max_app_info_len;
+ aidl_response->maxQueuedTransmitFollowupMsgs =
+ legacy_response.max_queued_transmit_followup_msgs;
+ aidl_response->maxSubscribeInterfaceAddresses = legacy_response.max_subscribe_address;
+ aidl_response->supportedCipherSuites =
+ static_cast<NanCipherSuiteType>(legacy_response.cipher_suites_supported);
+ aidl_response->instantCommunicationModeSupportFlag = legacy_response.is_instant_mode_supported;
+
+ return true;
+}
+
+bool convertLegacyNanMatchIndToAidl(const legacy_hal::NanMatchInd& legacy_ind,
+ NanMatchInd* aidl_ind) {
+ if (!aidl_ind) {
+ LOG(ERROR) << "convertLegacyNanMatchIndToAidl: aidl_ind is null";
+ return false;
+ }
+ *aidl_ind = {};
+
+ aidl_ind->discoverySessionId = legacy_ind.publish_subscribe_id;
+ aidl_ind->peerId = legacy_ind.requestor_instance_id;
+ aidl_ind->addr = std::array<uint8_t, 6>();
+ std::copy(legacy_ind.addr, legacy_ind.addr + 6, std::begin(aidl_ind->addr));
+ aidl_ind->serviceSpecificInfo = std::vector<uint8_t>(
+ legacy_ind.service_specific_info,
+ legacy_ind.service_specific_info + legacy_ind.service_specific_info_len);
+ aidl_ind->extendedServiceSpecificInfo = std::vector<uint8_t>(
+ legacy_ind.sdea_service_specific_info,
+ legacy_ind.sdea_service_specific_info + legacy_ind.sdea_service_specific_info_len);
+ aidl_ind->matchFilter =
+ std::vector<uint8_t>(legacy_ind.sdf_match_filter,
+ legacy_ind.sdf_match_filter + legacy_ind.sdf_match_filter_len);
+ aidl_ind->matchOccurredInBeaconFlag = legacy_ind.match_occured_flag == 1; // NOTYPO
+ aidl_ind->outOfResourceFlag = legacy_ind.out_of_resource_flag == 1;
+ aidl_ind->rssiValue = legacy_ind.rssi_value;
+ aidl_ind->peerCipherType = (NanCipherSuiteType)legacy_ind.peer_cipher_type;
+ aidl_ind->peerRequiresSecurityEnabledInNdp =
+ legacy_ind.peer_sdea_params.security_cfg == legacy_hal::NAN_DP_CONFIG_SECURITY;
+ aidl_ind->peerRequiresRanging =
+ legacy_ind.peer_sdea_params.ranging_state == legacy_hal::NAN_RANGING_ENABLE;
+ aidl_ind->rangingMeasurementInMm = legacy_ind.range_info.range_measurement_mm;
+ aidl_ind->rangingIndicationType =
+ static_cast<NanRangingIndication>(legacy_ind.range_info.ranging_event_type);
+ aidl_ind->scid = std::vector<uint8_t>(legacy_ind.scid, legacy_ind.scid + legacy_ind.scid_len);
+ return true;
+}
+
+bool convertLegacyNanFollowupIndToAidl(const legacy_hal::NanFollowupInd& legacy_ind,
+ NanFollowupReceivedInd* aidl_ind) {
+ if (!aidl_ind) {
+ LOG(ERROR) << "convertLegacyNanFollowupIndToAidl: aidl_ind is null";
+ return false;
+ }
+ *aidl_ind = {};
+
+ aidl_ind->discoverySessionId = legacy_ind.publish_subscribe_id;
+ aidl_ind->peerId = legacy_ind.requestor_instance_id;
+ aidl_ind->addr = std::array<uint8_t, 6>();
+ std::copy(legacy_ind.addr, legacy_ind.addr + 6, std::begin(aidl_ind->addr));
+ aidl_ind->receivedInFaw = legacy_ind.dw_or_faw == 1;
+ aidl_ind->serviceSpecificInfo = std::vector<uint8_t>(
+ legacy_ind.service_specific_info,
+ legacy_ind.service_specific_info + legacy_ind.service_specific_info_len);
+ aidl_ind->extendedServiceSpecificInfo = std::vector<uint8_t>(
+ legacy_ind.sdea_service_specific_info,
+ legacy_ind.sdea_service_specific_info + legacy_ind.sdea_service_specific_info_len);
+
+ return true;
+}
+
+bool convertLegacyNanDataPathRequestIndToAidl(const legacy_hal::NanDataPathRequestInd& legacy_ind,
+ NanDataPathRequestInd* aidl_ind) {
+ if (!aidl_ind) {
+ LOG(ERROR) << "convertLegacyNanDataPathRequestIndToAidl: aidl_ind is null";
+ return false;
+ }
+ *aidl_ind = {};
+
+ aidl_ind->discoverySessionId = legacy_ind.service_instance_id;
+ aidl_ind->peerDiscMacAddr = std::array<uint8_t, 6>();
+ std::copy(legacy_ind.peer_disc_mac_addr, legacy_ind.peer_disc_mac_addr + 6,
+ std::begin(aidl_ind->peerDiscMacAddr));
+ aidl_ind->ndpInstanceId = legacy_ind.ndp_instance_id;
+ aidl_ind->securityRequired =
+ legacy_ind.ndp_cfg.security_cfg == legacy_hal::NAN_DP_CONFIG_SECURITY;
+ aidl_ind->appInfo = std::vector<uint8_t>(
+ legacy_ind.app_info.ndp_app_info,
+ legacy_ind.app_info.ndp_app_info + legacy_ind.app_info.ndp_app_info_len);
+
+ return true;
+}
+
+bool convertLegacyNdpChannelInfoToAidl(const legacy_hal::NanChannelInfo& legacy_struct,
+ NanDataPathChannelInfo* aidl_struct) {
+ if (!aidl_struct) {
+ LOG(ERROR) << "convertLegacyNdpChannelInfoToAidl: aidl_struct is null";
+ return false;
+ }
+ *aidl_struct = {};
+
+ aidl_struct->channelFreq = legacy_struct.channel;
+ aidl_struct->channelBandwidth = convertLegacyWifiChannelWidthToAidl(
+ (legacy_hal::wifi_channel_width)legacy_struct.bandwidth);
+ aidl_struct->numSpatialStreams = legacy_struct.nss;
+
+ return true;
+}
+
+bool convertLegacyNanDataPathConfirmIndToAidl(const legacy_hal::NanDataPathConfirmInd& legacy_ind,
+ NanDataPathConfirmInd* aidl_ind) {
+ if (!aidl_ind) {
+ LOG(ERROR) << "convertLegacyNanDataPathConfirmIndToAidl: aidl_ind is null";
+ return false;
+ }
+ *aidl_ind = {};
+
+ aidl_ind->ndpInstanceId = legacy_ind.ndp_instance_id;
+ aidl_ind->dataPathSetupSuccess = legacy_ind.rsp_code == legacy_hal::NAN_DP_REQUEST_ACCEPT;
+ aidl_ind->peerNdiMacAddr = std::array<uint8_t, 6>();
+ std::copy(legacy_ind.peer_ndi_mac_addr, legacy_ind.peer_ndi_mac_addr + 6,
+ std::begin(aidl_ind->peerNdiMacAddr));
+ aidl_ind->appInfo = std::vector<uint8_t>(
+ legacy_ind.app_info.ndp_app_info,
+ legacy_ind.app_info.ndp_app_info + legacy_ind.app_info.ndp_app_info_len);
+ aidl_ind->status.status = convertLegacyNanStatusTypeToAidl(legacy_ind.reason_code);
+ aidl_ind->status.description = "";
+
+ std::vector<NanDataPathChannelInfo> channelInfo;
+ for (unsigned int i = 0; i < legacy_ind.num_channels; ++i) {
+ NanDataPathChannelInfo aidl_struct;
+ if (!convertLegacyNdpChannelInfoToAidl(legacy_ind.channel_info[i], &aidl_struct)) {
+ return false;
+ }
+ channelInfo.push_back(aidl_struct);
+ }
+ aidl_ind->channelInfo = channelInfo;
+
+ return true;
+}
+
+bool convertLegacyNanDataPathScheduleUpdateIndToAidl(
+ const legacy_hal::NanDataPathScheduleUpdateInd& legacy_ind,
+ NanDataPathScheduleUpdateInd* aidl_ind) {
+ if (!aidl_ind) {
+ LOG(ERROR) << "convertLegacyNanDataPathScheduleUpdateIndToAidl: "
+ "aidl_ind is null";
+ return false;
+ }
+ *aidl_ind = {};
+
+ aidl_ind->peerDiscoveryAddress = std::array<uint8_t, 6>();
+ std::copy(legacy_ind.peer_mac_addr, legacy_ind.peer_mac_addr + 6,
+ std::begin(aidl_ind->peerDiscoveryAddress));
+ std::vector<NanDataPathChannelInfo> channelInfo;
+ for (unsigned int i = 0; i < legacy_ind.num_channels; ++i) {
+ NanDataPathChannelInfo aidl_struct;
+ if (!convertLegacyNdpChannelInfoToAidl(legacy_ind.channel_info[i], &aidl_struct)) {
+ return false;
+ }
+ channelInfo.push_back(aidl_struct);
+ }
+ aidl_ind->channelInfo = channelInfo;
+ std::vector<uint32_t> ndpInstanceIds;
+ for (unsigned int i = 0; i < legacy_ind.num_ndp_instances; ++i) {
+ ndpInstanceIds.push_back(legacy_ind.ndp_instance_id[i]);
+ }
+ aidl_ind->ndpInstanceIds = uintToIntVec(ndpInstanceIds);
+
+ return true;
+}
+
+legacy_hal::wifi_rtt_type convertAidlRttTypeToLegacy(RttType type) {
+ switch (type) {
+ case RttType::ONE_SIDED:
+ return legacy_hal::RTT_TYPE_1_SIDED;
+ case RttType::TWO_SIDED:
+ return legacy_hal::RTT_TYPE_2_SIDED;
+ };
+ CHECK(false);
+}
+
+RttType convertLegacyRttTypeToAidl(legacy_hal::wifi_rtt_type type) {
+ switch (type) {
+ case legacy_hal::RTT_TYPE_1_SIDED:
+ return RttType::ONE_SIDED;
+ case legacy_hal::RTT_TYPE_2_SIDED:
+ return RttType::TWO_SIDED;
+ };
+ CHECK(false) << "Unknown legacy type: " << type;
+}
+
+legacy_hal::rtt_peer_type convertAidlRttPeerTypeToLegacy(RttPeerType type) {
+ switch (type) {
+ case RttPeerType::AP:
+ return legacy_hal::RTT_PEER_AP;
+ case RttPeerType::STA:
+ return legacy_hal::RTT_PEER_STA;
+ case RttPeerType::P2P_GO:
+ return legacy_hal::RTT_PEER_P2P_GO;
+ case RttPeerType::P2P_CLIENT:
+ return legacy_hal::RTT_PEER_P2P_CLIENT;
+ case RttPeerType::NAN_TYPE:
+ return legacy_hal::RTT_PEER_NAN;
+ };
+ CHECK(false);
+}
+
+legacy_hal::wifi_channel_width convertAidlWifiChannelWidthToLegacy(WifiChannelWidthInMhz type) {
+ switch (type) {
+ case WifiChannelWidthInMhz::WIDTH_20:
+ return legacy_hal::WIFI_CHAN_WIDTH_20;
+ case WifiChannelWidthInMhz::WIDTH_40:
+ return legacy_hal::WIFI_CHAN_WIDTH_40;
+ case WifiChannelWidthInMhz::WIDTH_80:
+ return legacy_hal::WIFI_CHAN_WIDTH_80;
+ case WifiChannelWidthInMhz::WIDTH_160:
+ return legacy_hal::WIFI_CHAN_WIDTH_160;
+ case WifiChannelWidthInMhz::WIDTH_80P80:
+ return legacy_hal::WIFI_CHAN_WIDTH_80P80;
+ case WifiChannelWidthInMhz::WIDTH_5:
+ return legacy_hal::WIFI_CHAN_WIDTH_5;
+ case WifiChannelWidthInMhz::WIDTH_10:
+ return legacy_hal::WIFI_CHAN_WIDTH_10;
+ case WifiChannelWidthInMhz::WIDTH_320:
+ return legacy_hal::WIFI_CHAN_WIDTH_320;
+ case WifiChannelWidthInMhz::WIDTH_INVALID:
+ return legacy_hal::WIFI_CHAN_WIDTH_INVALID;
+ };
+ CHECK(false);
+}
+
+WifiChannelWidthInMhz convertLegacyWifiChannelWidthToAidl(legacy_hal::wifi_channel_width type) {
+ switch (type) {
+ case legacy_hal::WIFI_CHAN_WIDTH_20:
+ return WifiChannelWidthInMhz::WIDTH_20;
+ case legacy_hal::WIFI_CHAN_WIDTH_40:
+ return WifiChannelWidthInMhz::WIDTH_40;
+ case legacy_hal::WIFI_CHAN_WIDTH_80:
+ return WifiChannelWidthInMhz::WIDTH_80;
+ case legacy_hal::WIFI_CHAN_WIDTH_160:
+ return WifiChannelWidthInMhz::WIDTH_160;
+ case legacy_hal::WIFI_CHAN_WIDTH_80P80:
+ return WifiChannelWidthInMhz::WIDTH_80P80;
+ case legacy_hal::WIFI_CHAN_WIDTH_5:
+ return WifiChannelWidthInMhz::WIDTH_5;
+ case legacy_hal::WIFI_CHAN_WIDTH_10:
+ return WifiChannelWidthInMhz::WIDTH_10;
+ case legacy_hal::WIFI_CHAN_WIDTH_320:
+ return WifiChannelWidthInMhz::WIDTH_320;
+ default:
+ return WifiChannelWidthInMhz::WIDTH_INVALID;
+ };
+}
+
+legacy_hal::wifi_rtt_preamble convertAidlRttPreambleToLegacy(RttPreamble type) {
+ switch (type) {
+ case RttPreamble::LEGACY:
+ return legacy_hal::WIFI_RTT_PREAMBLE_LEGACY;
+ case RttPreamble::HT:
+ return legacy_hal::WIFI_RTT_PREAMBLE_HT;
+ case RttPreamble::VHT:
+ return legacy_hal::WIFI_RTT_PREAMBLE_VHT;
+ case RttPreamble::HE:
+ return legacy_hal::WIFI_RTT_PREAMBLE_HE;
+ case RttPreamble::EHT:
+ return legacy_hal::WIFI_RTT_PREAMBLE_EHT;
+ };
+ CHECK(false);
+}
+
+RttPreamble convertLegacyRttPreambleToAidl(legacy_hal::wifi_rtt_preamble type) {
+ switch (type) {
+ case legacy_hal::WIFI_RTT_PREAMBLE_LEGACY:
+ return RttPreamble::LEGACY;
+ case legacy_hal::WIFI_RTT_PREAMBLE_HT:
+ return RttPreamble::HT;
+ case legacy_hal::WIFI_RTT_PREAMBLE_VHT:
+ return RttPreamble::VHT;
+ case legacy_hal::WIFI_RTT_PREAMBLE_HE:
+ return RttPreamble::HE;
+ case legacy_hal::WIFI_RTT_PREAMBLE_EHT:
+ return RttPreamble::EHT;
+ };
+ CHECK(false) << "Unknown legacy type: " << type;
+}
+
+legacy_hal::wifi_rtt_bw convertAidlRttBwToLegacy(RttBw type) {
+ switch (type) {
+ case RttBw::BW_5MHZ:
+ return legacy_hal::WIFI_RTT_BW_5;
+ case RttBw::BW_10MHZ:
+ return legacy_hal::WIFI_RTT_BW_10;
+ case RttBw::BW_20MHZ:
+ return legacy_hal::WIFI_RTT_BW_20;
+ case RttBw::BW_40MHZ:
+ return legacy_hal::WIFI_RTT_BW_40;
+ case RttBw::BW_80MHZ:
+ return legacy_hal::WIFI_RTT_BW_80;
+ case RttBw::BW_160MHZ:
+ return legacy_hal::WIFI_RTT_BW_160;
+ case RttBw::BW_320MHZ:
+ return legacy_hal::WIFI_RTT_BW_320;
+ };
+ CHECK(false);
+}
+
+RttBw convertLegacyRttBwToAidl(legacy_hal::wifi_rtt_bw type) {
+ switch (type) {
+ case legacy_hal::WIFI_RTT_BW_5:
+ return RttBw::BW_5MHZ;
+ case legacy_hal::WIFI_RTT_BW_10:
+ return RttBw::BW_10MHZ;
+ case legacy_hal::WIFI_RTT_BW_20:
+ return RttBw::BW_20MHZ;
+ case legacy_hal::WIFI_RTT_BW_40:
+ return RttBw::BW_40MHZ;
+ case legacy_hal::WIFI_RTT_BW_80:
+ return RttBw::BW_80MHZ;
+ case legacy_hal::WIFI_RTT_BW_160:
+ return RttBw::BW_160MHZ;
+ case legacy_hal::WIFI_RTT_BW_320:
+ return RttBw::BW_320MHZ;
+ };
+ CHECK(false) << "Unknown legacy type: " << type;
+}
+
+legacy_hal::wifi_motion_pattern convertAidlRttMotionPatternToLegacy(RttMotionPattern type) {
+ switch (type) {
+ case RttMotionPattern::NOT_EXPECTED:
+ return legacy_hal::WIFI_MOTION_NOT_EXPECTED;
+ case RttMotionPattern::EXPECTED:
+ return legacy_hal::WIFI_MOTION_EXPECTED;
+ case RttMotionPattern::UNKNOWN:
+ return legacy_hal::WIFI_MOTION_UNKNOWN;
+ };
+ CHECK(false);
+}
+
+WifiRatePreamble convertLegacyWifiRatePreambleToAidl(uint8_t preamble) {
+ switch (preamble) {
+ case 0:
+ return WifiRatePreamble::OFDM;
+ case 1:
+ return WifiRatePreamble::CCK;
+ case 2:
+ return WifiRatePreamble::HT;
+ case 3:
+ return WifiRatePreamble::VHT;
+ case 4:
+ return WifiRatePreamble::HE;
+ case 5:
+ return WifiRatePreamble::EHT;
+ default:
+ return WifiRatePreamble::RESERVED;
+ };
+ CHECK(false) << "Unknown legacy preamble: " << preamble;
+}
+
+WifiRateNss convertLegacyWifiRateNssToAidl(uint8_t nss) {
+ switch (nss) {
+ case 0:
+ return WifiRateNss::NSS_1x1;
+ case 1:
+ return WifiRateNss::NSS_2x2;
+ case 2:
+ return WifiRateNss::NSS_3x3;
+ case 3:
+ return WifiRateNss::NSS_4x4;
+ };
+ CHECK(false) << "Unknown legacy nss: " << nss;
+ return {};
+}
+
+RttStatus convertLegacyRttStatusToAidl(legacy_hal::wifi_rtt_status status) {
+ switch (status) {
+ case legacy_hal::RTT_STATUS_SUCCESS:
+ return RttStatus::SUCCESS;
+ case legacy_hal::RTT_STATUS_FAILURE:
+ return RttStatus::FAILURE;
+ case legacy_hal::RTT_STATUS_FAIL_NO_RSP:
+ return RttStatus::FAIL_NO_RSP;
+ case legacy_hal::RTT_STATUS_FAIL_REJECTED:
+ return RttStatus::FAIL_REJECTED;
+ case legacy_hal::RTT_STATUS_FAIL_NOT_SCHEDULED_YET:
+ return RttStatus::FAIL_NOT_SCHEDULED_YET;
+ case legacy_hal::RTT_STATUS_FAIL_TM_TIMEOUT:
+ return RttStatus::FAIL_TM_TIMEOUT;
+ case legacy_hal::RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL:
+ return RttStatus::FAIL_AP_ON_DIFF_CHANNEL;
+ case legacy_hal::RTT_STATUS_FAIL_NO_CAPABILITY:
+ return RttStatus::FAIL_NO_CAPABILITY;
+ case legacy_hal::RTT_STATUS_ABORTED:
+ return RttStatus::ABORTED;
+ case legacy_hal::RTT_STATUS_FAIL_INVALID_TS:
+ return RttStatus::FAIL_INVALID_TS;
+ case legacy_hal::RTT_STATUS_FAIL_PROTOCOL:
+ return RttStatus::FAIL_PROTOCOL;
+ case legacy_hal::RTT_STATUS_FAIL_SCHEDULE:
+ return RttStatus::FAIL_SCHEDULE;
+ case legacy_hal::RTT_STATUS_FAIL_BUSY_TRY_LATER:
+ return RttStatus::FAIL_BUSY_TRY_LATER;
+ case legacy_hal::RTT_STATUS_INVALID_REQ:
+ return RttStatus::INVALID_REQ;
+ case legacy_hal::RTT_STATUS_NO_WIFI:
+ return RttStatus::NO_WIFI;
+ case legacy_hal::RTT_STATUS_FAIL_FTM_PARAM_OVERRIDE:
+ return RttStatus::FAIL_FTM_PARAM_OVERRIDE;
+ case legacy_hal::RTT_STATUS_NAN_RANGING_PROTOCOL_FAILURE:
+ return RttStatus::NAN_RANGING_PROTOCOL_FAILURE;
+ case legacy_hal::RTT_STATUS_NAN_RANGING_CONCURRENCY_NOT_SUPPORTED:
+ return RttStatus::NAN_RANGING_CONCURRENCY_NOT_SUPPORTED;
+ };
+ CHECK(false) << "Unknown legacy status: " << status;
+}
+
+bool convertAidlWifiChannelInfoToLegacy(const WifiChannelInfo& aidl_info,
+ legacy_hal::wifi_channel_info* legacy_info) {
+ if (!legacy_info) {
+ return false;
+ }
+ *legacy_info = {};
+ legacy_info->width = convertAidlWifiChannelWidthToLegacy(aidl_info.width);
+ legacy_info->center_freq = aidl_info.centerFreq;
+ legacy_info->center_freq0 = aidl_info.centerFreq0;
+ legacy_info->center_freq1 = aidl_info.centerFreq1;
+ return true;
+}
+
+bool convertLegacyWifiChannelInfoToAidl(const legacy_hal::wifi_channel_info& legacy_info,
+ WifiChannelInfo* aidl_info) {
+ if (!aidl_info) {
+ return false;
+ }
+ *aidl_info = {};
+ aidl_info->width = convertLegacyWifiChannelWidthToAidl(legacy_info.width);
+ aidl_info->centerFreq = legacy_info.center_freq;
+ aidl_info->centerFreq0 = legacy_info.center_freq0;
+ aidl_info->centerFreq1 = legacy_info.center_freq1;
+ return true;
+}
+
+bool convertAidlRttConfigToLegacy(const RttConfig& aidl_config,
+ legacy_hal::wifi_rtt_config* legacy_config) {
+ if (!legacy_config) {
+ return false;
+ }
+ *legacy_config = {};
+ CHECK(aidl_config.addr.size() == sizeof(legacy_config->addr));
+ memcpy(legacy_config->addr, aidl_config.addr.data(), aidl_config.addr.size());
+ legacy_config->type = convertAidlRttTypeToLegacy(aidl_config.type);
+ legacy_config->peer = convertAidlRttPeerTypeToLegacy(aidl_config.peer);
+ if (!convertAidlWifiChannelInfoToLegacy(aidl_config.channel, &legacy_config->channel)) {
+ return false;
+ }
+ legacy_config->burst_period = aidl_config.burstPeriod;
+ legacy_config->num_burst = aidl_config.numBurst;
+ legacy_config->num_frames_per_burst = aidl_config.numFramesPerBurst;
+ legacy_config->num_retries_per_rtt_frame = aidl_config.numRetriesPerRttFrame;
+ legacy_config->num_retries_per_ftmr = aidl_config.numRetriesPerFtmr;
+ legacy_config->LCI_request = aidl_config.mustRequestLci;
+ legacy_config->LCR_request = aidl_config.mustRequestLcr;
+ legacy_config->burst_duration = aidl_config.burstDuration;
+ legacy_config->preamble = convertAidlRttPreambleToLegacy(aidl_config.preamble);
+ legacy_config->bw = convertAidlRttBwToLegacy(aidl_config.bw);
+ return true;
+}
+
+bool convertAidlVectorOfRttConfigToLegacy(
+ const std::vector<RttConfig>& aidl_configs,
+ std::vector<legacy_hal::wifi_rtt_config>* legacy_configs) {
+ if (!legacy_configs) {
+ return false;
+ }
+ *legacy_configs = {};
+ for (const auto& aidl_config : aidl_configs) {
+ legacy_hal::wifi_rtt_config legacy_config;
+ if (!convertAidlRttConfigToLegacy(aidl_config, &legacy_config)) {
+ return false;
+ }
+ legacy_configs->push_back(legacy_config);
+ }
+ return true;
+}
+
+bool convertAidlRttLciInformationToLegacy(const RttLciInformation& aidl_info,
+ legacy_hal::wifi_lci_information* legacy_info) {
+ if (!legacy_info) {
+ return false;
+ }
+ *legacy_info = {};
+ legacy_info->latitude = aidl_info.latitude;
+ legacy_info->longitude = aidl_info.longitude;
+ legacy_info->altitude = aidl_info.altitude;
+ legacy_info->latitude_unc = aidl_info.latitudeUnc;
+ legacy_info->longitude_unc = aidl_info.longitudeUnc;
+ legacy_info->altitude_unc = aidl_info.altitudeUnc;
+ legacy_info->motion_pattern = convertAidlRttMotionPatternToLegacy(aidl_info.motionPattern);
+ legacy_info->floor = aidl_info.floor;
+ legacy_info->height_above_floor = aidl_info.heightAboveFloor;
+ legacy_info->height_unc = aidl_info.heightUnc;
+ return true;
+}
+
+bool convertAidlRttLcrInformationToLegacy(const RttLcrInformation& aidl_info,
+ legacy_hal::wifi_lcr_information* legacy_info) {
+ if (!legacy_info) {
+ return false;
+ }
+ *legacy_info = {};
+ CHECK(aidl_info.countryCode.size() == sizeof(legacy_info->country_code));
+ memcpy(legacy_info->country_code, aidl_info.countryCode.data(), aidl_info.countryCode.size());
+ if (aidl_info.civicInfo.size() > sizeof(legacy_info->civic_info)) {
+ return false;
+ }
+ legacy_info->length = aidl_info.civicInfo.size();
+ memcpy(legacy_info->civic_info, aidl_info.civicInfo.c_str(), aidl_info.civicInfo.size());
+ return true;
+}
+
+bool convertAidlRttResponderToLegacy(const RttResponder& aidl_responder,
+ legacy_hal::wifi_rtt_responder* legacy_responder) {
+ if (!legacy_responder) {
+ return false;
+ }
+ *legacy_responder = {};
+ if (!convertAidlWifiChannelInfoToLegacy(aidl_responder.channel, &legacy_responder->channel)) {
+ return false;
+ }
+ legacy_responder->preamble = convertAidlRttPreambleToLegacy(aidl_responder.preamble);
+ return true;
+}
+
+bool convertLegacyRttResponderToAidl(const legacy_hal::wifi_rtt_responder& legacy_responder,
+ RttResponder* aidl_responder) {
+ if (!aidl_responder) {
+ return false;
+ }
+ *aidl_responder = {};
+ if (!convertLegacyWifiChannelInfoToAidl(legacy_responder.channel, &aidl_responder->channel)) {
+ return false;
+ }
+ aidl_responder->preamble = convertLegacyRttPreambleToAidl(legacy_responder.preamble);
+ return true;
+}
+
+bool convertLegacyRttCapabilitiesToAidl(
+ const legacy_hal::wifi_rtt_capabilities& legacy_capabilities,
+ RttCapabilities* aidl_capabilities) {
+ if (!aidl_capabilities) {
+ return false;
+ }
+ *aidl_capabilities = {};
+ aidl_capabilities->rttOneSidedSupported = legacy_capabilities.rtt_one_sided_supported;
+ aidl_capabilities->rttFtmSupported = legacy_capabilities.rtt_ftm_supported;
+ aidl_capabilities->lciSupported = legacy_capabilities.lci_support;
+ aidl_capabilities->lcrSupported = legacy_capabilities.lcr_support;
+ aidl_capabilities->responderSupported = legacy_capabilities.responder_supported;
+ int32_t preambleSupport = 0;
+ for (const auto flag : {legacy_hal::WIFI_RTT_PREAMBLE_LEGACY, legacy_hal::WIFI_RTT_PREAMBLE_HT,
+ legacy_hal::WIFI_RTT_PREAMBLE_VHT, legacy_hal::WIFI_RTT_PREAMBLE_HE,
+ legacy_hal::WIFI_RTT_PREAMBLE_EHT}) {
+ if (legacy_capabilities.preamble_support & flag) {
+ preambleSupport |= static_cast<std::underlying_type<RttPreamble>::type>(
+ convertLegacyRttPreambleToAidl(flag));
+ }
+ }
+ aidl_capabilities->preambleSupport = static_cast<RttPreamble>(preambleSupport);
+ int32_t bwSupport = 0;
+ for (const auto flag :
+ {legacy_hal::WIFI_RTT_BW_5, legacy_hal::WIFI_RTT_BW_10, legacy_hal::WIFI_RTT_BW_20,
+ legacy_hal::WIFI_RTT_BW_40, legacy_hal::WIFI_RTT_BW_80, legacy_hal::WIFI_RTT_BW_160,
+ legacy_hal::WIFI_RTT_BW_320}) {
+ if (legacy_capabilities.bw_support & flag) {
+ bwSupport |=
+ static_cast<std::underlying_type<RttBw>::type>(convertLegacyRttBwToAidl(flag));
+ }
+ }
+ aidl_capabilities->bwSupport = static_cast<RttBw>(bwSupport);
+ aidl_capabilities->mcVersion = legacy_capabilities.mc_version;
+ return true;
+}
+
+bool convertLegacyWifiRateInfoToAidl(const legacy_hal::wifi_rate& legacy_rate,
+ WifiRateInfo* aidl_rate) {
+ if (!aidl_rate) {
+ return false;
+ }
+ *aidl_rate = {};
+ aidl_rate->preamble = convertLegacyWifiRatePreambleToAidl(legacy_rate.preamble);
+ aidl_rate->nss = convertLegacyWifiRateNssToAidl(legacy_rate.nss);
+ aidl_rate->bw = convertLegacyWifiChannelWidthToAidl(
+ static_cast<legacy_hal::wifi_channel_width>(legacy_rate.bw));
+ aidl_rate->rateMcsIdx = legacy_rate.rateMcsIdx;
+ aidl_rate->bitRateInKbps = legacy_rate.bitrate;
+ return true;
+}
+
+bool convertLegacyRttResultToAidl(const legacy_hal::wifi_rtt_result& legacy_result,
+ RttResult* aidl_result) {
+ if (!aidl_result) {
+ return false;
+ }
+ *aidl_result = {};
+ aidl_result->addr = std::array<uint8_t, 6>();
+ CHECK(sizeof(legacy_result.addr) == aidl_result->addr.size());
+ std::copy(legacy_result.addr, legacy_result.addr + 6, std::begin(aidl_result->addr));
+ aidl_result->burstNum = legacy_result.burst_num;
+ aidl_result->measurementNumber = legacy_result.measurement_number;
+ aidl_result->successNumber = legacy_result.success_number;
+ aidl_result->numberPerBurstPeer = legacy_result.number_per_burst_peer;
+ aidl_result->status = convertLegacyRttStatusToAidl(legacy_result.status);
+ aidl_result->retryAfterDuration = legacy_result.retry_after_duration;
+ aidl_result->type = convertLegacyRttTypeToAidl(legacy_result.type);
+ aidl_result->rssi = legacy_result.rssi;
+ aidl_result->rssiSpread = legacy_result.rssi_spread;
+ if (!convertLegacyWifiRateInfoToAidl(legacy_result.tx_rate, &aidl_result->txRate)) {
+ return false;
+ }
+ if (!convertLegacyWifiRateInfoToAidl(legacy_result.rx_rate, &aidl_result->rxRate)) {
+ return false;
+ }
+ aidl_result->rtt = legacy_result.rtt;
+ aidl_result->rttSd = legacy_result.rtt_sd;
+ aidl_result->rttSpread = legacy_result.rtt_spread;
+ aidl_result->distanceInMm = legacy_result.distance_mm;
+ aidl_result->distanceSdInMm = legacy_result.distance_sd_mm;
+ aidl_result->distanceSpreadInMm = legacy_result.distance_spread_mm;
+ aidl_result->timeStampInUs = legacy_result.ts;
+ aidl_result->burstDurationInMs = legacy_result.burst_duration;
+ aidl_result->negotiatedBurstNum = legacy_result.negotiated_burst_num;
+ if (legacy_result.LCI && !convertLegacyIeToAidl(*legacy_result.LCI, &aidl_result->lci)) {
+ return false;
+ }
+ if (legacy_result.LCR && !convertLegacyIeToAidl(*legacy_result.LCR, &aidl_result->lcr)) {
+ return false;
+ }
+ return true;
+}
+
+bool convertLegacyVectorOfRttResultToAidl(
+ const std::vector<const legacy_hal::wifi_rtt_result*>& legacy_results,
+ std::vector<RttResult>* aidl_results) {
+ if (!aidl_results) {
+ return false;
+ }
+ *aidl_results = {};
+ for (const auto legacy_result : legacy_results) {
+ RttResult aidl_result;
+ if (!convertLegacyRttResultToAidl(*legacy_result, &aidl_result)) {
+ return false;
+ }
+ aidl_results->push_back(aidl_result);
+ }
+ return true;
+}
+
+legacy_hal::wifi_interface_type convertAidlIfaceTypeToLegacy(IfaceType aidl_interface_type) {
+ switch (aidl_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_IFACE:
+ return legacy_hal::WIFI_INTERFACE_TYPE_NAN;
+ }
+ CHECK(false);
+}
+
+legacy_hal::wifi_multi_sta_use_case convertAidlMultiStaUseCaseToLegacy(
+ IWifiChip::MultiStaUseCase use_case) {
+ switch (use_case) {
+ case IWifiChip::MultiStaUseCase::DUAL_STA_TRANSIENT_PREFER_PRIMARY:
+ return legacy_hal::WIFI_DUAL_STA_TRANSIENT_PREFER_PRIMARY;
+ case IWifiChip::MultiStaUseCase::DUAL_STA_NON_TRANSIENT_UNBIASED:
+ return legacy_hal::WIFI_DUAL_STA_NON_TRANSIENT_UNBIASED;
+ }
+ CHECK(false);
+}
+
+bool convertAidlCoexUnsafeChannelToLegacy(
+ const IWifiChip::CoexUnsafeChannel& aidl_unsafe_channel,
+ legacy_hal::wifi_coex_unsafe_channel* legacy_unsafe_channel) {
+ if (!legacy_unsafe_channel) {
+ return false;
+ }
+ *legacy_unsafe_channel = {};
+ switch (aidl_unsafe_channel.band) {
+ case WifiBand::BAND_24GHZ:
+ legacy_unsafe_channel->band = legacy_hal::WLAN_MAC_2_4_BAND;
+ break;
+ case WifiBand::BAND_5GHZ:
+ legacy_unsafe_channel->band = legacy_hal::WLAN_MAC_5_0_BAND;
+ break;
+ default:
+ return false;
+ };
+ legacy_unsafe_channel->channel = aidl_unsafe_channel.channel;
+ legacy_unsafe_channel->power_cap_dbm = aidl_unsafe_channel.powerCapDbm;
+ return true;
+}
+
+bool convertAidlVectorOfCoexUnsafeChannelToLegacy(
+ const std::vector<IWifiChip::CoexUnsafeChannel>& aidl_unsafe_channels,
+ std::vector<legacy_hal::wifi_coex_unsafe_channel>* legacy_unsafe_channels) {
+ if (!legacy_unsafe_channels) {
+ return false;
+ }
+ *legacy_unsafe_channels = {};
+ for (const auto& aidl_unsafe_channel : aidl_unsafe_channels) {
+ legacy_hal::wifi_coex_unsafe_channel legacy_unsafe_channel;
+ if (!aidl_struct_util::convertAidlCoexUnsafeChannelToLegacy(aidl_unsafe_channel,
+ &legacy_unsafe_channel)) {
+ return false;
+ }
+ legacy_unsafe_channels->push_back(legacy_unsafe_channel);
+ }
+ return true;
+}
+
+WifiAntennaMode convertLegacyAntennaConfigurationToAidl(uint32_t antenna_cfg) {
+ switch (antenna_cfg) {
+ case legacy_hal::WIFI_ANTENNA_1X1:
+ return WifiAntennaMode::WIFI_ANTENNA_MODE_1X1;
+ case legacy_hal::WIFI_ANTENNA_2X2:
+ return WifiAntennaMode::WIFI_ANTENNA_MODE_2X2;
+ case legacy_hal::WIFI_ANTENNA_3X3:
+ return WifiAntennaMode::WIFI_ANTENNA_MODE_3X3;
+ case legacy_hal::WIFI_ANTENNA_4X4:
+ return WifiAntennaMode::WIFI_ANTENNA_MODE_4X4;
+ default:
+ return WifiAntennaMode::WIFI_ANTENNA_MODE_UNSPECIFIED;
+ }
+}
+
+bool convertLegacyWifiRadioConfigurationToAidl(
+ legacy_hal::wifi_radio_configuration* radio_configuration,
+ WifiRadioConfiguration* aidl_radio_configuration) {
+ if (!aidl_radio_configuration) {
+ return false;
+ }
+ *aidl_radio_configuration = {};
+ aidl_radio_configuration->bandInfo =
+ aidl_struct_util::convertLegacyMacBandToAidlWifiBand(radio_configuration->band);
+ if (aidl_radio_configuration->bandInfo == WifiBand::BAND_UNSPECIFIED) {
+ LOG(ERROR) << "Unspecified band";
+ return false;
+ }
+ aidl_radio_configuration->antennaMode =
+ aidl_struct_util::convertLegacyAntennaConfigurationToAidl(
+ radio_configuration->antenna_cfg);
+ return true;
+}
+
+bool convertLegacyRadioCombinationsMatrixToAidl(
+ legacy_hal::wifi_radio_combination_matrix* legacy_matrix,
+ WifiRadioCombinationMatrix* aidl_matrix) {
+ if (!aidl_matrix || !legacy_matrix) {
+ return false;
+ }
+ *aidl_matrix = {};
+
+ int num_combinations = legacy_matrix->num_radio_combinations;
+ std::vector<WifiRadioCombination> radio_combinations_vec;
+ if (!num_combinations) {
+ LOG(ERROR) << "zero radio combinations";
+ return false;
+ }
+ wifi_radio_combination* l_radio_combinations_ptr = legacy_matrix->radio_combinations;
+ for (int i = 0; i < num_combinations; i++) {
+ int num_configurations = l_radio_combinations_ptr->num_radio_configurations;
+ WifiRadioCombination radioCombination;
+ std::vector<WifiRadioConfiguration> radio_configurations_vec;
+ if (!num_configurations) {
+ LOG(ERROR) << "zero radio configurations";
+ return false;
+ }
+ for (int j = 0; j < num_configurations; j++) {
+ WifiRadioConfiguration radioConfiguration;
+ wifi_radio_configuration* l_radio_configurations_ptr =
+ &l_radio_combinations_ptr->radio_configurations[j];
+ if (!aidl_struct_util::convertLegacyWifiRadioConfigurationToAidl(
+ l_radio_configurations_ptr, &radioConfiguration)) {
+ LOG(ERROR) << "Error converting wifi radio configuration";
+ return false;
+ }
+ radio_configurations_vec.push_back(radioConfiguration);
+ }
+ radioCombination.radioConfigurations = radio_configurations_vec;
+ radio_combinations_vec.push_back(radioCombination);
+ l_radio_combinations_ptr =
+ (wifi_radio_combination*)((u8*)l_radio_combinations_ptr +
+ sizeof(wifi_radio_combination) +
+ (sizeof(wifi_radio_configuration) * num_configurations));
+ }
+ aidl_matrix->radioCombinations = radio_combinations_vec;
+ return true;
+}
+
+} // namespace aidl_struct_util
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/aidl_struct_util.h b/wifi/aidl/default/aidl_struct_util.h
new file mode 100644
index 0000000..4ebfc10
--- /dev/null
+++ b/wifi/aidl/default/aidl_struct_util.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AIDL_STRUCT_UTIL_H_
+#define AIDL_STRUCT_UTIL_H_
+
+#include <aidl/android/hardware/wifi/IWifiChip.h>
+#include <aidl/android/hardware/wifi/IWifiChipEventCallback.h>
+#include <aidl/android/hardware/wifi/NanBandIndex.h>
+#include <aidl/android/hardware/wifi/WifiDebugRingBufferFlags.h>
+
+#include <vector>
+
+#include "wifi_legacy_hal.h"
+
+/**
+ * This file contains a bunch of functions to convert structs from the legacy
+ * HAL to AIDL and vice versa.
+ */
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace aidl_struct_util {
+
+// Chip conversion methods.
+bool convertLegacyFeaturesToAidlChipCapabilities(uint64_t legacy_feature_set,
+ uint32_t legacy_logger_feature_set,
+ uint32_t* aidl_caps);
+bool convertLegacyDebugRingBufferStatusToAidl(
+ const legacy_hal::wifi_ring_buffer_status& legacy_status,
+ WifiDebugRingBufferStatus* aidl_status);
+bool convertLegacyVectorOfDebugRingBufferStatusToAidl(
+ const std::vector<legacy_hal::wifi_ring_buffer_status>& legacy_status_vec,
+ std::vector<WifiDebugRingBufferStatus>* aidl_status_vec);
+bool convertLegacyWakeReasonStatsToAidl(const legacy_hal::WakeReasonStats& legacy_stats,
+ WifiDebugHostWakeReasonStats* aidl_stats);
+legacy_hal::wifi_power_scenario convertAidlTxPowerScenarioToLegacy(
+ IWifiChip::TxPowerScenario aidl_scenario);
+legacy_hal::wifi_latency_mode convertAidlLatencyModeToLegacy(
+ IWifiChip::LatencyMode aidl_latency_mode);
+bool convertLegacyWifiMacInfosToAidl(
+ const std::vector<legacy_hal::WifiMacInfo>& legacy_mac_infos,
+ std::vector<IWifiChipEventCallback::RadioModeInfo>* aidl_radio_mode_infos);
+legacy_hal::wifi_interface_type convertAidlIfaceTypeToLegacy(IfaceType aidl_interface_type);
+legacy_hal::wifi_multi_sta_use_case convertAidlMultiStaUseCaseToLegacy(
+ IWifiChip::MultiStaUseCase use_case);
+bool convertAidlCoexUnsafeChannelToLegacy(
+ const IWifiChip::CoexUnsafeChannel& aidl_unsafe_channel,
+ legacy_hal::wifi_coex_unsafe_channel* legacy_unsafe_channel);
+bool convertAidlVectorOfCoexUnsafeChannelToLegacy(
+ const std::vector<IWifiChip::CoexUnsafeChannel>& aidl_unsafe_channels,
+ std::vector<legacy_hal::wifi_coex_unsafe_channel>* legacy_unsafe_channels);
+bool convertLegacyRadioCombinationsMatrixToAidl(
+ legacy_hal::wifi_radio_combination_matrix* legacy_matrix,
+ WifiRadioCombinationMatrix* aidl_matrix);
+WifiBand convertLegacyMacBandToAidlWifiBand(uint32_t band);
+WifiAntennaMode convertLegacyAntennaConfigurationToAidl(uint32_t antenna_cfg);
+
+// STA iface conversion methods.
+bool convertLegacyFeaturesToAidlStaCapabilities(uint64_t legacy_feature_set,
+ uint32_t legacy_logger_feature_set,
+ uint32_t* aidl_caps);
+bool convertLegacyApfCapabilitiesToAidl(const legacy_hal::PacketFilterCapabilities& legacy_caps,
+ StaApfPacketFilterCapabilities* aidl_caps);
+bool convertLegacyGscanCapabilitiesToAidl(const legacy_hal::wifi_gscan_capabilities& legacy_caps,
+ StaBackgroundScanCapabilities* aidl_caps);
+legacy_hal::wifi_band convertAidlWifiBandToLegacy(WifiBand band);
+bool convertAidlGscanParamsToLegacy(const StaBackgroundScanParameters& aidl_scan_params,
+ legacy_hal::wifi_scan_cmd_params* legacy_scan_params);
+// |has_ie_data| indicates whether or not the wifi_scan_result includes 802.11
+// Information Elements (IEs)
+bool convertLegacyGscanResultToAidl(const legacy_hal::wifi_scan_result& legacy_scan_result,
+ bool has_ie_data, StaScanResult* aidl_scan_result);
+// |cached_results| is assumed to not include IEs.
+bool convertLegacyVectorOfCachedGscanResultsToAidl(
+ const std::vector<legacy_hal::wifi_cached_scan_results>& legacy_cached_scan_results,
+ std::vector<StaScanData>* aidl_scan_datas);
+bool convertLegacyLinkLayerStatsToAidl(const legacy_hal::LinkLayerStats& legacy_stats,
+ StaLinkLayerStats* aidl_stats);
+bool convertLegacyRoamingCapabilitiesToAidl(
+ const legacy_hal::wifi_roaming_capabilities& legacy_caps,
+ StaRoamingCapabilities* aidl_caps);
+bool convertAidlRoamingConfigToLegacy(const StaRoamingConfig& aidl_config,
+ legacy_hal::wifi_roaming_config* legacy_config);
+legacy_hal::fw_roaming_state_t convertAidlRoamingStateToLegacy(StaRoamingState state);
+bool convertLegacyVectorOfDebugTxPacketFateToAidl(
+ const std::vector<legacy_hal::wifi_tx_report>& legacy_fates,
+ std::vector<WifiDebugTxPacketFateReport>* aidl_fates);
+bool convertLegacyVectorOfDebugRxPacketFateToAidl(
+ const std::vector<legacy_hal::wifi_rx_report>& legacy_fates,
+ std::vector<WifiDebugRxPacketFateReport>* aidl_fates);
+
+// NAN iface conversion methods.
+void convertToNanStatus(legacy_hal::NanStatusType type, const char* str, size_t max_len,
+ NanStatus* nanStatus);
+bool convertAidlNanEnableRequestToLegacy(const NanEnableRequest& aidl_request1,
+ const NanConfigRequestSupplemental& aidl_request2,
+ legacy_hal::NanEnableRequest* legacy_request);
+bool convertAidlNanConfigRequestToLegacy(const NanConfigRequest& aidl_request1,
+ const NanConfigRequestSupplemental& aidl_request2,
+ legacy_hal::NanConfigRequest* legacy_request);
+bool convertAidlNanPublishRequestToLegacy(const NanPublishRequest& aidl_request,
+ legacy_hal::NanPublishRequest* legacy_request);
+bool convertAidlNanSubscribeRequestToLegacy(const NanSubscribeRequest& aidl_request,
+ legacy_hal::NanSubscribeRequest* legacy_request);
+bool convertAidlNanTransmitFollowupRequestToLegacy(
+ const NanTransmitFollowupRequest& aidl_request,
+ legacy_hal::NanTransmitFollowupRequest* legacy_request);
+bool convertAidlNanDataPathInitiatorRequestToLegacy(
+ const NanInitiateDataPathRequest& aidl_request,
+ legacy_hal::NanDataPathInitiatorRequest* legacy_request);
+bool convertAidlNanDataPathIndicationResponseToLegacy(
+ const NanRespondToDataPathIndicationRequest& aidl_response,
+ legacy_hal::NanDataPathIndicationResponse* legacy_response);
+bool convertLegacyNanResponseHeaderToAidl(const legacy_hal::NanResponseMsg& legacy_response,
+ NanStatus* nanStatus);
+bool convertLegacyNanCapabilitiesResponseToAidl(const legacy_hal::NanCapabilities& legacy_response,
+ NanCapabilities* aidl_response);
+bool convertLegacyNanMatchIndToAidl(const legacy_hal::NanMatchInd& legacy_ind,
+ NanMatchInd* aidl_ind);
+bool convertLegacyNanFollowupIndToAidl(const legacy_hal::NanFollowupInd& legacy_ind,
+ NanFollowupReceivedInd* aidl_ind);
+bool convertLegacyNanDataPathRequestIndToAidl(const legacy_hal::NanDataPathRequestInd& legacy_ind,
+ NanDataPathRequestInd* aidl_ind);
+bool convertLegacyNanDataPathConfirmIndToAidl(const legacy_hal::NanDataPathConfirmInd& legacy_ind,
+ NanDataPathConfirmInd* aidl_ind);
+bool convertLegacyNanDataPathScheduleUpdateIndToAidl(
+ const legacy_hal::NanDataPathScheduleUpdateInd& legacy_ind,
+ NanDataPathScheduleUpdateInd* aidl_ind);
+
+// RTT controller conversion methods.
+bool convertAidlVectorOfRttConfigToLegacy(const std::vector<RttConfig>& aidl_configs,
+ std::vector<legacy_hal::wifi_rtt_config>* legacy_configs);
+bool convertAidlRttLciInformationToLegacy(const RttLciInformation& aidl_info,
+ legacy_hal::wifi_lci_information* legacy_info);
+bool convertAidlRttLcrInformationToLegacy(const RttLcrInformation& aidl_info,
+ legacy_hal::wifi_lcr_information* legacy_info);
+bool convertAidlRttResponderToLegacy(const RttResponder& aidl_responder,
+ legacy_hal::wifi_rtt_responder* legacy_responder);
+bool convertAidlWifiChannelInfoToLegacy(const WifiChannelInfo& aidl_info,
+ legacy_hal::wifi_channel_info* legacy_info);
+bool convertLegacyRttResponderToAidl(const legacy_hal::wifi_rtt_responder& legacy_responder,
+ RttResponder* aidl_responder);
+bool convertLegacyRttCapabilitiesToAidl(
+ const legacy_hal::wifi_rtt_capabilities& legacy_capabilities,
+ RttCapabilities* aidl_capabilities);
+bool convertLegacyVectorOfRttResultToAidl(
+ const std::vector<const legacy_hal::wifi_rtt_result*>& legacy_results,
+ std::vector<RttResult>* aidl_results);
+uint32_t convertAidlWifiBandToLegacyMacBand(WifiBand band);
+uint32_t convertAidlWifiIfaceModeToLegacy(uint32_t aidl_iface_mask);
+uint32_t convertAidlUsableChannelFilterToLegacy(uint32_t aidl_filter_mask);
+bool convertLegacyWifiUsableChannelsToAidl(
+ const std::vector<legacy_hal::wifi_usable_channel>& legacy_usable_channels,
+ std::vector<WifiUsableChannel>* aidl_usable_channels);
+bool convertLegacyPeerInfoStatsToAidl(const legacy_hal::WifiPeerInfo& legacy_peer_info_stats,
+ StaPeerInfo* aidl_peer_info_stats);
+bool convertLegacyWifiRateInfoToAidl(const legacy_hal::wifi_rate& legacy_rate,
+ WifiRateInfo* aidl_rate);
+} // namespace aidl_struct_util
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // AIDL_STRUCT_UTIL_H_
diff --git a/wifi/aidl/default/aidl_sync_util.cpp b/wifi/aidl/default/aidl_sync_util.cpp
new file mode 100644
index 0000000..d81eb81
--- /dev/null
+++ b/wifi/aidl/default/aidl_sync_util.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "aidl_sync_util.h"
+
+namespace {
+std::recursive_mutex g_mutex;
+} // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace aidl_sync_util {
+
+std::unique_lock<std::recursive_mutex> acquireGlobalLock() {
+ return std::unique_lock<std::recursive_mutex>{g_mutex};
+}
+
+} // namespace aidl_sync_util
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/aidl_sync_util.h b/wifi/aidl/default/aidl_sync_util.h
new file mode 100644
index 0000000..a61cd3f
--- /dev/null
+++ b/wifi/aidl/default/aidl_sync_util.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AIDL_SYNC_UTIL_H_
+#define AIDL_SYNC_UTIL_H_
+
+#include <mutex>
+
+// Utility that provides a global lock to synchronize access between
+// the AIDL thread and the legacy HAL's event loop.
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace aidl_sync_util {
+std::unique_lock<std::recursive_mutex> acquireGlobalLock();
+} // namespace aidl_sync_util
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+#endif // AIDL_SYNC_UTIL_H_
diff --git a/wifi/aidl/default/android.hardware.wifi-service-lazy.rc b/wifi/aidl/default/android.hardware.wifi-service-lazy.rc
new file mode 100644
index 0000000..12d3ff7
--- /dev/null
+++ b/wifi/aidl/default/android.hardware.wifi-service-lazy.rc
@@ -0,0 +1,8 @@
+service vendor.wifi_hal_legacy /vendor/bin/hw/android.hardware.wifi-service-lazy
+ interface aidl android.hardware.wifi.IWifi/default
+ oneshot
+ disabled
+ class hal
+ capabilities NET_ADMIN NET_RAW SYS_MODULE
+ user wifi
+ group wifi gps
diff --git a/wifi/aidl/default/android.hardware.wifi-service.rc b/wifi/aidl/default/android.hardware.wifi-service.rc
new file mode 100644
index 0000000..ec8acf5
--- /dev/null
+++ b/wifi/aidl/default/android.hardware.wifi-service.rc
@@ -0,0 +1,6 @@
+service vendor.wifi_hal_legacy /vendor/bin/hw/android.hardware.wifi-service
+ interface aidl android.hardware.wifi.IWifi/default
+ class hal
+ capabilities NET_ADMIN NET_RAW SYS_MODULE
+ user wifi
+ group wifi gps
diff --git a/wifi/aidl/default/android.hardware.wifi-service.xml b/wifi/aidl/default/android.hardware.wifi-service.xml
new file mode 100644
index 0000000..5398ee7
--- /dev/null
+++ b/wifi/aidl/default/android.hardware.wifi-service.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.wifi</name>
+ <fqname>IWifi/default</fqname>
+ </hal>
+</manifest>
diff --git a/wifi/aidl/default/ringbuffer.cpp b/wifi/aidl/default/ringbuffer.cpp
new file mode 100644
index 0000000..9d08a73
--- /dev/null
+++ b/wifi/aidl/default/ringbuffer.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ringbuffer.h"
+
+#include <android-base/logging.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+Ringbuffer::Ringbuffer(size_t maxSize) : size_(0), maxSize_(maxSize) {}
+
+enum Ringbuffer::AppendStatus Ringbuffer::append(const std::vector<uint8_t>& input) {
+ if (input.size() == 0) {
+ return AppendStatus::FAIL_IP_BUFFER_ZERO;
+ }
+ if (input.size() > maxSize_) {
+ LOG(INFO) << "Oversized message of " << input.size() << " bytes is dropped";
+ return AppendStatus::FAIL_IP_BUFFER_EXCEEDED_MAXSIZE;
+ }
+ data_.push_back(input);
+ size_ += input.size() * sizeof(input[0]);
+ while (size_ > maxSize_) {
+ if (data_.front().size() <= 0 || data_.front().size() > maxSize_) {
+ LOG(ERROR) << "First buffer in the ring buffer is Invalid. Size: "
+ << data_.front().size();
+ return AppendStatus::FAIL_RING_BUFFER_CORRUPTED;
+ }
+ size_ -= data_.front().size() * sizeof(data_.front()[0]);
+ data_.pop_front();
+ }
+ return AppendStatus::SUCCESS;
+}
+
+const std::list<std::vector<uint8_t>>& Ringbuffer::getData() const {
+ return data_;
+}
+
+void Ringbuffer::clear() {
+ data_.clear();
+ size_ = 0;
+}
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/ringbuffer.h b/wifi/aidl/default/ringbuffer.h
new file mode 100644
index 0000000..80c0c11
--- /dev/null
+++ b/wifi/aidl/default/ringbuffer.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RINGBUFFER_H_
+#define RINGBUFFER_H_
+
+#include <list>
+#include <vector>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+/**
+ * Ringbuffer object used to store debug data.
+ */
+class Ringbuffer {
+ public:
+ // Error codes for the append ring buffer operation
+ enum AppendStatus {
+ SUCCESS,
+ FAIL_GENERIC,
+ FAIL_IP_BUFFER_ZERO,
+ FAIL_IP_BUFFER_EXCEEDED_MAXSIZE,
+ FAIL_RING_BUFFER_CORRUPTED
+ };
+ explicit Ringbuffer(size_t maxSize);
+
+ // Appends the data buffer and deletes from the front until buffer is
+ // within |maxSize_|.
+ enum AppendStatus append(const std::vector<uint8_t>& input);
+ const std::list<std::vector<uint8_t>>& getData() const;
+ void clear();
+
+ private:
+ std::list<std::vector<uint8_t>> data_;
+ size_t size_;
+ size_t maxSize_;
+};
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // RINGBUFFER_H_
diff --git a/wifi/aidl/default/service.cpp b/wifi/aidl/default/service.cpp
new file mode 100644
index 0000000..789a7a5
--- /dev/null
+++ b/wifi/aidl/default/service.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <signal.h>
+
+#include "wifi.h"
+#include "wifi_feature_flags.h"
+#include "wifi_legacy_hal.h"
+#include "wifi_legacy_hal_factory.h"
+#include "wifi_mode_controller.h"
+
+using aidl::android::hardware::wifi::feature_flags::WifiFeatureFlags;
+using aidl::android::hardware::wifi::legacy_hal::WifiLegacyHal;
+using aidl::android::hardware::wifi::legacy_hal::WifiLegacyHalFactory;
+using aidl::android::hardware::wifi::mode_controller::WifiModeController;
+
+#ifdef LAZY_SERVICE
+const bool kLazyService = true;
+#else
+const bool kLazyService = false;
+#endif
+
+int main(int /*argc*/, char** argv) {
+ signal(SIGPIPE, SIG_IGN);
+ android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
+ LOG(INFO) << "Wifi Hal is booting up...";
+
+ // Prepare the RPC-serving thread pool. Allocate 1 thread in the pool,
+ // which our main thread will join below.
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+
+ const auto iface_tool = std::make_shared<::android::wifi_system::InterfaceTool>();
+ const auto legacy_hal_factory = std::make_shared<WifiLegacyHalFactory>(iface_tool);
+
+ // Setup binder service
+ std::shared_ptr<aidl::android::hardware::wifi::Wifi> service =
+ ndk::SharedRefBase::make<aidl::android::hardware::wifi::Wifi>(
+ iface_tool, legacy_hal_factory, std::make_shared<WifiModeController>(),
+ std::make_shared<WifiFeatureFlags>());
+ std::string instance =
+ std::string() + aidl::android::hardware::wifi::Wifi::descriptor + "/default";
+ if (kLazyService) {
+ auto result =
+ AServiceManager_registerLazyService(service->asBinder().get(), instance.c_str());
+ CHECK_EQ(result, STATUS_OK) << "Failed to register lazy wifi HAL";
+ } else {
+ auto result = AServiceManager_addService(service->asBinder().get(), instance.c_str());
+ CHECK_EQ(result, STATUS_OK) << "Failed to register wifi HAL";
+ }
+
+ ABinderProcess_startThreadPool();
+ LOG(INFO) << "Joining RPC thread pool";
+ ABinderProcess_joinThreadPool();
+
+ LOG(INFO) << "Wifi Hal is terminating...";
+ return 0;
+}
diff --git a/wifi/aidl/default/tests/aidl_struct_util_unit_tests.cpp b/wifi/aidl/default/tests/aidl_struct_util_unit_tests.cpp
new file mode 100644
index 0000000..4a69c24
--- /dev/null
+++ b/wifi/aidl/default/tests/aidl_struct_util_unit_tests.cpp
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <gmock/gmock.h>
+
+#include "aidl_struct_util.h"
+
+using testing::Test;
+
+namespace {
+constexpr uint32_t kMacId1 = 1;
+constexpr uint32_t kMacId2 = 2;
+constexpr uint32_t kIfaceChannel1 = 3;
+constexpr uint32_t kIfaceChannel2 = 5;
+constexpr char kIfaceName1[] = "wlan0";
+constexpr char kIfaceName2[] = "wlan1";
+} // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+class AidlStructUtilTest : public Test {};
+
+TEST_F(AidlStructUtilTest, CanConvertLegacyWifiMacInfosToAidlWithOneMac) {
+ std::vector<legacy_hal::WifiMacInfo> legacy_mac_infos;
+ legacy_hal::WifiMacInfo legacy_mac_info1 = {
+ .wlan_mac_id = kMacId1,
+ .mac_band = legacy_hal::WLAN_MAC_5_0_BAND | legacy_hal::WLAN_MAC_2_4_BAND};
+ legacy_hal::WifiIfaceInfo legacy_iface_info1 = {.name = kIfaceName1, .channel = kIfaceChannel1};
+ legacy_hal::WifiIfaceInfo legacy_iface_info2 = {.name = kIfaceName2, .channel = kIfaceChannel2};
+ legacy_mac_info1.iface_infos.push_back(legacy_iface_info1);
+ legacy_mac_info1.iface_infos.push_back(legacy_iface_info2);
+ legacy_mac_infos.push_back(legacy_mac_info1);
+
+ std::vector<IWifiChipEventCallback::RadioModeInfo> aidl_radio_mode_infos;
+ ASSERT_TRUE(aidl_struct_util::convertLegacyWifiMacInfosToAidl(legacy_mac_infos,
+ &aidl_radio_mode_infos));
+
+ ASSERT_EQ(1u, aidl_radio_mode_infos.size());
+ auto aidl_radio_mode_info1 = aidl_radio_mode_infos[0];
+ EXPECT_EQ(legacy_mac_info1.wlan_mac_id, (uint32_t)aidl_radio_mode_info1.radioId);
+ EXPECT_EQ(WifiBand::BAND_24GHZ_5GHZ, aidl_radio_mode_info1.bandInfo);
+ ASSERT_EQ(2u, aidl_radio_mode_info1.ifaceInfos.size());
+ auto aidl_iface_info1 = aidl_radio_mode_info1.ifaceInfos[0];
+ EXPECT_EQ(legacy_iface_info1.name, aidl_iface_info1.name);
+ EXPECT_EQ(static_cast<int32_t>(legacy_iface_info1.channel), aidl_iface_info1.channel);
+ auto aidl_iface_info2 = aidl_radio_mode_info1.ifaceInfos[1];
+ EXPECT_EQ(legacy_iface_info2.name, aidl_iface_info2.name);
+ EXPECT_EQ(static_cast<int32_t>(legacy_iface_info2.channel), aidl_iface_info2.channel);
+}
+
+TEST_F(AidlStructUtilTest, CanConvertLegacyWifiMacInfosToAidlWithTwoMac) {
+ std::vector<legacy_hal::WifiMacInfo> legacy_mac_infos;
+ legacy_hal::WifiMacInfo legacy_mac_info1 = {.wlan_mac_id = kMacId1,
+ .mac_band = legacy_hal::WLAN_MAC_5_0_BAND};
+ legacy_hal::WifiIfaceInfo legacy_iface_info1 = {.name = kIfaceName1, .channel = kIfaceChannel1};
+ legacy_hal::WifiMacInfo legacy_mac_info2 = {.wlan_mac_id = kMacId2,
+ .mac_band = legacy_hal::WLAN_MAC_2_4_BAND};
+ legacy_hal::WifiIfaceInfo legacy_iface_info2 = {.name = kIfaceName2, .channel = kIfaceChannel2};
+ legacy_mac_info1.iface_infos.push_back(legacy_iface_info1);
+ legacy_mac_infos.push_back(legacy_mac_info1);
+ legacy_mac_info2.iface_infos.push_back(legacy_iface_info2);
+ legacy_mac_infos.push_back(legacy_mac_info2);
+
+ std::vector<IWifiChipEventCallback::RadioModeInfo> aidl_radio_mode_infos;
+ ASSERT_TRUE(aidl_struct_util::convertLegacyWifiMacInfosToAidl(legacy_mac_infos,
+ &aidl_radio_mode_infos));
+
+ ASSERT_EQ(2u, aidl_radio_mode_infos.size());
+
+ // Find mac info 1.
+ const auto aidl_radio_mode_info1 =
+ std::find_if(aidl_radio_mode_infos.begin(), aidl_radio_mode_infos.end(),
+ [&legacy_mac_info1](const IWifiChipEventCallback::RadioModeInfo& x) {
+ return (uint32_t)x.radioId == legacy_mac_info1.wlan_mac_id;
+ });
+ ASSERT_NE(aidl_radio_mode_infos.end(), aidl_radio_mode_info1);
+ EXPECT_EQ(WifiBand::BAND_5GHZ, aidl_radio_mode_info1->bandInfo);
+ ASSERT_EQ(1u, aidl_radio_mode_info1->ifaceInfos.size());
+ auto aidl_iface_info1 = aidl_radio_mode_info1->ifaceInfos[0];
+ EXPECT_EQ(legacy_iface_info1.name, aidl_iface_info1.name);
+ EXPECT_EQ(static_cast<int32_t>(legacy_iface_info1.channel), aidl_iface_info1.channel);
+
+ // Find mac info 2.
+ const auto aidl_radio_mode_info2 =
+ std::find_if(aidl_radio_mode_infos.begin(), aidl_radio_mode_infos.end(),
+ [&legacy_mac_info2](const IWifiChipEventCallback::RadioModeInfo& x) {
+ return (uint32_t)x.radioId == legacy_mac_info2.wlan_mac_id;
+ });
+ ASSERT_NE(aidl_radio_mode_infos.end(), aidl_radio_mode_info2);
+ EXPECT_EQ(WifiBand::BAND_24GHZ, aidl_radio_mode_info2->bandInfo);
+ ASSERT_EQ(1u, aidl_radio_mode_info2->ifaceInfos.size());
+ auto aidl_iface_info2 = aidl_radio_mode_info2->ifaceInfos[0];
+ EXPECT_EQ(legacy_iface_info2.name, aidl_iface_info2.name);
+ EXPECT_EQ(static_cast<int32_t>(legacy_iface_info2.channel), aidl_iface_info2.channel);
+}
+
+TEST_F(AidlStructUtilTest, canConvertLegacyLinkLayerStatsToAidl) {
+ legacy_hal::LinkLayerStats legacy_stats{};
+ legacy_stats.radios.push_back(legacy_hal::LinkLayerRadioStats{});
+ legacy_stats.radios.push_back(legacy_hal::LinkLayerRadioStats{});
+ legacy_stats.peers.push_back(legacy_hal::WifiPeerInfo{});
+ legacy_stats.peers.push_back(legacy_hal::WifiPeerInfo{});
+ legacy_stats.iface.beacon_rx = rand();
+ legacy_stats.iface.rssi_mgmt = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].rx_mpdu = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].tx_mpdu = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].mpdu_lost = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].retries = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_min = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_max = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_avg = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_num_samples = rand();
+
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].rx_mpdu = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].tx_mpdu = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].mpdu_lost = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].retries = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_min = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_max = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_avg = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_num_samples = rand();
+
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].rx_mpdu = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].tx_mpdu = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].mpdu_lost = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].retries = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_min = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_max = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_avg = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_num_samples = rand();
+
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].rx_mpdu = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].tx_mpdu = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].mpdu_lost = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].retries = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_min = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_max = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_avg = rand();
+ legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_num_samples = rand();
+
+ legacy_stats.iface.info.time_slicing_duty_cycle_percent = rand();
+ legacy_stats.iface.num_peers = 1;
+
+ for (auto& radio : legacy_stats.radios) {
+ radio.stats.radio = rand();
+ radio.stats.on_time = rand();
+ radio.stats.tx_time = rand();
+ radio.stats.rx_time = rand();
+ radio.stats.on_time_scan = rand();
+ radio.stats.on_time_nbd = rand();
+ radio.stats.on_time_gscan = rand();
+ radio.stats.on_time_roam_scan = rand();
+ radio.stats.on_time_pno_scan = rand();
+ radio.stats.on_time_hs20 = rand();
+ for (int i = 0; i < 4; i++) {
+ radio.tx_time_per_levels.push_back(rand());
+ }
+
+ legacy_hal::wifi_channel_stat channel_stat1 = {
+ .channel = {legacy_hal::WIFI_CHAN_WIDTH_20, 2437, 2437, 0},
+ .on_time = 0x1111,
+ .cca_busy_time = 0x55,
+ };
+ legacy_hal::wifi_channel_stat channel_stat2 = {
+ .channel = {legacy_hal::WIFI_CHAN_WIDTH_20, 5180, 5180, 0},
+ .on_time = 0x2222,
+ .cca_busy_time = 0x66,
+ };
+ radio.channel_stats.push_back(channel_stat1);
+ radio.channel_stats.push_back(channel_stat2);
+ }
+
+ for (auto& peer : legacy_stats.peers) {
+ peer.peer_info.bssload.sta_count = rand();
+ peer.peer_info.bssload.chan_util = rand();
+ wifi_rate_stat rate_stat1 = {
+ .rate = {3, 1, 2, 5, 0, 0},
+ .tx_mpdu = 0,
+ .rx_mpdu = 1,
+ .mpdu_lost = 2,
+ .retries = 3,
+ .retries_short = 4,
+ .retries_long = 5,
+ };
+ wifi_rate_stat rate_stat2 = {
+ .rate = {2, 2, 1, 6, 0, 1},
+ .tx_mpdu = 6,
+ .rx_mpdu = 7,
+ .mpdu_lost = 8,
+ .retries = 9,
+ .retries_short = 10,
+ .retries_long = 11,
+ };
+ peer.rate_stats.push_back(rate_stat1);
+ peer.rate_stats.push_back(rate_stat2);
+ }
+
+ StaLinkLayerStats converted{};
+ aidl_struct_util::convertLegacyLinkLayerStatsToAidl(legacy_stats, &converted);
+ EXPECT_EQ(legacy_stats.iface.beacon_rx, (uint32_t)converted.iface.beaconRx);
+ EXPECT_EQ(legacy_stats.iface.rssi_mgmt, converted.iface.avgRssiMgmt);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].rx_mpdu,
+ converted.iface.wmeBePktStats.rxMpdu);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].tx_mpdu,
+ converted.iface.wmeBePktStats.txMpdu);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].mpdu_lost,
+ converted.iface.wmeBePktStats.lostMpdu);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].retries,
+ converted.iface.wmeBePktStats.retries);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_min,
+ (uint32_t)converted.iface.wmeBeContentionTimeStats.contentionTimeMinInUsec);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_max,
+ (uint32_t)converted.iface.wmeBeContentionTimeStats.contentionTimeMaxInUsec);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_time_avg,
+ (uint32_t)converted.iface.wmeBeContentionTimeStats.contentionTimeAvgInUsec);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BE].contention_num_samples,
+ (uint32_t)converted.iface.wmeBeContentionTimeStats.contentionNumSamples);
+
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].rx_mpdu,
+ converted.iface.wmeBkPktStats.rxMpdu);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].tx_mpdu,
+ converted.iface.wmeBkPktStats.txMpdu);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].mpdu_lost,
+ converted.iface.wmeBkPktStats.lostMpdu);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].retries,
+ converted.iface.wmeBkPktStats.retries);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_min,
+ (uint32_t)converted.iface.wmeBkContentionTimeStats.contentionTimeMinInUsec);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_max,
+ (uint32_t)converted.iface.wmeBkContentionTimeStats.contentionTimeMaxInUsec);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_time_avg,
+ (uint32_t)converted.iface.wmeBkContentionTimeStats.contentionTimeAvgInUsec);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_BK].contention_num_samples,
+ (uint32_t)converted.iface.wmeBkContentionTimeStats.contentionNumSamples);
+
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].rx_mpdu,
+ converted.iface.wmeViPktStats.rxMpdu);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].tx_mpdu,
+ converted.iface.wmeViPktStats.txMpdu);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].mpdu_lost,
+ converted.iface.wmeViPktStats.lostMpdu);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].retries,
+ converted.iface.wmeViPktStats.retries);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_min,
+ (uint32_t)converted.iface.wmeViContentionTimeStats.contentionTimeMinInUsec);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_max,
+ (uint32_t)converted.iface.wmeViContentionTimeStats.contentionTimeMaxInUsec);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_time_avg,
+ (uint32_t)converted.iface.wmeViContentionTimeStats.contentionTimeAvgInUsec);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VI].contention_num_samples,
+ (uint32_t)converted.iface.wmeViContentionTimeStats.contentionNumSamples);
+
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].rx_mpdu,
+ converted.iface.wmeVoPktStats.rxMpdu);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].tx_mpdu,
+ converted.iface.wmeVoPktStats.txMpdu);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].mpdu_lost,
+ converted.iface.wmeVoPktStats.lostMpdu);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].retries,
+ converted.iface.wmeVoPktStats.retries);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_min,
+ (uint32_t)converted.iface.wmeVoContentionTimeStats.contentionTimeMinInUsec);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_max,
+ (uint32_t)converted.iface.wmeVoContentionTimeStats.contentionTimeMaxInUsec);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_time_avg,
+ (uint32_t)converted.iface.wmeVoContentionTimeStats.contentionTimeAvgInUsec);
+ EXPECT_EQ(legacy_stats.iface.ac[legacy_hal::WIFI_AC_VO].contention_num_samples,
+ (uint32_t)converted.iface.wmeVoContentionTimeStats.contentionNumSamples);
+
+ EXPECT_EQ(legacy_stats.iface.info.time_slicing_duty_cycle_percent,
+ converted.iface.timeSliceDutyCycleInPercent);
+
+ EXPECT_EQ(legacy_stats.radios.size(), converted.radios.size());
+ for (size_t i = 0; i < legacy_stats.radios.size(); i++) {
+ EXPECT_EQ(legacy_stats.radios[i].stats.radio, converted.radios[i].radioId);
+ EXPECT_EQ(legacy_stats.radios[i].stats.on_time, (uint32_t)converted.radios[i].onTimeInMs);
+ EXPECT_EQ(legacy_stats.radios[i].stats.tx_time, (uint32_t)converted.radios[i].txTimeInMs);
+ EXPECT_EQ(legacy_stats.radios[i].stats.rx_time, (uint32_t)converted.radios[i].rxTimeInMs);
+ EXPECT_EQ(legacy_stats.radios[i].stats.on_time_scan,
+ (uint32_t)converted.radios[i].onTimeInMsForScan);
+ EXPECT_EQ(legacy_stats.radios[i].tx_time_per_levels.size(),
+ converted.radios[i].txTimeInMsPerLevel.size());
+ for (size_t j = 0; j < legacy_stats.radios[i].tx_time_per_levels.size(); j++) {
+ EXPECT_EQ(legacy_stats.radios[i].tx_time_per_levels[j],
+ (uint32_t)converted.radios[i].txTimeInMsPerLevel[j]);
+ }
+ EXPECT_EQ(legacy_stats.radios[i].stats.on_time_nbd,
+ (uint32_t)converted.radios[i].onTimeInMsForNanScan);
+ EXPECT_EQ(legacy_stats.radios[i].stats.on_time_gscan,
+ (uint32_t)converted.radios[i].onTimeInMsForBgScan);
+ EXPECT_EQ(legacy_stats.radios[i].stats.on_time_roam_scan,
+ (uint32_t)converted.radios[i].onTimeInMsForRoamScan);
+ EXPECT_EQ(legacy_stats.radios[i].stats.on_time_pno_scan,
+ (uint32_t)converted.radios[i].onTimeInMsForPnoScan);
+ EXPECT_EQ(legacy_stats.radios[i].stats.on_time_hs20,
+ (uint32_t)converted.radios[i].onTimeInMsForHs20Scan);
+ EXPECT_EQ(legacy_stats.radios[i].channel_stats.size(),
+ converted.radios[i].channelStats.size());
+ for (size_t k = 0; k < legacy_stats.radios[i].channel_stats.size(); k++) {
+ auto& legacy_channel_st = legacy_stats.radios[i].channel_stats[k];
+ EXPECT_EQ(WifiChannelWidthInMhz::WIDTH_20,
+ converted.radios[i].channelStats[k].channel.width);
+ EXPECT_EQ(legacy_channel_st.channel.center_freq,
+ converted.radios[i].channelStats[k].channel.centerFreq);
+ EXPECT_EQ(legacy_channel_st.channel.center_freq0,
+ converted.radios[i].channelStats[k].channel.centerFreq0);
+ EXPECT_EQ(legacy_channel_st.channel.center_freq1,
+ converted.radios[i].channelStats[k].channel.centerFreq1);
+ EXPECT_EQ(legacy_channel_st.cca_busy_time,
+ (uint32_t)converted.radios[i].channelStats[k].ccaBusyTimeInMs);
+ EXPECT_EQ(legacy_channel_st.on_time,
+ (uint32_t)converted.radios[i].channelStats[k].onTimeInMs);
+ }
+ }
+
+ EXPECT_EQ(legacy_stats.peers.size(), converted.iface.peers.size());
+ for (size_t i = 0; i < legacy_stats.peers.size(); i++) {
+ EXPECT_EQ(legacy_stats.peers[i].peer_info.bssload.sta_count,
+ converted.iface.peers[i].staCount);
+ EXPECT_EQ(legacy_stats.peers[i].peer_info.bssload.chan_util,
+ converted.iface.peers[i].chanUtil);
+ for (size_t j = 0; j < legacy_stats.peers[i].rate_stats.size(); j++) {
+ EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].rate.preamble,
+ (uint32_t)converted.iface.peers[i].rateStats[j].rateInfo.preamble);
+ EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].rate.nss,
+ (uint32_t)converted.iface.peers[i].rateStats[j].rateInfo.nss);
+ EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].rate.bw,
+ (uint32_t)converted.iface.peers[i].rateStats[j].rateInfo.bw);
+ EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].rate.rateMcsIdx,
+ (uint32_t)converted.iface.peers[i].rateStats[j].rateInfo.rateMcsIdx);
+ EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].tx_mpdu,
+ (uint32_t)converted.iface.peers[i].rateStats[j].txMpdu);
+ EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].rx_mpdu,
+ (uint32_t)converted.iface.peers[i].rateStats[j].rxMpdu);
+ EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].mpdu_lost,
+ (uint32_t)converted.iface.peers[i].rateStats[j].mpduLost);
+ EXPECT_EQ(legacy_stats.peers[i].rate_stats[j].retries,
+ (uint32_t)converted.iface.peers[i].rateStats[j].retries);
+ }
+ }
+}
+
+TEST_F(AidlStructUtilTest, CanConvertLegacyFeaturesToAidl) {
+ using AidlChipCaps = IWifiChip::ChipCapabilityMask;
+
+ uint32_t aidl_caps;
+
+ uint32_t legacy_feature_set = WIFI_FEATURE_D2D_RTT | WIFI_FEATURE_SET_LATENCY_MODE;
+ uint32_t legacy_logger_feature_set = legacy_hal::WIFI_LOGGER_DRIVER_DUMP_SUPPORTED;
+
+ ASSERT_TRUE(aidl_struct_util::convertLegacyFeaturesToAidlChipCapabilities(
+ legacy_feature_set, legacy_logger_feature_set, &aidl_caps));
+
+ EXPECT_EQ((uint32_t)AidlChipCaps::DEBUG_RING_BUFFER_VENDOR_DATA |
+ (uint32_t)AidlChipCaps::DEBUG_HOST_WAKE_REASON_STATS |
+ (uint32_t)AidlChipCaps::DEBUG_ERROR_ALERTS | (uint32_t)AidlChipCaps::D2D_RTT |
+ (uint32_t)AidlChipCaps::SET_LATENCY_MODE |
+ (uint32_t)AidlChipCaps::DEBUG_MEMORY_DRIVER_DUMP,
+ aidl_caps);
+}
+
+void insertRadioCombination(legacy_hal::wifi_radio_combination* dst_radio_combination_ptr,
+ int num_radio_configurations,
+ legacy_hal::wifi_radio_configuration* radio_configuration) {
+ dst_radio_combination_ptr->num_radio_configurations = num_radio_configurations;
+ memcpy(dst_radio_combination_ptr->radio_configurations, radio_configuration,
+ num_radio_configurations * sizeof(legacy_hal::wifi_radio_configuration));
+}
+
+void verifyRadioCombination(WifiRadioCombination* radioCombination, size_t num_radio_configurations,
+ legacy_hal::wifi_radio_configuration* radio_configuration) {
+ EXPECT_EQ(num_radio_configurations, radioCombination->radioConfigurations.size());
+ for (size_t i = 0; i < num_radio_configurations; i++) {
+ EXPECT_EQ(aidl_struct_util::convertLegacyMacBandToAidlWifiBand(radio_configuration->band),
+ radioCombination->radioConfigurations[i].bandInfo);
+ EXPECT_EQ(aidl_struct_util::convertLegacyAntennaConfigurationToAidl(
+ radio_configuration->antenna_cfg),
+ radioCombination->radioConfigurations[i].antennaMode);
+ radio_configuration++;
+ }
+}
+
+TEST_F(AidlStructUtilTest, canConvertLegacyRadioCombinationsMatrixToAidl) {
+ legacy_hal::wifi_radio_configuration radio_configurations_array1[] = {
+ {.band = legacy_hal::WLAN_MAC_2_4_BAND, .antenna_cfg = legacy_hal::WIFI_ANTENNA_1X1},
+ };
+ legacy_hal::wifi_radio_configuration radio_configurations_array2[] = {
+ {.band = legacy_hal::WLAN_MAC_2_4_BAND, .antenna_cfg = legacy_hal::WIFI_ANTENNA_2X2},
+ {.band = legacy_hal::WLAN_MAC_5_0_BAND, .antenna_cfg = legacy_hal::WIFI_ANTENNA_3X3},
+ };
+ legacy_hal::wifi_radio_configuration radio_configurations_array3[] = {
+ {.band = legacy_hal::WLAN_MAC_2_4_BAND, .antenna_cfg = legacy_hal::WIFI_ANTENNA_2X2},
+ {.band = legacy_hal::WLAN_MAC_6_0_BAND, .antenna_cfg = legacy_hal::WIFI_ANTENNA_1X1},
+ {.band = legacy_hal::WLAN_MAC_5_0_BAND, .antenna_cfg = legacy_hal::WIFI_ANTENNA_4X4},
+ };
+
+ int num_radio_configs = 0;
+ int num_combinations = 0;
+ std::array<char, 256> buffer;
+ buffer.fill(0);
+ legacy_hal::wifi_radio_combination_matrix* legacy_matrix =
+ reinterpret_cast<wifi_radio_combination_matrix*>(buffer.data());
+ legacy_hal::wifi_radio_combination* radio_combinations;
+
+ // Prepare a legacy wifi_radio_combination_matrix
+ legacy_matrix->num_radio_combinations = 3;
+ // Insert first combination
+ radio_combinations =
+ (legacy_hal::wifi_radio_combination*)((char*)legacy_matrix->radio_combinations);
+ insertRadioCombination(
+ radio_combinations,
+ sizeof(radio_configurations_array1) / sizeof(radio_configurations_array1[0]),
+ radio_configurations_array1);
+ num_combinations++;
+ num_radio_configs +=
+ sizeof(radio_configurations_array1) / sizeof(radio_configurations_array1[0]);
+
+ // Insert second combination
+ radio_combinations =
+ (legacy_hal::wifi_radio_combination*)((char*)legacy_matrix->radio_combinations +
+ (num_combinations *
+ sizeof(legacy_hal::wifi_radio_combination)) +
+ (num_radio_configs *
+ sizeof(wifi_radio_configuration)));
+ insertRadioCombination(
+ radio_combinations,
+ sizeof(radio_configurations_array2) / sizeof(radio_configurations_array2[0]),
+ radio_configurations_array2);
+ num_combinations++;
+ num_radio_configs +=
+ sizeof(radio_configurations_array2) / sizeof(radio_configurations_array2[0]);
+
+ // Insert third combination
+ radio_combinations =
+ (legacy_hal::wifi_radio_combination*)((char*)legacy_matrix->radio_combinations +
+ (num_combinations *
+ sizeof(legacy_hal::wifi_radio_combination)) +
+ (num_radio_configs *
+ sizeof(wifi_radio_configuration)));
+ insertRadioCombination(
+ radio_combinations,
+ sizeof(radio_configurations_array3) / sizeof(radio_configurations_array3[0]),
+ radio_configurations_array3);
+
+ WifiRadioCombinationMatrix converted_matrix{};
+ aidl_struct_util::convertLegacyRadioCombinationsMatrixToAidl(legacy_matrix, &converted_matrix);
+
+ // Verify the conversion
+ EXPECT_EQ(legacy_matrix->num_radio_combinations, converted_matrix.radioCombinations.size());
+ verifyRadioCombination(
+ &converted_matrix.radioCombinations[0],
+ sizeof(radio_configurations_array1) / sizeof(radio_configurations_array1[0]),
+ radio_configurations_array1);
+ verifyRadioCombination(
+ &converted_matrix.radioCombinations[1],
+ sizeof(radio_configurations_array2) / sizeof(radio_configurations_array2[0]),
+ radio_configurations_array2);
+ verifyRadioCombination(
+ &converted_matrix.radioCombinations[2],
+ sizeof(radio_configurations_array3) / sizeof(radio_configurations_array3[0]),
+ radio_configurations_array3);
+}
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/tests/main.cpp b/wifi/aidl/default/tests/main.cpp
new file mode 100644
index 0000000..767422c
--- /dev/null
+++ b/wifi/aidl/default/tests/main.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::InitGoogleMock(&argc, argv);
+ // Force ourselves to always log to stderr
+ android::base::InitLogging(argv, android::base::StderrLogger);
+ return RUN_ALL_TESTS();
+}
diff --git a/wifi/aidl/default/tests/mock_interface_tool.cpp b/wifi/aidl/default/tests/mock_interface_tool.cpp
new file mode 100644
index 0000000..79f3d1e
--- /dev/null
+++ b/wifi/aidl/default/tests/mock_interface_tool.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <gmock/gmock.h>
+
+#include "mock_interface_tool.h"
+
+namespace android {
+namespace wifi_system {
+
+MockInterfaceTool::MockInterfaceTool() {}
+
+} // namespace wifi_system
+} // namespace android
diff --git a/wifi/aidl/default/tests/mock_interface_tool.h b/wifi/aidl/default/tests/mock_interface_tool.h
new file mode 100644
index 0000000..9795de8
--- /dev/null
+++ b/wifi/aidl/default/tests/mock_interface_tool.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MOCK_INTERFACE_TOOL_H
+#define MOCK_INTERFACE_TOOL_H
+
+#include <gmock/gmock.h>
+#include <wifi_system/interface_tool.h>
+
+namespace android {
+namespace wifi_system {
+
+class MockInterfaceTool : public InterfaceTool {
+ public:
+ MockInterfaceTool();
+
+ MOCK_METHOD1(GetUpState, bool(const char* if_name));
+ MOCK_METHOD2(SetUpState, bool(const char* if_name, bool request_up));
+ MOCK_METHOD1(SetWifiUpState, bool(bool request_up));
+ MOCK_METHOD2(SetMacAddress,
+ bool(const char* if_name, const std::array<uint8_t, ETH_ALEN>& address));
+ MOCK_METHOD1(GetFactoryMacAddress, std::array<uint8_t, ETH_ALEN>(const char* if_name));
+
+}; // class MockInterfaceTool
+
+} // namespace wifi_system
+} // namespace android
+
+#endif // MOCK_INTERFACE_TOOL_H
diff --git a/wifi/aidl/default/tests/mock_wifi_feature_flags.cpp b/wifi/aidl/default/tests/mock_wifi_feature_flags.cpp
new file mode 100644
index 0000000..0c4e59d
--- /dev/null
+++ b/wifi/aidl/default/tests/mock_wifi_feature_flags.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+
+#include "mock_wifi_feature_flags.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace feature_flags {
+
+MockWifiFeatureFlags::MockWifiFeatureFlags() {}
+
+} // namespace feature_flags
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/tests/mock_wifi_feature_flags.h b/wifi/aidl/default/tests/mock_wifi_feature_flags.h
new file mode 100644
index 0000000..9143d15
--- /dev/null
+++ b/wifi/aidl/default/tests/mock_wifi_feature_flags.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MOCK_WIFI_FEATURE_FLAGS_H_
+#define MOCK_WIFI_FEATURE_FLAGS_H_
+
+#include <gmock/gmock.h>
+
+#include "wifi_feature_flags.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace feature_flags {
+
+class MockWifiFeatureFlags : public WifiFeatureFlags {
+ public:
+ MockWifiFeatureFlags();
+
+ MOCK_METHOD1(getChipModes, std::vector<IWifiChip::ChipMode>(bool is_primary));
+ MOCK_METHOD0(isApMacRandomizationDisabled, bool());
+};
+
+} // namespace feature_flags
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // MOCK_WIFI_FEATURE_FLAGS_H_
diff --git a/wifi/aidl/default/tests/mock_wifi_iface_util.cpp b/wifi/aidl/default/tests/mock_wifi_iface_util.cpp
new file mode 100644
index 0000000..0f787f2
--- /dev/null
+++ b/wifi/aidl/default/tests/mock_wifi_iface_util.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <gmock/gmock.h>
+
+#include "mock_wifi_iface_util.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace iface_util {
+
+MockWifiIfaceUtil::MockWifiIfaceUtil(
+ const std::weak_ptr<::android::wifi_system::InterfaceTool> iface_tool,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal)
+ : WifiIfaceUtil(iface_tool, legacy_hal) {}
+
+} // namespace iface_util
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/tests/mock_wifi_iface_util.h b/wifi/aidl/default/tests/mock_wifi_iface_util.h
new file mode 100644
index 0000000..49a8636
--- /dev/null
+++ b/wifi/aidl/default/tests/mock_wifi_iface_util.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MOCK_WIFI_IFACE_UTIL_H_
+#define MOCK_WIFI_IFACE_UTIL_H_
+
+#include <gmock/gmock.h>
+
+#include "wifi_iface_util.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace iface_util {
+
+class MockWifiIfaceUtil : public iface_util::WifiIfaceUtil {
+ public:
+ MockWifiIfaceUtil(const std::weak_ptr<::android::wifi_system::InterfaceTool> iface_tool,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal);
+ MOCK_METHOD1(getFactoryMacAddress, std::array<uint8_t, 6>(const std::string&));
+ MOCK_METHOD2(setMacAddress, bool(const std::string&, const std::array<uint8_t, 6>&));
+ MOCK_METHOD0(getOrCreateRandomMacAddress, std::array<uint8_t, 6>());
+ MOCK_METHOD2(registerIfaceEventHandlers,
+ void(const std::string&, iface_util::IfaceEventHandlers));
+ MOCK_METHOD1(unregisterIfaceEventHandlers, void(const std::string&));
+ MOCK_METHOD2(setUpState, bool(const std::string&, bool));
+ MOCK_METHOD1(ifNameToIndex, unsigned(const std::string&));
+};
+
+} // namespace iface_util
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // MOCK_WIFI_IFACE_UTIL_H_
diff --git a/wifi/aidl/default/tests/mock_wifi_legacy_hal.cpp b/wifi/aidl/default/tests/mock_wifi_legacy_hal.cpp
new file mode 100644
index 0000000..33b2b1c
--- /dev/null
+++ b/wifi/aidl/default/tests/mock_wifi_legacy_hal.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <gmock/gmock.h>
+
+#include "mock_wifi_legacy_hal.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace legacy_hal {
+
+MockWifiLegacyHal::MockWifiLegacyHal(
+ const std::weak_ptr<::android::wifi_system::InterfaceTool> iface_tool,
+ const wifi_hal_fn& fn, bool is_primary)
+ : WifiLegacyHal(iface_tool, fn, is_primary) {}
+} // namespace legacy_hal
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/tests/mock_wifi_legacy_hal.h b/wifi/aidl/default/tests/mock_wifi_legacy_hal.h
new file mode 100644
index 0000000..28129a9
--- /dev/null
+++ b/wifi/aidl/default/tests/mock_wifi_legacy_hal.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MOCK_WIFI_LEGACY_HAL_H_
+#define MOCK_WIFI_LEGACY_HAL_H_
+
+#include <gmock/gmock.h>
+
+#include "wifi_legacy_hal.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace legacy_hal {
+
+class MockWifiLegacyHal : public WifiLegacyHal {
+ public:
+ MockWifiLegacyHal(const std::weak_ptr<::android::wifi_system::InterfaceTool> iface_tool,
+ const wifi_hal_fn& fn, bool is_primary);
+ MOCK_METHOD0(initialize, wifi_error());
+ MOCK_METHOD0(start, wifi_error());
+ MOCK_METHOD2(stop,
+ wifi_error(std::unique_lock<std::recursive_mutex>*, const std::function<void()>&));
+ MOCK_METHOD2(setDfsFlag, wifi_error(const std::string&, bool));
+ MOCK_METHOD2(registerRadioModeChangeCallbackHandler,
+ wifi_error(const std::string&, const on_radio_mode_change_callback&));
+ MOCK_METHOD1(getFirmwareVersion,
+ std::pair<wifi_error, std::string>(const std::string& iface_name));
+ MOCK_METHOD1(getDriverVersion,
+ std::pair<wifi_error, std::string>(const std::string& iface_name));
+
+ MOCK_METHOD2(selectTxPowerScenario,
+ wifi_error(const std::string& iface_name, wifi_power_scenario scenario));
+ MOCK_METHOD1(resetTxPowerScenario, wifi_error(const std::string& iface_name));
+ MOCK_METHOD2(nanRegisterCallbackHandlers,
+ wifi_error(const std::string&, const NanCallbackHandlers&));
+ MOCK_METHOD2(nanDisableRequest, wifi_error(const std::string&, transaction_id));
+ MOCK_METHOD3(nanDataInterfaceDelete,
+ wifi_error(const std::string&, transaction_id, const std::string&));
+ MOCK_METHOD2(createVirtualInterface,
+ wifi_error(const std::string& ifname, wifi_interface_type iftype));
+ MOCK_METHOD1(deleteVirtualInterface, wifi_error(const std::string& ifname));
+ MOCK_METHOD0(waitForDriverReady, wifi_error());
+ MOCK_METHOD2(getSupportedIfaceName, wifi_error(uint32_t, std::string&));
+ MOCK_METHOD1(registerSubsystemRestartCallbackHandler,
+ wifi_error(const on_subsystem_restart_callback&));
+ MOCK_METHOD1(getSupportedFeatureSet, std::pair<wifi_error, uint64_t>(const std::string&));
+};
+} // namespace legacy_hal
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // MOCK_WIFI_LEGACY_HAL_H_
diff --git a/wifi/aidl/default/tests/mock_wifi_mode_controller.cpp b/wifi/aidl/default/tests/mock_wifi_mode_controller.cpp
new file mode 100644
index 0000000..f4cc4c4
--- /dev/null
+++ b/wifi/aidl/default/tests/mock_wifi_mode_controller.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <gmock/gmock.h>
+
+#undef NAN // This is weird, NAN is defined in bionic/libc/include/math.h:38
+#include "mock_wifi_mode_controller.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace mode_controller {
+
+MockWifiModeController::MockWifiModeController() : WifiModeController() {}
+
+} // namespace mode_controller
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/tests/mock_wifi_mode_controller.h b/wifi/aidl/default/tests/mock_wifi_mode_controller.h
new file mode 100644
index 0000000..f77f7d0
--- /dev/null
+++ b/wifi/aidl/default/tests/mock_wifi_mode_controller.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MOCK_WIFI_MODE_CONTROLLER_H_
+#define MOCK_WIFI_MODE_CONTROLLER_H_
+
+#include <gmock/gmock.h>
+
+#include "wifi_mode_controller.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace mode_controller {
+
+class MockWifiModeController : public WifiModeController {
+ public:
+ MockWifiModeController();
+ MOCK_METHOD0(initialize, bool());
+ MOCK_METHOD1(changeFirmwareMode, bool(IfaceType));
+ MOCK_METHOD1(isFirmwareModeChangeNeeded, bool(IfaceType));
+ MOCK_METHOD0(deinitialize, bool());
+};
+} // namespace mode_controller
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // MOCK_WIFI_MODE_CONTROLLER_H_
diff --git a/wifi/aidl/default/tests/ringbuffer_unit_tests.cpp b/wifi/aidl/default/tests/ringbuffer_unit_tests.cpp
new file mode 100644
index 0000000..c257100
--- /dev/null
+++ b/wifi/aidl/default/tests/ringbuffer_unit_tests.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+
+#include "ringbuffer.h"
+
+using testing::Return;
+using testing::Test;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+class RingbufferTest : public Test {
+ public:
+ const uint32_t maxBufferSize_ = 10;
+ Ringbuffer buffer_{maxBufferSize_};
+};
+
+TEST_F(RingbufferTest, CreateEmptyBuffer) {
+ ASSERT_TRUE(buffer_.getData().empty());
+}
+
+TEST_F(RingbufferTest, CanUseFullBufferCapacity) {
+ const std::vector<uint8_t> input(maxBufferSize_ / 2, '0');
+ const std::vector<uint8_t> input2(maxBufferSize_ / 2, '1');
+ buffer_.append(input);
+ buffer_.append(input2);
+ ASSERT_EQ(2u, buffer_.getData().size());
+ EXPECT_EQ(input, buffer_.getData().front());
+ EXPECT_EQ(input2, buffer_.getData().back());
+}
+
+TEST_F(RingbufferTest, OldDataIsRemovedOnOverflow) {
+ const std::vector<uint8_t> input(maxBufferSize_ / 2, '0');
+ const std::vector<uint8_t> input2(maxBufferSize_ / 2, '1');
+ const std::vector<uint8_t> input3 = {'G'};
+ buffer_.append(input);
+ buffer_.append(input2);
+ buffer_.append(input3);
+ ASSERT_EQ(2u, buffer_.getData().size());
+ EXPECT_EQ(input2, buffer_.getData().front());
+ EXPECT_EQ(input3, buffer_.getData().back());
+}
+
+TEST_F(RingbufferTest, MultipleOldDataIsRemovedOnOverflow) {
+ const std::vector<uint8_t> input(maxBufferSize_ / 2, '0');
+ const std::vector<uint8_t> input2(maxBufferSize_ / 2, '1');
+ const std::vector<uint8_t> input3(maxBufferSize_, '2');
+ buffer_.append(input);
+ buffer_.append(input2);
+ buffer_.append(input3);
+ ASSERT_EQ(1u, buffer_.getData().size());
+ EXPECT_EQ(input3, buffer_.getData().front());
+}
+
+TEST_F(RingbufferTest, AppendingEmptyBufferDoesNotAddGarbage) {
+ const std::vector<uint8_t> input = {};
+ buffer_.append(input);
+ ASSERT_TRUE(buffer_.getData().empty());
+}
+
+TEST_F(RingbufferTest, OversizedAppendIsDropped) {
+ const std::vector<uint8_t> input(maxBufferSize_ + 1, '0');
+ buffer_.append(input);
+ ASSERT_TRUE(buffer_.getData().empty());
+}
+
+TEST_F(RingbufferTest, OversizedAppendDoesNotDropExistingData) {
+ const std::vector<uint8_t> input(maxBufferSize_, '0');
+ const std::vector<uint8_t> input2(maxBufferSize_ + 1, '1');
+ buffer_.append(input);
+ buffer_.append(input2);
+ ASSERT_EQ(1u, buffer_.getData().size());
+ EXPECT_EQ(input, buffer_.getData().front());
+}
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/tests/runtests.sh b/wifi/aidl/default/tests/runtests.sh
new file mode 100755
index 0000000..1f53ab8
--- /dev/null
+++ b/wifi/aidl/default/tests/runtests.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+# Copyright(C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0(the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http:// www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if [ -z $ANDROID_BUILD_TOP ]; then
+ echo "You need to source and lunch before you can use this script"
+ exit 1
+fi
+set -e
+
+$ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode android.hardware.wifi-service-tests
+adb root
+adb sync data
+adb shell /data/nativetest64/vendor/android.hardware.wifi-service-tests/android.hardware.wifi-service-tests
diff --git a/wifi/aidl/default/tests/wifi_chip_unit_tests.cpp b/wifi/aidl/default/tests/wifi_chip_unit_tests.cpp
new file mode 100644
index 0000000..e66b650
--- /dev/null
+++ b/wifi/aidl/default/tests/wifi_chip_unit_tests.cpp
@@ -0,0 +1,855 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <cutils/properties.h>
+#include <gmock/gmock.h>
+
+#include "wifi_chip.h"
+
+#include "mock_interface_tool.h"
+#include "mock_wifi_feature_flags.h"
+#include "mock_wifi_iface_util.h"
+#include "mock_wifi_legacy_hal.h"
+#include "mock_wifi_mode_controller.h"
+
+using testing::NiceMock;
+using testing::Return;
+using testing::Test;
+
+namespace {
+constexpr int kFakeChipId = 5;
+} // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+class WifiChipTest : public Test {
+ protected:
+ void setupV1IfaceCombination() {
+ // clang-format off
+ // 1 STA + 1 P2P
+ const std::vector<IWifiChip::ChipConcurrencyCombination> combinationsSta =
+ {
+ {
+ {
+ {{IfaceConcurrencyType::STA}, 1},
+ {{IfaceConcurrencyType::P2P}, 1}
+ }
+ }
+ };
+ // 1 AP
+ const std::vector<IWifiChip::ChipConcurrencyCombination> combinationsAp =
+ {
+ {
+ {
+ {{IfaceConcurrencyType::AP}, 1}
+ }
+ }
+ };
+ const std::vector<IWifiChip::ChipMode> modes = {
+ {feature_flags::chip_mode_ids::kV1Sta, combinationsSta},
+ {feature_flags::chip_mode_ids::kV1Ap, combinationsAp}
+ };
+ // clang-format on
+ EXPECT_CALL(*feature_flags_, getChipModes(true)).WillRepeatedly(testing::Return(modes));
+ }
+
+ void setupV1_AwareIfaceCombination() {
+ // clang-format off
+ // 1 STA + 1 of (P2P or NAN)
+ const std::vector<IWifiChip::ChipConcurrencyCombination> combinationsSta =
+ {
+ {
+ {
+ {{IfaceConcurrencyType::STA}, 1},
+ {{IfaceConcurrencyType::P2P, IfaceConcurrencyType::NAN_IFACE}, 1}
+ }
+ }
+ };
+ // 1 AP
+ const std::vector<IWifiChip::ChipConcurrencyCombination> combinationsAp =
+ {
+ {
+ {
+ {{IfaceConcurrencyType::AP}, 1}
+ }
+ }
+ };
+ const std::vector<IWifiChip::ChipMode> modes = {
+ {feature_flags::chip_mode_ids::kV1Sta, combinationsSta},
+ {feature_flags::chip_mode_ids::kV1Ap, combinationsAp}
+ };
+ // clang-format on
+ EXPECT_CALL(*feature_flags_, getChipModes(true)).WillRepeatedly(testing::Return(modes));
+ }
+
+ void setupV1_AwareDisabledApIfaceCombination() {
+ // clang-format off
+ // 1 STA + 1 of (P2P or NAN)
+ const std::vector<IWifiChip::ChipConcurrencyCombination> combinationsSta =
+ {
+ {
+ {
+ {{IfaceConcurrencyType::STA}, 1},
+ {{IfaceConcurrencyType::P2P, IfaceConcurrencyType::NAN_IFACE}, 1}
+ }
+ }
+ };
+ const std::vector<IWifiChip::ChipMode> modes = {
+ {feature_flags::chip_mode_ids::kV1Sta, combinationsSta}
+ };
+ // clang-format on
+ EXPECT_CALL(*feature_flags_, getChipModes(true)).WillRepeatedly(testing::Return(modes));
+ }
+
+ void setupV2_AwareIfaceCombination() {
+ // clang-format off
+ // (1 STA + 1 AP) or (1 STA + 1 of (P2P or NAN))
+ const std::vector<IWifiChip::ChipConcurrencyCombination> combinations =
+ {
+ {
+ {
+ {{IfaceConcurrencyType::STA}, 1},
+ {{IfaceConcurrencyType::AP}, 1}
+ }
+ },
+ {
+ {
+ {{IfaceConcurrencyType::STA}, 1},
+ {{IfaceConcurrencyType::P2P, IfaceConcurrencyType::NAN_IFACE}, 1}
+ }
+ }
+ };
+ const std::vector<IWifiChip::ChipMode> modes = {
+ {feature_flags::chip_mode_ids::kV3, combinations}
+ };
+ // clang-format on
+ EXPECT_CALL(*feature_flags_, getChipModes(true)).WillRepeatedly(testing::Return(modes));
+ }
+
+ void setupV2_AwareDisabledApIfaceCombination() {
+ // clang-format off
+ // 1 STA + 1 of (P2P or NAN)
+ const std::vector<IWifiChip::ChipConcurrencyCombination> combinations =
+ {
+ {
+ {
+ {{IfaceConcurrencyType::STA}, 1},
+ {{IfaceConcurrencyType::P2P, IfaceConcurrencyType::NAN_IFACE}, 1}
+ }
+ }
+ };
+ const std::vector<IWifiChip::ChipMode> modes = {
+ {feature_flags::chip_mode_ids::kV3, combinations}
+ };
+ // clang-format on
+ EXPECT_CALL(*feature_flags_, getChipModes(true)).WillRepeatedly(testing::Return(modes));
+ }
+
+ void setup_MultiIfaceCombination() {
+ // clang-format off
+ // 3 STA + 1 AP
+ const std::vector<IWifiChip::ChipConcurrencyCombination> combinations =
+ {
+ {
+ {
+ {{IfaceConcurrencyType::STA}, 3},
+ {{IfaceConcurrencyType::AP}, 1}
+ }
+ }
+ };
+ const std::vector<IWifiChip::ChipMode> modes = {
+ {feature_flags::chip_mode_ids::kV3, combinations}
+ };
+ // clang-format on
+ EXPECT_CALL(*feature_flags_, getChipModes(true)).WillRepeatedly(testing::Return(modes));
+ }
+
+ void assertNumberOfModes(uint32_t num_modes) {
+ std::vector<IWifiChip::ChipMode> modes;
+ ASSERT_TRUE(chip_->getAvailableModes(&modes).isOk());
+ // V2_Aware has 1 mode of operation.
+ ASSERT_EQ(num_modes, modes.size());
+ }
+
+ void findModeAndConfigureForIfaceType(const IfaceConcurrencyType& type) {
+ // This should be aligned with kInvalidModeId in wifi_chip.cpp
+ int32_t mode_id = INT32_MAX;
+ std::vector<IWifiChip::ChipMode> modes;
+ ASSERT_TRUE(chip_->getAvailableModes(&modes).isOk());
+
+ for (const auto& mode : modes) {
+ for (const auto& combination : mode.availableCombinations) {
+ for (const auto& limit : combination.limits) {
+ if (limit.types.end() !=
+ std::find(limit.types.begin(), limit.types.end(), type)) {
+ mode_id = mode.id;
+ }
+ }
+ }
+ }
+
+ ASSERT_NE(INT32_MAX, mode_id);
+ ASSERT_TRUE(chip_->configureChip(mode_id).isOk());
+ }
+
+ // Returns an empty string on error.
+ std::string createIface(const IfaceType& type) {
+ std::string iface_name;
+ if (type == IfaceType::AP) {
+ std::shared_ptr<IWifiApIface> iface;
+ if (!chip_->createApIface(&iface).isOk()) {
+ return "";
+ }
+ EXPECT_NE(iface.get(), nullptr);
+ EXPECT_TRUE(iface->getName(&iface_name).isOk());
+ } else if (type == IfaceType::NAN_IFACE) {
+ std::shared_ptr<IWifiNanIface> iface;
+ if (!chip_->createNanIface(&iface).isOk()) {
+ return "";
+ }
+ EXPECT_NE(iface.get(), nullptr);
+ EXPECT_TRUE(iface->getName(&iface_name).isOk());
+ } else if (type == IfaceType::P2P) {
+ std::shared_ptr<IWifiP2pIface> iface;
+ if (!chip_->createP2pIface(&iface).isOk()) {
+ return "";
+ }
+ EXPECT_NE(iface.get(), nullptr);
+ EXPECT_TRUE(iface->getName(&iface_name).isOk());
+ } else if (type == IfaceType::STA) {
+ std::shared_ptr<IWifiStaIface> iface;
+ if (!chip_->createStaIface(&iface).isOk()) {
+ return "";
+ }
+ EXPECT_NE(iface.get(), nullptr);
+ EXPECT_TRUE(iface->getName(&iface_name).isOk());
+ }
+ return iface_name;
+ }
+
+ void removeIface(const IfaceType& type, const std::string& iface_name) {
+ if (type == IfaceType::AP) {
+ ASSERT_TRUE(chip_->removeApIface(iface_name).isOk());
+ } else if (type == IfaceType::NAN_IFACE) {
+ ASSERT_TRUE(chip_->removeNanIface(iface_name).isOk());
+ } else if (type == IfaceType::P2P) {
+ ASSERT_TRUE(chip_->removeP2pIface(iface_name).isOk());
+ } else if (type == IfaceType::STA) {
+ ASSERT_TRUE(chip_->removeStaIface(iface_name).isOk());
+ }
+ }
+
+ bool createRttController() {
+ std::shared_ptr<IWifiRttController> rtt_controller;
+ auto status = chip_->createRttController(nullptr, &rtt_controller);
+ return status.isOk();
+ }
+
+ static void subsystemRestartHandler(const std::string& /*error*/) {}
+
+ std::shared_ptr<WifiChip> chip_;
+ int chip_id_ = kFakeChipId;
+ legacy_hal::wifi_hal_fn fake_func_table_;
+ std::shared_ptr<NiceMock<::android::wifi_system::MockInterfaceTool>> iface_tool_{
+ new NiceMock<::android::wifi_system::MockInterfaceTool>};
+ std::shared_ptr<NiceMock<legacy_hal::MockWifiLegacyHal>> legacy_hal_{
+ new NiceMock<legacy_hal::MockWifiLegacyHal>(iface_tool_, fake_func_table_, true)};
+ std::shared_ptr<NiceMock<mode_controller::MockWifiModeController>> mode_controller_{
+ new NiceMock<mode_controller::MockWifiModeController>};
+ std::shared_ptr<NiceMock<iface_util::MockWifiIfaceUtil>> iface_util_{
+ new NiceMock<iface_util::MockWifiIfaceUtil>(iface_tool_, legacy_hal_)};
+ std::shared_ptr<NiceMock<feature_flags::MockWifiFeatureFlags>> feature_flags_{
+ new NiceMock<feature_flags::MockWifiFeatureFlags>};
+
+ public:
+ void SetUp() override {
+ chip_ = WifiChip::create(chip_id_, true, legacy_hal_, mode_controller_, iface_util_,
+ feature_flags_, subsystemRestartHandler);
+
+ EXPECT_CALL(*mode_controller_, changeFirmwareMode(testing::_))
+ .WillRepeatedly(testing::Return(true));
+ EXPECT_CALL(*legacy_hal_, start())
+ .WillRepeatedly(testing::Return(legacy_hal::WIFI_SUCCESS));
+ // Vendor HAL does not override the name by default.
+ EXPECT_CALL(*legacy_hal_, getSupportedIfaceName(testing::_, testing::_))
+ .WillRepeatedly(testing::Return(legacy_hal::WIFI_ERROR_UNKNOWN));
+ }
+
+ void TearDown() override {
+ // Restore default system iface names (This should ideally be using a
+ // mock).
+ property_set("wifi.interface", "wlan0");
+ property_set("wifi.concurrent.interface", "wlan1");
+ property_set("wifi.aware.interface", nullptr);
+ }
+};
+
+////////// V1 Iface Combinations ////////////
+// Mode 1 - STA + P2P
+// Mode 2 - AP
+class WifiChipV1IfaceCombinationTest : public WifiChipTest {
+ public:
+ void SetUp() override {
+ setupV1IfaceCombination();
+ WifiChipTest::SetUp();
+ // V1 has 2 modes of operation.
+ assertNumberOfModes(2);
+ }
+};
+
+TEST_F(WifiChipV1IfaceCombinationTest, StaMode_CreateSta_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, StaMode_CreateP2p_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, StaMode_CreateNan_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_TRUE(createIface(IfaceType::NAN_IFACE).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, StaMode_CreateAp_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_TRUE(createIface(IfaceType::AP).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, StaMode_CreateStaP2p_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, ApMode_CreateAp_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan0");
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, ApMode_CreateSta_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_TRUE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, ApMode_CreateP2p_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_TRUE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, ApMode_CreateNan_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_TRUE(createIface(IfaceType::NAN_IFACE).empty());
+}
+
+////////// V1 + Aware Iface Combinations ////////////
+// Mode 1 - STA + P2P/NAN
+// Mode 2 - AP
+class WifiChipV1_AwareIfaceCombinationTest : public WifiChipTest {
+ public:
+ void SetUp() override {
+ setupV1_AwareIfaceCombination();
+ WifiChipTest::SetUp();
+ // V1_Aware has 2 modes of operation.
+ assertNumberOfModes(2u);
+ }
+};
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateSta_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateP2p_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateNan_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::NAN_IFACE).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateAp_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_TRUE(createIface(IfaceType::AP).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateStaP2p_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateStaNan_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_FALSE(createIface(IfaceType::NAN_IFACE).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateStaP2PNan_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+ ASSERT_TRUE(createIface(IfaceType::NAN_IFACE).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateStaNan_AfterP2pRemove_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ std::string p2p_iface_name = createIface(IfaceType::P2P);
+ ASSERT_FALSE(p2p_iface_name.empty());
+ ASSERT_TRUE(createIface(IfaceType::NAN_IFACE).empty());
+
+ // After removing P2P iface, NAN iface creation should succeed.
+ removeIface(IfaceType::P2P, p2p_iface_name);
+ ASSERT_FALSE(createIface(IfaceType::NAN_IFACE).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateStaP2p_AfterNanRemove_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ std::string nan_iface_name = createIface(IfaceType::NAN_IFACE);
+ ASSERT_FALSE(nan_iface_name.empty());
+ ASSERT_TRUE(createIface(IfaceType::P2P).empty());
+
+ // After removing NAN iface, P2P iface creation should succeed.
+ removeIface(IfaceType::NAN_IFACE, nan_iface_name);
+ ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, ApMode_CreateAp_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan0");
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, ApMode_CreateSta_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_TRUE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, ApMode_CreateP2p_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_TRUE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, ApMode_CreateNan_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_TRUE(createIface(IfaceType::NAN_IFACE).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, RttControllerFlowStaModeNoSta) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_TRUE(createRttController());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, RttControllerFlowStaModeWithSta) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_TRUE(createRttController());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, RttControllerFlowApToSta) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ std::string ap_iface_name = createIface(IfaceType::AP);
+ ASSERT_FALSE(ap_iface_name.empty());
+ ASSERT_FALSE(createRttController());
+
+ removeIface(IfaceType::AP, ap_iface_name);
+
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_TRUE(createRttController());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, SelectTxScenarioWithOnlySta) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+ EXPECT_CALL(*legacy_hal_, selectTxPowerScenario("wlan0", testing::_))
+ .WillOnce(testing::Return(legacy_hal::WIFI_SUCCESS));
+ ASSERT_TRUE(chip_->selectTxPowerScenario(IWifiChip::TxPowerScenario::ON_HEAD_CELL_OFF).isOk());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, SelectTxScenarioWithOnlyAp) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan0");
+ EXPECT_CALL(*legacy_hal_, selectTxPowerScenario("wlan0", testing::_))
+ .WillOnce(testing::Return(legacy_hal::WIFI_SUCCESS));
+ ASSERT_TRUE(chip_->selectTxPowerScenario(IWifiChip::TxPowerScenario::ON_HEAD_CELL_OFF).isOk());
+}
+
+////////// V2 + Aware Iface Combinations ////////////
+// Mode 1 - STA + STA/AP
+// - STA + P2P/NAN
+class WifiChipV2_AwareIfaceCombinationTest : public WifiChipTest {
+ public:
+ void SetUp() override {
+ setupV2_AwareIfaceCombination();
+ WifiChipTest::SetUp();
+ // V2_Aware has 1 mode of operation.
+ assertNumberOfModes(1u);
+ }
+};
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateSta_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateP2p_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateNan_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::NAN_IFACE).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateAp_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan1");
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaSta_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+ ASSERT_TRUE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaAp_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan1");
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateApSta_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan1");
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateSta_AfterStaApRemove_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ std::string sta_iface_name = createIface(IfaceType::STA);
+ ASSERT_FALSE(sta_iface_name.empty());
+ std::string ap_iface_name = createIface(IfaceType::AP);
+ ASSERT_FALSE(ap_iface_name.empty());
+
+ ASSERT_TRUE(createIface(IfaceType::STA).empty());
+
+ // After removing AP & STA iface, STA iface creation should succeed.
+ removeIface(IfaceType::STA, sta_iface_name);
+ removeIface(IfaceType::AP, ap_iface_name);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaP2p_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaNan_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_FALSE(createIface(IfaceType::NAN_IFACE).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaP2PNan_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+ ASSERT_TRUE(createIface(IfaceType::NAN_IFACE).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaNan_AfterP2pRemove_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ std::string p2p_iface_name = createIface(IfaceType::P2P);
+ ASSERT_FALSE(p2p_iface_name.empty());
+ ASSERT_TRUE(createIface(IfaceType::NAN_IFACE).empty());
+
+ // After removing P2P iface, NAN iface creation should succeed.
+ removeIface(IfaceType::P2P, p2p_iface_name);
+ ASSERT_FALSE(createIface(IfaceType::NAN_IFACE).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaP2p_AfterNanRemove_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ std::string nan_iface_name = createIface(IfaceType::NAN_IFACE);
+ ASSERT_FALSE(nan_iface_name.empty());
+ ASSERT_TRUE(createIface(IfaceType::P2P).empty());
+
+ // After removing NAN iface, P2P iface creation should succeed.
+ removeIface(IfaceType::NAN_IFACE, nan_iface_name);
+ ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateApNan_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_FALSE(createIface(IfaceType::AP).empty());
+ ASSERT_TRUE(createIface(IfaceType::NAN_IFACE).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateApP2p_ShouldFail) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_FALSE(createIface(IfaceType::AP).empty());
+ ASSERT_TRUE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, StaMode_CreateStaNan_AfterP2pRemove_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ std::string p2p_iface_name = createIface(IfaceType::P2P);
+ ASSERT_FALSE(p2p_iface_name.empty());
+ ASSERT_TRUE(createIface(IfaceType::NAN_IFACE).empty());
+
+ // After removing P2P iface, NAN iface creation should succeed.
+ removeIface(IfaceType::P2P, p2p_iface_name);
+ ASSERT_FALSE(createIface(IfaceType::NAN_IFACE).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, StaMode_CreateStaP2p_AfterNanRemove_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ std::string nan_iface_name = createIface(IfaceType::NAN_IFACE);
+ ASSERT_FALSE(nan_iface_name.empty());
+ ASSERT_TRUE(createIface(IfaceType::P2P).empty());
+
+ // After removing NAN iface, P2P iface creation should succeed.
+ removeIface(IfaceType::NAN_IFACE, nan_iface_name);
+ ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaAp_EnsureDifferentIfaceNames) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ std::string sta_iface_name = createIface(IfaceType::STA);
+ std::string ap_iface_name = createIface(IfaceType::AP);
+ ASSERT_FALSE(sta_iface_name.empty());
+ ASSERT_FALSE(ap_iface_name.empty());
+ ASSERT_NE(sta_iface_name, ap_iface_name);
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, RttControllerFlowStaModeNoSta) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_TRUE(createRttController());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, RttControllerFlowStaModeWithSta) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_TRUE(createRttController());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, RttControllerFlow) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_FALSE(createIface(IfaceType::AP).empty());
+ ASSERT_TRUE(createRttController());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, SelectTxScenarioWithOnlySta) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+ EXPECT_CALL(*legacy_hal_, selectTxPowerScenario("wlan0", testing::_))
+ .WillOnce(testing::Return(legacy_hal::WIFI_SUCCESS));
+ ASSERT_TRUE(chip_->selectTxPowerScenario(IWifiChip::TxPowerScenario::ON_HEAD_CELL_OFF).isOk());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, SelectTxScenarioWithOnlyAp) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::AP);
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan1");
+ EXPECT_CALL(*legacy_hal_, selectTxPowerScenario("wlan1", testing::_))
+ .WillOnce(testing::Return(legacy_hal::WIFI_SUCCESS));
+ ASSERT_TRUE(chip_->selectTxPowerScenario(IWifiChip::TxPowerScenario::ON_HEAD_CELL_OFF).isOk());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, InvalidateAndRemoveNanOnStaRemove) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+
+ // Create NAN iface
+ ASSERT_EQ(createIface(IfaceType::NAN_IFACE), "wlan0");
+
+ // We should have 1 nan iface.
+ std::vector<std::string> iface_names;
+ ASSERT_TRUE(chip_->getNanIfaceNames(&iface_names).isOk());
+ ASSERT_EQ(iface_names.size(), 1u);
+ ASSERT_EQ(iface_names[0], "wlan0");
+
+ // Retrieve the nan iface object.
+ std::shared_ptr<IWifiNanIface> nan_iface;
+ ASSERT_TRUE(chip_->getNanIface("wlan0", &nan_iface).isOk());
+ ASSERT_NE(nan_iface.get(), nullptr);
+
+ // Remove the STA iface. We should have 0 nan ifaces now.
+ removeIface(IfaceType::STA, "wlan0");
+ ASSERT_TRUE(chip_->getNanIfaceNames(&iface_names).isOk());
+ ASSERT_EQ(iface_names.size(), 0u);
+
+ // Any operation on the nan iface object should now return an error.
+ std::string name;
+ auto status = nan_iface->getName(&name);
+ ASSERT_EQ(status.getServiceSpecificError(),
+ static_cast<int32_t>(WifiStatusCode::ERROR_WIFI_IFACE_INVALID));
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, InvalidateAndRemoveRttControllerOnStaRemove) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+
+ // Create RTT controller
+ std::shared_ptr<IWifiRttController> rtt_controller;
+ ASSERT_TRUE(chip_->createRttController(nullptr, &rtt_controller).isOk());
+
+ // Remove the STA iface.
+ removeIface(IfaceType::STA, "wlan0");
+
+ // Any operation on the rtt controller object should now return an error.
+ std::shared_ptr<IWifiStaIface> bound_iface;
+ auto status = rtt_controller->getBoundIface(&bound_iface);
+ ASSERT_EQ(status.getServiceSpecificError(),
+ static_cast<int32_t>(WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID));
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateNanWithSharedNanIface) {
+ property_set("wifi.aware.interface", nullptr);
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+ ASSERT_EQ(createIface(IfaceType::NAN_IFACE), "wlan0");
+ removeIface(IfaceType::NAN_IFACE, "wlan0");
+ EXPECT_CALL(*iface_util_, setUpState(testing::_, testing::_)).Times(0);
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateNanWithDedicatedNanIface) {
+ property_set("wifi.aware.interface", "aware0");
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+ EXPECT_CALL(*iface_util_, ifNameToIndex("aware0")).WillOnce(testing::Return(4));
+ EXPECT_CALL(*iface_util_, setUpState("aware0", true)).WillOnce(testing::Return(true));
+ ASSERT_EQ(createIface(IfaceType::NAN_IFACE), "aware0");
+
+ EXPECT_CALL(*iface_util_, setUpState("aware0", false)).WillOnce(testing::Return(true));
+ removeIface(IfaceType::NAN_IFACE, "aware0");
+}
+
+////////// V1 Iface Combinations when AP creation is disabled //////////
+class WifiChipV1_AwareDisabledApIfaceCombinationTest : public WifiChipTest {
+ public:
+ void SetUp() override {
+ setupV1_AwareDisabledApIfaceCombination();
+ WifiChipTest::SetUp();
+ }
+};
+
+TEST_F(WifiChipV1_AwareDisabledApIfaceCombinationTest, StaMode_CreateSta_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_TRUE(createIface(IfaceType::AP).empty());
+}
+
+////////// V2 Iface Combinations when AP creation is disabled //////////
+class WifiChipV2_AwareDisabledApIfaceCombinationTest : public WifiChipTest {
+ public:
+ void SetUp() override {
+ setupV2_AwareDisabledApIfaceCombination();
+ WifiChipTest::SetUp();
+ }
+};
+
+TEST_F(WifiChipV2_AwareDisabledApIfaceCombinationTest, CreateSta_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_TRUE(createIface(IfaceType::AP).empty());
+}
+
+////////// Hypothetical Iface Combination with multiple ifaces //////////
+class WifiChip_MultiIfaceTest : public WifiChipTest {
+ public:
+ void SetUp() override {
+ setup_MultiIfaceCombination();
+ WifiChipTest::SetUp();
+ }
+};
+
+TEST_F(WifiChip_MultiIfaceTest, Create3Sta) {
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_TRUE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChip_MultiIfaceTest, CreateStaWithDefaultNames) {
+ property_set("wifi.interface.0", "");
+ property_set("wifi.interface.1", "");
+ property_set("wifi.interface.2", "");
+ property_set("wifi.interface", "");
+ property_set("wifi.concurrent.interface", "");
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan1");
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan2");
+}
+
+TEST_F(WifiChip_MultiIfaceTest, CreateStaWithCustomNames) {
+ property_set("wifi.interface.0", "test0");
+ property_set("wifi.interface.1", "test1");
+ property_set("wifi.interface.2", "test2");
+ property_set("wifi.interface", "bad0");
+ property_set("wifi.concurrent.interface", "bad1");
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "bad0");
+ ASSERT_EQ(createIface(IfaceType::STA), "bad1");
+ ASSERT_EQ(createIface(IfaceType::STA), "test2");
+}
+
+TEST_F(WifiChip_MultiIfaceTest, CreateStaWithCustomAltNames) {
+ property_set("wifi.interface.0", "");
+ property_set("wifi.interface.1", "");
+ property_set("wifi.interface.2", "");
+ property_set("wifi.interface", "testA0");
+ property_set("wifi.concurrent.interface", "testA1");
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "testA0");
+ ASSERT_EQ(createIface(IfaceType::STA), "testA1");
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan2");
+}
+
+TEST_F(WifiChip_MultiIfaceTest, CreateApStartsWithIdx1) {
+ // WifiChip_MultiIfaceTest iface combo: STAx3 + APx1
+ // When the HAL support dual STAs, AP should start with idx 2.
+ findModeAndConfigureForIfaceType(IfaceConcurrencyType::STA);
+ // First AP will be slotted to wlan1.
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan2");
+ // First STA will be slotted to wlan0.
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+ // All further STA will be slotted to the remaining free indices.
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan1");
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan3");
+}
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/tests/wifi_iface_util_unit_tests.cpp b/wifi/aidl/default/tests/wifi_iface_util_unit_tests.cpp
new file mode 100644
index 0000000..e0db6fd
--- /dev/null
+++ b/wifi/aidl/default/tests/wifi_iface_util_unit_tests.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <gmock/gmock.h>
+
+#include "wifi_iface_util.h"
+
+#include "mock_interface_tool.h"
+#include "mock_wifi_legacy_hal.h"
+
+using testing::NiceMock;
+using testing::Test;
+
+namespace {
+constexpr uint8_t kValidUnicastLocallyAssignedMacAddressMask = 0x02;
+constexpr uint8_t kMacAddress[] = {0x02, 0x12, 0x45, 0x56, 0xab, 0xcc};
+constexpr char kIfaceName[] = "test-wlan0";
+
+bool isValidUnicastLocallyAssignedMacAddress(const std::array<uint8_t, 6>& mac_address) {
+ uint8_t first_byte = mac_address[0];
+ return (first_byte & 0x3) == kValidUnicastLocallyAssignedMacAddressMask;
+}
+} // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace iface_util {
+
+class WifiIfaceUtilTest : public Test {
+ protected:
+ std::shared_ptr<NiceMock<::android::wifi_system::MockInterfaceTool>> iface_tool_{
+ new NiceMock<::android::wifi_system::MockInterfaceTool>};
+ legacy_hal::wifi_hal_fn fake_func_table_;
+ std::shared_ptr<NiceMock<legacy_hal::MockWifiLegacyHal>> legacy_hal_{
+ new NiceMock<legacy_hal::MockWifiLegacyHal>(iface_tool_, fake_func_table_, true)};
+ WifiIfaceUtil* iface_util_ = new WifiIfaceUtil(iface_tool_, legacy_hal_);
+};
+
+TEST_F(WifiIfaceUtilTest, GetOrCreateRandomMacAddress) {
+ auto mac_address = iface_util_->getOrCreateRandomMacAddress();
+ ASSERT_TRUE(isValidUnicastLocallyAssignedMacAddress(mac_address));
+
+ // All further calls should return the same MAC address.
+ ASSERT_EQ(mac_address, iface_util_->getOrCreateRandomMacAddress());
+ ASSERT_EQ(mac_address, iface_util_->getOrCreateRandomMacAddress());
+}
+
+TEST_F(WifiIfaceUtilTest, IfaceEventHandlers_SetMacAddress) {
+ std::array<uint8_t, 6> mac_address = {};
+ std::copy(std::begin(kMacAddress), std::end(kMacAddress), std::begin(mac_address));
+ EXPECT_CALL(*iface_tool_, SetMacAddress(testing::_, testing::_))
+ .WillRepeatedly(testing::Return(true));
+ EXPECT_CALL(*iface_tool_, SetUpState(testing::_, testing::_))
+ .WillRepeatedly(testing::Return(true));
+
+ // Register for iface state toggle events.
+ bool callback_invoked = false;
+ iface_util::IfaceEventHandlers event_handlers = {};
+ event_handlers.on_state_toggle_off_on =
+ [&callback_invoked](const std::string& /* iface_name */) { callback_invoked = true; };
+ iface_util_->registerIfaceEventHandlers(kIfaceName, event_handlers);
+ // Invoke setMacAddress and ensure that the cb is invoked.
+ ASSERT_TRUE(iface_util_->setMacAddress(kIfaceName, mac_address));
+ ASSERT_TRUE(callback_invoked);
+
+ // Unregister for iface state toggle events.
+ callback_invoked = false;
+ iface_util_->unregisterIfaceEventHandlers(kIfaceName);
+ // Invoke setMacAddress and ensure that the cb is not invoked.
+ ASSERT_TRUE(iface_util_->setMacAddress(kIfaceName, mac_address));
+ ASSERT_FALSE(callback_invoked);
+}
+} // namespace iface_util
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/tests/wifi_nan_iface_unit_tests.cpp b/wifi/aidl/default/tests/wifi_nan_iface_unit_tests.cpp
new file mode 100644
index 0000000..d40801f
--- /dev/null
+++ b/wifi/aidl/default/tests/wifi_nan_iface_unit_tests.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <cutils/properties.h>
+#include <gmock/gmock.h>
+
+#include "wifi_nan_iface.h"
+
+#include "mock_interface_tool.h"
+#include "mock_wifi_feature_flags.h"
+#include "mock_wifi_iface_util.h"
+#include "mock_wifi_legacy_hal.h"
+
+using testing::NiceMock;
+using testing::Return;
+using testing::Test;
+
+namespace {
+constexpr char kIfaceName[] = "mockWlan0";
+} // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+bool CaptureIfaceEventHandlers(const std::string& /* iface_name*/,
+ iface_util::IfaceEventHandlers in_iface_event_handlers,
+ iface_util::IfaceEventHandlers* out_iface_event_handlers) {
+ *out_iface_event_handlers = in_iface_event_handlers;
+ return true;
+}
+
+class MockNanIface : public WifiNanIface {
+ public:
+ MockNanIface(const std::string& ifname, bool is_dedicated_iface,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util)
+ : WifiNanIface(ifname, is_dedicated_iface, legacy_hal, iface_util) {}
+
+ static std::shared_ptr<MockNanIface> createMock(
+ const std::string& ifname, bool is_dedicated_iface,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util) {
+ std::shared_ptr<MockNanIface> ptr = ndk::SharedRefBase::make<MockNanIface>(
+ ifname, is_dedicated_iface, legacy_hal, iface_util);
+ std::weak_ptr<MockNanIface> weak_ptr_this(ptr);
+ ptr->setWeakPtr(weak_ptr_this);
+ ptr->registerCallbackHandlers();
+ return ptr;
+ }
+
+ // Override getEventCallbacks() so that we can return a mocked callback object.
+ std::set<std::shared_ptr<IWifiNanIfaceEventCallback>> getEventCallbacks() override {
+ return {callback_};
+ }
+
+ void setMockCallback(std::shared_ptr<IWifiNanIfaceEventCallback> cb) { callback_ = cb; }
+
+ private:
+ std::shared_ptr<IWifiNanIfaceEventCallback> callback_;
+};
+
+class MockNanIfaceEventCallback : public IWifiNanIfaceEventCallback {
+ public:
+ ndk::SpAIBinder asBinder() override { return ::ndk::SpAIBinder{}; }
+ bool isRemote() override { return false; }
+
+ ::ndk::ScopedAStatus getInterfaceVersion(int32_t* _aidl_return) override {
+ *_aidl_return = 1;
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus getInterfaceHash(std::string* _aidl_return) override {
+ *_aidl_return = "some_hash";
+ return ndk::ScopedAStatus::ok();
+ }
+
+ MOCK_METHOD3(notifyCapabilitiesResponse,
+ ndk::ScopedAStatus(char16_t, const NanStatus&, const NanCapabilities&));
+ MOCK_METHOD2(notifyEnableResponse, ndk::ScopedAStatus(char16_t, const NanStatus&));
+ MOCK_METHOD2(notifyConfigResponse, ndk::ScopedAStatus(char16_t, const NanStatus&));
+ MOCK_METHOD2(notifyDisableResponse, ndk::ScopedAStatus(char16_t, const NanStatus&));
+ MOCK_METHOD3(notifyStartPublishResponse,
+ ndk::ScopedAStatus(char16_t, const NanStatus&, int8_t));
+ MOCK_METHOD2(notifyStopPublishResponse, ndk::ScopedAStatus(char16_t, const NanStatus&));
+ MOCK_METHOD3(notifyStartSubscribeResponse,
+ ndk::ScopedAStatus(char16_t, const NanStatus&, int8_t));
+ MOCK_METHOD2(notifyStopSubscribeResponse, ndk::ScopedAStatus(char16_t, const NanStatus&));
+ MOCK_METHOD2(notifyTransmitFollowupResponse, ndk::ScopedAStatus(char16_t, const NanStatus&));
+ MOCK_METHOD2(notifyCreateDataInterfaceResponse, ndk::ScopedAStatus(char16_t, const NanStatus&));
+ MOCK_METHOD2(notifyDeleteDataInterfaceResponse, ndk::ScopedAStatus(char16_t, const NanStatus&));
+ MOCK_METHOD3(notifyInitiateDataPathResponse,
+ ndk::ScopedAStatus(char16_t, const NanStatus&, int32_t));
+ MOCK_METHOD2(notifyRespondToDataPathIndicationResponse,
+ ndk::ScopedAStatus(char16_t, const NanStatus&));
+ MOCK_METHOD2(notifyTerminateDataPathResponse, ndk::ScopedAStatus(char16_t, const NanStatus&));
+ MOCK_METHOD1(eventClusterEvent, ndk::ScopedAStatus(const NanClusterEventInd&));
+ MOCK_METHOD1(eventDisabled, ndk::ScopedAStatus(const NanStatus&));
+ MOCK_METHOD2(eventPublishTerminated, ndk::ScopedAStatus(int8_t, const NanStatus&));
+ MOCK_METHOD2(eventSubscribeTerminated, ndk::ScopedAStatus(int8_t, const NanStatus&));
+ MOCK_METHOD1(eventMatch, ndk::ScopedAStatus(const NanMatchInd&));
+ MOCK_METHOD2(eventMatchExpired, ndk::ScopedAStatus(int8_t, int32_t));
+ MOCK_METHOD1(eventFollowupReceived, ndk::ScopedAStatus(const NanFollowupReceivedInd&));
+ MOCK_METHOD2(eventTransmitFollowup, ndk::ScopedAStatus(char16_t, const NanStatus&));
+ MOCK_METHOD1(eventDataPathRequest, ndk::ScopedAStatus(const NanDataPathRequestInd&));
+ MOCK_METHOD1(eventDataPathConfirm, ndk::ScopedAStatus(const NanDataPathConfirmInd&));
+ MOCK_METHOD1(eventDataPathTerminated, ndk::ScopedAStatus(int32_t));
+ MOCK_METHOD1(eventDataPathScheduleUpdate,
+ ndk::ScopedAStatus(const NanDataPathScheduleUpdateInd&));
+};
+
+class WifiNanIfaceTest : public Test {
+ protected:
+ legacy_hal::wifi_hal_fn fake_func_table_;
+ std::shared_ptr<NiceMock<::android::wifi_system::MockInterfaceTool>> iface_tool_{
+ new NiceMock<::android::wifi_system::MockInterfaceTool>};
+ std::shared_ptr<NiceMock<legacy_hal::MockWifiLegacyHal>> legacy_hal_{
+ new NiceMock<legacy_hal::MockWifiLegacyHal>(iface_tool_, fake_func_table_, true)};
+ std::shared_ptr<NiceMock<iface_util::MockWifiIfaceUtil>> iface_util_{
+ new NiceMock<iface_util::MockWifiIfaceUtil>(iface_tool_, legacy_hal_)};
+};
+
+TEST_F(WifiNanIfaceTest, IfacEventHandlers_OnStateToggleOffOn) {
+ // Ensure that event handlers are registered during nan iface creation.
+ iface_util::IfaceEventHandlers captured_iface_event_handlers = {};
+ EXPECT_CALL(*legacy_hal_, nanRegisterCallbackHandlers(testing::_, testing::_))
+ .WillOnce(testing::Return(legacy_hal::WIFI_SUCCESS));
+ EXPECT_CALL(*iface_util_, registerIfaceEventHandlers(testing::_, testing::_))
+ .WillOnce(testing::Invoke(bind(CaptureIfaceEventHandlers, std::placeholders::_1,
+ std::placeholders::_2, &captured_iface_event_handlers)));
+
+ // Create nan iface and register a callback.
+ // Note: Since we can't register a callback directly (gTest fails on
+ // AIBinder_linkToDeath), simulate the registration by overriding
+ // getEventCallbacks() to return our mock callback object.
+ std::shared_ptr<MockNanIface> mock_nan_iface =
+ MockNanIface::createMock(kIfaceName, false, legacy_hal_, iface_util_);
+ std::shared_ptr<MockNanIfaceEventCallback> mock_event_callback =
+ ndk::SharedRefBase::make<MockNanIfaceEventCallback>();
+ mock_nan_iface->setMockCallback(mock_event_callback);
+
+ // Ensure that the eventDisabled() function in the mock callback will be invoked.
+ NanStatus expected_nan_status = {NanStatusCode::UNSUPPORTED_CONCURRENCY_NAN_DISABLED, ""};
+ EXPECT_CALL(*mock_event_callback, eventDisabled(expected_nan_status)).Times(1);
+
+ // Trigger the iface state toggle callback.
+ captured_iface_event_handlers.on_state_toggle_off_on(kIfaceName);
+}
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi.cpp b/wifi/aidl/default/wifi.cpp
new file mode 100644
index 0000000..e30c38a
--- /dev/null
+++ b/wifi/aidl/default/wifi.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi.h"
+
+#include <android-base/logging.h>
+
+#include "aidl_return_util.h"
+#include "wifi_status_util.h"
+
+namespace {
+// Starting Chip ID, will be assigned to primary chip
+static constexpr int32_t kPrimaryChipId = 0;
+} // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+using aidl_return_util::validateAndCall;
+using aidl_return_util::validateAndCallWithLock;
+
+Wifi::Wifi(const std::shared_ptr<::android::wifi_system::InterfaceTool> iface_tool,
+ const std::shared_ptr<legacy_hal::WifiLegacyHalFactory> legacy_hal_factory,
+ const std::shared_ptr<mode_controller::WifiModeController> mode_controller,
+ const std::shared_ptr<feature_flags::WifiFeatureFlags> feature_flags)
+ : iface_tool_(iface_tool),
+ legacy_hal_factory_(legacy_hal_factory),
+ mode_controller_(mode_controller),
+ feature_flags_(feature_flags),
+ run_state_(RunState::STOPPED) {}
+
+bool Wifi::isValid() {
+ // This object is always valid.
+ return true;
+}
+
+ndk::ScopedAStatus Wifi::registerEventCallback(
+ const std::shared_ptr<IWifiEventCallback>& in_callback) {
+ return validateAndCall(this, WifiStatusCode::ERROR_UNKNOWN,
+ &Wifi::registerEventCallbackInternal, in_callback);
+}
+
+ndk::ScopedAStatus Wifi::isStarted(bool* _aidl_return) {
+ *_aidl_return = (run_state_ != RunState::STOPPED);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Wifi::start() {
+ return validateAndCall(this, WifiStatusCode::ERROR_UNKNOWN, &Wifi::startInternal);
+}
+
+ndk::ScopedAStatus Wifi::stop() {
+ return validateAndCallWithLock(this, WifiStatusCode::ERROR_UNKNOWN, &Wifi::stopInternal);
+}
+
+ndk::ScopedAStatus Wifi::getChipIds(std::vector<int32_t>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_UNKNOWN, &Wifi::getChipIdsInternal,
+ _aidl_return);
+}
+
+ndk::ScopedAStatus Wifi::getChip(int32_t in_chipId, std::shared_ptr<IWifiChip>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_UNKNOWN, &Wifi::getChipInternal,
+ _aidl_return, in_chipId);
+}
+
+binder_status_t Wifi::dump(int fd, const char** args, uint32_t numArgs) {
+ LOG(INFO) << "-----------Debug was called----------------";
+ if (chips_.size() == 0) {
+ LOG(INFO) << "No chips to display.";
+ return STATUS_OK;
+ }
+
+ for (std::shared_ptr<WifiChip> chip : chips_) {
+ if (!chip.get()) continue;
+ chip->dump(fd, args, numArgs);
+ }
+ return STATUS_OK;
+}
+
+ndk::ScopedAStatus Wifi::registerEventCallbackInternal(
+ const std::shared_ptr<IWifiEventCallback>& event_callback) {
+ if (!event_cb_handler_.addCallback(event_callback)) {
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Wifi::startInternal() {
+ if (run_state_ == RunState::STARTED) {
+ return ndk::ScopedAStatus::ok();
+ } else if (run_state_ == RunState::STOPPING) {
+ return createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE, "HAL is stopping");
+ }
+ ndk::ScopedAStatus wifi_status = initializeModeControllerAndLegacyHal();
+ if (wifi_status.isOk()) {
+ // Register the callback for subsystem restart
+ const auto& on_subsystem_restart_callback = [this](const std::string& error) {
+ ndk::ScopedAStatus wifi_status = createWifiStatus(WifiStatusCode::ERROR_UNKNOWN, error);
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ LOG(INFO) << "Attempting to invoke onSubsystemRestart "
+ "callback";
+ WifiStatusCode errorCode =
+ static_cast<WifiStatusCode>(wifi_status.getServiceSpecificError());
+ if (!callback->onSubsystemRestart(errorCode).isOk()) {
+ LOG(ERROR) << "Failed to invoke onSubsystemRestart callback";
+ } else {
+ LOG(INFO) << "Succeeded to invoke onSubsystemRestart "
+ "callback";
+ }
+ }
+ };
+
+ // Create the chip instance once the HAL is started.
+ int32_t chipId = kPrimaryChipId;
+ for (auto& hal : legacy_hals_) {
+ chips_.push_back(
+ WifiChip::create(chipId, chipId == kPrimaryChipId, hal, mode_controller_,
+ std::make_shared<iface_util::WifiIfaceUtil>(iface_tool_, hal),
+ feature_flags_, on_subsystem_restart_callback));
+ chipId++;
+ }
+ run_state_ = RunState::STARTED;
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ if (!callback->onStart().isOk()) {
+ LOG(ERROR) << "Failed to invoke onStart callback";
+ };
+ }
+ LOG(INFO) << "Wifi HAL started";
+ } else {
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ WifiStatusCode errorCode =
+ static_cast<WifiStatusCode>(wifi_status.getServiceSpecificError());
+ if (!callback->onFailure(errorCode).isOk()) {
+ LOG(ERROR) << "Failed to invoke onFailure callback";
+ }
+ }
+ LOG(ERROR) << "Wifi HAL start failed";
+ // Clear the event callback objects since the HAL start failed.
+ event_cb_handler_.invalidate();
+ }
+ return wifi_status;
+}
+
+ndk::ScopedAStatus Wifi::stopInternal(
+ /* NONNULL */ std::unique_lock<std::recursive_mutex>* lock) {
+ if (run_state_ == RunState::STOPPED) {
+ return ndk::ScopedAStatus::ok();
+ } else if (run_state_ == RunState::STOPPING) {
+ return createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE, "HAL is stopping");
+ }
+ // Clear the chip object and its child objects since the HAL is now
+ // stopped.
+ for (auto& chip : chips_) {
+ if (chip.get()) {
+ chip->invalidate();
+ chip.reset();
+ }
+ }
+ chips_.clear();
+ ndk::ScopedAStatus wifi_status = stopLegacyHalAndDeinitializeModeController(lock);
+ if (wifi_status.isOk()) {
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ if (!callback->onStop().isOk()) {
+ LOG(ERROR) << "Failed to invoke onStop callback";
+ };
+ }
+ LOG(INFO) << "Wifi HAL stopped";
+ } else {
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ WifiStatusCode errorCode =
+ static_cast<WifiStatusCode>(wifi_status.getServiceSpecificError());
+ if (!callback->onFailure(errorCode).isOk()) {
+ LOG(ERROR) << "Failed to invoke onFailure callback";
+ }
+ }
+ LOG(ERROR) << "Wifi HAL stop failed";
+ }
+ // Clear the event callback objects since the HAL is now stopped.
+ event_cb_handler_.invalidate();
+ return wifi_status;
+}
+
+std::pair<std::vector<int32_t>, ndk::ScopedAStatus> Wifi::getChipIdsInternal() {
+ std::vector<int32_t> chip_ids;
+
+ for (auto& chip : chips_) {
+ int32_t chip_id = getChipIdFromWifiChip(chip);
+ if (chip_id != INT32_MAX) chip_ids.emplace_back(chip_id);
+ }
+ return {std::move(chip_ids), ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::shared_ptr<IWifiChip>, ndk::ScopedAStatus> Wifi::getChipInternal(int32_t chip_id) {
+ for (auto& chip : chips_) {
+ int32_t cand_id = getChipIdFromWifiChip(chip);
+ if ((cand_id != INT32_MAX) && (cand_id == chip_id)) return {chip, ndk::ScopedAStatus::ok()};
+ }
+
+ return {nullptr, createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS)};
+}
+
+ndk::ScopedAStatus Wifi::initializeModeControllerAndLegacyHal() {
+ if (!mode_controller_->initialize()) {
+ LOG(ERROR) << "Failed to initialize firmware mode controller";
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+
+ legacy_hals_ = legacy_hal_factory_->getHals();
+ if (legacy_hals_.empty()) return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ int index = 0; // for failure log
+ for (auto& hal : legacy_hals_) {
+ legacy_hal::wifi_error legacy_status = hal->initialize();
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ // Currently WifiLegacyHal::initialize does not allocate extra mem,
+ // only initializes the function table. If this changes, need to
+ // implement WifiLegacyHal::deinitialize and deinitalize the
+ // HALs already initialized
+ LOG(ERROR) << "Failed to initialize legacy HAL index: " << index
+ << " error: " << legacyErrorToString(legacy_status);
+ return createWifiStatusFromLegacyError(legacy_status);
+ }
+ index++;
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Wifi::stopLegacyHalAndDeinitializeModeController(
+ /* NONNULL */ std::unique_lock<std::recursive_mutex>* lock) {
+ legacy_hal::wifi_error legacy_status = legacy_hal::WIFI_SUCCESS;
+ int index = 0;
+
+ run_state_ = RunState::STOPPING;
+ for (auto& hal : legacy_hals_) {
+ legacy_hal::wifi_error tmp = hal->stop(lock, [&]() {});
+ if (tmp != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to stop legacy HAL index: " << index
+ << " error: " << legacyErrorToString(legacy_status);
+ legacy_status = tmp;
+ }
+ index++;
+ }
+ run_state_ = RunState::STOPPED;
+
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "One or more legacy HALs failed to stop";
+ return createWifiStatusFromLegacyError(legacy_status);
+ }
+ if (!mode_controller_->deinitialize()) {
+ LOG(ERROR) << "Failed to deinitialize firmware mode controller";
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+int32_t Wifi::getChipIdFromWifiChip(std::shared_ptr<WifiChip>& chip) {
+ int32_t chip_id = INT32_MAX;
+ if (chip.get()) {
+ ndk::ScopedAStatus status = chip->getId(&chip_id);
+ if (!status.isOk()) {
+ // Reset value if operation failed.
+ chip_id = INT32_MAX;
+ }
+ }
+ return chip_id;
+}
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi.h b/wifi/aidl/default/wifi.h
new file mode 100644
index 0000000..9334524
--- /dev/null
+++ b/wifi/aidl/default/wifi.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_H_
+#define WIFI_H_
+
+#include <aidl/android/hardware/wifi/BnWifi.h>
+#include <android-base/macros.h>
+#include <utils/Looper.h>
+
+#include <functional>
+
+#include "aidl_callback_util.h"
+#include "wifi_chip.h"
+#include "wifi_feature_flags.h"
+#include "wifi_legacy_hal.h"
+#include "wifi_legacy_hal_factory.h"
+#include "wifi_mode_controller.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+/**
+ * Root AIDL interface object used to control the Wifi HAL.
+ */
+class Wifi : public BnWifi {
+ public:
+ Wifi(const std::shared_ptr<::android::wifi_system::InterfaceTool> iface_tool,
+ const std::shared_ptr<legacy_hal::WifiLegacyHalFactory> legacy_hal_factory,
+ const std::shared_ptr<mode_controller::WifiModeController> mode_controller,
+ const std::shared_ptr<feature_flags::WifiFeatureFlags> feature_flags);
+
+ bool isValid();
+
+ // AIDL methods exposed.
+ ndk::ScopedAStatus registerEventCallback(
+ const std::shared_ptr<IWifiEventCallback>& in_callback) override;
+ ndk::ScopedAStatus isStarted(bool* _aidl_return) override;
+ ndk::ScopedAStatus start() override;
+ ndk::ScopedAStatus stop() override;
+ ndk::ScopedAStatus getChipIds(std::vector<int32_t>* _aidl_return) override;
+ ndk::ScopedAStatus getChip(int32_t in_chipId,
+ std::shared_ptr<IWifiChip>* _aidl_return) override;
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+
+ private:
+ enum class RunState { STOPPED, STARTED, STOPPING };
+
+ // Corresponding worker functions for the AIDL methods.
+ ndk::ScopedAStatus registerEventCallbackInternal(
+ const std::shared_ptr<IWifiEventCallback>& event_callback __unused);
+ ndk::ScopedAStatus startInternal();
+ ndk::ScopedAStatus stopInternal(std::unique_lock<std::recursive_mutex>* lock);
+ std::pair<std::vector<int32_t>, ndk::ScopedAStatus> getChipIdsInternal();
+ std::pair<std::shared_ptr<IWifiChip>, ndk::ScopedAStatus> getChipInternal(int32_t chip_id);
+
+ ndk::ScopedAStatus initializeModeControllerAndLegacyHal();
+ ndk::ScopedAStatus stopLegacyHalAndDeinitializeModeController(
+ std::unique_lock<std::recursive_mutex>* lock);
+ int32_t getChipIdFromWifiChip(std::shared_ptr<WifiChip>& chip);
+
+ // Instance is created in this root level |IWifi| AIDL interface object
+ // and shared with all the child AIDL interface objects.
+ std::shared_ptr<::android::wifi_system::InterfaceTool> iface_tool_;
+ std::shared_ptr<legacy_hal::WifiLegacyHalFactory> legacy_hal_factory_;
+ std::shared_ptr<mode_controller::WifiModeController> mode_controller_;
+ std::vector<std::shared_ptr<legacy_hal::WifiLegacyHal>> legacy_hals_;
+ std::shared_ptr<feature_flags::WifiFeatureFlags> feature_flags_;
+ RunState run_state_;
+ std::vector<std::shared_ptr<WifiChip>> chips_;
+ aidl_callback_util::AidlCallbackHandler<IWifiEventCallback> event_cb_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(Wifi);
+};
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_H_
diff --git a/wifi/aidl/default/wifi_ap_iface.cpp b/wifi/aidl/default/wifi_ap_iface.cpp
new file mode 100644
index 0000000..6cd932d
--- /dev/null
+++ b/wifi/aidl/default/wifi_ap_iface.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_ap_iface.h"
+
+#include <android-base/logging.h>
+
+#include "aidl_return_util.h"
+#include "aidl_struct_util.h"
+#include "wifi_status_util.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+using aidl_return_util::validateAndCall;
+
+WifiApIface::WifiApIface(const std::string& ifname, const std::vector<std::string>& instances,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util)
+ : ifname_(ifname),
+ instances_(instances),
+ legacy_hal_(legacy_hal),
+ iface_util_(iface_util),
+ is_valid_(true) {}
+
+void WifiApIface::invalidate() {
+ legacy_hal_.reset();
+ is_valid_ = false;
+}
+
+bool WifiApIface::isValid() {
+ return is_valid_;
+}
+
+std::string WifiApIface::getName() {
+ return ifname_;
+}
+
+void WifiApIface::removeInstance(std::string instance) {
+ instances_.erase(std::remove(instances_.begin(), instances_.end(), instance), instances_.end());
+}
+
+ndk::ScopedAStatus WifiApIface::getName(std::string* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiApIface::getNameInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiApIface::setCountryCode(const std::array<uint8_t, 2>& in_code) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiApIface::setCountryCodeInternal, in_code);
+}
+
+ndk::ScopedAStatus WifiApIface::getValidFrequenciesForBand(WifiBand in_band,
+ std::vector<int32_t>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiApIface::getValidFrequenciesForBandInternal, _aidl_return, in_band);
+}
+
+ndk::ScopedAStatus WifiApIface::setMacAddress(const std::array<uint8_t, 6>& in_mac) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiApIface::setMacAddressInternal, in_mac);
+}
+
+ndk::ScopedAStatus WifiApIface::getFactoryMacAddress(std::array<uint8_t, 6>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiApIface::getFactoryMacAddressInternal, _aidl_return,
+ instances_.size() > 0 ? instances_[0] : ifname_);
+}
+
+ndk::ScopedAStatus WifiApIface::resetToFactoryMacAddress() {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiApIface::resetToFactoryMacAddressInternal);
+}
+
+ndk::ScopedAStatus WifiApIface::getBridgedInstances(std::vector<std::string>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiApIface::getBridgedInstancesInternal, _aidl_return);
+}
+
+std::pair<std::string, ndk::ScopedAStatus> WifiApIface::getNameInternal() {
+ return {ifname_, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiApIface::setCountryCodeInternal(const std::array<uint8_t, 2>& code) {
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->setCountryCode(
+ instances_.size() > 0 ? instances_[0] : ifname_, code);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+std::pair<std::vector<int32_t>, ndk::ScopedAStatus> WifiApIface::getValidFrequenciesForBandInternal(
+ WifiBand band) {
+ static_assert(sizeof(WifiChannelWidthInMhz) == sizeof(int32_t), "Size mismatch");
+ legacy_hal::wifi_error legacy_status;
+ std::vector<uint32_t> valid_frequencies;
+ std::tie(legacy_status, valid_frequencies) = legacy_hal_.lock()->getValidFrequenciesForBand(
+ instances_.size() > 0 ? instances_[0] : ifname_,
+ aidl_struct_util::convertAidlWifiBandToLegacy(band));
+ return {std::vector<int32_t>(valid_frequencies.begin(), valid_frequencies.end()),
+ createWifiStatusFromLegacyError(legacy_status)};
+}
+
+ndk::ScopedAStatus WifiApIface::setMacAddressInternal(const std::array<uint8_t, 6>& mac) {
+ // Support random MAC up to 2 interfaces
+ if (instances_.size() == 2) {
+ int rbyte = 1;
+ for (auto const& intf : instances_) {
+ std::array<uint8_t, 6> rmac = mac;
+ // reverse the bits to avoid collision
+ rmac[rbyte] = 0xff - rmac[rbyte];
+ if (!iface_util_.lock()->setMacAddress(intf, rmac)) {
+ LOG(INFO) << "Failed to set random mac address on " << intf;
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ rbyte++;
+ }
+ }
+ // It also needs to set mac address for bridged interface, otherwise the mac
+ // address of bridged interface will be changed after one of instance
+ // down.
+ if (!iface_util_.lock()->setMacAddress(ifname_, mac)) {
+ LOG(ERROR) << "Fail to config MAC for interface " << ifname_;
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+std::pair<std::array<uint8_t, 6>, ndk::ScopedAStatus> WifiApIface::getFactoryMacAddressInternal(
+ const std::string& ifaceName) {
+ std::array<uint8_t, 6> mac = iface_util_.lock()->getFactoryMacAddress(ifaceName);
+ if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 && mac[4] == 0 && mac[5] == 0) {
+ return {mac, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {mac, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiApIface::resetToFactoryMacAddressInternal() {
+ std::pair<std::array<uint8_t, 6>, ndk::ScopedAStatus> getMacResult;
+ if (instances_.size() == 2) {
+ for (auto const& intf : instances_) {
+ getMacResult = getFactoryMacAddressInternal(intf);
+ LOG(DEBUG) << "Reset MAC to factory MAC on " << intf;
+ if (!getMacResult.second.isOk() ||
+ !iface_util_.lock()->setMacAddress(intf, getMacResult.first)) {
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ }
+ // We need to set mac address for bridged interface, otherwise the mac
+ // address of the bridged interface will be changed after one of the
+ // instances goes down. Thus we are generating a random MAC address for
+ // the bridged interface even if we got the request to reset the Factory
+ // MAC. This is because the bridged interface is an internal interface
+ // for the operation of bpf and other networking operations.
+ if (!iface_util_.lock()->setMacAddress(ifname_,
+ iface_util_.lock()->createRandomMacAddress())) {
+ LOG(ERROR) << "Fail to config MAC for bridged interface " << ifname_;
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ } else {
+ getMacResult = getFactoryMacAddressInternal(ifname_);
+ LOG(DEBUG) << "Reset MAC to factory MAC on " << ifname_;
+ if (!getMacResult.second.isOk() ||
+ !iface_util_.lock()->setMacAddress(ifname_, getMacResult.first)) {
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+std::pair<std::vector<std::string>, ndk::ScopedAStatus> WifiApIface::getBridgedInstancesInternal() {
+ return {instances_, ndk::ScopedAStatus::ok()};
+}
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi_ap_iface.h b/wifi/aidl/default/wifi_ap_iface.h
new file mode 100644
index 0000000..b5673fc
--- /dev/null
+++ b/wifi/aidl/default/wifi_ap_iface.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_AP_IFACE_H_
+#define WIFI_AP_IFACE_H_
+
+#include <aidl/android/hardware/wifi/BnWifiApIface.h>
+#include <android-base/macros.h>
+
+#include "wifi_iface_util.h"
+#include "wifi_legacy_hal.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+/**
+ * AIDL interface object used to control an AP Iface instance.
+ */
+class WifiApIface : public BnWifiApIface {
+ public:
+ WifiApIface(const std::string& ifname, const std::vector<std::string>& instances,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util);
+ // Refer to |WifiChip::invalidate()|.
+ void invalidate();
+ bool isValid();
+ std::string getName();
+ void removeInstance(std::string instance);
+
+ // AIDL methods exposed.
+ ndk::ScopedAStatus getName(std::string* _aidl_return) override;
+ ndk::ScopedAStatus setCountryCode(const std::array<uint8_t, 2>& in_code) override;
+ ndk::ScopedAStatus getValidFrequenciesForBand(WifiBand in_band,
+ std::vector<int32_t>* _aidl_return) override;
+ ndk::ScopedAStatus setMacAddress(const std::array<uint8_t, 6>& in_mac) override;
+ ndk::ScopedAStatus getFactoryMacAddress(std::array<uint8_t, 6>* _aidl_return) override;
+ ndk::ScopedAStatus resetToFactoryMacAddress() override;
+ ndk::ScopedAStatus getBridgedInstances(std::vector<std::string>* _aidl_return) override;
+
+ private:
+ // Corresponding worker functions for the AIDL methods.
+ std::pair<std::string, ndk::ScopedAStatus> getNameInternal();
+ ndk::ScopedAStatus setCountryCodeInternal(const std::array<uint8_t, 2>& code);
+ std::pair<std::vector<int32_t>, ndk::ScopedAStatus> getValidFrequenciesForBandInternal(
+ WifiBand band);
+ ndk::ScopedAStatus setMacAddressInternal(const std::array<uint8_t, 6>& mac);
+ std::pair<std::array<uint8_t, 6>, ndk::ScopedAStatus> getFactoryMacAddressInternal(
+ const std::string& ifaceName);
+ ndk::ScopedAStatus resetToFactoryMacAddressInternal();
+ std::pair<std::vector<std::string>, ndk::ScopedAStatus> getBridgedInstancesInternal();
+
+ std::string ifname_;
+ std::vector<std::string> instances_;
+ std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
+ std::weak_ptr<iface_util::WifiIfaceUtil> iface_util_;
+ bool is_valid_;
+
+ DISALLOW_COPY_AND_ASSIGN(WifiApIface);
+};
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_AP_IFACE_H_
diff --git a/wifi/aidl/default/wifi_chip.cpp b/wifi/aidl/default/wifi_chip.cpp
new file mode 100644
index 0000000..076f351
--- /dev/null
+++ b/wifi/aidl/default/wifi_chip.cpp
@@ -0,0 +1,1910 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_chip.h"
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <cutils/properties.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+
+#include "aidl_return_util.h"
+#include "aidl_struct_util.h"
+#include "wifi_status_util.h"
+
+#define P2P_MGMT_DEVICE_PREFIX "p2p-dev-"
+
+namespace {
+using aidl::android::hardware::wifi::IfaceType;
+using aidl::android::hardware::wifi::IWifiChip;
+using CoexRestriction = aidl::android::hardware::wifi::IWifiChip::CoexRestriction;
+using android::base::unique_fd;
+
+constexpr char kCpioMagic[] = "070701";
+constexpr size_t kMaxBufferSizeBytes = 1024 * 1024 * 3;
+constexpr uint32_t kMaxRingBufferFileAgeSeconds = 60 * 60 * 10;
+constexpr uint32_t kMaxRingBufferFileNum = 20;
+constexpr char kTombstoneFolderPath[] = "/data/vendor/tombstones/wifi/";
+constexpr char kActiveWlanIfaceNameProperty[] = "wifi.active.interface";
+constexpr char kNoActiveWlanIfaceNamePropertyValue[] = "";
+constexpr unsigned kMaxWlanIfaces = 5;
+constexpr char kApBridgeIfacePrefix[] = "ap_br_";
+
+template <typename Iface>
+void invalidateAndClear(std::vector<std::shared_ptr<Iface>>& ifaces, std::shared_ptr<Iface> iface) {
+ iface->invalidate();
+ ifaces.erase(std::remove(ifaces.begin(), ifaces.end(), iface), ifaces.end());
+}
+
+template <typename Iface>
+void invalidateAndClearAll(std::vector<std::shared_ptr<Iface>>& ifaces) {
+ for (const auto& iface : ifaces) {
+ iface->invalidate();
+ }
+ ifaces.clear();
+}
+
+template <typename Iface>
+std::vector<std::string> getNames(std::vector<std::shared_ptr<Iface>>& ifaces) {
+ std::vector<std::string> names;
+ for (const auto& iface : ifaces) {
+ names.emplace_back(iface->getName());
+ }
+ return names;
+}
+
+template <typename Iface>
+std::shared_ptr<Iface> findUsingName(std::vector<std::shared_ptr<Iface>>& ifaces,
+ const std::string& name) {
+ std::vector<std::string> names;
+ for (const auto& iface : ifaces) {
+ if (name == iface->getName()) {
+ return iface;
+ }
+ }
+ return nullptr;
+}
+
+std::string getWlanIfaceName(unsigned idx) {
+ if (idx >= kMaxWlanIfaces) {
+ CHECK(false) << "Requested interface beyond wlan" << kMaxWlanIfaces;
+ return {};
+ }
+
+ std::array<char, PROPERTY_VALUE_MAX> buffer;
+ if (idx == 0 || idx == 1) {
+ const char* altPropName = (idx == 0) ? "wifi.interface" : "wifi.concurrent.interface";
+ auto res = property_get(altPropName, buffer.data(), nullptr);
+ if (res > 0) return buffer.data();
+ }
+ std::string propName = "wifi.interface." + std::to_string(idx);
+ auto res = property_get(propName.c_str(), buffer.data(), nullptr);
+ if (res > 0) return buffer.data();
+
+ return "wlan" + std::to_string(idx);
+}
+
+// Returns the dedicated iface name if defined.
+// Returns two ifaces in bridged mode.
+std::vector<std::string> getPredefinedApIfaceNames(bool is_bridged) {
+ std::vector<std::string> ifnames;
+ std::array<char, PROPERTY_VALUE_MAX> buffer;
+ buffer.fill(0);
+ if (property_get("ro.vendor.wifi.sap.interface", buffer.data(), nullptr) == 0) {
+ return ifnames;
+ }
+ ifnames.push_back(buffer.data());
+ if (is_bridged) {
+ buffer.fill(0);
+ if (property_get("ro.vendor.wifi.sap.concurrent.iface", buffer.data(), nullptr) == 0) {
+ return ifnames;
+ }
+ ifnames.push_back(buffer.data());
+ }
+ return ifnames;
+}
+
+std::string getPredefinedP2pIfaceName() {
+ std::array<char, PROPERTY_VALUE_MAX> primaryIfaceName;
+ char p2pParentIfname[100];
+ std::string p2pDevIfName = "";
+ std::array<char, PROPERTY_VALUE_MAX> buffer;
+ property_get("wifi.direct.interface", buffer.data(), "p2p0");
+ if (strncmp(buffer.data(), P2P_MGMT_DEVICE_PREFIX, strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) {
+ /* Get the p2p parent interface name from p2p device interface name set
+ * in property */
+ strlcpy(p2pParentIfname, buffer.data() + strlen(P2P_MGMT_DEVICE_PREFIX),
+ strlen(buffer.data()) - strlen(P2P_MGMT_DEVICE_PREFIX));
+ if (property_get(kActiveWlanIfaceNameProperty, primaryIfaceName.data(), nullptr) == 0) {
+ return buffer.data();
+ }
+ /* Check if the parent interface derived from p2p device interface name
+ * is active */
+ if (strncmp(p2pParentIfname, primaryIfaceName.data(),
+ strlen(buffer.data()) - strlen(P2P_MGMT_DEVICE_PREFIX)) != 0) {
+ /*
+ * Update the predefined p2p device interface parent interface name
+ * with current active wlan interface
+ */
+ p2pDevIfName += P2P_MGMT_DEVICE_PREFIX;
+ p2pDevIfName += primaryIfaceName.data();
+ LOG(INFO) << "update the p2p device interface name to " << p2pDevIfName.c_str();
+ return p2pDevIfName;
+ }
+ }
+ return buffer.data();
+}
+
+// Returns the dedicated iface name if one is defined.
+std::string getPredefinedNanIfaceName() {
+ std::array<char, PROPERTY_VALUE_MAX> buffer;
+ if (property_get("wifi.aware.interface", buffer.data(), nullptr) == 0) {
+ return {};
+ }
+ return buffer.data();
+}
+
+void setActiveWlanIfaceNameProperty(const std::string& ifname) {
+ auto res = property_set(kActiveWlanIfaceNameProperty, ifname.data());
+ if (res != 0) {
+ PLOG(ERROR) << "Failed to set active wlan iface name property";
+ }
+}
+
+// Delete files that meet either condition:
+// 1. Older than a predefined time in the wifi tombstone dir.
+// 2. Files in excess to a predefined amount, starting from the oldest ones
+bool removeOldFilesInternal() {
+ time_t now = time(0);
+ const time_t delete_files_before = now - kMaxRingBufferFileAgeSeconds;
+ std::unique_ptr<DIR, decltype(&closedir)> dir_dump(opendir(kTombstoneFolderPath), closedir);
+ if (!dir_dump) {
+ PLOG(ERROR) << "Failed to open directory";
+ return false;
+ }
+ struct dirent* dp;
+ bool success = true;
+ std::list<std::pair<const time_t, std::string>> valid_files;
+ while ((dp = readdir(dir_dump.get()))) {
+ if (dp->d_type != DT_REG) {
+ continue;
+ }
+ std::string cur_file_name(dp->d_name);
+ struct stat cur_file_stat;
+ std::string cur_file_path = kTombstoneFolderPath + cur_file_name;
+ if (stat(cur_file_path.c_str(), &cur_file_stat) == -1) {
+ PLOG(ERROR) << "Failed to get file stat for " << cur_file_path;
+ success = false;
+ continue;
+ }
+ const time_t cur_file_time = cur_file_stat.st_mtime;
+ valid_files.push_back(std::pair<const time_t, std::string>(cur_file_time, cur_file_path));
+ }
+ valid_files.sort(); // sort the list of files by last modified time from
+ // small to big.
+ uint32_t cur_file_count = valid_files.size();
+ for (auto cur_file : valid_files) {
+ if (cur_file_count > kMaxRingBufferFileNum || cur_file.first < delete_files_before) {
+ if (unlink(cur_file.second.c_str()) != 0) {
+ PLOG(ERROR) << "Error deleting file";
+ success = false;
+ }
+ cur_file_count--;
+ } else {
+ break;
+ }
+ }
+ return success;
+}
+
+// Helper function for |cpioArchiveFilesInDir|
+bool cpioWriteHeader(int out_fd, struct stat& st, const char* file_name, size_t file_name_len) {
+ const int buf_size = 32 * 1024;
+ std::array<char, buf_size> read_buf;
+ ssize_t llen = snprintf(
+ read_buf.data(), buf_size, "%s%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X",
+ kCpioMagic, static_cast<int>(st.st_ino), st.st_mode, st.st_uid, st.st_gid,
+ static_cast<int>(st.st_nlink), static_cast<int>(st.st_mtime),
+ static_cast<int>(st.st_size), major(st.st_dev), minor(st.st_dev), major(st.st_rdev),
+ minor(st.st_rdev), static_cast<uint32_t>(file_name_len), 0);
+ if (write(out_fd, read_buf.data(), llen < buf_size ? llen : buf_size - 1) == -1) {
+ PLOG(ERROR) << "Error writing cpio header to file " << file_name;
+ return false;
+ }
+ if (write(out_fd, file_name, file_name_len) == -1) {
+ PLOG(ERROR) << "Error writing filename to file " << file_name;
+ return false;
+ }
+
+ // NUL Pad header up to 4 multiple bytes.
+ llen = (llen + file_name_len) % 4;
+ if (llen != 0) {
+ const uint32_t zero = 0;
+ if (write(out_fd, &zero, 4 - llen) == -1) {
+ PLOG(ERROR) << "Error padding 0s to file " << file_name;
+ return false;
+ }
+ }
+ return true;
+}
+
+// Helper function for |cpioArchiveFilesInDir|
+size_t cpioWriteFileContent(int fd_read, int out_fd, struct stat& st) {
+ // writing content of file
+ std::array<char, 32 * 1024> read_buf;
+ ssize_t llen = st.st_size;
+ size_t n_error = 0;
+ while (llen > 0) {
+ ssize_t bytes_read = read(fd_read, read_buf.data(), read_buf.size());
+ if (bytes_read == -1) {
+ PLOG(ERROR) << "Error reading file";
+ return ++n_error;
+ }
+ llen -= bytes_read;
+ if (write(out_fd, read_buf.data(), bytes_read) == -1) {
+ PLOG(ERROR) << "Error writing data to file";
+ return ++n_error;
+ }
+ if (bytes_read == 0) { // this should never happen, but just in case
+ // to unstuck from while loop
+ PLOG(ERROR) << "Unexpected read result";
+ n_error++;
+ break;
+ }
+ }
+ llen = st.st_size % 4;
+ if (llen != 0) {
+ const uint32_t zero = 0;
+ if (write(out_fd, &zero, 4 - llen) == -1) {
+ PLOG(ERROR) << "Error padding 0s to file";
+ return ++n_error;
+ }
+ }
+ return n_error;
+}
+
+// Helper function for |cpioArchiveFilesInDir|
+bool cpioWriteFileTrailer(int out_fd) {
+ const int buf_size = 4096;
+ std::array<char, buf_size> read_buf;
+ read_buf.fill(0);
+ ssize_t llen = snprintf(read_buf.data(), 4096, "070701%040X%056X%08XTRAILER!!!", 1, 0x0b, 0);
+ if (write(out_fd, read_buf.data(), (llen < buf_size ? llen : buf_size - 1) + 4) == -1) {
+ PLOG(ERROR) << "Error writing trailing bytes";
+ return false;
+ }
+ return true;
+}
+
+// Archives all files in |input_dir| and writes result into |out_fd|
+// Logic obtained from //external/toybox/toys/posix/cpio.c "Output cpio archive"
+// portion
+size_t cpioArchiveFilesInDir(int out_fd, const char* input_dir) {
+ struct dirent* dp;
+ size_t n_error = 0;
+ std::unique_ptr<DIR, decltype(&closedir)> dir_dump(opendir(input_dir), closedir);
+ if (!dir_dump) {
+ PLOG(ERROR) << "Failed to open directory";
+ return ++n_error;
+ }
+ while ((dp = readdir(dir_dump.get()))) {
+ if (dp->d_type != DT_REG) {
+ continue;
+ }
+ std::string cur_file_name(dp->d_name);
+ struct stat st;
+ const std::string cur_file_path = kTombstoneFolderPath + cur_file_name;
+ if (stat(cur_file_path.c_str(), &st) == -1) {
+ PLOG(ERROR) << "Failed to get file stat for " << cur_file_path;
+ n_error++;
+ continue;
+ }
+ const int fd_read = open(cur_file_path.c_str(), O_RDONLY);
+ if (fd_read == -1) {
+ PLOG(ERROR) << "Failed to open file " << cur_file_path;
+ n_error++;
+ continue;
+ }
+ std::string file_name_with_last_modified_time =
+ cur_file_name + "-" + std::to_string(st.st_mtime);
+ // string.size() does not include the null terminator. The cpio FreeBSD
+ // file header expects the null character to be included in the length.
+ const size_t file_name_len = file_name_with_last_modified_time.size() + 1;
+ unique_fd file_auto_closer(fd_read);
+ if (!cpioWriteHeader(out_fd, st, file_name_with_last_modified_time.c_str(),
+ file_name_len)) {
+ return ++n_error;
+ }
+ size_t write_error = cpioWriteFileContent(fd_read, out_fd, st);
+ if (write_error) {
+ return n_error + write_error;
+ }
+ }
+ if (!cpioWriteFileTrailer(out_fd)) {
+ return ++n_error;
+ }
+ return n_error;
+}
+
+// Helper function to create a non-const char*.
+std::vector<char> makeCharVec(const std::string& str) {
+ std::vector<char> vec(str.size() + 1);
+ vec.assign(str.begin(), str.end());
+ vec.push_back('\0');
+ return vec;
+}
+
+} // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+using aidl_return_util::validateAndCall;
+using aidl_return_util::validateAndCallWithLock;
+
+WifiChip::WifiChip(int32_t chip_id, bool is_primary,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<mode_controller::WifiModeController> mode_controller,
+ const std::shared_ptr<iface_util::WifiIfaceUtil> iface_util,
+ const std::weak_ptr<feature_flags::WifiFeatureFlags> feature_flags,
+ const std::function<void(const std::string&)>& handler)
+ : chip_id_(chip_id),
+ legacy_hal_(legacy_hal),
+ mode_controller_(mode_controller),
+ iface_util_(iface_util),
+ is_valid_(true),
+ current_mode_id_(feature_flags::chip_mode_ids::kInvalid),
+ modes_(feature_flags.lock()->getChipModes(is_primary)),
+ debug_ring_buffer_cb_registered_(false),
+ subsystemCallbackHandler_(handler) {
+ setActiveWlanIfaceNameProperty(kNoActiveWlanIfaceNamePropertyValue);
+}
+
+std::shared_ptr<WifiChip> WifiChip::create(
+ int32_t chip_id, bool is_primary, const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<mode_controller::WifiModeController> mode_controller,
+ const std::shared_ptr<iface_util::WifiIfaceUtil> iface_util,
+ const std::weak_ptr<feature_flags::WifiFeatureFlags> feature_flags,
+ const std::function<void(const std::string&)>& handler) {
+ std::shared_ptr<WifiChip> ptr = ndk::SharedRefBase::make<WifiChip>(
+ chip_id, is_primary, legacy_hal, mode_controller, iface_util, feature_flags, handler);
+ std::weak_ptr<WifiChip> weak_ptr_this(ptr);
+ ptr->setWeakPtr(weak_ptr_this);
+ return ptr;
+}
+
+void WifiChip::invalidate() {
+ if (!writeRingbufferFilesInternal()) {
+ LOG(ERROR) << "Error writing files to flash";
+ }
+ invalidateAndRemoveAllIfaces();
+ setActiveWlanIfaceNameProperty(kNoActiveWlanIfaceNamePropertyValue);
+ legacy_hal_.reset();
+ event_cb_handler_.invalidate();
+ is_valid_ = false;
+}
+
+void WifiChip::setWeakPtr(std::weak_ptr<WifiChip> ptr) {
+ weak_ptr_this_ = ptr;
+}
+
+bool WifiChip::isValid() {
+ return is_valid_;
+}
+
+std::set<std::shared_ptr<IWifiChipEventCallback>> WifiChip::getEventCallbacks() {
+ return event_cb_handler_.getCallbacks();
+}
+
+ndk::ScopedAStatus WifiChip::getId(int32_t* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID, &WifiChip::getIdInternal,
+ _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::registerEventCallback(
+ const std::shared_ptr<IWifiChipEventCallback>& event_callback) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::registerEventCallbackInternal, event_callback);
+}
+
+ndk::ScopedAStatus WifiChip::getCapabilities(IWifiChip::ChipCapabilityMask* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getCapabilitiesInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::getAvailableModes(std::vector<IWifiChip::ChipMode>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getAvailableModesInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::configureChip(int32_t in_modeId) {
+ return validateAndCallWithLock(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::configureChipInternal, in_modeId);
+}
+
+ndk::ScopedAStatus WifiChip::getMode(int32_t* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getModeInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::requestChipDebugInfo(IWifiChip::ChipDebugInfo* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::requestChipDebugInfoInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::requestDriverDebugDump(std::vector<uint8_t>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::requestDriverDebugDumpInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::requestFirmwareDebugDump(std::vector<uint8_t>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::requestFirmwareDebugDumpInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::createApIface(std::shared_ptr<IWifiApIface>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::createApIfaceInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::createBridgedApIface(std::shared_ptr<IWifiApIface>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::createBridgedApIfaceInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::getApIfaceNames(std::vector<std::string>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getApIfaceNamesInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::getApIface(const std::string& in_ifname,
+ std::shared_ptr<IWifiApIface>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getApIfaceInternal, _aidl_return, in_ifname);
+}
+
+ndk::ScopedAStatus WifiChip::removeApIface(const std::string& in_ifname) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::removeApIfaceInternal, in_ifname);
+}
+
+ndk::ScopedAStatus WifiChip::removeIfaceInstanceFromBridgedApIface(
+ const std::string& in_brIfaceName, const std::string& in_ifaceInstanceName) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::removeIfaceInstanceFromBridgedApIfaceInternal, in_brIfaceName,
+ in_ifaceInstanceName);
+}
+
+ndk::ScopedAStatus WifiChip::createNanIface(std::shared_ptr<IWifiNanIface>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::createNanIfaceInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::getNanIfaceNames(std::vector<std::string>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getNanIfaceNamesInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::getNanIface(const std::string& in_ifname,
+ std::shared_ptr<IWifiNanIface>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getNanIfaceInternal, _aidl_return, in_ifname);
+}
+
+ndk::ScopedAStatus WifiChip::removeNanIface(const std::string& in_ifname) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::removeNanIfaceInternal, in_ifname);
+}
+
+ndk::ScopedAStatus WifiChip::createP2pIface(std::shared_ptr<IWifiP2pIface>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::createP2pIfaceInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::getP2pIfaceNames(std::vector<std::string>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getP2pIfaceNamesInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::getP2pIface(const std::string& in_ifname,
+ std::shared_ptr<IWifiP2pIface>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getP2pIfaceInternal, _aidl_return, in_ifname);
+}
+
+ndk::ScopedAStatus WifiChip::removeP2pIface(const std::string& in_ifname) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::removeP2pIfaceInternal, in_ifname);
+}
+
+ndk::ScopedAStatus WifiChip::createStaIface(std::shared_ptr<IWifiStaIface>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::createStaIfaceInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::getStaIfaceNames(std::vector<std::string>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getStaIfaceNamesInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::getStaIface(const std::string& in_ifname,
+ std::shared_ptr<IWifiStaIface>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getStaIfaceInternal, _aidl_return, in_ifname);
+}
+
+ndk::ScopedAStatus WifiChip::removeStaIface(const std::string& in_ifname) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::removeStaIfaceInternal, in_ifname);
+}
+
+ndk::ScopedAStatus WifiChip::createRttController(
+ const std::shared_ptr<IWifiStaIface>& in_boundIface,
+ std::shared_ptr<IWifiRttController>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::createRttControllerInternal, _aidl_return, in_boundIface);
+}
+
+ndk::ScopedAStatus WifiChip::getDebugRingBuffersStatus(
+ std::vector<WifiDebugRingBufferStatus>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getDebugRingBuffersStatusInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::startLoggingToDebugRingBuffer(
+ const std::string& in_ringName, WifiDebugRingBufferVerboseLevel in_verboseLevel,
+ int32_t in_maxIntervalInSec, int32_t in_minDataSizeInBytes) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::startLoggingToDebugRingBufferInternal, in_ringName,
+ in_verboseLevel, in_maxIntervalInSec, in_minDataSizeInBytes);
+}
+
+ndk::ScopedAStatus WifiChip::forceDumpToDebugRingBuffer(const std::string& in_ringName) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::forceDumpToDebugRingBufferInternal, in_ringName);
+}
+
+ndk::ScopedAStatus WifiChip::flushRingBufferToFile() {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::flushRingBufferToFileInternal);
+}
+
+ndk::ScopedAStatus WifiChip::stopLoggingToDebugRingBuffer() {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::stopLoggingToDebugRingBufferInternal);
+}
+
+ndk::ScopedAStatus WifiChip::getDebugHostWakeReasonStats(
+ WifiDebugHostWakeReasonStats* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getDebugHostWakeReasonStatsInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiChip::enableDebugErrorAlerts(bool in_enable) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::enableDebugErrorAlertsInternal, in_enable);
+}
+
+ndk::ScopedAStatus WifiChip::selectTxPowerScenario(IWifiChip::TxPowerScenario in_scenario) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::selectTxPowerScenarioInternal, in_scenario);
+}
+
+ndk::ScopedAStatus WifiChip::resetTxPowerScenario() {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::resetTxPowerScenarioInternal);
+}
+
+ndk::ScopedAStatus WifiChip::setLatencyMode(IWifiChip::LatencyMode in_mode) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::setLatencyModeInternal, in_mode);
+}
+
+binder_status_t WifiChip::dump(int fd, const char**, uint32_t) {
+ {
+ std::unique_lock<std::mutex> lk(lock_t);
+ for (const auto& item : ringbuffer_map_) {
+ forceDumpToDebugRingBufferInternal(item.first);
+ }
+ // unique_lock unlocked here
+ }
+ usleep(100 * 1000); // sleep for 100 milliseconds to wait for
+ // ringbuffer updates.
+ if (!writeRingbufferFilesInternal()) {
+ LOG(ERROR) << "Error writing files to flash";
+ }
+ uint32_t n_error = cpioArchiveFilesInDir(fd, kTombstoneFolderPath);
+ if (n_error != 0) {
+ LOG(ERROR) << n_error << " errors occurred in cpio function";
+ }
+ fsync(fd);
+ return STATUS_OK;
+}
+
+ndk::ScopedAStatus WifiChip::setMultiStaPrimaryConnection(const std::string& in_ifName) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::setMultiStaPrimaryConnectionInternal, in_ifName);
+}
+
+ndk::ScopedAStatus WifiChip::setMultiStaUseCase(IWifiChip::MultiStaUseCase in_useCase) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::setMultiStaUseCaseInternal, in_useCase);
+}
+
+ndk::ScopedAStatus WifiChip::setCoexUnsafeChannels(
+ const std::vector<IWifiChip::CoexUnsafeChannel>& in_unsafeChannels,
+ CoexRestriction in_restrictions) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::setCoexUnsafeChannelsInternal, in_unsafeChannels,
+ in_restrictions);
+}
+
+ndk::ScopedAStatus WifiChip::setCountryCode(const std::array<uint8_t, 2>& in_code) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiChip::setCountryCodeInternal, in_code);
+}
+
+ndk::ScopedAStatus WifiChip::getUsableChannels(WifiBand in_band, WifiIfaceMode in_ifaceModeMask,
+ UsableChannelFilter in_filterMask,
+ std::vector<WifiUsableChannel>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getUsableChannelsInternal, _aidl_return, in_band,
+ in_ifaceModeMask, in_filterMask);
+}
+
+ndk::ScopedAStatus WifiChip::triggerSubsystemRestart() {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::triggerSubsystemRestartInternal);
+}
+
+ndk::ScopedAStatus WifiChip::getSupportedRadioCombinationsMatrix(
+ WifiRadioCombinationMatrix* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::getSupportedRadioCombinationsMatrixInternal, _aidl_return);
+}
+
+void WifiChip::invalidateAndRemoveAllIfaces() {
+ invalidateAndClearBridgedApAll();
+ invalidateAndClearAll(ap_ifaces_);
+ invalidateAndClearAll(nan_ifaces_);
+ invalidateAndClearAll(p2p_ifaces_);
+ invalidateAndClearAll(sta_ifaces_);
+ // Since all the ifaces are invalid now, all RTT controller objects
+ // using those ifaces also need to be invalidated.
+ for (const auto& rtt : rtt_controllers_) {
+ rtt->invalidate();
+ }
+ rtt_controllers_.clear();
+}
+
+void WifiChip::invalidateAndRemoveDependencies(const std::string& removed_iface_name) {
+ for (auto it = nan_ifaces_.begin(); it != nan_ifaces_.end();) {
+ auto nan_iface = *it;
+ if (nan_iface->getName() == removed_iface_name) {
+ nan_iface->invalidate();
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ if (!callback->onIfaceRemoved(IfaceType::NAN_IFACE, removed_iface_name).isOk()) {
+ LOG(ERROR) << "Failed to invoke onIfaceRemoved callback";
+ }
+ }
+ it = nan_ifaces_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ for (auto it = rtt_controllers_.begin(); it != rtt_controllers_.end();) {
+ auto rtt = *it;
+ if (rtt->getIfaceName() == removed_iface_name) {
+ rtt->invalidate();
+ it = rtt_controllers_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+std::pair<int32_t, ndk::ScopedAStatus> WifiChip::getIdInternal() {
+ return {chip_id_, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiChip::registerEventCallbackInternal(
+ const std::shared_ptr<IWifiChipEventCallback>& event_callback) {
+ if (!event_cb_handler_.addCallback(event_callback)) {
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+std::pair<IWifiChip::ChipCapabilityMask, ndk::ScopedAStatus> WifiChip::getCapabilitiesInternal() {
+ legacy_hal::wifi_error legacy_status;
+ uint64_t legacy_feature_set;
+ uint32_t legacy_logger_feature_set;
+ const auto ifname = getFirstActiveWlanIfaceName();
+ std::tie(legacy_status, legacy_feature_set) =
+ legacy_hal_.lock()->getSupportedFeatureSet(ifname);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ return {IWifiChip::ChipCapabilityMask{}, createWifiStatusFromLegacyError(legacy_status)};
+ }
+ std::tie(legacy_status, legacy_logger_feature_set) =
+ legacy_hal_.lock()->getLoggerSupportedFeatureSet(ifname);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ // some devices don't support querying logger feature set
+ legacy_logger_feature_set = 0;
+ }
+ uint32_t aidl_caps;
+ if (!aidl_struct_util::convertLegacyFeaturesToAidlChipCapabilities(
+ legacy_feature_set, legacy_logger_feature_set, &aidl_caps)) {
+ return {IWifiChip::ChipCapabilityMask{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {static_cast<IWifiChip::ChipCapabilityMask>(aidl_caps), ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::vector<IWifiChip::ChipMode>, ndk::ScopedAStatus>
+WifiChip::getAvailableModesInternal() {
+ return {modes_, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiChip::configureChipInternal(
+ /* NONNULL */ std::unique_lock<std::recursive_mutex>* lock, int32_t mode_id) {
+ if (!isValidModeId(mode_id)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ if (mode_id == current_mode_id_) {
+ LOG(DEBUG) << "Already in the specified mode " << mode_id;
+ return ndk::ScopedAStatus::ok();
+ }
+ ndk::ScopedAStatus status = handleChipConfiguration(lock, mode_id);
+ if (!status.isOk()) {
+ WifiStatusCode errorCode = static_cast<WifiStatusCode>(status.getServiceSpecificError());
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ if (!callback->onChipReconfigureFailure(errorCode).isOk()) {
+ LOG(ERROR) << "Failed to invoke onChipReconfigureFailure callback";
+ }
+ }
+ return status;
+ }
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ if (!callback->onChipReconfigured(mode_id).isOk()) {
+ LOG(ERROR) << "Failed to invoke onChipReconfigured callback";
+ }
+ }
+ current_mode_id_ = mode_id;
+ LOG(INFO) << "Configured chip in mode " << mode_id;
+ setActiveWlanIfaceNameProperty(getFirstActiveWlanIfaceName());
+
+ legacy_hal_.lock()->registerSubsystemRestartCallbackHandler(subsystemCallbackHandler_);
+
+ return status;
+}
+
+std::pair<int32_t, ndk::ScopedAStatus> WifiChip::getModeInternal() {
+ if (!isValidModeId(current_mode_id_)) {
+ return {current_mode_id_, createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE)};
+ }
+ return {current_mode_id_, ndk::ScopedAStatus::ok()};
+}
+
+std::pair<IWifiChip::ChipDebugInfo, ndk::ScopedAStatus> WifiChip::requestChipDebugInfoInternal() {
+ IWifiChip::ChipDebugInfo result;
+ legacy_hal::wifi_error legacy_status;
+ std::string driver_desc;
+ const auto ifname = getFirstActiveWlanIfaceName();
+ std::tie(legacy_status, driver_desc) = legacy_hal_.lock()->getDriverVersion(ifname);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to get driver version: " << legacyErrorToString(legacy_status);
+ ndk::ScopedAStatus status =
+ createWifiStatusFromLegacyError(legacy_status, "failed to get driver version");
+ return {std::move(result), std::move(status)};
+ }
+ result.driverDescription = driver_desc.c_str();
+
+ std::string firmware_desc;
+ std::tie(legacy_status, firmware_desc) = legacy_hal_.lock()->getFirmwareVersion(ifname);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to get firmware version: " << legacyErrorToString(legacy_status);
+ ndk::ScopedAStatus status =
+ createWifiStatusFromLegacyError(legacy_status, "failed to get firmware version");
+ return {std::move(result), std::move(status)};
+ }
+ result.firmwareDescription = firmware_desc.c_str();
+
+ return {std::move(result), ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::vector<uint8_t>, ndk::ScopedAStatus> WifiChip::requestDriverDebugDumpInternal() {
+ legacy_hal::wifi_error legacy_status;
+ std::vector<uint8_t> driver_dump;
+ std::tie(legacy_status, driver_dump) =
+ legacy_hal_.lock()->requestDriverMemoryDump(getFirstActiveWlanIfaceName());
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to get driver debug dump: " << legacyErrorToString(legacy_status);
+ return {std::vector<uint8_t>(), createWifiStatusFromLegacyError(legacy_status)};
+ }
+ return {driver_dump, ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::vector<uint8_t>, ndk::ScopedAStatus> WifiChip::requestFirmwareDebugDumpInternal() {
+ legacy_hal::wifi_error legacy_status;
+ std::vector<uint8_t> firmware_dump;
+ std::tie(legacy_status, firmware_dump) =
+ legacy_hal_.lock()->requestFirmwareMemoryDump(getFirstActiveWlanIfaceName());
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to get firmware debug dump: " << legacyErrorToString(legacy_status);
+ return {std::vector<uint8_t>(), createWifiStatusFromLegacyError(legacy_status)};
+ }
+ return {firmware_dump, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiChip::createVirtualApInterface(const std::string& apVirtIf) {
+ legacy_hal::wifi_error legacy_status;
+ legacy_status = legacy_hal_.lock()->createVirtualInterface(
+ apVirtIf, aidl_struct_util::convertAidlIfaceTypeToLegacy(IfaceType::AP));
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to add interface: " << apVirtIf << " "
+ << legacyErrorToString(legacy_status);
+ return createWifiStatusFromLegacyError(legacy_status);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+std::shared_ptr<WifiApIface> WifiChip::newWifiApIface(std::string& ifname) {
+ std::vector<std::string> ap_instances;
+ for (auto const& it : br_ifaces_ap_instances_) {
+ if (it.first == ifname) {
+ ap_instances = it.second;
+ }
+ }
+ std::shared_ptr<WifiApIface> iface =
+ ndk::SharedRefBase::make<WifiApIface>(ifname, ap_instances, legacy_hal_, iface_util_);
+ ap_ifaces_.push_back(iface);
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ if (!callback->onIfaceAdded(IfaceType::AP, ifname).isOk()) {
+ LOG(ERROR) << "Failed to invoke onIfaceAdded callback";
+ }
+ }
+ setActiveWlanIfaceNameProperty(getFirstActiveWlanIfaceName());
+ return iface;
+}
+
+std::pair<std::shared_ptr<IWifiApIface>, ndk::ScopedAStatus> WifiChip::createApIfaceInternal() {
+ if (!canCurrentModeSupportConcurrencyTypeWithCurrentTypes(IfaceConcurrencyType::AP)) {
+ return {std::shared_ptr<WifiApIface>(),
+ createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE)};
+ }
+ std::string ifname = allocateApIfaceName();
+ ndk::ScopedAStatus status = createVirtualApInterface(ifname);
+ if (!status.isOk()) {
+ return {std::shared_ptr<WifiApIface>(), std::move(status)};
+ }
+ std::shared_ptr<WifiApIface> iface = newWifiApIface(ifname);
+ return {iface, ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::shared_ptr<IWifiApIface>, ndk::ScopedAStatus>
+WifiChip::createBridgedApIfaceInternal() {
+ if (!canCurrentModeSupportConcurrencyTypeWithCurrentTypes(IfaceConcurrencyType::AP_BRIDGED)) {
+ return {nullptr, createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE)};
+ }
+ std::vector<std::string> ap_instances = allocateBridgedApInstanceNames();
+ if (ap_instances.size() < 2) {
+ LOG(ERROR) << "Fail to allocate two instances";
+ return {nullptr, createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE)};
+ }
+ std::string br_ifname = kApBridgeIfacePrefix + ap_instances[0];
+ for (int i = 0; i < 2; i++) {
+ ndk::ScopedAStatus status = createVirtualApInterface(ap_instances[i]);
+ if (!status.isOk()) {
+ if (i != 0) { // The failure happened when creating second virtual
+ // iface.
+ legacy_hal_.lock()->deleteVirtualInterface(
+ ap_instances.front()); // Remove the first virtual iface.
+ }
+ return {nullptr, std::move(status)};
+ }
+ }
+ br_ifaces_ap_instances_[br_ifname] = ap_instances;
+ if (!iface_util_->createBridge(br_ifname)) {
+ LOG(ERROR) << "Failed createBridge - br_name=" << br_ifname.c_str();
+ invalidateAndClearBridgedAp(br_ifname);
+ return {nullptr, createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE)};
+ }
+ for (auto const& instance : ap_instances) {
+ // Bind ap instance interface to AP bridge
+ if (!iface_util_->addIfaceToBridge(br_ifname, instance)) {
+ LOG(ERROR) << "Failed add if to Bridge - if_name=" << instance.c_str();
+ invalidateAndClearBridgedAp(br_ifname);
+ return {nullptr, createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE)};
+ }
+ }
+ std::shared_ptr<WifiApIface> iface = newWifiApIface(br_ifname);
+ return {iface, ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::vector<std::string>, ndk::ScopedAStatus> WifiChip::getApIfaceNamesInternal() {
+ if (ap_ifaces_.empty()) {
+ return {std::vector<std::string>(), ndk::ScopedAStatus::ok()};
+ }
+ return {getNames(ap_ifaces_), ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::shared_ptr<IWifiApIface>, ndk::ScopedAStatus> WifiChip::getApIfaceInternal(
+ const std::string& ifname) {
+ const auto iface = findUsingName(ap_ifaces_, ifname);
+ if (!iface.get()) {
+ return {nullptr, createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS)};
+ }
+ return {iface, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiChip::removeApIfaceInternal(const std::string& ifname) {
+ const auto iface = findUsingName(ap_ifaces_, ifname);
+ if (!iface.get()) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ // Invalidate & remove any dependent objects first.
+ // Note: This is probably not required because we never create
+ // 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);
+ // Clear the bridge interface and the iface instance.
+ invalidateAndClearBridgedAp(ifname);
+ invalidateAndClear(ap_ifaces_, iface);
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ if (!callback->onIfaceRemoved(IfaceType::AP, ifname).isOk()) {
+ LOG(ERROR) << "Failed to invoke onIfaceRemoved callback";
+ }
+ }
+ setActiveWlanIfaceNameProperty(getFirstActiveWlanIfaceName());
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WifiChip::removeIfaceInstanceFromBridgedApIfaceInternal(
+ const std::string& ifname, const std::string& ifInstanceName) {
+ const auto iface = findUsingName(ap_ifaces_, ifname);
+ if (!iface.get() || ifInstanceName.empty()) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ // Requires to remove one of the instance in bridge mode
+ for (auto const& it : br_ifaces_ap_instances_) {
+ if (it.first == ifname) {
+ std::vector<std::string> ap_instances = it.second;
+ for (auto const& iface : ap_instances) {
+ if (iface == ifInstanceName) {
+ if (!iface_util_->removeIfaceFromBridge(it.first, iface)) {
+ LOG(ERROR) << "Failed to remove interface: " << ifInstanceName << " from "
+ << ifname;
+ return createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE);
+ }
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->deleteVirtualInterface(iface);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to del interface: " << iface << " "
+ << legacyErrorToString(legacy_status);
+ return createWifiStatusFromLegacyError(legacy_status);
+ }
+ ap_instances.erase(
+ std::remove(ap_instances.begin(), ap_instances.end(), ifInstanceName),
+ ap_instances.end());
+ br_ifaces_ap_instances_[ifname] = ap_instances;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ iface->removeInstance(ifInstanceName);
+ setActiveWlanIfaceNameProperty(getFirstActiveWlanIfaceName());
+
+ return ndk::ScopedAStatus::ok();
+}
+
+std::pair<std::shared_ptr<IWifiNanIface>, ndk::ScopedAStatus> WifiChip::createNanIfaceInternal() {
+ if (!canCurrentModeSupportConcurrencyTypeWithCurrentTypes(IfaceConcurrencyType::NAN_IFACE)) {
+ return {nullptr, createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE)};
+ }
+ bool is_dedicated_iface = true;
+ std::string ifname = getPredefinedNanIfaceName();
+ if (ifname.empty() || !iface_util_->ifNameToIndex(ifname)) {
+ // Use the first shared STA iface (wlan0) if a dedicated aware iface is
+ // not defined.
+ ifname = getFirstActiveWlanIfaceName();
+ is_dedicated_iface = false;
+ }
+ std::shared_ptr<WifiNanIface> iface =
+ WifiNanIface::create(ifname, is_dedicated_iface, legacy_hal_, iface_util_);
+ nan_ifaces_.push_back(iface);
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ if (!callback->onIfaceAdded(IfaceType::NAN_IFACE, ifname).isOk()) {
+ LOG(ERROR) << "Failed to invoke onIfaceAdded callback";
+ }
+ }
+ return {iface, ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::vector<std::string>, ndk::ScopedAStatus> WifiChip::getNanIfaceNamesInternal() {
+ if (nan_ifaces_.empty()) {
+ return {std::vector<std::string>(), ndk::ScopedAStatus::ok()};
+ }
+ return {getNames(nan_ifaces_), ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::shared_ptr<IWifiNanIface>, ndk::ScopedAStatus> WifiChip::getNanIfaceInternal(
+ const std::string& ifname) {
+ const auto iface = findUsingName(nan_ifaces_, ifname);
+ if (!iface.get()) {
+ return {nullptr, createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS)};
+ }
+ return {iface, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiChip::removeNanIfaceInternal(const std::string& ifname) {
+ const auto iface = findUsingName(nan_ifaces_, ifname);
+ if (!iface.get()) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ invalidateAndClear(nan_ifaces_, iface);
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ if (!callback->onIfaceRemoved(IfaceType::NAN_IFACE, ifname).isOk()) {
+ LOG(ERROR) << "Failed to invoke onIfaceAdded callback";
+ }
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+std::pair<std::shared_ptr<IWifiP2pIface>, ndk::ScopedAStatus> WifiChip::createP2pIfaceInternal() {
+ if (!canCurrentModeSupportConcurrencyTypeWithCurrentTypes(IfaceConcurrencyType::P2P)) {
+ return {nullptr, createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE)};
+ }
+ std::string ifname = getPredefinedP2pIfaceName();
+ std::shared_ptr<WifiP2pIface> iface =
+ ndk::SharedRefBase::make<WifiP2pIface>(ifname, legacy_hal_);
+ p2p_ifaces_.push_back(iface);
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ if (!callback->onIfaceAdded(IfaceType::P2P, ifname).isOk()) {
+ LOG(ERROR) << "Failed to invoke onIfaceAdded callback";
+ }
+ }
+ return {iface, ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::vector<std::string>, ndk::ScopedAStatus> WifiChip::getP2pIfaceNamesInternal() {
+ if (p2p_ifaces_.empty()) {
+ return {std::vector<std::string>(), ndk::ScopedAStatus::ok()};
+ }
+ return {getNames(p2p_ifaces_), ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::shared_ptr<IWifiP2pIface>, ndk::ScopedAStatus> WifiChip::getP2pIfaceInternal(
+ const std::string& ifname) {
+ const auto iface = findUsingName(p2p_ifaces_, ifname);
+ if (!iface.get()) {
+ return {nullptr, createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS)};
+ }
+ return {iface, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiChip::removeP2pIfaceInternal(const std::string& ifname) {
+ const auto iface = findUsingName(p2p_ifaces_, ifname);
+ if (!iface.get()) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ invalidateAndClear(p2p_ifaces_, iface);
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ if (!callback->onIfaceRemoved(IfaceType::P2P, ifname).isOk()) {
+ LOG(ERROR) << "Failed to invoke onIfaceRemoved callback";
+ }
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+std::pair<std::shared_ptr<IWifiStaIface>, ndk::ScopedAStatus> WifiChip::createStaIfaceInternal() {
+ if (!canCurrentModeSupportConcurrencyTypeWithCurrentTypes(IfaceConcurrencyType::STA)) {
+ return {nullptr, createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE)};
+ }
+ std::string ifname = allocateStaIfaceName();
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->createVirtualInterface(
+ ifname, aidl_struct_util::convertAidlIfaceTypeToLegacy(IfaceType::STA));
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to add interface: " << ifname << " "
+ << legacyErrorToString(legacy_status);
+ return {nullptr, createWifiStatusFromLegacyError(legacy_status)};
+ }
+ std::shared_ptr<WifiStaIface> iface =
+ ndk::SharedRefBase::make<WifiStaIface>(ifname, legacy_hal_, iface_util_);
+ sta_ifaces_.push_back(iface);
+ for (const auto& callback : event_cb_handler_.getCallbacks()) {
+ if (!callback->onIfaceAdded(IfaceType::STA, ifname).isOk()) {
+ LOG(ERROR) << "Failed to invoke onIfaceAdded callback";
+ }
+ }
+ setActiveWlanIfaceNameProperty(getFirstActiveWlanIfaceName());
+ return {iface, ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::vector<std::string>, ndk::ScopedAStatus> WifiChip::getStaIfaceNamesInternal() {
+ if (sta_ifaces_.empty()) {
+ return {std::vector<std::string>(), ndk::ScopedAStatus::ok()};
+ }
+ return {getNames(sta_ifaces_), ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::shared_ptr<IWifiStaIface>, ndk::ScopedAStatus> WifiChip::getStaIfaceInternal(
+ const std::string& ifname) {
+ const auto iface = findUsingName(sta_ifaces_, ifname);
+ if (!iface.get()) {
+ return {nullptr, createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS)};
+ }
+ return {iface, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiChip::removeStaIfaceInternal(const std::string& ifname) {
+ const auto iface = findUsingName(sta_ifaces_, ifname);
+ if (!iface.get()) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ // 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()) {
+ LOG(ERROR) << "Failed to invoke onIfaceRemoved callback";
+ }
+ }
+ setActiveWlanIfaceNameProperty(getFirstActiveWlanIfaceName());
+ return ndk::ScopedAStatus::ok();
+}
+
+std::pair<std::shared_ptr<IWifiRttController>, ndk::ScopedAStatus>
+WifiChip::createRttControllerInternal(const std::shared_ptr<IWifiStaIface>& bound_iface) {
+ if (sta_ifaces_.size() == 0 &&
+ !canCurrentModeSupportConcurrencyTypeWithCurrentTypes(IfaceConcurrencyType::STA)) {
+ LOG(ERROR) << "createRttControllerInternal: Chip cannot support STAs "
+ "(and RTT by extension)";
+ return {nullptr, createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE)};
+ }
+ std::shared_ptr<WifiRttController> rtt =
+ WifiRttController::create(getFirstActiveWlanIfaceName(), bound_iface, legacy_hal_);
+ rtt_controllers_.emplace_back(rtt);
+ return {rtt, ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::vector<WifiDebugRingBufferStatus>, ndk::ScopedAStatus>
+WifiChip::getDebugRingBuffersStatusInternal() {
+ legacy_hal::wifi_error legacy_status;
+ std::vector<legacy_hal::wifi_ring_buffer_status> legacy_ring_buffer_status_vec;
+ std::tie(legacy_status, legacy_ring_buffer_status_vec) =
+ legacy_hal_.lock()->getRingBuffersStatus(getFirstActiveWlanIfaceName());
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ return {std::vector<WifiDebugRingBufferStatus>(),
+ createWifiStatusFromLegacyError(legacy_status)};
+ }
+ std::vector<WifiDebugRingBufferStatus> aidl_ring_buffer_status_vec;
+ if (!aidl_struct_util::convertLegacyVectorOfDebugRingBufferStatusToAidl(
+ legacy_ring_buffer_status_vec, &aidl_ring_buffer_status_vec)) {
+ return {std::vector<WifiDebugRingBufferStatus>(),
+ createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {aidl_ring_buffer_status_vec, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiChip::startLoggingToDebugRingBufferInternal(
+ const std::string& ring_name, WifiDebugRingBufferVerboseLevel verbose_level,
+ uint32_t max_interval_in_sec, uint32_t min_data_size_in_bytes) {
+ ndk::ScopedAStatus status = registerDebugRingBufferCallback();
+ if (!status.isOk()) {
+ return status;
+ }
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->startRingBufferLogging(
+ getFirstActiveWlanIfaceName(), ring_name,
+ static_cast<std::underlying_type<WifiDebugRingBufferVerboseLevel>::type>(verbose_level),
+ max_interval_in_sec, min_data_size_in_bytes);
+ ringbuffer_map_.insert(
+ std::pair<std::string, Ringbuffer>(ring_name, Ringbuffer(kMaxBufferSizeBytes)));
+ // if verbose logging enabled, turn up HAL daemon logging as well.
+ if (verbose_level < WifiDebugRingBufferVerboseLevel::VERBOSE) {
+ ::android::base::SetMinimumLogSeverity(::android::base::DEBUG);
+ } else {
+ ::android::base::SetMinimumLogSeverity(::android::base::VERBOSE);
+ }
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiChip::forceDumpToDebugRingBufferInternal(const std::string& ring_name) {
+ ndk::ScopedAStatus status = registerDebugRingBufferCallback();
+ if (!status.isOk()) {
+ return status;
+ }
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->getRingBufferData(getFirstActiveWlanIfaceName(), ring_name);
+
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiChip::flushRingBufferToFileInternal() {
+ if (!writeRingbufferFilesInternal()) {
+ LOG(ERROR) << "Error writing files to flash";
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WifiChip::stopLoggingToDebugRingBufferInternal() {
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->deregisterRingBufferCallbackHandler(getFirstActiveWlanIfaceName());
+ if (legacy_status == legacy_hal::WIFI_SUCCESS) {
+ debug_ring_buffer_cb_registered_ = false;
+ }
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+std::pair<WifiDebugHostWakeReasonStats, ndk::ScopedAStatus>
+WifiChip::getDebugHostWakeReasonStatsInternal() {
+ legacy_hal::wifi_error legacy_status;
+ legacy_hal::WakeReasonStats legacy_stats;
+ std::tie(legacy_status, legacy_stats) =
+ legacy_hal_.lock()->getWakeReasonStats(getFirstActiveWlanIfaceName());
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ return {WifiDebugHostWakeReasonStats{}, createWifiStatusFromLegacyError(legacy_status)};
+ }
+ WifiDebugHostWakeReasonStats aidl_stats;
+ if (!aidl_struct_util::convertLegacyWakeReasonStatsToAidl(legacy_stats, &aidl_stats)) {
+ return {WifiDebugHostWakeReasonStats{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {aidl_stats, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiChip::enableDebugErrorAlertsInternal(bool enable) {
+ legacy_hal::wifi_error legacy_status;
+ if (enable) {
+ std::weak_ptr<WifiChip> weak_ptr_this = weak_ptr_this_;
+ const auto& on_alert_callback = [weak_ptr_this](int32_t error_code,
+ std::vector<uint8_t> debug_data) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->onDebugErrorAlert(error_code, debug_data).isOk()) {
+ LOG(ERROR) << "Failed to invoke onDebugErrorAlert callback";
+ }
+ }
+ };
+ legacy_status = legacy_hal_.lock()->registerErrorAlertCallbackHandler(
+ getFirstActiveWlanIfaceName(), on_alert_callback);
+ } else {
+ legacy_status = legacy_hal_.lock()->deregisterErrorAlertCallbackHandler(
+ getFirstActiveWlanIfaceName());
+ }
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiChip::selectTxPowerScenarioInternal(IWifiChip::TxPowerScenario scenario) {
+ auto legacy_status = legacy_hal_.lock()->selectTxPowerScenario(
+ getFirstActiveWlanIfaceName(),
+ aidl_struct_util::convertAidlTxPowerScenarioToLegacy(scenario));
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiChip::resetTxPowerScenarioInternal() {
+ auto legacy_status = legacy_hal_.lock()->resetTxPowerScenario(getFirstActiveWlanIfaceName());
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiChip::setLatencyModeInternal(IWifiChip::LatencyMode mode) {
+ auto legacy_status = legacy_hal_.lock()->setLatencyMode(
+ getFirstActiveWlanIfaceName(), aidl_struct_util::convertAidlLatencyModeToLegacy(mode));
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiChip::setMultiStaPrimaryConnectionInternal(const std::string& ifname) {
+ auto legacy_status = legacy_hal_.lock()->multiStaSetPrimaryConnection(ifname);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiChip::setMultiStaUseCaseInternal(IWifiChip::MultiStaUseCase use_case) {
+ auto legacy_status = legacy_hal_.lock()->multiStaSetUseCase(
+ aidl_struct_util::convertAidlMultiStaUseCaseToLegacy(use_case));
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiChip::setCoexUnsafeChannelsInternal(
+ std::vector<IWifiChip::CoexUnsafeChannel> unsafe_channels, CoexRestriction restrictions) {
+ std::vector<legacy_hal::wifi_coex_unsafe_channel> legacy_unsafe_channels;
+ if (!aidl_struct_util::convertAidlVectorOfCoexUnsafeChannelToLegacy(unsafe_channels,
+ &legacy_unsafe_channels)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ uint32_t aidl_restrictions = static_cast<uint32_t>(restrictions);
+ uint32_t legacy_restrictions = 0;
+ if (aidl_restrictions & static_cast<uint32_t>(CoexRestriction::WIFI_DIRECT)) {
+ legacy_restrictions |= legacy_hal::wifi_coex_restriction::WIFI_DIRECT;
+ }
+ if (aidl_restrictions & static_cast<uint32_t>(CoexRestriction::SOFTAP)) {
+ legacy_restrictions |= legacy_hal::wifi_coex_restriction::SOFTAP;
+ }
+ if (aidl_restrictions & static_cast<uint32_t>(CoexRestriction::WIFI_AWARE)) {
+ legacy_restrictions |= legacy_hal::wifi_coex_restriction::WIFI_AWARE;
+ }
+ auto legacy_status =
+ legacy_hal_.lock()->setCoexUnsafeChannels(legacy_unsafe_channels, legacy_restrictions);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiChip::setCountryCodeInternal(const std::array<uint8_t, 2>& code) {
+ auto legacy_status = legacy_hal_.lock()->setCountryCode(getFirstActiveWlanIfaceName(), code);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+std::pair<std::vector<WifiUsableChannel>, ndk::ScopedAStatus> WifiChip::getUsableChannelsInternal(
+ WifiBand band, WifiIfaceMode ifaceModeMask, UsableChannelFilter filterMask) {
+ legacy_hal::wifi_error legacy_status;
+ std::vector<legacy_hal::wifi_usable_channel> legacy_usable_channels;
+ std::tie(legacy_status, legacy_usable_channels) = legacy_hal_.lock()->getUsableChannels(
+ aidl_struct_util::convertAidlWifiBandToLegacyMacBand(band),
+ aidl_struct_util::convertAidlWifiIfaceModeToLegacy(
+ static_cast<uint32_t>(ifaceModeMask)),
+ aidl_struct_util::convertAidlUsableChannelFilterToLegacy(
+ static_cast<uint32_t>(filterMask)));
+
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ return {std::vector<WifiUsableChannel>(), createWifiStatusFromLegacyError(legacy_status)};
+ }
+ std::vector<WifiUsableChannel> aidl_usable_channels;
+ if (!aidl_struct_util::convertLegacyWifiUsableChannelsToAidl(legacy_usable_channels,
+ &aidl_usable_channels)) {
+ return {std::vector<WifiUsableChannel>(), createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {aidl_usable_channels, ndk::ScopedAStatus::ok()};
+}
+
+std::pair<WifiRadioCombinationMatrix, ndk::ScopedAStatus>
+WifiChip::getSupportedRadioCombinationsMatrixInternal() {
+ legacy_hal::wifi_error legacy_status;
+ legacy_hal::wifi_radio_combination_matrix* legacy_matrix;
+
+ std::tie(legacy_status, legacy_matrix) =
+ legacy_hal_.lock()->getSupportedRadioCombinationsMatrix();
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to get SupportedRadioCombinations matrix from legacy HAL: "
+ << legacyErrorToString(legacy_status);
+ return {WifiRadioCombinationMatrix{}, createWifiStatusFromLegacyError(legacy_status)};
+ }
+
+ WifiRadioCombinationMatrix aidl_matrix;
+ if (!aidl_struct_util::convertLegacyRadioCombinationsMatrixToAidl(legacy_matrix,
+ &aidl_matrix)) {
+ LOG(ERROR) << "Failed convertLegacyRadioCombinationsMatrixToAidl() ";
+ return {WifiRadioCombinationMatrix(), createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS)};
+ }
+ return {aidl_matrix, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiChip::triggerSubsystemRestartInternal() {
+ auto legacy_status = legacy_hal_.lock()->triggerSubsystemRestart();
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiChip::handleChipConfiguration(
+ /* NONNULL */ std::unique_lock<std::recursive_mutex>* lock, int32_t mode_id) {
+ // If the chip is already configured in a different mode, stop
+ // the legacy HAL and then start it after firmware mode change.
+ if (isValidModeId(current_mode_id_)) {
+ LOG(INFO) << "Reconfiguring chip from mode " << current_mode_id_ << " to mode " << mode_id;
+ invalidateAndRemoveAllIfaces();
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->stop(lock, []() {});
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to stop legacy HAL: " << legacyErrorToString(legacy_status);
+ return createWifiStatusFromLegacyError(legacy_status);
+ }
+ }
+ // Firmware mode change not needed for V2 devices.
+ bool success = true;
+ if (mode_id == feature_flags::chip_mode_ids::kV1Sta) {
+ success = mode_controller_.lock()->changeFirmwareMode(IfaceType::STA);
+ } else if (mode_id == feature_flags::chip_mode_ids::kV1Ap) {
+ success = mode_controller_.lock()->changeFirmwareMode(IfaceType::AP);
+ }
+ if (!success) {
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->start();
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to start legacy HAL: " << legacyErrorToString(legacy_status);
+ return createWifiStatusFromLegacyError(legacy_status);
+ }
+ // Every time the HAL is restarted, we need to register the
+ // radio mode change callback.
+ ndk::ScopedAStatus status = registerRadioModeChangeCallback();
+ if (!status.isOk()) {
+ // This is probably not a critical failure?
+ LOG(ERROR) << "Failed to register radio mode change callback";
+ }
+ // Extract and save the version information into property.
+ std::pair<IWifiChip::ChipDebugInfo, ndk::ScopedAStatus> version_info;
+ version_info = WifiChip::requestChipDebugInfoInternal();
+ if (version_info.second.isOk()) {
+ property_set("vendor.wlan.firmware.version",
+ version_info.first.firmwareDescription.c_str());
+ property_set("vendor.wlan.driver.version", version_info.first.driverDescription.c_str());
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WifiChip::registerDebugRingBufferCallback() {
+ if (debug_ring_buffer_cb_registered_) {
+ return ndk::ScopedAStatus::ok();
+ }
+
+ std::weak_ptr<WifiChip> weak_ptr_this = weak_ptr_this_;
+ const auto& on_ring_buffer_data_callback =
+ [weak_ptr_this](const std::string& name, const std::vector<uint8_t>& data,
+ const legacy_hal::wifi_ring_buffer_status& status) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ WifiDebugRingBufferStatus aidl_status;
+ Ringbuffer::AppendStatus appendstatus;
+ if (!aidl_struct_util::convertLegacyDebugRingBufferStatusToAidl(status,
+ &aidl_status)) {
+ LOG(ERROR) << "Error converting ring buffer status";
+ return;
+ }
+ {
+ std::unique_lock<std::mutex> lk(shared_ptr_this->lock_t);
+ const auto& target = shared_ptr_this->ringbuffer_map_.find(name);
+ if (target != shared_ptr_this->ringbuffer_map_.end()) {
+ Ringbuffer& cur_buffer = target->second;
+ appendstatus = cur_buffer.append(data);
+ } else {
+ LOG(ERROR) << "Ringname " << name << " not found";
+ return;
+ }
+ // unique_lock unlocked here
+ }
+ if (appendstatus == Ringbuffer::AppendStatus::FAIL_RING_BUFFER_CORRUPTED) {
+ LOG(ERROR) << "Ringname " << name << " is corrupted. Clear the ring buffer";
+ shared_ptr_this->writeRingbufferFilesInternal();
+ return;
+ }
+ };
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->registerRingBufferCallbackHandler(
+ getFirstActiveWlanIfaceName(), on_ring_buffer_data_callback);
+
+ if (legacy_status == legacy_hal::WIFI_SUCCESS) {
+ debug_ring_buffer_cb_registered_ = true;
+ }
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiChip::registerRadioModeChangeCallback() {
+ std::weak_ptr<WifiChip> weak_ptr_this = weak_ptr_this_;
+ const auto& on_radio_mode_change_callback =
+ [weak_ptr_this](const std::vector<legacy_hal::WifiMacInfo>& mac_infos) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ std::vector<IWifiChipEventCallback::RadioModeInfo> aidl_radio_mode_infos;
+ if (!aidl_struct_util::convertLegacyWifiMacInfosToAidl(mac_infos,
+ &aidl_radio_mode_infos)) {
+ LOG(ERROR) << "Error converting wifi mac info";
+ return;
+ }
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->onRadioModeChange(aidl_radio_mode_infos).isOk()) {
+ LOG(ERROR) << "Failed to invoke onRadioModeChange callback";
+ }
+ }
+ };
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->registerRadioModeChangeCallbackHandler(
+ getFirstActiveWlanIfaceName(), on_radio_mode_change_callback);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+std::vector<IWifiChip::ChipConcurrencyCombination>
+WifiChip::getCurrentModeConcurrencyCombinations() {
+ if (!isValidModeId(current_mode_id_)) {
+ LOG(ERROR) << "Chip not configured in a mode yet";
+ return std::vector<IWifiChip::ChipConcurrencyCombination>();
+ }
+ for (const auto& mode : modes_) {
+ if (mode.id == current_mode_id_) {
+ return mode.availableCombinations;
+ }
+ }
+ CHECK(0) << "Expected to find concurrency combinations for current mode!";
+ return std::vector<IWifiChip::ChipConcurrencyCombination>();
+}
+
+// Returns a map indexed by IfaceConcurrencyType with the number of ifaces currently
+// created of the corresponding concurrency type.
+std::map<IfaceConcurrencyType, size_t> WifiChip::getCurrentConcurrencyCombination() {
+ std::map<IfaceConcurrencyType, size_t> iface_counts;
+ uint32_t num_ap = 0;
+ uint32_t num_ap_bridged = 0;
+ for (const auto& ap_iface : ap_ifaces_) {
+ std::string ap_iface_name = ap_iface->getName();
+ if (br_ifaces_ap_instances_.count(ap_iface_name) > 0 &&
+ br_ifaces_ap_instances_[ap_iface_name].size() > 1) {
+ num_ap_bridged++;
+ } else {
+ num_ap++;
+ }
+ }
+ iface_counts[IfaceConcurrencyType::AP] = num_ap;
+ iface_counts[IfaceConcurrencyType::AP_BRIDGED] = num_ap_bridged;
+ iface_counts[IfaceConcurrencyType::NAN_IFACE] = nan_ifaces_.size();
+ iface_counts[IfaceConcurrencyType::P2P] = p2p_ifaces_.size();
+ iface_counts[IfaceConcurrencyType::STA] = sta_ifaces_.size();
+ return iface_counts;
+}
+
+// This expands the provided concurrency combinations to a more parseable
+// form. Returns a vector of available combinations possible with the number
+// of each concurrency type in the combination.
+// This method is a port of HalDeviceManager.expandConcurrencyCombos() from framework.
+std::vector<std::map<IfaceConcurrencyType, size_t>> WifiChip::expandConcurrencyCombinations(
+ const IWifiChip::ChipConcurrencyCombination& combination) {
+ int32_t num_expanded_combos = 1;
+ for (const auto& limit : combination.limits) {
+ for (int32_t i = 0; i < limit.maxIfaces; i++) {
+ num_expanded_combos *= limit.types.size();
+ }
+ }
+
+ // Allocate the vector of expanded combos and reset all concurrency type counts to 0
+ // in each combo.
+ std::vector<std::map<IfaceConcurrencyType, size_t>> expanded_combos;
+ expanded_combos.resize(num_expanded_combos);
+ for (auto& expanded_combo : expanded_combos) {
+ for (const auto type : {IfaceConcurrencyType::AP, IfaceConcurrencyType::AP_BRIDGED,
+ IfaceConcurrencyType::NAN_IFACE, IfaceConcurrencyType::P2P,
+ IfaceConcurrencyType::STA}) {
+ expanded_combo[type] = 0;
+ }
+ }
+ int32_t span = num_expanded_combos;
+ for (const auto& limit : combination.limits) {
+ for (int32_t i = 0; i < limit.maxIfaces; i++) {
+ span /= limit.types.size();
+ for (int32_t k = 0; k < num_expanded_combos; ++k) {
+ const auto iface_type = limit.types[(k / span) % limit.types.size()];
+ expanded_combos[k][iface_type]++;
+ }
+ }
+ }
+ return expanded_combos;
+}
+
+bool WifiChip::canExpandedConcurrencyComboSupportConcurrencyTypeWithCurrentTypes(
+ const std::map<IfaceConcurrencyType, size_t>& expanded_combo,
+ IfaceConcurrencyType requested_type) {
+ const auto current_combo = getCurrentConcurrencyCombination();
+
+ // Check if we have space for 1 more iface of |type| in this combo
+ for (const auto type :
+ {IfaceConcurrencyType::AP, IfaceConcurrencyType::AP_BRIDGED,
+ IfaceConcurrencyType::NAN_IFACE, IfaceConcurrencyType::P2P, IfaceConcurrencyType::STA}) {
+ size_t num_ifaces_needed = current_combo.at(type);
+ if (type == requested_type) {
+ num_ifaces_needed++;
+ }
+ size_t num_ifaces_allowed = expanded_combo.at(type);
+ if (num_ifaces_needed > num_ifaces_allowed) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// This method does the following:
+// a) Enumerate all possible concurrency combos by expanding the current
+// ChipConcurrencyCombination.
+// b) Check if the requested concurrency type can be added to the current mode
+// with the concurrency combination that is already active.
+bool WifiChip::canCurrentModeSupportConcurrencyTypeWithCurrentTypes(
+ IfaceConcurrencyType requested_type) {
+ if (!isValidModeId(current_mode_id_)) {
+ LOG(ERROR) << "Chip not configured in a mode yet";
+ return false;
+ }
+ const auto combinations = getCurrentModeConcurrencyCombinations();
+ for (const auto& combination : combinations) {
+ const auto expanded_combos = expandConcurrencyCombinations(combination);
+ for (const auto& expanded_combo : expanded_combos) {
+ if (canExpandedConcurrencyComboSupportConcurrencyTypeWithCurrentTypes(expanded_combo,
+ requested_type)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// Note: This does not consider concurrency types already active. It only checks if the
+// provided expanded concurrency combination can support the requested combo.
+bool WifiChip::canExpandedConcurrencyComboSupportConcurrencyCombo(
+ const std::map<IfaceConcurrencyType, size_t>& expanded_combo,
+ const std::map<IfaceConcurrencyType, size_t>& req_combo) {
+ // Check if we have space for 1 more |type| in this combo
+ for (const auto type :
+ {IfaceConcurrencyType::AP, IfaceConcurrencyType::AP_BRIDGED,
+ IfaceConcurrencyType::NAN_IFACE, IfaceConcurrencyType::P2P, IfaceConcurrencyType::STA}) {
+ if (req_combo.count(type) == 0) {
+ // Concurrency type not in the req_combo.
+ continue;
+ }
+ size_t num_ifaces_needed = req_combo.at(type);
+ size_t num_ifaces_allowed = expanded_combo.at(type);
+ if (num_ifaces_needed > num_ifaces_allowed) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// This method does the following:
+// a) Enumerate all possible concurrency combos by expanding the current
+// ChipConcurrencyCombination.
+// b) Check if the requested concurrency combo can be added to the current mode.
+// Note: This does not consider concurrency types already active. It only checks if the
+// current mode can support the requested combo.
+bool WifiChip::canCurrentModeSupportConcurrencyCombo(
+ const std::map<IfaceConcurrencyType, size_t>& req_combo) {
+ if (!isValidModeId(current_mode_id_)) {
+ LOG(ERROR) << "Chip not configured in a mode yet";
+ return false;
+ }
+ const auto combinations = getCurrentModeConcurrencyCombinations();
+ for (const auto& combination : combinations) {
+ const auto expanded_combos = expandConcurrencyCombinations(combination);
+ for (const auto& expanded_combo : expanded_combos) {
+ if (canExpandedConcurrencyComboSupportConcurrencyCombo(expanded_combo, req_combo)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// This method does the following:
+// a) Enumerate all possible concurrency combos by expanding the current
+// ChipConcurrencyCombination.
+// b) Check if the requested concurrency type can be added to the current mode.
+bool WifiChip::canCurrentModeSupportConcurrencyType(IfaceConcurrencyType requested_type) {
+ // Check if we can support at least 1 of the requested concurrency type.
+ std::map<IfaceConcurrencyType, size_t> req_iface_combo;
+ req_iface_combo[requested_type] = 1;
+ return canCurrentModeSupportConcurrencyCombo(req_iface_combo);
+}
+
+bool WifiChip::isValidModeId(int32_t mode_id) {
+ for (const auto& mode : modes_) {
+ if (mode.id == mode_id) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool WifiChip::isStaApConcurrencyAllowedInCurrentMode() {
+ // Check if we can support at least 1 STA & 1 AP concurrently.
+ std::map<IfaceConcurrencyType, size_t> req_iface_combo;
+ req_iface_combo[IfaceConcurrencyType::STA] = 1;
+ req_iface_combo[IfaceConcurrencyType::AP] = 1;
+ return canCurrentModeSupportConcurrencyCombo(req_iface_combo);
+}
+
+bool WifiChip::isDualStaConcurrencyAllowedInCurrentMode() {
+ // Check if we can support at least 2 STA concurrently.
+ std::map<IfaceConcurrencyType, size_t> req_iface_combo;
+ req_iface_combo[IfaceConcurrencyType::STA] = 2;
+ return canCurrentModeSupportConcurrencyCombo(req_iface_combo);
+}
+
+std::string WifiChip::getFirstActiveWlanIfaceName() {
+ if (sta_ifaces_.size() > 0) return sta_ifaces_[0]->getName();
+ if (ap_ifaces_.size() > 0) {
+ // If the first active wlan iface is bridged iface.
+ // Return first instance name.
+ for (auto const& it : br_ifaces_ap_instances_) {
+ if (it.first == ap_ifaces_[0]->getName()) {
+ return it.second[0];
+ }
+ }
+ return ap_ifaces_[0]->getName();
+ }
+ // This could happen if the chip call is made before any STA/AP
+ // iface is created. Default to wlan0 for such cases.
+ LOG(WARNING) << "No active wlan interfaces in use! Using default";
+ return getWlanIfaceNameWithType(IfaceType::STA, 0);
+}
+
+// Return the first wlan (wlan0, wlan1 etc.) starting from |start_idx|
+// not already in use.
+// Note: This doesn't check the actual presence of these interfaces.
+std::string WifiChip::allocateApOrStaIfaceName(IfaceType type, uint32_t start_idx) {
+ for (unsigned idx = start_idx; idx < kMaxWlanIfaces; idx++) {
+ const auto ifname = getWlanIfaceNameWithType(type, idx);
+ if (findUsingNameFromBridgedApInstances(ifname)) continue;
+ if (findUsingName(ap_ifaces_, ifname)) continue;
+ if (findUsingName(sta_ifaces_, ifname)) continue;
+ return ifname;
+ }
+ // This should never happen. We screwed up somewhere if it did.
+ CHECK(false) << "All wlan interfaces in use already!";
+ return {};
+}
+
+uint32_t WifiChip::startIdxOfApIface() {
+ if (isDualStaConcurrencyAllowedInCurrentMode()) {
+ // When the HAL support dual STAs, AP should start with idx 2.
+ return 2;
+ } else if (isStaApConcurrencyAllowedInCurrentMode()) {
+ // When the HAL support STA + AP but it doesn't support dual STAs.
+ // AP should start with idx 1.
+ return 1;
+ }
+ // No concurrency support.
+ return 0;
+}
+
+// AP iface names start with idx 1 for modes supporting
+// concurrent STA and not dual AP, else start with idx 0.
+std::string WifiChip::allocateApIfaceName() {
+ // Check if we have a dedicated iface for AP.
+ std::vector<std::string> ifnames = getPredefinedApIfaceNames(true);
+ for (auto const& ifname : ifnames) {
+ if (findUsingName(ap_ifaces_, ifname)) continue;
+ return ifname;
+ }
+ return allocateApOrStaIfaceName(IfaceType::AP, startIdxOfApIface());
+}
+
+std::vector<std::string> WifiChip::allocateBridgedApInstanceNames() {
+ // Check if we have a dedicated iface for AP.
+ std::vector<std::string> instances = getPredefinedApIfaceNames(true);
+ if (instances.size() == 2) {
+ return instances;
+ } else {
+ int num_ifaces_need_to_allocate = 2 - instances.size();
+ for (int i = 0; i < num_ifaces_need_to_allocate; i++) {
+ std::string instance_name =
+ allocateApOrStaIfaceName(IfaceType::AP, startIdxOfApIface() + i);
+ if (!instance_name.empty()) {
+ instances.push_back(instance_name);
+ }
+ }
+ }
+ return instances;
+}
+
+// STA iface names start with idx 0.
+// Primary STA iface will always be 0.
+std::string WifiChip::allocateStaIfaceName() {
+ return allocateApOrStaIfaceName(IfaceType::STA, 0);
+}
+
+bool WifiChip::writeRingbufferFilesInternal() {
+ if (!removeOldFilesInternal()) {
+ LOG(ERROR) << "Error occurred while deleting old tombstone files";
+ return false;
+ }
+ // write ringbuffers to file
+ {
+ std::unique_lock<std::mutex> lk(lock_t);
+ for (auto& item : ringbuffer_map_) {
+ Ringbuffer& cur_buffer = item.second;
+ if (cur_buffer.getData().empty()) {
+ continue;
+ }
+ const std::string file_path_raw = kTombstoneFolderPath + item.first + "XXXXXXXXXX";
+ const int dump_fd = mkstemp(makeCharVec(file_path_raw).data());
+ if (dump_fd == -1) {
+ PLOG(ERROR) << "create file failed";
+ return false;
+ }
+ unique_fd file_auto_closer(dump_fd);
+ for (const auto& cur_block : cur_buffer.getData()) {
+ if (cur_block.size() <= 0 || cur_block.size() > kMaxBufferSizeBytes) {
+ PLOG(ERROR) << "Ring buffer: " << item.first
+ << " is corrupted. Invalid block size: " << cur_block.size();
+ break;
+ }
+ if (write(dump_fd, cur_block.data(), sizeof(cur_block[0]) * cur_block.size()) ==
+ -1) {
+ PLOG(ERROR) << "Error writing to file";
+ }
+ }
+ cur_buffer.clear();
+ }
+ // unique_lock unlocked here
+ }
+ return true;
+}
+
+std::string WifiChip::getWlanIfaceNameWithType(IfaceType type, unsigned idx) {
+ std::string ifname;
+
+ // let the legacy hal override the interface name
+ legacy_hal::wifi_error err = legacy_hal_.lock()->getSupportedIfaceName((uint32_t)type, ifname);
+ if (err == legacy_hal::WIFI_SUCCESS) return ifname;
+
+ return getWlanIfaceName(idx);
+}
+
+void WifiChip::invalidateAndClearBridgedApAll() {
+ for (auto const& it : br_ifaces_ap_instances_) {
+ for (auto const& iface : it.second) {
+ iface_util_->removeIfaceFromBridge(it.first, iface);
+ legacy_hal_.lock()->deleteVirtualInterface(iface);
+ }
+ iface_util_->deleteBridge(it.first);
+ }
+ br_ifaces_ap_instances_.clear();
+}
+
+void WifiChip::invalidateAndClearBridgedAp(const std::string& br_name) {
+ if (br_name.empty()) return;
+ // delete managed interfaces
+ for (auto const& it : br_ifaces_ap_instances_) {
+ if (it.first == br_name) {
+ for (auto const& iface : it.second) {
+ iface_util_->removeIfaceFromBridge(br_name, iface);
+ legacy_hal_.lock()->deleteVirtualInterface(iface);
+ }
+ iface_util_->deleteBridge(br_name);
+ br_ifaces_ap_instances_.erase(br_name);
+ break;
+ }
+ }
+ return;
+}
+
+bool WifiChip::findUsingNameFromBridgedApInstances(const std::string& name) {
+ for (auto const& it : br_ifaces_ap_instances_) {
+ if (it.first == name) {
+ return true;
+ }
+ for (auto const& iface : it.second) {
+ if (iface == name) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi_chip.h b/wifi/aidl/default/wifi_chip.h
new file mode 100644
index 0000000..59eaa4a
--- /dev/null
+++ b/wifi/aidl/default/wifi_chip.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_CHIP_H_
+#define WIFI_CHIP_H_
+
+#include <aidl/android/hardware/wifi/BnWifiChip.h>
+#include <aidl/android/hardware/wifi/IWifiRttController.h>
+#include <android-base/macros.h>
+
+#include <list>
+#include <map>
+#include <mutex>
+
+#include "aidl_callback_util.h"
+#include "ringbuffer.h"
+#include "wifi_ap_iface.h"
+#include "wifi_feature_flags.h"
+#include "wifi_legacy_hal.h"
+#include "wifi_mode_controller.h"
+#include "wifi_nan_iface.h"
+#include "wifi_p2p_iface.h"
+#include "wifi_rtt_controller.h"
+#include "wifi_sta_iface.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+/**
+ * AIDL interface object used to control a Wifi HAL chip instance.
+ * Since there is only a single chip instance used today, there is no
+ * identifying handle information stored here.
+ */
+class WifiChip : public BnWifiChip {
+ public:
+ WifiChip(int32_t chip_id, bool is_primary,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<mode_controller::WifiModeController> mode_controller,
+ const std::shared_ptr<iface_util::WifiIfaceUtil> iface_util,
+ const std::weak_ptr<feature_flags::WifiFeatureFlags> feature_flags,
+ const std::function<void(const std::string&)>& subsystemCallbackHandler);
+
+ // Factory method - use instead of default constructor.
+ static std::shared_ptr<WifiChip> create(
+ int32_t chip_id, bool is_primary,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<mode_controller::WifiModeController> mode_controller,
+ const std::shared_ptr<iface_util::WifiIfaceUtil> iface_util,
+ const std::weak_ptr<feature_flags::WifiFeatureFlags> feature_flags,
+ const std::function<void(const std::string&)>& subsystemCallbackHandler);
+
+ // AIDL does not provide a built-in mechanism to let the server invalidate
+ // an AIDL interface object after creation. If any client process holds onto
+ // a reference to the object in their context, any method calls on that
+ // reference will continue to be directed to the server.
+ //
+ // However Wifi HAL needs to control the lifetime of these objects. So, add
+ // a public |invalidate| method to |WifiChip| and its child objects. This
+ // will be used to mark an object invalid when either:
+ // a) Wifi HAL is stopped, or
+ // b) Wifi Chip is reconfigured.
+ //
+ // All AIDL method implementations should check if the object is still
+ // marked valid before processing them.
+ void invalidate();
+ bool isValid();
+ std::set<std::shared_ptr<IWifiChipEventCallback>> getEventCallbacks();
+
+ // AIDL methods exposed.
+ ndk::ScopedAStatus getId(int32_t* _aidl_return) override;
+ ndk::ScopedAStatus registerEventCallback(
+ const std::shared_ptr<IWifiChipEventCallback>& in_callback) override;
+ ndk::ScopedAStatus getCapabilities(IWifiChip::ChipCapabilityMask* _aidl_return) override;
+ ndk::ScopedAStatus getAvailableModes(std::vector<IWifiChip::ChipMode>* _aidl_return) override;
+ ndk::ScopedAStatus configureChip(int32_t in_modeId) override;
+ ndk::ScopedAStatus getMode(int32_t* _aidl_return) override;
+ ndk::ScopedAStatus requestChipDebugInfo(IWifiChip::ChipDebugInfo* _aidl_return) override;
+ ndk::ScopedAStatus requestDriverDebugDump(std::vector<uint8_t>* _aidl_return) override;
+ ndk::ScopedAStatus requestFirmwareDebugDump(std::vector<uint8_t>* _aidl_return) override;
+ ndk::ScopedAStatus createApIface(std::shared_ptr<IWifiApIface>* _aidl_return) override;
+ ndk::ScopedAStatus createBridgedApIface(std::shared_ptr<IWifiApIface>* _aidl_return) override;
+ ndk::ScopedAStatus getApIfaceNames(std::vector<std::string>* _aidl_return) override;
+ ndk::ScopedAStatus getApIface(const std::string& in_ifname,
+ std::shared_ptr<IWifiApIface>* _aidl_return) override;
+ ndk::ScopedAStatus removeApIface(const std::string& in_ifname) override;
+ ndk::ScopedAStatus removeIfaceInstanceFromBridgedApIface(
+ const std::string& in_brIfaceName, const std::string& in_ifaceInstanceName) override;
+ ndk::ScopedAStatus createNanIface(std::shared_ptr<IWifiNanIface>* _aidl_return) override;
+ ndk::ScopedAStatus getNanIfaceNames(std::vector<std::string>* _aidl_return) override;
+ ndk::ScopedAStatus getNanIface(const std::string& in_ifname,
+ std::shared_ptr<IWifiNanIface>* _aidl_return) override;
+ ndk::ScopedAStatus removeNanIface(const std::string& in_ifname) override;
+ ndk::ScopedAStatus createP2pIface(std::shared_ptr<IWifiP2pIface>* _aidl_return) override;
+ ndk::ScopedAStatus getP2pIfaceNames(std::vector<std::string>* _aidl_return) override;
+ ndk::ScopedAStatus getP2pIface(const std::string& in_ifname,
+ std::shared_ptr<IWifiP2pIface>* _aidl_return) override;
+ ndk::ScopedAStatus removeP2pIface(const std::string& in_ifname) override;
+ ndk::ScopedAStatus createStaIface(std::shared_ptr<IWifiStaIface>* _aidl_return) override;
+ ndk::ScopedAStatus getStaIfaceNames(std::vector<std::string>* _aidl_return) override;
+ ndk::ScopedAStatus getStaIface(const std::string& in_ifname,
+ std::shared_ptr<IWifiStaIface>* _aidl_return) override;
+ ndk::ScopedAStatus removeStaIface(const std::string& in_ifname) override;
+ ndk::ScopedAStatus createRttController(
+ const std::shared_ptr<IWifiStaIface>& in_boundIface,
+ std::shared_ptr<IWifiRttController>* _aidl_return) override;
+ ndk::ScopedAStatus getDebugRingBuffersStatus(
+ std::vector<WifiDebugRingBufferStatus>* _aidl_return) override;
+ ndk::ScopedAStatus startLoggingToDebugRingBuffer(
+ const std::string& in_ringName, WifiDebugRingBufferVerboseLevel in_verboseLevel,
+ int32_t in_maxIntervalInSec, int32_t in_minDataSizeInBytes) override;
+ ndk::ScopedAStatus forceDumpToDebugRingBuffer(const std::string& in_ringName) override;
+ ndk::ScopedAStatus flushRingBufferToFile() override;
+ ndk::ScopedAStatus stopLoggingToDebugRingBuffer() override;
+ ndk::ScopedAStatus getDebugHostWakeReasonStats(
+ WifiDebugHostWakeReasonStats* _aidl_return) override;
+ ndk::ScopedAStatus enableDebugErrorAlerts(bool in_enable) override;
+ ndk::ScopedAStatus selectTxPowerScenario(IWifiChip::TxPowerScenario in_scenario) override;
+ ndk::ScopedAStatus resetTxPowerScenario() override;
+ ndk::ScopedAStatus setLatencyMode(IWifiChip::LatencyMode in_mode) override;
+ ndk::ScopedAStatus setMultiStaPrimaryConnection(const std::string& in_ifName) override;
+ ndk::ScopedAStatus setMultiStaUseCase(IWifiChip::MultiStaUseCase in_useCase) override;
+ ndk::ScopedAStatus setCoexUnsafeChannels(
+ const std::vector<IWifiChip::CoexUnsafeChannel>& in_unsafeChannels,
+ CoexRestriction in_restrictions) override;
+ ndk::ScopedAStatus setCountryCode(const std::array<uint8_t, 2>& in_code) override;
+ ndk::ScopedAStatus getUsableChannels(WifiBand in_band, WifiIfaceMode in_ifaceModeMask,
+ UsableChannelFilter in_filterMask,
+ std::vector<WifiUsableChannel>* _aidl_return) override;
+ ndk::ScopedAStatus triggerSubsystemRestart() override;
+ ndk::ScopedAStatus getSupportedRadioCombinationsMatrix(
+ WifiRadioCombinationMatrix* _aidl_return) override;
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+
+ private:
+ void invalidateAndRemoveAllIfaces();
+ // When a STA iface is removed any dependent NAN-ifaces/RTT-controllers are
+ // invalidated & removed.
+ void invalidateAndRemoveDependencies(const std::string& removed_iface_name);
+
+ // Corresponding worker functions for the AIDL methods.
+ std::pair<int32_t, ndk::ScopedAStatus> getIdInternal();
+ ndk::ScopedAStatus registerEventCallbackInternal(
+ const std::shared_ptr<IWifiChipEventCallback>& event_callback);
+ std::pair<IWifiChip::ChipCapabilityMask, ndk::ScopedAStatus> getCapabilitiesInternal();
+ std::pair<std::vector<IWifiChip::ChipMode>, ndk::ScopedAStatus> getAvailableModesInternal();
+ ndk::ScopedAStatus configureChipInternal(std::unique_lock<std::recursive_mutex>* lock,
+ int32_t mode_id);
+ std::pair<int32_t, ndk::ScopedAStatus> getModeInternal();
+ std::pair<IWifiChip::ChipDebugInfo, ndk::ScopedAStatus> requestChipDebugInfoInternal();
+ std::pair<std::vector<uint8_t>, ndk::ScopedAStatus> requestDriverDebugDumpInternal();
+ std::pair<std::vector<uint8_t>, ndk::ScopedAStatus> requestFirmwareDebugDumpInternal();
+ std::shared_ptr<WifiApIface> newWifiApIface(std::string& ifname);
+ ndk::ScopedAStatus createVirtualApInterface(const std::string& apVirtIf);
+ std::pair<std::shared_ptr<IWifiApIface>, ndk::ScopedAStatus> createApIfaceInternal();
+ std::pair<std::shared_ptr<IWifiApIface>, ndk::ScopedAStatus> createBridgedApIfaceInternal();
+ std::pair<std::vector<std::string>, ndk::ScopedAStatus> getApIfaceNamesInternal();
+ std::pair<std::shared_ptr<IWifiApIface>, ndk::ScopedAStatus> getApIfaceInternal(
+ const std::string& ifname);
+ ndk::ScopedAStatus removeApIfaceInternal(const std::string& ifname);
+ ndk::ScopedAStatus removeIfaceInstanceFromBridgedApIfaceInternal(
+ const std::string& brIfaceName, const std::string& ifInstanceName);
+ std::pair<std::shared_ptr<IWifiNanIface>, ndk::ScopedAStatus> createNanIfaceInternal();
+ std::pair<std::vector<std::string>, ndk::ScopedAStatus> getNanIfaceNamesInternal();
+ std::pair<std::shared_ptr<IWifiNanIface>, ndk::ScopedAStatus> getNanIfaceInternal(
+ const std::string& ifname);
+ ndk::ScopedAStatus removeNanIfaceInternal(const std::string& ifname);
+ std::pair<std::shared_ptr<IWifiP2pIface>, ndk::ScopedAStatus> createP2pIfaceInternal();
+ std::pair<std::vector<std::string>, ndk::ScopedAStatus> getP2pIfaceNamesInternal();
+ std::pair<std::shared_ptr<IWifiP2pIface>, ndk::ScopedAStatus> getP2pIfaceInternal(
+ const std::string& ifname);
+ ndk::ScopedAStatus removeP2pIfaceInternal(const std::string& ifname);
+ std::pair<std::shared_ptr<IWifiStaIface>, ndk::ScopedAStatus> createStaIfaceInternal();
+ std::pair<std::vector<std::string>, ndk::ScopedAStatus> getStaIfaceNamesInternal();
+ std::pair<std::shared_ptr<IWifiStaIface>, ndk::ScopedAStatus> getStaIfaceInternal(
+ const std::string& ifname);
+ ndk::ScopedAStatus removeStaIfaceInternal(const std::string& ifname);
+ std::pair<std::shared_ptr<IWifiRttController>, ndk::ScopedAStatus> createRttControllerInternal(
+ const std::shared_ptr<IWifiStaIface>& bound_iface);
+ std::pair<std::vector<WifiDebugRingBufferStatus>, ndk::ScopedAStatus>
+ getDebugRingBuffersStatusInternal();
+ ndk::ScopedAStatus startLoggingToDebugRingBufferInternal(
+ const std::string& ring_name, WifiDebugRingBufferVerboseLevel verbose_level,
+ uint32_t max_interval_in_sec, uint32_t min_data_size_in_bytes);
+ ndk::ScopedAStatus forceDumpToDebugRingBufferInternal(const std::string& ring_name);
+ ndk::ScopedAStatus flushRingBufferToFileInternal();
+ ndk::ScopedAStatus stopLoggingToDebugRingBufferInternal();
+ std::pair<WifiDebugHostWakeReasonStats, ndk::ScopedAStatus>
+ getDebugHostWakeReasonStatsInternal();
+ ndk::ScopedAStatus enableDebugErrorAlertsInternal(bool enable);
+ ndk::ScopedAStatus selectTxPowerScenarioInternal(IWifiChip::TxPowerScenario scenario);
+ ndk::ScopedAStatus resetTxPowerScenarioInternal();
+ ndk::ScopedAStatus setLatencyModeInternal(IWifiChip::LatencyMode mode);
+ ndk::ScopedAStatus setMultiStaPrimaryConnectionInternal(const std::string& ifname);
+ ndk::ScopedAStatus setMultiStaUseCaseInternal(IWifiChip::MultiStaUseCase use_case);
+ ndk::ScopedAStatus setCoexUnsafeChannelsInternal(
+ std::vector<IWifiChip::CoexUnsafeChannel> unsafe_channels,
+ CoexRestriction restrictions);
+ ndk::ScopedAStatus setCountryCodeInternal(const std::array<uint8_t, 2>& in_code);
+ std::pair<std::vector<WifiUsableChannel>, ndk::ScopedAStatus> getUsableChannelsInternal(
+ WifiBand band, WifiIfaceMode ifaceModeMask, UsableChannelFilter filterMask);
+ ndk::ScopedAStatus handleChipConfiguration(std::unique_lock<std::recursive_mutex>* lock,
+ int32_t mode_id);
+ ndk::ScopedAStatus registerDebugRingBufferCallback();
+ ndk::ScopedAStatus registerRadioModeChangeCallback();
+
+ std::vector<ChipConcurrencyCombination> getCurrentModeConcurrencyCombinations();
+ std::map<IfaceConcurrencyType, size_t> getCurrentConcurrencyCombination();
+ std::vector<std::map<IfaceConcurrencyType, size_t>> expandConcurrencyCombinations(
+ const ChipConcurrencyCombination& combination);
+ bool canExpandedConcurrencyComboSupportConcurrencyTypeWithCurrentTypes(
+ const std::map<IfaceConcurrencyType, size_t>& expanded_combo,
+ IfaceConcurrencyType requested_type);
+ bool canCurrentModeSupportConcurrencyTypeWithCurrentTypes(IfaceConcurrencyType requested_type);
+ bool canExpandedConcurrencyComboSupportConcurrencyCombo(
+ const std::map<IfaceConcurrencyType, size_t>& expanded_combo,
+ const std::map<IfaceConcurrencyType, size_t>& req_combo);
+ bool canCurrentModeSupportConcurrencyCombo(
+ const std::map<IfaceConcurrencyType, size_t>& req_combo);
+ bool canCurrentModeSupportConcurrencyType(IfaceConcurrencyType requested_type);
+
+ bool isValidModeId(int32_t mode_id);
+ bool isStaApConcurrencyAllowedInCurrentMode();
+ bool isDualStaConcurrencyAllowedInCurrentMode();
+ uint32_t startIdxOfApIface();
+ std::string getFirstActiveWlanIfaceName();
+ std::string allocateApOrStaIfaceName(IfaceType type, uint32_t start_idx);
+ std::string allocateApIfaceName();
+ std::vector<std::string> allocateBridgedApInstanceNames();
+ std::string allocateStaIfaceName();
+ bool writeRingbufferFilesInternal();
+ std::string getWlanIfaceNameWithType(IfaceType type, unsigned idx);
+ void invalidateAndClearBridgedApAll();
+ void invalidateAndClearBridgedAp(const std::string& br_name);
+ bool findUsingNameFromBridgedApInstances(const std::string& name);
+ ndk::ScopedAStatus triggerSubsystemRestartInternal();
+ std::pair<WifiRadioCombinationMatrix, ndk::ScopedAStatus>
+ getSupportedRadioCombinationsMatrixInternal();
+ void setWeakPtr(std::weak_ptr<WifiChip> ptr);
+
+ int32_t chip_id_;
+ std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
+ std::weak_ptr<mode_controller::WifiModeController> mode_controller_;
+ std::shared_ptr<iface_util::WifiIfaceUtil> iface_util_;
+ std::vector<std::shared_ptr<WifiApIface>> ap_ifaces_;
+ std::vector<std::shared_ptr<WifiNanIface>> nan_ifaces_;
+ std::vector<std::shared_ptr<WifiP2pIface>> p2p_ifaces_;
+ std::vector<std::shared_ptr<WifiStaIface>> sta_ifaces_;
+ std::vector<std::shared_ptr<WifiRttController>> rtt_controllers_;
+ std::map<std::string, Ringbuffer> ringbuffer_map_;
+ bool is_valid_;
+ // Members pertaining to chip configuration.
+ int32_t current_mode_id_;
+ std::mutex lock_t;
+ std::vector<IWifiChip::ChipMode> modes_;
+ // The legacy ring buffer callback API has only a global callback
+ // registration mechanism. Use this to check if we have already
+ // registered a callback.
+ bool debug_ring_buffer_cb_registered_;
+ aidl_callback_util::AidlCallbackHandler<IWifiChipEventCallback> event_cb_handler_;
+ std::weak_ptr<WifiChip> weak_ptr_this_;
+
+ const std::function<void(const std::string&)> subsystemCallbackHandler_;
+ std::map<std::string, std::vector<std::string>> br_ifaces_ap_instances_;
+ DISALLOW_COPY_AND_ASSIGN(WifiChip);
+};
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_CHIP_H_
diff --git a/wifi/aidl/default/wifi_feature_flags.cpp b/wifi/aidl/default/wifi_feature_flags.cpp
new file mode 100644
index 0000000..3c9f042
--- /dev/null
+++ b/wifi/aidl/default/wifi_feature_flags.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+
+#include "wifi_feature_flags.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace feature_flags {
+
+/* The chip may either have a single mode supporting any number of combinations,
+ * or a fixed dual-mode (so it involves firmware loading to switch between
+ * modes) setting. If there is a need to support more modes, it needs to be
+ * implemented manually in WiFi HAL (see changeFirmwareMode in
+ * WifiChip::handleChipConfiguration).
+ *
+ * Supported combinations are defined in device's makefile, for example:
+ * WIFI_HAL_INTERFACE_COMBINATIONS := {{{STA, AP}, 1}, {{P2P, NAN}, 1}},
+ * WIFI_HAL_INTERFACE_COMBINATIONS += {{{STA}, 1}, {{AP}, 2}}
+ * What this means:
+ * Interface concurrency combination 1: 1 STA or AP and 1 P2P or NAN concurrent iface
+ * operations.
+ * Interface concurrency combination 2: 1 STA and 2 AP concurrent iface operations.
+ *
+ * For backward compatibility, the following makefile flags can be used to
+ * generate combinations list:
+ * - WIFI_HIDL_FEATURE_DUAL_INTERFACE
+ * - WIFI_HIDL_FEATURE_DISABLE_AP
+ * - WIFI_HIDL_FEATURE_AWARE
+ * However, they are ignored if WIFI_HAL_INTERFACE_COMBINATIONS was provided.
+ * With WIFI_HIDL_FEATURE_DUAL_INTERFACE flag set, there is a single mode with
+ * two concurrency combinations:
+ * Interface Concurrency Combination 1: Will support 1 STA and 1 P2P or NAN (optional)
+ * concurrent iface operations.
+ * Interface Concurrency Combination 2: Will support 1 STA and 1 AP concurrent
+ * iface operations.
+ *
+ * The only dual-mode configuration supported is for alternating STA and AP
+ * mode, that may involve firmware reloading. In such case, there are 2 separate
+ * modes of operation with 1 concurrency combination each:
+ * Mode 1 (STA mode): Will support 1 STA and 1 P2P or NAN (optional)
+ * concurrent iface operations.
+ * Mode 2 (AP mode): Will support 1 AP iface operation.
+ *
+ * If Aware is enabled, the concurrency combination will be modified to support either
+ * P2P or NAN in place of just P2P.
+ */
+// clang-format off
+#ifdef WIFI_HAL_INTERFACE_COMBINATIONS
+constexpr int kMainModeId = chip_mode_ids::kV3;
+#elif defined(WIFI_HIDL_FEATURE_DUAL_INTERFACE)
+// former V2 (fixed dual interface) setup expressed as V3
+constexpr int kMainModeId = chip_mode_ids::kV3;
+# ifdef WIFI_HIDL_FEATURE_DISABLE_AP
+# ifdef WIFI_HIDL_FEATURE_AWARE
+// 1 STA + 1 of (P2P or NAN)
+# define WIFI_HAL_INTERFACE_COMBINATIONS {{{STA}, 1}, {{P2P, NAN}, 1}}
+# else
+// 1 STA + 1 P2P
+# define WIFI_HAL_INTERFACE_COMBINATIONS {{{STA}, 1}, {{P2P}, 1}}
+# endif
+# else
+# ifdef WIFI_HIDL_FEATURE_AWARE
+// (1 STA + 1 AP) or (1 STA + 1 of (P2P or NAN))
+# define WIFI_HAL_INTERFACE_COMBINATIONS {{{STA}, 1}, {{AP}, 1}},\
+ {{{STA}, 1}, {{P2P, NAN}, 1}}
+# else
+// (1 STA + 1 AP) or (1 STA + 1 P2P)
+# define WIFI_HAL_INTERFACE_COMBINATIONS {{{STA}, 1}, {{AP}, 1}},\
+ {{{STA}, 1}, {{P2P}, 1}}
+# endif
+# endif
+#else
+// V1 (fixed single interface, dual-mode chip)
+constexpr int kMainModeId = chip_mode_ids::kV1Sta;
+# ifdef WIFI_HIDL_FEATURE_AWARE
+// 1 STA + 1 of (P2P or NAN)
+# define WIFI_HAL_INTERFACE_COMBINATIONS {{{STA}, 1}, {{P2P, NAN}, 1}}
+# else
+// 1 STA + 1 P2P
+# define WIFI_HAL_INTERFACE_COMBINATIONS {{{STA}, 1}, {{P2P}, 1}}
+# endif
+
+# ifndef WIFI_HIDL_FEATURE_DISABLE_AP
+# define WIFI_HAL_INTERFACE_COMBINATIONS_AP {{{AP}, 1}}
+# endif
+#endif
+// clang-format on
+
+// Convert from the legacy format (used by the WIFI_HAL_INTERFACE_COMBINATIONS
+// config variable) to a list of ChipConcurrencyCombination objects.
+std::vector<IWifiChip::ChipConcurrencyCombination> legacyToChipConcurrencyComboList(
+ std::vector<std::vector<IWifiChip::ChipConcurrencyCombinationLimit>> legacyLimits) {
+ std::vector<IWifiChip::ChipConcurrencyCombination> combos;
+ for (auto& legacyLimit : legacyLimits) {
+ IWifiChip::ChipConcurrencyCombination combo = {legacyLimit};
+ combos.push_back(combo);
+ }
+ return combos;
+}
+
+#define STA IfaceConcurrencyType::STA
+#define AP IfaceConcurrencyType::AP
+#define AP_BRIDGED IfaceConcurrencyType::AP_BRIDGED
+#define P2P IfaceConcurrencyType::P2P
+#define NAN IfaceConcurrencyType::NAN_IFACE
+static const std::vector<IWifiChip::ChipMode> kChipModesPrimary{
+ {kMainModeId, legacyToChipConcurrencyComboList({WIFI_HAL_INTERFACE_COMBINATIONS})},
+#ifdef WIFI_HAL_INTERFACE_COMBINATIONS_AP
+ {chip_mode_ids::kV1Ap,
+ legacyToChipConcurrencyComboList({WIFI_HAL_INTERFACE_COMBINATIONS_AP})},
+#endif
+};
+
+static const std::vector<IWifiChip::ChipMode> kChipModesSecondary{
+#ifdef WIFI_HAL_INTERFACE_COMBINATIONS_SECONDARY_CHIP
+ {chip_mode_ids::kV3,
+ legacyToChipConcurrencyComboList({WIFI_HAL_INTERFACE_COMBINATIONS_SECONDARY_CHIP})},
+#endif
+};
+
+constexpr char kDebugPresetInterfaceCombinationIdxProperty[] =
+ "persist.vendor.debug.wifi.hal.preset_interface_combination_idx";
+// List of pre-defined concurrency combinations that can be enabled at runtime via
+// setting the property: "kDebugPresetInterfaceCombinationIdxProperty" to the
+// corresponding index value.
+static const std::vector<std::pair<std::string, std::vector<IWifiChip::ChipMode>>> kDebugChipModes{
+ // Legacy combination - No STA/AP concurrencies.
+ // 0 - (1 AP) or (1 STA + 1 of (P2P or NAN))
+ {"No STA/AP Concurrency",
+ {{kMainModeId,
+ legacyToChipConcurrencyComboList({{{{AP}, 1}}, {{{STA}, 1}, {{P2P, NAN}, 1}}})}}},
+
+ // STA + AP concurrency
+ // 1 - (1 STA + 1 AP) or (1 STA + 1 of (P2P or NAN))
+ {"STA + AP Concurrency",
+ {{kMainModeId, legacyToChipConcurrencyComboList(
+ {{{{STA}, 1}, {{AP}, 1}}, {{{STA}, 1}, {{P2P, NAN}, 1}}})}}},
+
+ // STA + STA concurrency
+ // 2 - (1 STA + 1 AP) or (2 STA + 1 of (P2P or NAN))
+ {"Dual STA Concurrency",
+ {{kMainModeId, legacyToChipConcurrencyComboList(
+ {{{{STA}, 1}, {{AP}, 1}}, {{{STA}, 2}, {{P2P, NAN}, 1}}})}}},
+
+ // AP + AP + STA concurrency
+ // 3 - (1 STA + 2 AP) or (1 STA + 1 of (P2P or NAN))
+ {"Dual AP Concurrency",
+ {{kMainModeId, legacyToChipConcurrencyComboList(
+ {{{{STA}, 1}, {{AP}, 2}}, {{{STA}, 1}, {{P2P, NAN}, 1}}})}}},
+
+ // STA + STA concurrency and AP + AP + STA concurrency
+ // 4 - (1 STA + 2 AP) or (2 STA + 1 of (P2P or NAN))
+ {"Dual STA & Dual AP Concurrency",
+ {{kMainModeId, legacyToChipConcurrencyComboList(
+ {{{{STA}, 1}, {{AP}, 2}}, {{{STA}, 2}, {{P2P, NAN}, 1}}})}}},
+
+ // STA + STA concurrency
+ // 5 - (1 STA + 1 AP (bridged or single) | P2P | NAN), or (2 STA))
+ {"Dual STA or STA plus single other interface",
+ {{kMainModeId, legacyToChipConcurrencyComboList(
+ {{{{STA}, 1}, {{P2P, NAN, AP, AP_BRIDGED}, 1}}, {{{STA}, 2}}})}}}};
+
+#undef STA
+#undef AP
+#undef AP_BRIDGED
+#undef P2P
+#undef NAN
+
+#ifdef WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION
+#pragma message \
+ "WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION is deprecated; override " \
+ "'config_wifi_ap_randomization_supported' in " \
+ "frameworks/base/core/res/res/values/config.xml in the device overlay " \
+ "instead"
+#endif // WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION
+
+WifiFeatureFlags::WifiFeatureFlags() {}
+
+std::vector<IWifiChip::ChipMode> WifiFeatureFlags::getChipModesForPrimary() {
+ std::array<char, PROPERTY_VALUE_MAX> buffer;
+ auto res = property_get(kDebugPresetInterfaceCombinationIdxProperty, buffer.data(), nullptr);
+ // Debug property not set, use the device preset concurrency combination.
+ if (res <= 0) return kChipModesPrimary;
+
+ // Debug property set, use one of the debug preset concurrency combination.
+ unsigned long idx = std::stoul(buffer.data());
+ if (idx >= kDebugChipModes.size()) {
+ LOG(ERROR) << "Invalid index set in property: "
+ << kDebugPresetInterfaceCombinationIdxProperty;
+ return kChipModesPrimary;
+ }
+ std::string name;
+ std::vector<IWifiChip::ChipMode> chip_modes;
+ std::tie(name, chip_modes) = kDebugChipModes[idx];
+ LOG(INFO) << "Using debug chip mode: <" << name
+ << "> set via property: " << kDebugPresetInterfaceCombinationIdxProperty;
+ return chip_modes;
+}
+
+std::vector<IWifiChip::ChipMode> WifiFeatureFlags::getChipModes(bool is_primary) {
+ return (is_primary) ? getChipModesForPrimary() : kChipModesSecondary;
+}
+
+} // namespace feature_flags
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi_feature_flags.h b/wifi/aidl/default/wifi_feature_flags.h
new file mode 100644
index 0000000..af1b6a6
--- /dev/null
+++ b/wifi/aidl/default/wifi_feature_flags.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_FEATURE_FLAGS_H_
+#define WIFI_FEATURE_FLAGS_H_
+
+#include <aidl/android/hardware/wifi/IWifiChip.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace feature_flags {
+
+namespace chip_mode_ids {
+// These mode ID's should be unique (even across combo versions). Refer to
+// handleChipConfiguration() for its usage.
+constexpr uint32_t kInvalid = UINT32_MAX;
+// Mode ID's for V1
+constexpr uint32_t kV1Sta = 0;
+constexpr uint32_t kV1Ap = 1;
+// Mode ID for V3
+constexpr uint32_t kV3 = 3;
+} // namespace chip_mode_ids
+
+class WifiFeatureFlags {
+ public:
+ WifiFeatureFlags();
+ virtual ~WifiFeatureFlags() = default;
+
+ virtual std::vector<IWifiChip::ChipMode> getChipModes(bool is_primary);
+
+ private:
+ std::vector<IWifiChip::ChipMode> getChipModesForPrimary();
+};
+
+} // namespace feature_flags
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_FEATURE_FLAGS_H_
diff --git a/wifi/aidl/default/wifi_iface_util.cpp b/wifi/aidl/default/wifi_iface_util.cpp
new file mode 100644
index 0000000..f788217
--- /dev/null
+++ b/wifi/aidl/default/wifi_iface_util.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <net/if.h>
+#include <private/android_filesystem_config.h>
+
+#include <cstddef>
+#include <iostream>
+#include <limits>
+#include <random>
+
+#include "wifi_iface_util.h"
+
+namespace {
+// Constants to set the local bit & clear the multicast bit.
+constexpr uint8_t kMacAddressMulticastMask = 0x01;
+constexpr uint8_t kMacAddressLocallyAssignedMask = 0x02;
+
+} // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace iface_util {
+
+WifiIfaceUtil::WifiIfaceUtil(const std::weak_ptr<::android::wifi_system::InterfaceTool> iface_tool,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal)
+ : iface_tool_(iface_tool),
+ legacy_hal_(legacy_hal),
+ random_mac_address_(nullptr),
+ event_handlers_map_() {}
+
+std::array<uint8_t, 6> WifiIfaceUtil::getFactoryMacAddress(const std::string& iface_name) {
+ return iface_tool_.lock()->GetFactoryMacAddress(iface_name.c_str());
+}
+
+bool WifiIfaceUtil::setMacAddress(const std::string& iface_name,
+ const std::array<uint8_t, 6>& mac) {
+#ifndef WIFI_AVOID_IFACE_RESET_MAC_CHANGE
+ legacy_hal::wifi_error legacy_status;
+ uint64_t legacy_feature_set;
+ std::tie(legacy_status, legacy_feature_set) =
+ legacy_hal_.lock()->getSupportedFeatureSet(iface_name);
+
+ if (!(legacy_feature_set & WIFI_FEATURE_DYNAMIC_SET_MAC) &&
+ !iface_tool_.lock()->SetUpState(iface_name.c_str(), false)) {
+ LOG(ERROR) << "SetUpState(false) failed.";
+ return false;
+ }
+#endif
+ bool success = iface_tool_.lock()->SetMacAddress(iface_name.c_str(), mac);
+#ifndef WIFI_AVOID_IFACE_RESET_MAC_CHANGE
+ if (!(legacy_feature_set & WIFI_FEATURE_DYNAMIC_SET_MAC) &&
+ !iface_tool_.lock()->SetUpState(iface_name.c_str(), true)) {
+ LOG(ERROR) << "SetUpState(true) failed. Wait for driver ready.";
+ // Wait for driver ready and try to set iface UP again
+ if (legacy_hal_.lock()->waitForDriverReady() != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "SetUpState(true) wait for driver ready failed.";
+ return false;
+ }
+ if (!iface_tool_.lock()->SetUpState(iface_name.c_str(), true)) {
+ LOG(ERROR) << "SetUpState(true) failed after retry.";
+ return false;
+ }
+ }
+#endif
+ IfaceEventHandlers event_handlers = {};
+ const auto it = event_handlers_map_.find(iface_name);
+ if (it != event_handlers_map_.end()) {
+ event_handlers = it->second;
+ }
+ if (event_handlers.on_state_toggle_off_on != nullptr) {
+ event_handlers.on_state_toggle_off_on(iface_name);
+ }
+ if (!success) {
+ LOG(ERROR) << "SetMacAddress failed on " << iface_name;
+ } else {
+ LOG(DEBUG) << "SetMacAddress succeeded on " << iface_name;
+ }
+ return success;
+}
+
+std::array<uint8_t, 6> WifiIfaceUtil::getOrCreateRandomMacAddress() {
+ if (random_mac_address_) {
+ return *random_mac_address_.get();
+ }
+ random_mac_address_ = std::make_unique<std::array<uint8_t, 6>>(createRandomMacAddress());
+ return *random_mac_address_.get();
+}
+
+void WifiIfaceUtil::registerIfaceEventHandlers(const std::string& iface_name,
+ IfaceEventHandlers handlers) {
+ event_handlers_map_[iface_name] = handlers;
+}
+
+void WifiIfaceUtil::unregisterIfaceEventHandlers(const std::string& iface_name) {
+ event_handlers_map_.erase(iface_name);
+}
+
+std::array<uint8_t, 6> WifiIfaceUtil::createRandomMacAddress() {
+ std::array<uint8_t, 6> address = {};
+ std::random_device rd;
+ std::default_random_engine engine(rd());
+ std::uniform_int_distribution<uint8_t> dist(std::numeric_limits<uint8_t>::min(),
+ std::numeric_limits<uint8_t>::max());
+ for (size_t i = 0; i < address.size(); i++) {
+ address[i] = dist(engine);
+ }
+ // Set the local bit and clear the multicast bit.
+ address[0] |= kMacAddressLocallyAssignedMask;
+ address[0] &= ~kMacAddressMulticastMask;
+ return address;
+}
+
+bool WifiIfaceUtil::setUpState(const std::string& iface_name, bool request_up) {
+ if (!iface_tool_.lock()->SetUpState(iface_name.c_str(), request_up)) {
+ LOG(ERROR) << "SetUpState to " << request_up << " failed";
+ return false;
+ }
+ return true;
+}
+
+unsigned WifiIfaceUtil::ifNameToIndex(const std::string& iface_name) {
+ return if_nametoindex(iface_name.c_str());
+}
+
+bool WifiIfaceUtil::createBridge(const std::string& br_name) {
+ if (!iface_tool_.lock()->createBridge(br_name)) {
+ return false;
+ }
+
+ if (!iface_tool_.lock()->SetUpState(br_name.c_str(), true)) {
+ LOG(ERROR) << "bridge SetUpState(true) failed.";
+ }
+ return true;
+}
+
+bool WifiIfaceUtil::deleteBridge(const std::string& br_name) {
+ if (!iface_tool_.lock()->SetUpState(br_name.c_str(), false)) {
+ LOG(INFO) << "SetUpState(false) failed for bridge=" << br_name.c_str();
+ }
+
+ return iface_tool_.lock()->deleteBridge(br_name);
+}
+
+bool WifiIfaceUtil::addIfaceToBridge(const std::string& br_name, const std::string& if_name) {
+ return iface_tool_.lock()->addIfaceToBridge(br_name, if_name);
+}
+
+bool WifiIfaceUtil::removeIfaceFromBridge(const std::string& br_name, const std::string& if_name) {
+ return iface_tool_.lock()->removeIfaceFromBridge(br_name, if_name);
+}
+
+} // namespace iface_util
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi_iface_util.h b/wifi/aidl/default/wifi_iface_util.h
new file mode 100644
index 0000000..a3236a5
--- /dev/null
+++ b/wifi/aidl/default/wifi_iface_util.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_IFACE_UTIL_H_
+#define WIFI_IFACE_UTIL_H_
+
+#include <aidl/android/hardware/wifi/IWifi.h>
+#include <wifi_system/interface_tool.h>
+
+#include "wifi_legacy_hal.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace iface_util {
+
+// Iface event handlers.
+struct IfaceEventHandlers {
+ // Callback to be invoked when the iface is set down & up for MAC address
+ // change.
+ std::function<void(const std::string& iface_name)> on_state_toggle_off_on;
+};
+
+/**
+ * Util class for common iface operations.
+ */
+class WifiIfaceUtil {
+ public:
+ WifiIfaceUtil(const std::weak_ptr<::android::wifi_system::InterfaceTool> iface_tool,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal);
+ virtual ~WifiIfaceUtil() = default;
+
+ virtual std::array<uint8_t, 6> getFactoryMacAddress(const std::string& iface_name);
+ virtual bool setMacAddress(const std::string& iface_name, const std::array<uint8_t, 6>& mac);
+ // Get or create a random MAC address. The MAC address returned from
+ // this method will remain the same throughout the lifetime of the HAL
+ // daemon. (So, changes on every reboot)
+ virtual std::array<uint8_t, 6> getOrCreateRandomMacAddress();
+
+ // Register for any iface event callbacks for the provided interface.
+ virtual void registerIfaceEventHandlers(const std::string& iface_name,
+ IfaceEventHandlers handlers);
+ virtual void unregisterIfaceEventHandlers(const std::string& iface_name);
+ virtual bool setUpState(const std::string& iface_name, bool request_up);
+ virtual unsigned ifNameToIndex(const std::string& iface_name);
+
+ virtual bool createBridge(const std::string& br_name);
+
+ virtual bool deleteBridge(const std::string& br_name);
+
+ virtual bool addIfaceToBridge(const std::string& br_name, const std::string& if_name);
+
+ virtual bool removeIfaceFromBridge(const std::string& br_name, const std::string& if_name);
+ // Get a random MAC address.
+ virtual std::array<uint8_t, 6> createRandomMacAddress();
+
+ private:
+ std::weak_ptr<::android::wifi_system::InterfaceTool> iface_tool_;
+ std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
+ std::unique_ptr<std::array<uint8_t, 6>> random_mac_address_;
+ std::map<std::string, IfaceEventHandlers> event_handlers_map_;
+};
+
+} // namespace iface_util
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_IFACE_UTIL_H_
diff --git a/wifi/aidl/default/wifi_legacy_hal.cpp b/wifi/aidl/default/wifi_legacy_hal.cpp
new file mode 100644
index 0000000..43e73ca
--- /dev/null
+++ b/wifi/aidl/default/wifi_legacy_hal.cpp
@@ -0,0 +1,1654 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_legacy_hal.h"
+
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+#include <net/if.h>
+
+#include <array>
+#include <chrono>
+
+#include "aidl_sync_util.h"
+#include "wifi_legacy_hal_stubs.h"
+
+namespace {
+// Constants ported over from the legacy HAL calling code
+// (com_android_server_wifi_WifiNative.cpp). This will all be thrown
+// away when this shim layer is replaced by the real vendor
+// implementation.
+static constexpr uint32_t kMaxVersionStringLength = 256;
+static constexpr uint32_t kMaxCachedGscanResults = 64;
+static constexpr uint32_t kMaxGscanFrequenciesForBand = 64;
+static constexpr uint32_t kLinkLayerStatsDataMpduSizeThreshold = 128;
+static constexpr uint32_t kMaxWakeReasonStatsArraySize = 32;
+static constexpr uint32_t kMaxRingBuffers = 10;
+static constexpr uint32_t kMaxWifiUsableChannels = 256;
+static constexpr uint32_t kMaxSupportedRadioCombinationsMatrixLength = 256;
+// Need a long timeout (1000ms) for chips that unload their driver.
+static constexpr uint32_t kMaxStopCompleteWaitMs = 1000;
+static constexpr char kDriverPropName[] = "wlan.driver.status";
+
+// Helper function to create a non-const char* for legacy Hal API's.
+std::vector<char> makeCharVec(const std::string& str) {
+ std::vector<char> vec(str.size() + 1);
+ vec.assign(str.begin(), str.end());
+ vec.push_back('\0');
+ return vec;
+}
+} // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace legacy_hal {
+
+// Legacy HAL functions accept "C" style function pointers, so use global
+// functions to pass to the legacy HAL function and store the corresponding
+// std::function methods to be invoked.
+//
+// Callback to be invoked once |stop| is complete
+std::function<void(wifi_handle handle)> on_stop_complete_internal_callback;
+void onAsyncStopComplete(wifi_handle handle) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_stop_complete_internal_callback) {
+ on_stop_complete_internal_callback(handle);
+ // Invalidate this callback since we don't want this firing again.
+ on_stop_complete_internal_callback = nullptr;
+ }
+}
+
+// Callback to be invoked for driver dump.
+std::function<void(char*, int)> on_driver_memory_dump_internal_callback;
+void onSyncDriverMemoryDump(char* buffer, int buffer_size) {
+ if (on_driver_memory_dump_internal_callback) {
+ on_driver_memory_dump_internal_callback(buffer, buffer_size);
+ }
+}
+
+// Callback to be invoked for firmware dump.
+std::function<void(char*, int)> on_firmware_memory_dump_internal_callback;
+void onSyncFirmwareMemoryDump(char* buffer, int buffer_size) {
+ if (on_firmware_memory_dump_internal_callback) {
+ on_firmware_memory_dump_internal_callback(buffer, buffer_size);
+ }
+}
+
+// Callback to be invoked for Gscan events.
+std::function<void(wifi_request_id, wifi_scan_event)> on_gscan_event_internal_callback;
+void onAsyncGscanEvent(wifi_request_id id, wifi_scan_event event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_gscan_event_internal_callback) {
+ on_gscan_event_internal_callback(id, event);
+ }
+}
+
+// Callback to be invoked for Gscan full results.
+std::function<void(wifi_request_id, wifi_scan_result*, uint32_t)>
+ on_gscan_full_result_internal_callback;
+void onAsyncGscanFullResult(wifi_request_id id, wifi_scan_result* result,
+ uint32_t buckets_scanned) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_gscan_full_result_internal_callback) {
+ on_gscan_full_result_internal_callback(id, result, buckets_scanned);
+ }
+}
+
+// Callback to be invoked for link layer stats results.
+std::function<void((wifi_request_id, wifi_iface_stat*, int, wifi_radio_stat*))>
+ on_link_layer_stats_result_internal_callback;
+void onSyncLinkLayerStatsResult(wifi_request_id id, wifi_iface_stat* iface_stat, int num_radios,
+ wifi_radio_stat* radio_stat) {
+ if (on_link_layer_stats_result_internal_callback) {
+ on_link_layer_stats_result_internal_callback(id, iface_stat, num_radios, radio_stat);
+ }
+}
+
+// Callback to be invoked for rssi threshold breach.
+std::function<void((wifi_request_id, uint8_t*, int8_t))>
+ on_rssi_threshold_breached_internal_callback;
+void onAsyncRssiThresholdBreached(wifi_request_id id, uint8_t* bssid, int8_t rssi) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_rssi_threshold_breached_internal_callback) {
+ on_rssi_threshold_breached_internal_callback(id, bssid, rssi);
+ }
+}
+
+// Callback to be invoked for ring buffer data indication.
+std::function<void(char*, char*, int, wifi_ring_buffer_status*)>
+ on_ring_buffer_data_internal_callback;
+void onAsyncRingBufferData(char* ring_name, char* buffer, int buffer_size,
+ wifi_ring_buffer_status* status) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_ring_buffer_data_internal_callback) {
+ on_ring_buffer_data_internal_callback(ring_name, buffer, buffer_size, status);
+ }
+}
+
+// Callback to be invoked for error alert indication.
+std::function<void(wifi_request_id, char*, int, int)> on_error_alert_internal_callback;
+void onAsyncErrorAlert(wifi_request_id id, char* buffer, int buffer_size, int err_code) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_error_alert_internal_callback) {
+ on_error_alert_internal_callback(id, buffer, buffer_size, err_code);
+ }
+}
+
+// Callback to be invoked for radio mode change indication.
+std::function<void(wifi_request_id, uint32_t, wifi_mac_info*)>
+ on_radio_mode_change_internal_callback;
+void onAsyncRadioModeChange(wifi_request_id id, uint32_t num_macs, wifi_mac_info* mac_infos) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_radio_mode_change_internal_callback) {
+ on_radio_mode_change_internal_callback(id, num_macs, mac_infos);
+ }
+}
+
+// Callback to be invoked to report subsystem restart
+std::function<void(const char*)> on_subsystem_restart_internal_callback;
+void onAsyncSubsystemRestart(const char* error) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_subsystem_restart_internal_callback) {
+ on_subsystem_restart_internal_callback(error);
+ }
+}
+
+// Callback to be invoked for rtt results results.
+std::function<void(wifi_request_id, unsigned num_results, wifi_rtt_result* rtt_results[])>
+ on_rtt_results_internal_callback;
+void onAsyncRttResults(wifi_request_id id, unsigned num_results, wifi_rtt_result* rtt_results[]) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_rtt_results_internal_callback) {
+ on_rtt_results_internal_callback(id, num_results, rtt_results);
+ on_rtt_results_internal_callback = nullptr;
+ }
+}
+
+// Callbacks for the various NAN operations.
+// NOTE: These have very little conversions to perform before invoking the user
+// callbacks.
+// So, handle all of them here directly to avoid adding an unnecessary layer.
+std::function<void(transaction_id, const NanResponseMsg&)> on_nan_notify_response_user_callback;
+void onAsyncNanNotifyResponse(transaction_id id, NanResponseMsg* msg) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_notify_response_user_callback && msg) {
+ on_nan_notify_response_user_callback(id, *msg);
+ }
+}
+
+std::function<void(const NanPublishRepliedInd&)> on_nan_event_publish_replied_user_callback;
+void onAsyncNanEventPublishReplied(NanPublishRepliedInd* /* event */) {
+ LOG(ERROR) << "onAsyncNanEventPublishReplied triggered";
+}
+
+std::function<void(const NanPublishTerminatedInd&)> on_nan_event_publish_terminated_user_callback;
+void onAsyncNanEventPublishTerminated(NanPublishTerminatedInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_publish_terminated_user_callback && event) {
+ on_nan_event_publish_terminated_user_callback(*event);
+ }
+}
+
+std::function<void(const NanMatchInd&)> on_nan_event_match_user_callback;
+void onAsyncNanEventMatch(NanMatchInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_match_user_callback && event) {
+ on_nan_event_match_user_callback(*event);
+ }
+}
+
+std::function<void(const NanMatchExpiredInd&)> on_nan_event_match_expired_user_callback;
+void onAsyncNanEventMatchExpired(NanMatchExpiredInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_match_expired_user_callback && event) {
+ on_nan_event_match_expired_user_callback(*event);
+ }
+}
+
+std::function<void(const NanSubscribeTerminatedInd&)>
+ on_nan_event_subscribe_terminated_user_callback;
+void onAsyncNanEventSubscribeTerminated(NanSubscribeTerminatedInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_subscribe_terminated_user_callback && event) {
+ on_nan_event_subscribe_terminated_user_callback(*event);
+ }
+}
+
+std::function<void(const NanFollowupInd&)> on_nan_event_followup_user_callback;
+void onAsyncNanEventFollowup(NanFollowupInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_followup_user_callback && event) {
+ on_nan_event_followup_user_callback(*event);
+ }
+}
+
+std::function<void(const NanDiscEngEventInd&)> on_nan_event_disc_eng_event_user_callback;
+void onAsyncNanEventDiscEngEvent(NanDiscEngEventInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_disc_eng_event_user_callback && event) {
+ on_nan_event_disc_eng_event_user_callback(*event);
+ }
+}
+
+std::function<void(const NanDisabledInd&)> on_nan_event_disabled_user_callback;
+void onAsyncNanEventDisabled(NanDisabledInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_disabled_user_callback && event) {
+ on_nan_event_disabled_user_callback(*event);
+ }
+}
+
+std::function<void(const NanTCAInd&)> on_nan_event_tca_user_callback;
+void onAsyncNanEventTca(NanTCAInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_tca_user_callback && event) {
+ on_nan_event_tca_user_callback(*event);
+ }
+}
+
+std::function<void(const NanBeaconSdfPayloadInd&)> on_nan_event_beacon_sdf_payload_user_callback;
+void onAsyncNanEventBeaconSdfPayload(NanBeaconSdfPayloadInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_beacon_sdf_payload_user_callback && event) {
+ on_nan_event_beacon_sdf_payload_user_callback(*event);
+ }
+}
+
+std::function<void(const NanDataPathRequestInd&)> on_nan_event_data_path_request_user_callback;
+void onAsyncNanEventDataPathRequest(NanDataPathRequestInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_data_path_request_user_callback && event) {
+ on_nan_event_data_path_request_user_callback(*event);
+ }
+}
+std::function<void(const NanDataPathConfirmInd&)> on_nan_event_data_path_confirm_user_callback;
+void onAsyncNanEventDataPathConfirm(NanDataPathConfirmInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_data_path_confirm_user_callback && event) {
+ on_nan_event_data_path_confirm_user_callback(*event);
+ }
+}
+
+std::function<void(const NanDataPathEndInd&)> on_nan_event_data_path_end_user_callback;
+void onAsyncNanEventDataPathEnd(NanDataPathEndInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_data_path_end_user_callback && event) {
+ on_nan_event_data_path_end_user_callback(*event);
+ }
+}
+
+std::function<void(const NanTransmitFollowupInd&)> on_nan_event_transmit_follow_up_user_callback;
+void onAsyncNanEventTransmitFollowUp(NanTransmitFollowupInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_transmit_follow_up_user_callback && event) {
+ on_nan_event_transmit_follow_up_user_callback(*event);
+ }
+}
+
+std::function<void(const NanRangeRequestInd&)> on_nan_event_range_request_user_callback;
+void onAsyncNanEventRangeRequest(NanRangeRequestInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_range_request_user_callback && event) {
+ on_nan_event_range_request_user_callback(*event);
+ }
+}
+
+std::function<void(const NanRangeReportInd&)> on_nan_event_range_report_user_callback;
+void onAsyncNanEventRangeReport(NanRangeReportInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_range_report_user_callback && event) {
+ on_nan_event_range_report_user_callback(*event);
+ }
+}
+
+std::function<void(const NanDataPathScheduleUpdateInd&)> on_nan_event_schedule_update_user_callback;
+void onAsyncNanEventScheduleUpdate(NanDataPathScheduleUpdateInd* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_nan_event_schedule_update_user_callback && event) {
+ on_nan_event_schedule_update_user_callback(*event);
+ }
+}
+
+// Callbacks for the various TWT operations.
+std::function<void(const TwtSetupResponse&)> on_twt_event_setup_response_callback;
+void onAsyncTwtEventSetupResponse(TwtSetupResponse* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_twt_event_setup_response_callback && event) {
+ on_twt_event_setup_response_callback(*event);
+ }
+}
+
+std::function<void(const TwtTeardownCompletion&)> on_twt_event_teardown_completion_callback;
+void onAsyncTwtEventTeardownCompletion(TwtTeardownCompletion* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_twt_event_teardown_completion_callback && event) {
+ on_twt_event_teardown_completion_callback(*event);
+ }
+}
+
+std::function<void(const TwtInfoFrameReceived&)> on_twt_event_info_frame_received_callback;
+void onAsyncTwtEventInfoFrameReceived(TwtInfoFrameReceived* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_twt_event_info_frame_received_callback && event) {
+ on_twt_event_info_frame_received_callback(*event);
+ }
+}
+
+std::function<void(const TwtDeviceNotify&)> on_twt_event_device_notify_callback;
+void onAsyncTwtEventDeviceNotify(TwtDeviceNotify* event) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_twt_event_device_notify_callback && event) {
+ on_twt_event_device_notify_callback(*event);
+ }
+}
+
+// Callback to report current CHRE NAN state
+std::function<void(chre_nan_rtt_state)> on_chre_nan_rtt_internal_callback;
+void onAsyncChreNanRttState(chre_nan_rtt_state state) {
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (on_chre_nan_rtt_internal_callback) {
+ on_chre_nan_rtt_internal_callback(state);
+ }
+}
+
+// Callback to report cached scan results
+std::function<void(wifi_cached_scan_report*)> on_cached_scan_results_internal_callback;
+void onSyncCachedScanResults(wifi_cached_scan_report* cache_report) {
+ if (on_cached_scan_results_internal_callback) {
+ on_cached_scan_results_internal_callback(cache_report);
+ }
+}
+
+// End of the free-standing "C" style callbacks.
+
+WifiLegacyHal::WifiLegacyHal(const std::weak_ptr<::android::wifi_system::InterfaceTool> iface_tool,
+ const wifi_hal_fn& fn, bool is_primary)
+ : global_func_table_(fn),
+ global_handle_(nullptr),
+ awaiting_event_loop_termination_(false),
+ is_started_(false),
+ iface_tool_(iface_tool),
+ is_primary_(is_primary) {}
+
+wifi_error WifiLegacyHal::initialize() {
+ LOG(DEBUG) << "Initialize legacy HAL";
+ // this now does nothing, since HAL function table is provided
+ // to the constructor
+ return WIFI_SUCCESS;
+}
+
+wifi_error WifiLegacyHal::start() {
+ // Ensure that we're starting in a good state.
+ CHECK(global_func_table_.wifi_initialize && !global_handle_ && iface_name_to_handle_.empty() &&
+ !awaiting_event_loop_termination_);
+ if (is_started_) {
+ LOG(DEBUG) << "Legacy HAL already started";
+ return WIFI_SUCCESS;
+ }
+ LOG(DEBUG) << "Waiting for the driver ready";
+ wifi_error status = global_func_table_.wifi_wait_for_driver_ready();
+ if (status == WIFI_ERROR_TIMED_OUT || status == WIFI_ERROR_UNKNOWN) {
+ LOG(ERROR) << "Failed or timed out awaiting driver ready";
+ return status;
+ }
+
+ if (is_primary_) {
+ property_set(kDriverPropName, "ok");
+
+ if (!iface_tool_.lock()->SetWifiUpState(true)) {
+ LOG(ERROR) << "Failed to set WiFi interface up";
+ return WIFI_ERROR_UNKNOWN;
+ }
+ }
+
+ LOG(DEBUG) << "Starting legacy HAL";
+ status = global_func_table_.wifi_initialize(&global_handle_);
+ if (status != WIFI_SUCCESS || !global_handle_) {
+ LOG(ERROR) << "Failed to retrieve global handle";
+ return status;
+ }
+ std::thread(&WifiLegacyHal::runEventLoop, this).detach();
+ status = retrieveIfaceHandles();
+ if (status != WIFI_SUCCESS || iface_name_to_handle_.empty()) {
+ LOG(ERROR) << "Failed to retrieve wlan interface handle";
+ return status;
+ }
+ LOG(DEBUG) << "Legacy HAL start complete";
+ is_started_ = true;
+ return WIFI_SUCCESS;
+}
+
+wifi_error WifiLegacyHal::stop(
+ /* NONNULL */ std::unique_lock<std::recursive_mutex>* lock,
+ const std::function<void()>& on_stop_complete_user_callback) {
+ if (!is_started_) {
+ LOG(DEBUG) << "Legacy HAL already stopped";
+ on_stop_complete_user_callback();
+ return WIFI_SUCCESS;
+ }
+ LOG(DEBUG) << "Stopping legacy HAL";
+ on_stop_complete_internal_callback = [on_stop_complete_user_callback,
+ this](wifi_handle handle) {
+ CHECK_EQ(global_handle_, handle) << "Handle mismatch";
+ LOG(INFO) << "Legacy HAL stop complete callback received";
+ // Invalidate all the internal pointers now that the HAL is
+ // stopped.
+ invalidate();
+ if (is_primary_) iface_tool_.lock()->SetWifiUpState(false);
+ on_stop_complete_user_callback();
+ is_started_ = false;
+ };
+ awaiting_event_loop_termination_ = true;
+ global_func_table_.wifi_cleanup(global_handle_, onAsyncStopComplete);
+ const auto status =
+ stop_wait_cv_.wait_for(*lock, std::chrono::milliseconds(kMaxStopCompleteWaitMs),
+ [this] { return !awaiting_event_loop_termination_; });
+ if (!status) {
+ LOG(ERROR) << "Legacy HAL stop failed or timed out";
+ return WIFI_ERROR_UNKNOWN;
+ }
+ LOG(DEBUG) << "Legacy HAL stop complete";
+ return WIFI_SUCCESS;
+}
+
+bool WifiLegacyHal::isStarted() {
+ return is_started_;
+}
+
+wifi_error WifiLegacyHal::waitForDriverReady() {
+ return global_func_table_.wifi_wait_for_driver_ready();
+}
+
+std::pair<wifi_error, std::string> WifiLegacyHal::getDriverVersion(const std::string& iface_name) {
+ std::array<char, kMaxVersionStringLength> buffer;
+ buffer.fill(0);
+ wifi_error status = global_func_table_.wifi_get_driver_version(getIfaceHandle(iface_name),
+ buffer.data(), buffer.size());
+ return {status, buffer.data()};
+}
+
+std::pair<wifi_error, std::string> WifiLegacyHal::getFirmwareVersion(
+ const std::string& iface_name) {
+ std::array<char, kMaxVersionStringLength> buffer;
+ buffer.fill(0);
+ wifi_error status = global_func_table_.wifi_get_firmware_version(getIfaceHandle(iface_name),
+ buffer.data(), buffer.size());
+ return {status, buffer.data()};
+}
+
+std::pair<wifi_error, std::vector<uint8_t>> WifiLegacyHal::requestDriverMemoryDump(
+ const std::string& iface_name) {
+ std::vector<uint8_t> driver_dump;
+ on_driver_memory_dump_internal_callback = [&driver_dump](char* buffer, int buffer_size) {
+ driver_dump.insert(driver_dump.end(), reinterpret_cast<uint8_t*>(buffer),
+ reinterpret_cast<uint8_t*>(buffer) + buffer_size);
+ };
+ wifi_error status = global_func_table_.wifi_get_driver_memory_dump(getIfaceHandle(iface_name),
+ {onSyncDriverMemoryDump});
+ on_driver_memory_dump_internal_callback = nullptr;
+ return {status, std::move(driver_dump)};
+}
+
+std::pair<wifi_error, std::vector<uint8_t>> WifiLegacyHal::requestFirmwareMemoryDump(
+ const std::string& iface_name) {
+ std::vector<uint8_t> firmware_dump;
+ on_firmware_memory_dump_internal_callback = [&firmware_dump](char* buffer, int buffer_size) {
+ firmware_dump.insert(firmware_dump.end(), reinterpret_cast<uint8_t*>(buffer),
+ reinterpret_cast<uint8_t*>(buffer) + buffer_size);
+ };
+ wifi_error status = global_func_table_.wifi_get_firmware_memory_dump(
+ getIfaceHandle(iface_name), {onSyncFirmwareMemoryDump});
+ on_firmware_memory_dump_internal_callback = nullptr;
+ return {status, std::move(firmware_dump)};
+}
+
+std::pair<wifi_error, uint64_t> WifiLegacyHal::getSupportedFeatureSet(
+ const std::string& iface_name) {
+ feature_set set = 0, chip_set = 0;
+ wifi_error status = WIFI_SUCCESS;
+
+ static_assert(sizeof(set) == sizeof(uint64_t),
+ "Some feature_flags can not be represented in output");
+ wifi_interface_handle iface_handle = getIfaceHandle(iface_name);
+
+ global_func_table_.wifi_get_chip_feature_set(
+ global_handle_, &chip_set); /* ignore error, chip_set will stay 0 */
+
+ if (iface_handle) {
+ status = global_func_table_.wifi_get_supported_feature_set(iface_handle, &set);
+ }
+ return {status, static_cast<uint64_t>(set | chip_set)};
+}
+
+std::pair<wifi_error, PacketFilterCapabilities> WifiLegacyHal::getPacketFilterCapabilities(
+ const std::string& iface_name) {
+ PacketFilterCapabilities caps;
+ wifi_error status = global_func_table_.wifi_get_packet_filter_capabilities(
+ getIfaceHandle(iface_name), &caps.version, &caps.max_len);
+ return {status, caps};
+}
+
+wifi_error WifiLegacyHal::setPacketFilter(const std::string& iface_name,
+ const std::vector<uint8_t>& program) {
+ return global_func_table_.wifi_set_packet_filter(getIfaceHandle(iface_name), program.data(),
+ program.size());
+}
+
+std::pair<wifi_error, std::vector<uint8_t>> WifiLegacyHal::readApfPacketFilterData(
+ const std::string& iface_name) {
+ PacketFilterCapabilities caps;
+ wifi_error status = global_func_table_.wifi_get_packet_filter_capabilities(
+ getIfaceHandle(iface_name), &caps.version, &caps.max_len);
+ if (status != WIFI_SUCCESS) {
+ return {status, {}};
+ }
+
+ // Size the buffer to read the entire program & work memory.
+ std::vector<uint8_t> buffer(caps.max_len);
+
+ status = global_func_table_.wifi_read_packet_filter(
+ getIfaceHandle(iface_name), /*src_offset=*/0, buffer.data(), buffer.size());
+ return {status, std::move(buffer)};
+}
+
+std::pair<wifi_error, wifi_gscan_capabilities> WifiLegacyHal::getGscanCapabilities(
+ const std::string& iface_name) {
+ wifi_gscan_capabilities caps;
+ wifi_error status =
+ global_func_table_.wifi_get_gscan_capabilities(getIfaceHandle(iface_name), &caps);
+ return {status, caps};
+}
+
+wifi_error WifiLegacyHal::startGscan(
+ const std::string& iface_name, wifi_request_id id, const wifi_scan_cmd_params& params,
+ const std::function<void(wifi_request_id)>& on_failure_user_callback,
+ const on_gscan_results_callback& on_results_user_callback,
+ const on_gscan_full_result_callback& on_full_result_user_callback) {
+ // If there is already an ongoing background scan, reject new scan requests.
+ if (on_gscan_event_internal_callback || on_gscan_full_result_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+
+ // This callback will be used to either trigger |on_results_user_callback|
+ // or |on_failure_user_callback|.
+ on_gscan_event_internal_callback = [iface_name, on_failure_user_callback,
+ on_results_user_callback,
+ this](wifi_request_id id, wifi_scan_event event) {
+ switch (event) {
+ case WIFI_SCAN_RESULTS_AVAILABLE:
+ case WIFI_SCAN_THRESHOLD_NUM_SCANS:
+ case WIFI_SCAN_THRESHOLD_PERCENT: {
+ wifi_error status;
+ std::vector<wifi_cached_scan_results> cached_scan_results;
+ std::tie(status, cached_scan_results) = getGscanCachedResults(iface_name);
+ if (status == WIFI_SUCCESS) {
+ on_results_user_callback(id, cached_scan_results);
+ return;
+ }
+ FALLTHROUGH_INTENDED;
+ }
+ // Fall through if failed. Failure to retrieve cached scan
+ // results should trigger a background scan failure.
+ case WIFI_SCAN_FAILED:
+ on_failure_user_callback(id);
+ on_gscan_event_internal_callback = nullptr;
+ on_gscan_full_result_internal_callback = nullptr;
+ return;
+ }
+ LOG(FATAL) << "Unexpected gscan event received: " << event;
+ };
+
+ on_gscan_full_result_internal_callback = [on_full_result_user_callback](
+ wifi_request_id id, wifi_scan_result* result,
+ uint32_t buckets_scanned) {
+ if (result) {
+ on_full_result_user_callback(id, result, buckets_scanned);
+ }
+ };
+
+ wifi_scan_result_handler handler = {onAsyncGscanFullResult, onAsyncGscanEvent};
+ wifi_error status =
+ global_func_table_.wifi_start_gscan(id, getIfaceHandle(iface_name), params, handler);
+ if (status != WIFI_SUCCESS) {
+ on_gscan_event_internal_callback = nullptr;
+ on_gscan_full_result_internal_callback = nullptr;
+ }
+ return status;
+}
+
+wifi_error WifiLegacyHal::stopGscan(const std::string& iface_name, wifi_request_id id) {
+ // If there is no an ongoing background scan, reject stop requests.
+ // TODO(b/32337212): This needs to be handled by the HIDL object because we
+ // need to return the NOT_STARTED error code.
+ if (!on_gscan_event_internal_callback && !on_gscan_full_result_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+ wifi_error status = global_func_table_.wifi_stop_gscan(id, getIfaceHandle(iface_name));
+ // If the request Id is wrong, don't stop the ongoing background scan. Any
+ // other error should be treated as the end of background scan.
+ if (status != WIFI_ERROR_INVALID_REQUEST_ID) {
+ on_gscan_event_internal_callback = nullptr;
+ on_gscan_full_result_internal_callback = nullptr;
+ }
+ return status;
+}
+
+std::pair<wifi_error, std::vector<uint32_t>> WifiLegacyHal::getValidFrequenciesForBand(
+ const std::string& iface_name, wifi_band band) {
+ static_assert(sizeof(uint32_t) >= sizeof(wifi_channel),
+ "Wifi Channel cannot be represented in output");
+ std::vector<uint32_t> freqs;
+ freqs.resize(kMaxGscanFrequenciesForBand);
+ int32_t num_freqs = 0;
+ wifi_error status = global_func_table_.wifi_get_valid_channels(
+ getIfaceHandle(iface_name), band, freqs.size(),
+ reinterpret_cast<wifi_channel*>(freqs.data()), &num_freqs);
+ CHECK(num_freqs >= 0 && static_cast<uint32_t>(num_freqs) <= kMaxGscanFrequenciesForBand);
+ freqs.resize(num_freqs);
+ return {status, std::move(freqs)};
+}
+
+wifi_error WifiLegacyHal::setDfsFlag(const std::string& iface_name, bool dfs_on) {
+ return global_func_table_.wifi_set_nodfs_flag(getIfaceHandle(iface_name), dfs_on ? 0 : 1);
+}
+
+wifi_error WifiLegacyHal::enableLinkLayerStats(const std::string& iface_name, bool debug) {
+ wifi_link_layer_params params;
+ params.mpdu_size_threshold = kLinkLayerStatsDataMpduSizeThreshold;
+ params.aggressive_statistics_gathering = debug;
+ return global_func_table_.wifi_set_link_stats(getIfaceHandle(iface_name), params);
+}
+
+wifi_error WifiLegacyHal::disableLinkLayerStats(const std::string& iface_name) {
+ // TODO: Do we care about these responses?
+ uint32_t clear_mask_rsp;
+ uint8_t stop_rsp;
+ return global_func_table_.wifi_clear_link_stats(getIfaceHandle(iface_name), 0xFFFFFFFF,
+ &clear_mask_rsp, 1, &stop_rsp);
+}
+
+std::pair<wifi_error, LinkLayerStats> WifiLegacyHal::getLinkLayerStats(
+ const std::string& iface_name) {
+ LinkLayerStats link_stats{};
+ LinkLayerStats* link_stats_ptr = &link_stats;
+
+ on_link_layer_stats_result_internal_callback = [&link_stats_ptr](
+ wifi_request_id /* id */,
+ wifi_iface_stat* iface_stats_ptr,
+ int num_radios,
+ wifi_radio_stat* radio_stats_ptr) {
+ wifi_radio_stat* l_radio_stats_ptr;
+ wifi_peer_info* l_peer_info_stats_ptr;
+
+ if (iface_stats_ptr != nullptr) {
+ link_stats_ptr->iface = *iface_stats_ptr;
+ l_peer_info_stats_ptr = iface_stats_ptr->peer_info;
+ for (uint32_t i = 0; i < iface_stats_ptr->num_peers; i++) {
+ WifiPeerInfo peer;
+ peer.peer_info = *l_peer_info_stats_ptr;
+ if (l_peer_info_stats_ptr->num_rate > 0) {
+ /* Copy the rate stats */
+ peer.rate_stats.assign(
+ l_peer_info_stats_ptr->rate_stats,
+ l_peer_info_stats_ptr->rate_stats + l_peer_info_stats_ptr->num_rate);
+ }
+ peer.peer_info.num_rate = 0;
+ link_stats_ptr->peers.push_back(peer);
+ l_peer_info_stats_ptr =
+ (wifi_peer_info*)((u8*)l_peer_info_stats_ptr + sizeof(wifi_peer_info) +
+ (sizeof(wifi_rate_stat) *
+ l_peer_info_stats_ptr->num_rate));
+ }
+ link_stats_ptr->iface.num_peers = 0;
+ } else {
+ LOG(ERROR) << "Invalid iface stats in link layer stats";
+ }
+ if (num_radios <= 0 || radio_stats_ptr == nullptr) {
+ LOG(ERROR) << "Invalid radio stats in link layer stats";
+ return;
+ }
+ l_radio_stats_ptr = radio_stats_ptr;
+ for (int i = 0; i < num_radios; i++) {
+ LinkLayerRadioStats radio;
+
+ radio.stats = *l_radio_stats_ptr;
+ // Copy over the tx level array to the separate vector.
+ if (l_radio_stats_ptr->num_tx_levels > 0 &&
+ l_radio_stats_ptr->tx_time_per_levels != nullptr) {
+ radio.tx_time_per_levels.assign(
+ l_radio_stats_ptr->tx_time_per_levels,
+ l_radio_stats_ptr->tx_time_per_levels + l_radio_stats_ptr->num_tx_levels);
+ }
+ radio.stats.num_tx_levels = 0;
+ radio.stats.tx_time_per_levels = nullptr;
+ /* Copy over the channel stat to separate vector */
+ if (l_radio_stats_ptr->num_channels > 0) {
+ /* Copy the channel stats */
+ radio.channel_stats.assign(
+ l_radio_stats_ptr->channels,
+ l_radio_stats_ptr->channels + l_radio_stats_ptr->num_channels);
+ }
+ link_stats_ptr->radios.push_back(radio);
+ l_radio_stats_ptr =
+ (wifi_radio_stat*)((u8*)l_radio_stats_ptr + sizeof(wifi_radio_stat) +
+ (sizeof(wifi_channel_stat) *
+ l_radio_stats_ptr->num_channels));
+ }
+ };
+
+ wifi_error status = global_func_table_.wifi_get_link_stats(0, getIfaceHandle(iface_name),
+ {onSyncLinkLayerStatsResult});
+ on_link_layer_stats_result_internal_callback = nullptr;
+ return {status, link_stats};
+}
+
+wifi_error WifiLegacyHal::startRssiMonitoring(
+ const std::string& iface_name, wifi_request_id id, int8_t max_rssi, int8_t min_rssi,
+ const on_rssi_threshold_breached_callback& on_threshold_breached_user_callback) {
+ if (on_rssi_threshold_breached_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+ on_rssi_threshold_breached_internal_callback = [on_threshold_breached_user_callback](
+ wifi_request_id id, uint8_t* bssid_ptr,
+ int8_t rssi) {
+ if (!bssid_ptr) {
+ return;
+ }
+ std::array<uint8_t, ETH_ALEN> bssid_arr;
+ // |bssid_ptr| pointer is assumed to have 6 bytes for the mac
+ // address.
+ std::copy(bssid_ptr, bssid_ptr + 6, std::begin(bssid_arr));
+ on_threshold_breached_user_callback(id, bssid_arr, rssi);
+ };
+ wifi_error status = global_func_table_.wifi_start_rssi_monitoring(
+ id, getIfaceHandle(iface_name), max_rssi, min_rssi, {onAsyncRssiThresholdBreached});
+ if (status != WIFI_SUCCESS) {
+ on_rssi_threshold_breached_internal_callback = nullptr;
+ }
+ return status;
+}
+
+wifi_error WifiLegacyHal::stopRssiMonitoring(const std::string& iface_name, wifi_request_id id) {
+ if (!on_rssi_threshold_breached_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+ wifi_error status =
+ global_func_table_.wifi_stop_rssi_monitoring(id, getIfaceHandle(iface_name));
+ // If the request Id is wrong, don't stop the ongoing rssi monitoring. Any
+ // other error should be treated as the end of background scan.
+ if (status != WIFI_ERROR_INVALID_REQUEST_ID) {
+ on_rssi_threshold_breached_internal_callback = nullptr;
+ }
+ return status;
+}
+
+std::pair<wifi_error, wifi_roaming_capabilities> WifiLegacyHal::getRoamingCapabilities(
+ const std::string& iface_name) {
+ wifi_roaming_capabilities caps;
+ wifi_error status =
+ global_func_table_.wifi_get_roaming_capabilities(getIfaceHandle(iface_name), &caps);
+ return {status, caps};
+}
+
+wifi_error WifiLegacyHal::configureRoaming(const std::string& iface_name,
+ const wifi_roaming_config& config) {
+ wifi_roaming_config config_internal = config;
+ return global_func_table_.wifi_configure_roaming(getIfaceHandle(iface_name), &config_internal);
+}
+
+wifi_error WifiLegacyHal::enableFirmwareRoaming(const std::string& iface_name,
+ fw_roaming_state_t state) {
+ return global_func_table_.wifi_enable_firmware_roaming(getIfaceHandle(iface_name), state);
+}
+
+wifi_error WifiLegacyHal::configureNdOffload(const std::string& iface_name, bool enable) {
+ return global_func_table_.wifi_configure_nd_offload(getIfaceHandle(iface_name), enable);
+}
+
+wifi_error WifiLegacyHal::startSendingOffloadedPacket(const std::string& iface_name, int32_t cmd_id,
+ uint16_t ether_type,
+ const std::vector<uint8_t>& ip_packet_data,
+ const std::array<uint8_t, 6>& src_address,
+ const std::array<uint8_t, 6>& dst_address,
+ int32_t period_in_ms) {
+ std::vector<uint8_t> ip_packet_data_internal(ip_packet_data);
+ std::vector<uint8_t> src_address_internal(src_address.data(),
+ src_address.data() + src_address.size());
+ std::vector<uint8_t> dst_address_internal(dst_address.data(),
+ dst_address.data() + dst_address.size());
+ return global_func_table_.wifi_start_sending_offloaded_packet(
+ cmd_id, getIfaceHandle(iface_name), ether_type, ip_packet_data_internal.data(),
+ ip_packet_data_internal.size(), src_address_internal.data(),
+ dst_address_internal.data(), period_in_ms);
+}
+
+wifi_error WifiLegacyHal::stopSendingOffloadedPacket(const std::string& iface_name,
+ uint32_t cmd_id) {
+ return global_func_table_.wifi_stop_sending_offloaded_packet(cmd_id,
+ getIfaceHandle(iface_name));
+}
+
+wifi_error WifiLegacyHal::selectTxPowerScenario(const std::string& iface_name,
+ wifi_power_scenario scenario) {
+ return global_func_table_.wifi_select_tx_power_scenario(getIfaceHandle(iface_name), scenario);
+}
+
+wifi_error WifiLegacyHal::resetTxPowerScenario(const std::string& iface_name) {
+ return global_func_table_.wifi_reset_tx_power_scenario(getIfaceHandle(iface_name));
+}
+
+wifi_error WifiLegacyHal::setLatencyMode(const std::string& iface_name, wifi_latency_mode mode) {
+ return global_func_table_.wifi_set_latency_mode(getIfaceHandle(iface_name), mode);
+}
+
+wifi_error WifiLegacyHal::setThermalMitigationMode(wifi_thermal_mode mode,
+ uint32_t completion_window) {
+ return global_func_table_.wifi_set_thermal_mitigation_mode(global_handle_, mode,
+ completion_window);
+}
+
+wifi_error WifiLegacyHal::setDscpToAccessCategoryMapping(uint32_t start, uint32_t end,
+ uint32_t access_category) {
+ return global_func_table_.wifi_map_dscp_access_category(global_handle_, start, end,
+ access_category);
+}
+
+wifi_error WifiLegacyHal::resetDscpToAccessCategoryMapping() {
+ return global_func_table_.wifi_reset_dscp_mapping(global_handle_);
+}
+
+std::pair<wifi_error, uint32_t> WifiLegacyHal::getLoggerSupportedFeatureSet(
+ const std::string& iface_name) {
+ uint32_t supported_feature_flags = 0;
+ wifi_error status = WIFI_SUCCESS;
+
+ wifi_interface_handle iface_handle = getIfaceHandle(iface_name);
+
+ if (iface_handle) {
+ status = global_func_table_.wifi_get_logger_supported_feature_set(iface_handle,
+ &supported_feature_flags);
+ }
+ return {status, supported_feature_flags};
+}
+
+wifi_error WifiLegacyHal::startPktFateMonitoring(const std::string& iface_name) {
+ return global_func_table_.wifi_start_pkt_fate_monitoring(getIfaceHandle(iface_name));
+}
+
+std::pair<wifi_error, std::vector<wifi_tx_report>> WifiLegacyHal::getTxPktFates(
+ const std::string& iface_name) {
+ std::vector<wifi_tx_report> tx_pkt_fates;
+ tx_pkt_fates.resize(MAX_FATE_LOG_LEN);
+ size_t num_fates = 0;
+ wifi_error status = global_func_table_.wifi_get_tx_pkt_fates(
+ getIfaceHandle(iface_name), tx_pkt_fates.data(), tx_pkt_fates.size(), &num_fates);
+ CHECK(num_fates <= MAX_FATE_LOG_LEN);
+ tx_pkt_fates.resize(num_fates);
+ return {status, std::move(tx_pkt_fates)};
+}
+
+std::pair<wifi_error, std::vector<wifi_rx_report>> WifiLegacyHal::getRxPktFates(
+ const std::string& iface_name) {
+ std::vector<wifi_rx_report> rx_pkt_fates;
+ rx_pkt_fates.resize(MAX_FATE_LOG_LEN);
+ size_t num_fates = 0;
+ wifi_error status = global_func_table_.wifi_get_rx_pkt_fates(
+ getIfaceHandle(iface_name), rx_pkt_fates.data(), rx_pkt_fates.size(), &num_fates);
+ CHECK(num_fates <= MAX_FATE_LOG_LEN);
+ rx_pkt_fates.resize(num_fates);
+ return {status, std::move(rx_pkt_fates)};
+}
+
+std::pair<wifi_error, WakeReasonStats> WifiLegacyHal::getWakeReasonStats(
+ const std::string& iface_name) {
+ WakeReasonStats stats;
+ stats.cmd_event_wake_cnt.resize(kMaxWakeReasonStatsArraySize);
+ stats.driver_fw_local_wake_cnt.resize(kMaxWakeReasonStatsArraySize);
+
+ // This legacy struct needs separate memory to store the variable sized wake
+ // reason types.
+ stats.wake_reason_cnt.cmd_event_wake_cnt =
+ reinterpret_cast<int32_t*>(stats.cmd_event_wake_cnt.data());
+ stats.wake_reason_cnt.cmd_event_wake_cnt_sz = stats.cmd_event_wake_cnt.size();
+ stats.wake_reason_cnt.cmd_event_wake_cnt_used = 0;
+ stats.wake_reason_cnt.driver_fw_local_wake_cnt =
+ reinterpret_cast<int32_t*>(stats.driver_fw_local_wake_cnt.data());
+ stats.wake_reason_cnt.driver_fw_local_wake_cnt_sz = stats.driver_fw_local_wake_cnt.size();
+ stats.wake_reason_cnt.driver_fw_local_wake_cnt_used = 0;
+
+ wifi_error status = global_func_table_.wifi_get_wake_reason_stats(getIfaceHandle(iface_name),
+ &stats.wake_reason_cnt);
+
+ CHECK(stats.wake_reason_cnt.cmd_event_wake_cnt_used >= 0 &&
+ static_cast<uint32_t>(stats.wake_reason_cnt.cmd_event_wake_cnt_used) <=
+ kMaxWakeReasonStatsArraySize);
+ stats.cmd_event_wake_cnt.resize(stats.wake_reason_cnt.cmd_event_wake_cnt_used);
+ stats.wake_reason_cnt.cmd_event_wake_cnt = nullptr;
+
+ CHECK(stats.wake_reason_cnt.driver_fw_local_wake_cnt_used >= 0 &&
+ static_cast<uint32_t>(stats.wake_reason_cnt.driver_fw_local_wake_cnt_used) <=
+ kMaxWakeReasonStatsArraySize);
+ stats.driver_fw_local_wake_cnt.resize(stats.wake_reason_cnt.driver_fw_local_wake_cnt_used);
+ stats.wake_reason_cnt.driver_fw_local_wake_cnt = nullptr;
+
+ return {status, stats};
+}
+
+wifi_error WifiLegacyHal::registerRingBufferCallbackHandler(
+ const std::string& iface_name, const on_ring_buffer_data_callback& on_user_data_callback) {
+ if (on_ring_buffer_data_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+ on_ring_buffer_data_internal_callback = [on_user_data_callback](
+ char* ring_name, char* buffer, int buffer_size,
+ wifi_ring_buffer_status* status) {
+ if (status && buffer) {
+ std::vector<uint8_t> buffer_vector(reinterpret_cast<uint8_t*>(buffer),
+ reinterpret_cast<uint8_t*>(buffer) + buffer_size);
+ on_user_data_callback(ring_name, buffer_vector, *status);
+ }
+ };
+ wifi_error status = global_func_table_.wifi_set_log_handler(0, getIfaceHandle(iface_name),
+ {onAsyncRingBufferData});
+ if (status != WIFI_SUCCESS) {
+ on_ring_buffer_data_internal_callback = nullptr;
+ }
+ return status;
+}
+
+wifi_error WifiLegacyHal::deregisterRingBufferCallbackHandler(const std::string& iface_name) {
+ if (!on_ring_buffer_data_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+ on_ring_buffer_data_internal_callback = nullptr;
+ return global_func_table_.wifi_reset_log_handler(0, getIfaceHandle(iface_name));
+}
+
+std::pair<wifi_error, std::vector<wifi_ring_buffer_status>> WifiLegacyHal::getRingBuffersStatus(
+ const std::string& iface_name) {
+ std::vector<wifi_ring_buffer_status> ring_buffers_status;
+ ring_buffers_status.resize(kMaxRingBuffers);
+ uint32_t num_rings = kMaxRingBuffers;
+ wifi_error status = global_func_table_.wifi_get_ring_buffers_status(
+ getIfaceHandle(iface_name), &num_rings, ring_buffers_status.data());
+ CHECK(num_rings <= kMaxRingBuffers);
+ ring_buffers_status.resize(num_rings);
+ return {status, std::move(ring_buffers_status)};
+}
+
+wifi_error WifiLegacyHal::startRingBufferLogging(const std::string& iface_name,
+ const std::string& ring_name,
+ uint32_t verbose_level, uint32_t max_interval_sec,
+ uint32_t min_data_size) {
+ return global_func_table_.wifi_start_logging(getIfaceHandle(iface_name), verbose_level, 0,
+ max_interval_sec, min_data_size,
+ makeCharVec(ring_name).data());
+}
+
+wifi_error WifiLegacyHal::getRingBufferData(const std::string& iface_name,
+ const std::string& ring_name) {
+ return global_func_table_.wifi_get_ring_data(getIfaceHandle(iface_name),
+ makeCharVec(ring_name).data());
+}
+
+wifi_error WifiLegacyHal::registerErrorAlertCallbackHandler(
+ const std::string& iface_name, const on_error_alert_callback& on_user_alert_callback) {
+ if (on_error_alert_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+ on_error_alert_internal_callback = [on_user_alert_callback](wifi_request_id id, char* buffer,
+ int buffer_size, int err_code) {
+ if (buffer) {
+ CHECK(id == 0);
+ on_user_alert_callback(
+ err_code,
+ std::vector<uint8_t>(reinterpret_cast<uint8_t*>(buffer),
+ reinterpret_cast<uint8_t*>(buffer) + buffer_size));
+ }
+ };
+ wifi_error status = global_func_table_.wifi_set_alert_handler(0, getIfaceHandle(iface_name),
+ {onAsyncErrorAlert});
+ if (status != WIFI_SUCCESS) {
+ on_error_alert_internal_callback = nullptr;
+ }
+ return status;
+}
+
+wifi_error WifiLegacyHal::deregisterErrorAlertCallbackHandler(const std::string& iface_name) {
+ if (!on_error_alert_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+ on_error_alert_internal_callback = nullptr;
+ return global_func_table_.wifi_reset_alert_handler(0, getIfaceHandle(iface_name));
+}
+
+wifi_error WifiLegacyHal::registerRadioModeChangeCallbackHandler(
+ const std::string& iface_name,
+ const on_radio_mode_change_callback& on_user_change_callback) {
+ if (on_radio_mode_change_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+ on_radio_mode_change_internal_callback = [on_user_change_callback](
+ wifi_request_id /* id */, uint32_t num_macs,
+ wifi_mac_info* mac_infos_arr) {
+ if (num_macs > 0 && mac_infos_arr) {
+ std::vector<WifiMacInfo> mac_infos_vec;
+ for (uint32_t i = 0; i < num_macs; i++) {
+ WifiMacInfo mac_info;
+ mac_info.wlan_mac_id = mac_infos_arr[i].wlan_mac_id;
+ mac_info.mac_band = mac_infos_arr[i].mac_band;
+ for (int32_t j = 0; j < mac_infos_arr[i].num_iface; j++) {
+ WifiIfaceInfo iface_info;
+ iface_info.name = mac_infos_arr[i].iface_info[j].iface_name;
+ iface_info.channel = mac_infos_arr[i].iface_info[j].channel;
+ mac_info.iface_infos.push_back(iface_info);
+ }
+ mac_infos_vec.push_back(mac_info);
+ }
+ on_user_change_callback(mac_infos_vec);
+ }
+ };
+ wifi_error status = global_func_table_.wifi_set_radio_mode_change_handler(
+ 0, getIfaceHandle(iface_name), {onAsyncRadioModeChange});
+ if (status != WIFI_SUCCESS) {
+ on_radio_mode_change_internal_callback = nullptr;
+ }
+ return status;
+}
+
+wifi_error WifiLegacyHal::registerSubsystemRestartCallbackHandler(
+ const on_subsystem_restart_callback& on_restart_callback) {
+ if (on_subsystem_restart_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+ on_subsystem_restart_internal_callback = [on_restart_callback](const char* error) {
+ on_restart_callback(error);
+ };
+ wifi_error status = global_func_table_.wifi_set_subsystem_restart_handler(
+ global_handle_, {onAsyncSubsystemRestart});
+ if (status != WIFI_SUCCESS) {
+ on_subsystem_restart_internal_callback = nullptr;
+ }
+ return status;
+}
+
+wifi_error WifiLegacyHal::startRttRangeRequest(
+ const std::string& iface_name, wifi_request_id id,
+ const std::vector<wifi_rtt_config>& rtt_configs,
+ const on_rtt_results_callback& on_results_user_callback) {
+ if (on_rtt_results_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+
+ on_rtt_results_internal_callback = [on_results_user_callback](wifi_request_id id,
+ unsigned num_results,
+ wifi_rtt_result* rtt_results[]) {
+ if (num_results > 0 && !rtt_results) {
+ LOG(ERROR) << "Unexpected nullptr in RTT results";
+ return;
+ }
+ std::vector<const wifi_rtt_result*> rtt_results_vec;
+ std::copy_if(rtt_results, rtt_results + num_results, back_inserter(rtt_results_vec),
+ [](wifi_rtt_result* rtt_result) { return rtt_result != nullptr; });
+ on_results_user_callback(id, rtt_results_vec);
+ };
+
+ std::vector<wifi_rtt_config> rtt_configs_internal(rtt_configs);
+ wifi_error status = global_func_table_.wifi_rtt_range_request(
+ id, getIfaceHandle(iface_name), rtt_configs.size(), rtt_configs_internal.data(),
+ {onAsyncRttResults});
+ if (status != WIFI_SUCCESS) {
+ on_rtt_results_internal_callback = nullptr;
+ }
+ return status;
+}
+
+wifi_error WifiLegacyHal::cancelRttRangeRequest(
+ const std::string& iface_name, wifi_request_id id,
+ const std::vector<std::array<uint8_t, ETH_ALEN>>& mac_addrs) {
+ if (!on_rtt_results_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+ static_assert(sizeof(mac_addr) == sizeof(std::array<uint8_t, ETH_ALEN>),
+ "MAC address size mismatch");
+ // TODO: How do we handle partial cancels (i.e only a subset of enabled mac
+ // addressed are cancelled).
+ std::vector<std::array<uint8_t, ETH_ALEN>> mac_addrs_internal(mac_addrs);
+ wifi_error status = global_func_table_.wifi_rtt_range_cancel(
+ id, getIfaceHandle(iface_name), mac_addrs.size(),
+ reinterpret_cast<mac_addr*>(mac_addrs_internal.data()));
+ // If the request Id is wrong, don't stop the ongoing range request. Any
+ // other error should be treated as the end of rtt ranging.
+ if (status != WIFI_ERROR_INVALID_REQUEST_ID) {
+ on_rtt_results_internal_callback = nullptr;
+ }
+ return status;
+}
+
+std::pair<wifi_error, wifi_rtt_capabilities> WifiLegacyHal::getRttCapabilities(
+ const std::string& iface_name) {
+ wifi_rtt_capabilities rtt_caps;
+ wifi_error status =
+ global_func_table_.wifi_get_rtt_capabilities(getIfaceHandle(iface_name), &rtt_caps);
+ return {status, rtt_caps};
+}
+
+std::pair<wifi_error, wifi_rtt_responder> WifiLegacyHal::getRttResponderInfo(
+ const std::string& iface_name) {
+ wifi_rtt_responder rtt_responder;
+ wifi_error status = global_func_table_.wifi_rtt_get_responder_info(getIfaceHandle(iface_name),
+ &rtt_responder);
+ return {status, rtt_responder};
+}
+
+wifi_error WifiLegacyHal::enableRttResponder(const std::string& iface_name, wifi_request_id id,
+ const wifi_channel_info& channel_hint,
+ uint32_t max_duration_secs,
+ const wifi_rtt_responder& info) {
+ wifi_rtt_responder info_internal(info);
+ return global_func_table_.wifi_enable_responder(id, getIfaceHandle(iface_name), channel_hint,
+ max_duration_secs, &info_internal);
+}
+
+wifi_error WifiLegacyHal::disableRttResponder(const std::string& iface_name, wifi_request_id id) {
+ return global_func_table_.wifi_disable_responder(id, getIfaceHandle(iface_name));
+}
+
+wifi_error WifiLegacyHal::setRttLci(const std::string& iface_name, wifi_request_id id,
+ const wifi_lci_information& info) {
+ wifi_lci_information info_internal(info);
+ return global_func_table_.wifi_set_lci(id, getIfaceHandle(iface_name), &info_internal);
+}
+
+wifi_error WifiLegacyHal::setRttLcr(const std::string& iface_name, wifi_request_id id,
+ const wifi_lcr_information& info) {
+ wifi_lcr_information info_internal(info);
+ return global_func_table_.wifi_set_lcr(id, getIfaceHandle(iface_name), &info_internal);
+}
+
+wifi_error WifiLegacyHal::nanRegisterCallbackHandlers(const std::string& iface_name,
+ const NanCallbackHandlers& user_callbacks) {
+ on_nan_notify_response_user_callback = user_callbacks.on_notify_response;
+ on_nan_event_publish_terminated_user_callback = user_callbacks.on_event_publish_terminated;
+ on_nan_event_match_user_callback = user_callbacks.on_event_match;
+ on_nan_event_match_expired_user_callback = user_callbacks.on_event_match_expired;
+ on_nan_event_subscribe_terminated_user_callback = user_callbacks.on_event_subscribe_terminated;
+ on_nan_event_followup_user_callback = user_callbacks.on_event_followup;
+ on_nan_event_disc_eng_event_user_callback = user_callbacks.on_event_disc_eng_event;
+ on_nan_event_disabled_user_callback = user_callbacks.on_event_disabled;
+ on_nan_event_tca_user_callback = user_callbacks.on_event_tca;
+ on_nan_event_beacon_sdf_payload_user_callback = user_callbacks.on_event_beacon_sdf_payload;
+ on_nan_event_data_path_request_user_callback = user_callbacks.on_event_data_path_request;
+ on_nan_event_data_path_confirm_user_callback = user_callbacks.on_event_data_path_confirm;
+ on_nan_event_data_path_end_user_callback = user_callbacks.on_event_data_path_end;
+ on_nan_event_transmit_follow_up_user_callback = user_callbacks.on_event_transmit_follow_up;
+ on_nan_event_range_request_user_callback = user_callbacks.on_event_range_request;
+ on_nan_event_range_report_user_callback = user_callbacks.on_event_range_report;
+ on_nan_event_schedule_update_user_callback = user_callbacks.on_event_schedule_update;
+
+ return global_func_table_.wifi_nan_register_handler(
+ getIfaceHandle(iface_name),
+ {onAsyncNanNotifyResponse, onAsyncNanEventPublishReplied,
+ onAsyncNanEventPublishTerminated, onAsyncNanEventMatch, onAsyncNanEventMatchExpired,
+ onAsyncNanEventSubscribeTerminated, onAsyncNanEventFollowup,
+ onAsyncNanEventDiscEngEvent, onAsyncNanEventDisabled, onAsyncNanEventTca,
+ onAsyncNanEventBeaconSdfPayload, onAsyncNanEventDataPathRequest,
+ onAsyncNanEventDataPathConfirm, onAsyncNanEventDataPathEnd,
+ onAsyncNanEventTransmitFollowUp, onAsyncNanEventRangeRequest,
+ onAsyncNanEventRangeReport, onAsyncNanEventScheduleUpdate});
+}
+
+wifi_error WifiLegacyHal::nanEnableRequest(const std::string& iface_name, transaction_id id,
+ const NanEnableRequest& msg) {
+ NanEnableRequest msg_internal(msg);
+ return global_func_table_.wifi_nan_enable_request(id, getIfaceHandle(iface_name),
+ &msg_internal);
+}
+
+wifi_error WifiLegacyHal::nanDisableRequest(const std::string& iface_name, transaction_id id) {
+ return global_func_table_.wifi_nan_disable_request(id, getIfaceHandle(iface_name));
+}
+
+wifi_error WifiLegacyHal::nanPublishRequest(const std::string& iface_name, transaction_id id,
+ const NanPublishRequest& msg) {
+ NanPublishRequest msg_internal(msg);
+ return global_func_table_.wifi_nan_publish_request(id, getIfaceHandle(iface_name),
+ &msg_internal);
+}
+
+wifi_error WifiLegacyHal::nanPublishCancelRequest(const std::string& iface_name, transaction_id id,
+ const NanPublishCancelRequest& msg) {
+ NanPublishCancelRequest msg_internal(msg);
+ return global_func_table_.wifi_nan_publish_cancel_request(id, getIfaceHandle(iface_name),
+ &msg_internal);
+}
+
+wifi_error WifiLegacyHal::nanSubscribeRequest(const std::string& iface_name, transaction_id id,
+ const NanSubscribeRequest& msg) {
+ NanSubscribeRequest msg_internal(msg);
+ return global_func_table_.wifi_nan_subscribe_request(id, getIfaceHandle(iface_name),
+ &msg_internal);
+}
+
+wifi_error WifiLegacyHal::nanSubscribeCancelRequest(const std::string& iface_name,
+ transaction_id id,
+ const NanSubscribeCancelRequest& msg) {
+ NanSubscribeCancelRequest msg_internal(msg);
+ return global_func_table_.wifi_nan_subscribe_cancel_request(id, getIfaceHandle(iface_name),
+ &msg_internal);
+}
+
+wifi_error WifiLegacyHal::nanTransmitFollowupRequest(const std::string& iface_name,
+ transaction_id id,
+ const NanTransmitFollowupRequest& msg) {
+ NanTransmitFollowupRequest msg_internal(msg);
+ return global_func_table_.wifi_nan_transmit_followup_request(id, getIfaceHandle(iface_name),
+ &msg_internal);
+}
+
+wifi_error WifiLegacyHal::nanStatsRequest(const std::string& iface_name, transaction_id id,
+ const NanStatsRequest& msg) {
+ NanStatsRequest msg_internal(msg);
+ return global_func_table_.wifi_nan_stats_request(id, getIfaceHandle(iface_name), &msg_internal);
+}
+
+wifi_error WifiLegacyHal::nanConfigRequest(const std::string& iface_name, transaction_id id,
+ const NanConfigRequest& msg) {
+ NanConfigRequest msg_internal(msg);
+ return global_func_table_.wifi_nan_config_request(id, getIfaceHandle(iface_name),
+ &msg_internal);
+}
+
+wifi_error WifiLegacyHal::nanTcaRequest(const std::string& iface_name, transaction_id id,
+ const NanTCARequest& msg) {
+ NanTCARequest msg_internal(msg);
+ return global_func_table_.wifi_nan_tca_request(id, getIfaceHandle(iface_name), &msg_internal);
+}
+
+wifi_error WifiLegacyHal::nanBeaconSdfPayloadRequest(const std::string& iface_name,
+ transaction_id id,
+ const NanBeaconSdfPayloadRequest& msg) {
+ NanBeaconSdfPayloadRequest msg_internal(msg);
+ return global_func_table_.wifi_nan_beacon_sdf_payload_request(id, getIfaceHandle(iface_name),
+ &msg_internal);
+}
+
+std::pair<wifi_error, NanVersion> WifiLegacyHal::nanGetVersion() {
+ NanVersion version;
+ wifi_error status = global_func_table_.wifi_nan_get_version(global_handle_, &version);
+ return {status, version};
+}
+
+wifi_error WifiLegacyHal::nanGetCapabilities(const std::string& iface_name, transaction_id id) {
+ return global_func_table_.wifi_nan_get_capabilities(id, getIfaceHandle(iface_name));
+}
+
+wifi_error WifiLegacyHal::nanDataInterfaceCreate(const std::string& iface_name, transaction_id id,
+ const std::string& data_iface_name) {
+ return global_func_table_.wifi_nan_data_interface_create(id, getIfaceHandle(iface_name),
+ makeCharVec(data_iface_name).data());
+}
+
+wifi_error WifiLegacyHal::nanDataInterfaceDelete(const std::string& iface_name, transaction_id id,
+ const std::string& data_iface_name) {
+ return global_func_table_.wifi_nan_data_interface_delete(id, getIfaceHandle(iface_name),
+ makeCharVec(data_iface_name).data());
+}
+
+wifi_error WifiLegacyHal::nanDataRequestInitiator(const std::string& iface_name, transaction_id id,
+ const NanDataPathInitiatorRequest& msg) {
+ NanDataPathInitiatorRequest msg_internal(msg);
+ return global_func_table_.wifi_nan_data_request_initiator(id, getIfaceHandle(iface_name),
+ &msg_internal);
+}
+
+wifi_error WifiLegacyHal::nanDataIndicationResponse(const std::string& iface_name,
+ transaction_id id,
+ const NanDataPathIndicationResponse& msg) {
+ NanDataPathIndicationResponse msg_internal(msg);
+ return global_func_table_.wifi_nan_data_indication_response(id, getIfaceHandle(iface_name),
+ &msg_internal);
+}
+
+typedef struct {
+ u8 num_ndp_instances;
+ NanDataPathId ndp_instance_id;
+} NanDataPathEndSingleNdpIdRequest;
+
+wifi_error WifiLegacyHal::nanDataEnd(const std::string& iface_name, transaction_id id,
+ uint32_t ndpInstanceId) {
+ NanDataPathEndSingleNdpIdRequest msg;
+ msg.num_ndp_instances = 1;
+ msg.ndp_instance_id = ndpInstanceId;
+ wifi_error status = global_func_table_.wifi_nan_data_end(id, getIfaceHandle(iface_name),
+ (NanDataPathEndRequest*)&msg);
+ return status;
+}
+
+wifi_error WifiLegacyHal::setCountryCode(const std::string& iface_name,
+ const std::array<uint8_t, 2> code) {
+ std::string code_str(code.data(), code.data() + code.size());
+ return global_func_table_.wifi_set_country_code(getIfaceHandle(iface_name), code_str.c_str());
+}
+
+wifi_error WifiLegacyHal::retrieveIfaceHandles() {
+ wifi_interface_handle* iface_handles = nullptr;
+ int num_iface_handles = 0;
+ wifi_error status =
+ global_func_table_.wifi_get_ifaces(global_handle_, &num_iface_handles, &iface_handles);
+ if (status != WIFI_SUCCESS) {
+ 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(iface_handles[i], iface_name_arr.data(),
+ iface_name_arr.size());
+ if (status != WIFI_SUCCESS) {
+ LOG(WARNING) << "Failed to get interface handle name";
+ continue;
+ }
+ // Assuming the interface name is null terminated since the legacy HAL
+ // API does not return a size.
+ std::string iface_name(iface_name_arr.data());
+ LOG(INFO) << "Adding interface handle for " << iface_name;
+ iface_name_to_handle_[iface_name] = iface_handles[i];
+ }
+ return WIFI_SUCCESS;
+}
+
+wifi_interface_handle WifiLegacyHal::getIfaceHandle(const std::string& iface_name) {
+ const auto iface_handle_iter = iface_name_to_handle_.find(iface_name);
+ if (iface_handle_iter == iface_name_to_handle_.end()) {
+ LOG(ERROR) << "Unknown iface name: " << iface_name;
+ return nullptr;
+ }
+ return iface_handle_iter->second;
+}
+
+void WifiLegacyHal::runEventLoop() {
+ LOG(DEBUG) << "Starting legacy HAL event loop";
+ global_func_table_.wifi_event_loop(global_handle_);
+ const auto lock = aidl_sync_util::acquireGlobalLock();
+ if (!awaiting_event_loop_termination_) {
+ LOG(FATAL) << "Legacy HAL event loop terminated, but HAL was not stopping";
+ }
+ LOG(DEBUG) << "Legacy HAL event loop terminated";
+ awaiting_event_loop_termination_ = false;
+ stop_wait_cv_.notify_one();
+}
+
+std::pair<wifi_error, std::vector<wifi_cached_scan_results>> WifiLegacyHal::getGscanCachedResults(
+ const std::string& iface_name) {
+ std::vector<wifi_cached_scan_results> cached_scan_results;
+ cached_scan_results.resize(kMaxCachedGscanResults);
+ int32_t num_results = 0;
+ wifi_error status = global_func_table_.wifi_get_cached_gscan_results(
+ getIfaceHandle(iface_name), true /* always flush */, cached_scan_results.size(),
+ cached_scan_results.data(), &num_results);
+ CHECK(num_results >= 0 && static_cast<uint32_t>(num_results) <= kMaxCachedGscanResults);
+ cached_scan_results.resize(num_results);
+ // Check for invalid IE lengths in these cached scan results and correct it.
+ for (auto& cached_scan_result : cached_scan_results) {
+ int num_scan_results = cached_scan_result.num_results;
+ for (int i = 0; i < num_scan_results; i++) {
+ auto& scan_result = cached_scan_result.results[i];
+ if (scan_result.ie_length > 0) {
+ LOG(DEBUG) << "Cached scan result has non-zero IE length " << scan_result.ie_length;
+ scan_result.ie_length = 0;
+ }
+ }
+ }
+ 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;
+}
+
+wifi_error WifiLegacyHal::getSupportedIfaceName(uint32_t iface_type, std::string& ifname) {
+ std::array<char, IFNAMSIZ> buffer;
+
+ wifi_error res = global_func_table_.wifi_get_supported_iface_name(
+ global_handle_, (uint32_t)iface_type, buffer.data(), buffer.size());
+ if (res == WIFI_SUCCESS) ifname = buffer.data();
+
+ return res;
+}
+
+wifi_error WifiLegacyHal::multiStaSetPrimaryConnection(const std::string& ifname) {
+ return global_func_table_.wifi_multi_sta_set_primary_connection(global_handle_,
+ getIfaceHandle(ifname));
+}
+
+wifi_error WifiLegacyHal::multiStaSetUseCase(wifi_multi_sta_use_case use_case) {
+ return global_func_table_.wifi_multi_sta_set_use_case(global_handle_, use_case);
+}
+
+wifi_error WifiLegacyHal::setCoexUnsafeChannels(
+ std::vector<wifi_coex_unsafe_channel> unsafe_channels, uint32_t restrictions) {
+ return global_func_table_.wifi_set_coex_unsafe_channels(global_handle_, unsafe_channels.size(),
+ unsafe_channels.data(), restrictions);
+}
+
+wifi_error WifiLegacyHal::setVoipMode(const std::string& iface_name, wifi_voip_mode mode) {
+ return global_func_table_.wifi_set_voip_mode(getIfaceHandle(iface_name), mode);
+}
+
+wifi_error WifiLegacyHal::twtRegisterHandler(const std::string& iface_name,
+ const TwtCallbackHandlers& user_callbacks) {
+ on_twt_event_setup_response_callback = user_callbacks.on_setup_response;
+ on_twt_event_teardown_completion_callback = user_callbacks.on_teardown_completion;
+ on_twt_event_info_frame_received_callback = user_callbacks.on_info_frame_received;
+ on_twt_event_device_notify_callback = user_callbacks.on_device_notify;
+
+ return global_func_table_.wifi_twt_register_handler(
+ getIfaceHandle(iface_name),
+ {onAsyncTwtEventSetupResponse, onAsyncTwtEventTeardownCompletion,
+ onAsyncTwtEventInfoFrameReceived, onAsyncTwtEventDeviceNotify});
+}
+
+std::pair<wifi_error, TwtCapabilitySet> WifiLegacyHal::twtGetCapability(
+ const std::string& iface_name) {
+ TwtCapabilitySet capSet;
+ wifi_error status =
+ global_func_table_.wifi_twt_get_capability(getIfaceHandle(iface_name), &capSet);
+ return {status, capSet};
+}
+
+wifi_error WifiLegacyHal::twtSetupRequest(const std::string& iface_name,
+ const TwtSetupRequest& msg) {
+ TwtSetupRequest msgInternal(msg);
+ return global_func_table_.wifi_twt_setup_request(getIfaceHandle(iface_name), &msgInternal);
+}
+
+wifi_error WifiLegacyHal::twtTearDownRequest(const std::string& iface_name,
+ const TwtTeardownRequest& msg) {
+ TwtTeardownRequest msgInternal(msg);
+ return global_func_table_.wifi_twt_teardown_request(getIfaceHandle(iface_name), &msgInternal);
+}
+
+wifi_error WifiLegacyHal::twtInfoFrameRequest(const std::string& iface_name,
+ const TwtInfoFrameRequest& msg) {
+ TwtInfoFrameRequest msgInternal(msg);
+ return global_func_table_.wifi_twt_info_frame_request(getIfaceHandle(iface_name), &msgInternal);
+}
+
+std::pair<wifi_error, TwtStats> WifiLegacyHal::twtGetStats(const std::string& iface_name,
+ uint8_t configId) {
+ TwtStats stats;
+ wifi_error status =
+ global_func_table_.wifi_twt_get_stats(getIfaceHandle(iface_name), configId, &stats);
+ return {status, stats};
+}
+
+wifi_error WifiLegacyHal::twtClearStats(const std::string& iface_name, uint8_t configId) {
+ return global_func_table_.wifi_twt_clear_stats(getIfaceHandle(iface_name), configId);
+}
+
+wifi_error WifiLegacyHal::setDtimConfig(const std::string& iface_name, uint32_t multiplier) {
+ return global_func_table_.wifi_set_dtim_config(getIfaceHandle(iface_name), multiplier);
+}
+
+std::pair<wifi_error, std::vector<wifi_usable_channel>> WifiLegacyHal::getUsableChannels(
+ uint32_t band_mask, uint32_t iface_mode_mask, uint32_t filter_mask) {
+ std::vector<wifi_usable_channel> channels;
+ channels.resize(kMaxWifiUsableChannels);
+ uint32_t size = 0;
+ wifi_error status = global_func_table_.wifi_get_usable_channels(
+ global_handle_, band_mask, iface_mode_mask, filter_mask, channels.size(), &size,
+ reinterpret_cast<wifi_usable_channel*>(channels.data()));
+ CHECK(size >= 0 && size <= kMaxWifiUsableChannels);
+ channels.resize(size);
+ return {status, std::move(channels)};
+}
+
+wifi_error WifiLegacyHal::triggerSubsystemRestart() {
+ return global_func_table_.wifi_trigger_subsystem_restart(global_handle_);
+}
+
+wifi_error WifiLegacyHal::setIndoorState(bool isIndoor) {
+ return global_func_table_.wifi_set_indoor_state(global_handle_, isIndoor);
+}
+
+std::pair<wifi_error, wifi_radio_combination_matrix*>
+WifiLegacyHal::getSupportedRadioCombinationsMatrix() {
+ char* buffer = new char[kMaxSupportedRadioCombinationsMatrixLength];
+ std::fill(buffer, buffer + kMaxSupportedRadioCombinationsMatrixLength, 0);
+ uint32_t size = 0;
+ wifi_radio_combination_matrix* radio_combination_matrix_ptr =
+ reinterpret_cast<wifi_radio_combination_matrix*>(buffer);
+ wifi_error status = global_func_table_.wifi_get_supported_radio_combinations_matrix(
+ global_handle_, kMaxSupportedRadioCombinationsMatrixLength, &size,
+ radio_combination_matrix_ptr);
+ CHECK(size >= 0 && size <= kMaxSupportedRadioCombinationsMatrixLength);
+ return {status, radio_combination_matrix_ptr};
+}
+
+wifi_error WifiLegacyHal::chreNanRttRequest(const std::string& iface_name, bool enable) {
+ if (enable)
+ return global_func_table_.wifi_nan_rtt_chre_enable_request(0, getIfaceHandle(iface_name),
+ NULL);
+ else
+ return global_func_table_.wifi_nan_rtt_chre_disable_request(0, getIfaceHandle(iface_name));
+}
+
+wifi_error WifiLegacyHal::chreRegisterHandler(const std::string& iface_name,
+ const ChreCallbackHandlers& handler) {
+ if (on_chre_nan_rtt_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+
+ on_chre_nan_rtt_internal_callback = handler.on_wifi_chre_nan_rtt_state;
+
+ wifi_error status = global_func_table_.wifi_chre_register_handler(getIfaceHandle(iface_name),
+ {onAsyncChreNanRttState});
+ if (status != WIFI_SUCCESS) {
+ on_chre_nan_rtt_internal_callback = nullptr;
+ }
+ return status;
+}
+
+wifi_error WifiLegacyHal::enableWifiTxPowerLimits(const std::string& iface_name, bool enable) {
+ return global_func_table_.wifi_enable_tx_power_limits(getIfaceHandle(iface_name), enable);
+}
+
+wifi_error WifiLegacyHal::getWifiCachedScanResults(
+ const std::string& iface_name, const CachedScanResultsCallbackHandlers& handler) {
+ on_cached_scan_results_internal_callback = handler.on_cached_scan_results;
+
+ wifi_error status = global_func_table_.wifi_get_cached_scan_results(getIfaceHandle(iface_name),
+ {onSyncCachedScanResults});
+
+ on_cached_scan_results_internal_callback = nullptr;
+ return status;
+}
+
+void WifiLegacyHal::invalidate() {
+ global_handle_ = nullptr;
+ iface_name_to_handle_.clear();
+ on_driver_memory_dump_internal_callback = nullptr;
+ on_firmware_memory_dump_internal_callback = nullptr;
+ on_gscan_event_internal_callback = nullptr;
+ on_gscan_full_result_internal_callback = nullptr;
+ on_link_layer_stats_result_internal_callback = nullptr;
+ on_rssi_threshold_breached_internal_callback = nullptr;
+ on_ring_buffer_data_internal_callback = nullptr;
+ on_error_alert_internal_callback = nullptr;
+ on_radio_mode_change_internal_callback = nullptr;
+ on_subsystem_restart_internal_callback = nullptr;
+ on_rtt_results_internal_callback = nullptr;
+ on_nan_notify_response_user_callback = nullptr;
+ on_nan_event_publish_terminated_user_callback = nullptr;
+ on_nan_event_match_user_callback = nullptr;
+ on_nan_event_match_expired_user_callback = nullptr;
+ on_nan_event_subscribe_terminated_user_callback = nullptr;
+ on_nan_event_followup_user_callback = nullptr;
+ on_nan_event_disc_eng_event_user_callback = nullptr;
+ on_nan_event_disabled_user_callback = nullptr;
+ on_nan_event_tca_user_callback = nullptr;
+ on_nan_event_beacon_sdf_payload_user_callback = nullptr;
+ on_nan_event_data_path_request_user_callback = nullptr;
+ on_nan_event_data_path_confirm_user_callback = nullptr;
+ on_nan_event_data_path_end_user_callback = nullptr;
+ on_nan_event_transmit_follow_up_user_callback = nullptr;
+ on_nan_event_range_request_user_callback = nullptr;
+ on_nan_event_range_report_user_callback = nullptr;
+ on_nan_event_schedule_update_user_callback = nullptr;
+ on_twt_event_setup_response_callback = nullptr;
+ on_twt_event_teardown_completion_callback = nullptr;
+ on_twt_event_info_frame_received_callback = nullptr;
+ on_twt_event_device_notify_callback = nullptr;
+ on_chre_nan_rtt_internal_callback = nullptr;
+}
+
+} // namespace legacy_hal
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi_legacy_hal.h b/wifi/aidl/default/wifi_legacy_hal.h
new file mode 100644
index 0000000..cd8c0b1
--- /dev/null
+++ b/wifi/aidl/default/wifi_legacy_hal.h
@@ -0,0 +1,738 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_LEGACY_HAL_H_
+#define WIFI_LEGACY_HAL_H_
+
+#include <hardware_legacy/wifi_hal.h>
+#include <wifi_system/interface_tool.h>
+
+#include <condition_variable>
+#include <functional>
+#include <map>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+// This is in a separate namespace to prevent typename conflicts between
+// the legacy HAL types and the AIDL interface types.
+namespace legacy_hal {
+// Import all the types defined inside the legacy HAL header files into this
+// namespace.
+using ::chre_nan_rtt_state;
+using ::frame_info;
+using ::frame_type;
+using ::FRAME_TYPE_80211_MGMT;
+using ::FRAME_TYPE_ETHERNET_II;
+using ::FRAME_TYPE_UNKNOWN;
+using ::fw_roaming_state_t;
+using ::mac_addr;
+using ::NAN_CHANNEL_24G_BAND;
+using ::NAN_CHANNEL_5G_BAND_HIGH;
+using ::NAN_CHANNEL_5G_BAND_LOW;
+using ::NAN_DISABLE_RANGE_REPORT;
+using ::NAN_DO_NOT_USE_SRF;
+using ::NAN_DP_CHANNEL_NOT_REQUESTED;
+using ::NAN_DP_CONFIG_NO_SECURITY;
+using ::NAN_DP_CONFIG_SECURITY;
+using ::NAN_DP_END;
+using ::NAN_DP_FORCE_CHANNEL_SETUP;
+using ::NAN_DP_INITIATOR_RESPONSE;
+using ::NAN_DP_INTERFACE_CREATE;
+using ::NAN_DP_INTERFACE_DELETE;
+using ::NAN_DP_REQUEST_ACCEPT;
+using ::NAN_DP_REQUEST_CHANNEL_SETUP;
+using ::NAN_DP_REQUEST_REJECT;
+using ::NAN_DP_RESPONDER_RESPONSE;
+using ::NAN_GET_CAPABILITIES;
+using ::NAN_MATCH_ALG_MATCH_CONTINUOUS;
+using ::NAN_MATCH_ALG_MATCH_NEVER;
+using ::NAN_MATCH_ALG_MATCH_ONCE;
+using ::NAN_PUBLISH_TYPE_SOLICITED;
+using ::NAN_PUBLISH_TYPE_UNSOLICITED;
+using ::NAN_PUBLISH_TYPE_UNSOLICITED_SOLICITED;
+using ::NAN_RANGING_AUTO_RESPONSE_DISABLE;
+using ::NAN_RANGING_AUTO_RESPONSE_ENABLE;
+using ::NAN_RANGING_DISABLE;
+using ::NAN_RANGING_ENABLE;
+using ::NAN_RESPONSE_BEACON_SDF_PAYLOAD;
+using ::NAN_RESPONSE_CONFIG;
+using ::NAN_RESPONSE_DISABLED;
+using ::NAN_RESPONSE_ENABLED;
+using ::NAN_RESPONSE_ERROR;
+using ::NAN_RESPONSE_PUBLISH;
+using ::NAN_RESPONSE_PUBLISH_CANCEL;
+using ::NAN_RESPONSE_STATS;
+using ::NAN_RESPONSE_SUBSCRIBE;
+using ::NAN_RESPONSE_SUBSCRIBE_CANCEL;
+using ::NAN_RESPONSE_TCA;
+using ::NAN_RESPONSE_TRANSMIT_FOLLOWUP;
+using ::NAN_SECURITY_KEY_INPUT_PASSPHRASE;
+using ::NAN_SECURITY_KEY_INPUT_PMK;
+using ::NAN_SERVICE_ACCEPT_POLICY_ALL;
+using ::NAN_SERVICE_ACCEPT_POLICY_NONE;
+using ::NAN_SRF_ATTR_BLOOM_FILTER;
+using ::NAN_SRF_ATTR_PARTIAL_MAC_ADDR;
+using ::NAN_SRF_INCLUDE_DO_NOT_RESPOND;
+using ::NAN_SRF_INCLUDE_RESPOND;
+using ::NAN_SSI_NOT_REQUIRED_IN_MATCH_IND;
+using ::NAN_SSI_REQUIRED_IN_MATCH_IND;
+using ::NAN_STATUS_ALREADY_ENABLED;
+using ::NAN_STATUS_FOLLOWUP_QUEUE_FULL;
+using ::NAN_STATUS_INTERNAL_FAILURE;
+using ::NAN_STATUS_INVALID_NDP_ID;
+using ::NAN_STATUS_INVALID_PARAM;
+using ::NAN_STATUS_INVALID_PUBLISH_SUBSCRIBE_ID;
+using ::NAN_STATUS_INVALID_REQUESTOR_INSTANCE_ID;
+using ::NAN_STATUS_NAN_NOT_ALLOWED;
+using ::NAN_STATUS_NO_OTA_ACK;
+using ::NAN_STATUS_NO_RESOURCE_AVAILABLE;
+using ::NAN_STATUS_PROTOCOL_FAILURE;
+using ::NAN_STATUS_SUCCESS;
+using ::NAN_STATUS_UNSUPPORTED_CONCURRENCY_NAN_DISABLED;
+using ::NAN_SUBSCRIBE_TYPE_ACTIVE;
+using ::NAN_SUBSCRIBE_TYPE_PASSIVE;
+using ::NAN_TRANSMIT_IN_DW;
+using ::NAN_TRANSMIT_IN_FAW;
+using ::NAN_TX_PRIORITY_HIGH;
+using ::NAN_TX_PRIORITY_NORMAL;
+using ::NAN_TX_TYPE_BROADCAST;
+using ::NAN_TX_TYPE_UNICAST;
+using ::NAN_USE_SRF;
+using ::NanBeaconSdfPayloadInd;
+using ::NanCapabilities;
+using ::NanChannelInfo;
+using ::NanConfigRequest;
+using ::NanDataPathChannelCfg;
+using ::NanDataPathConfirmInd;
+using ::NanDataPathEndInd;
+using ::NanDataPathIndicationResponse;
+using ::NanDataPathInitiatorRequest;
+using ::NanDataPathRequestInd;
+using ::NanDataPathScheduleUpdateInd;
+using ::NanDisabledInd;
+using ::NanDiscEngEventInd;
+using ::NanEnableRequest;
+using ::NanFollowupInd;
+using ::NanMatchAlg;
+using ::NanMatchExpiredInd;
+using ::NanMatchInd;
+using ::NanPublishCancelRequest;
+using ::NanPublishRequest;
+using ::NanPublishTerminatedInd;
+using ::NanPublishType;
+using ::NanRangeReportInd;
+using ::NanRangeRequestInd;
+using ::NanResponseMsg;
+using ::NanSRFType;
+using ::NanStatusType;
+using ::NanSubscribeCancelRequest;
+using ::NanSubscribeRequest;
+using ::NanSubscribeTerminatedInd;
+using ::NanSubscribeType;
+using ::NanTransmitFollowupInd;
+using ::NanTransmitFollowupRequest;
+using ::NanTxType;
+using ::ROAMING_DISABLE;
+using ::ROAMING_ENABLE;
+using ::RTT_PEER_AP;
+using ::RTT_PEER_NAN;
+using ::RTT_PEER_P2P_CLIENT;
+using ::RTT_PEER_P2P_GO;
+using ::RTT_PEER_STA;
+using ::rtt_peer_type;
+using ::RTT_STATUS_ABORTED;
+using ::RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL;
+using ::RTT_STATUS_FAIL_BUSY_TRY_LATER;
+using ::RTT_STATUS_FAIL_FTM_PARAM_OVERRIDE;
+using ::RTT_STATUS_FAIL_INVALID_TS;
+using ::RTT_STATUS_FAIL_NO_CAPABILITY;
+using ::RTT_STATUS_FAIL_NO_RSP;
+using ::RTT_STATUS_FAIL_NOT_SCHEDULED_YET;
+using ::RTT_STATUS_FAIL_PROTOCOL;
+using ::RTT_STATUS_FAIL_REJECTED;
+using ::RTT_STATUS_FAIL_SCHEDULE;
+using ::RTT_STATUS_FAIL_TM_TIMEOUT;
+using ::RTT_STATUS_FAILURE;
+using ::RTT_STATUS_INVALID_REQ;
+using ::RTT_STATUS_NAN_RANGING_CONCURRENCY_NOT_SUPPORTED;
+using ::RTT_STATUS_NAN_RANGING_PROTOCOL_FAILURE;
+using ::RTT_STATUS_NO_WIFI;
+using ::RTT_STATUS_SUCCESS;
+using ::RTT_TYPE_1_SIDED;
+using ::RTT_TYPE_2_SIDED;
+using ::RX_PKT_FATE_DRV_DROP_FILTER;
+using ::RX_PKT_FATE_DRV_DROP_INVALID;
+using ::RX_PKT_FATE_DRV_DROP_NOBUFS;
+using ::RX_PKT_FATE_DRV_DROP_OTHER;
+using ::RX_PKT_FATE_DRV_QUEUED;
+using ::RX_PKT_FATE_FW_DROP_FILTER;
+using ::RX_PKT_FATE_FW_DROP_INVALID;
+using ::RX_PKT_FATE_FW_DROP_NOBUFS;
+using ::RX_PKT_FATE_FW_DROP_OTHER;
+using ::RX_PKT_FATE_FW_QUEUED;
+using ::RX_PKT_FATE_SUCCESS;
+using ::ssid_t;
+using ::transaction_id;
+using ::TX_PKT_FATE_ACKED;
+using ::TX_PKT_FATE_DRV_DROP_INVALID;
+using ::TX_PKT_FATE_DRV_DROP_NOBUFS;
+using ::TX_PKT_FATE_DRV_DROP_OTHER;
+using ::TX_PKT_FATE_DRV_QUEUED;
+using ::TX_PKT_FATE_FW_DROP_INVALID;
+using ::TX_PKT_FATE_FW_DROP_NOBUFS;
+using ::TX_PKT_FATE_FW_DROP_OTHER;
+using ::TX_PKT_FATE_FW_QUEUED;
+using ::TX_PKT_FATE_SENT;
+using ::WIFI_AC_BE;
+using ::WIFI_AC_BK;
+using ::WIFI_AC_VI;
+using ::WIFI_AC_VO;
+using ::WIFI_ANTENNA_1X1;
+using ::WIFI_ANTENNA_2X2;
+using ::WIFI_ANTENNA_3X3;
+using ::WIFI_ANTENNA_4X4;
+using ::WIFI_ANTENNA_UNSPECIFIED;
+using ::wifi_band;
+using ::WIFI_BAND_A;
+using ::WIFI_BAND_A_DFS;
+using ::WIFI_BAND_A_WITH_DFS;
+using ::WIFI_BAND_ABG;
+using ::WIFI_BAND_ABG_WITH_DFS;
+using ::WIFI_BAND_BG;
+using ::WIFI_BAND_UNSPECIFIED;
+using ::wifi_cached_scan_report;
+using ::wifi_cached_scan_results;
+using ::WIFI_CHAN_WIDTH_10;
+using ::WIFI_CHAN_WIDTH_160;
+using ::WIFI_CHAN_WIDTH_20;
+using ::WIFI_CHAN_WIDTH_320;
+using ::WIFI_CHAN_WIDTH_40;
+using ::WIFI_CHAN_WIDTH_5;
+using ::WIFI_CHAN_WIDTH_80;
+using ::WIFI_CHAN_WIDTH_80P80;
+using ::WIFI_CHAN_WIDTH_INVALID;
+using ::wifi_channel_info;
+using ::wifi_channel_stat;
+using ::wifi_channel_width;
+using ::wifi_coex_restriction;
+using ::wifi_coex_unsafe_channel;
+using ::WIFI_DUAL_STA_NON_TRANSIENT_UNBIASED;
+using ::WIFI_DUAL_STA_TRANSIENT_PREFER_PRIMARY;
+using ::wifi_error;
+using ::WIFI_ERROR_BUSY;
+using ::WIFI_ERROR_INVALID_ARGS;
+using ::WIFI_ERROR_INVALID_REQUEST_ID;
+using ::WIFI_ERROR_NONE;
+using ::WIFI_ERROR_NOT_AVAILABLE;
+using ::WIFI_ERROR_NOT_SUPPORTED;
+using ::WIFI_ERROR_OUT_OF_MEMORY;
+using ::WIFI_ERROR_TIMED_OUT;
+using ::WIFI_ERROR_TOO_MANY_REQUESTS;
+using ::WIFI_ERROR_UNINITIALIZED;
+using ::WIFI_ERROR_UNKNOWN;
+using ::wifi_gscan_capabilities;
+using ::wifi_hal_fn;
+using ::wifi_information_element;
+using ::WIFI_INTERFACE_IBSS;
+using ::WIFI_INTERFACE_MESH;
+using ::wifi_interface_mode;
+using ::WIFI_INTERFACE_NAN;
+using ::WIFI_INTERFACE_P2P_CLIENT;
+using ::WIFI_INTERFACE_P2P_GO;
+using ::WIFI_INTERFACE_SOFTAP;
+using ::WIFI_INTERFACE_STA;
+using ::WIFI_INTERFACE_TDLS;
+using ::wifi_interface_type;
+using ::WIFI_INTERFACE_TYPE_AP;
+using ::WIFI_INTERFACE_TYPE_NAN;
+using ::WIFI_INTERFACE_TYPE_P2P;
+using ::WIFI_INTERFACE_TYPE_STA;
+using ::WIFI_INTERFACE_UNKNOWN;
+using ::wifi_latency_mode;
+using ::WIFI_LATENCY_MODE_LOW;
+using ::WIFI_LATENCY_MODE_NORMAL;
+using ::wifi_lci_information;
+using ::wifi_lcr_information;
+using ::WIFI_LOGGER_CONNECT_EVENT_SUPPORTED;
+using ::WIFI_LOGGER_DRIVER_DUMP_SUPPORTED;
+using ::WIFI_LOGGER_MEMORY_DUMP_SUPPORTED;
+using ::WIFI_LOGGER_PACKET_FATE_SUPPORTED;
+using ::WIFI_LOGGER_POWER_EVENT_SUPPORTED;
+using ::WIFI_LOGGER_WAKE_LOCK_SUPPORTED;
+using ::WIFI_MOTION_EXPECTED;
+using ::WIFI_MOTION_NOT_EXPECTED;
+using ::wifi_motion_pattern;
+using ::WIFI_MOTION_UNKNOWN;
+using ::wifi_multi_sta_use_case;
+using ::wifi_power_scenario;
+using ::WIFI_POWER_SCENARIO_ON_BODY_CELL_OFF;
+using ::WIFI_POWER_SCENARIO_ON_BODY_CELL_ON;
+using ::WIFI_POWER_SCENARIO_ON_HEAD_CELL_OFF;
+using ::WIFI_POWER_SCENARIO_ON_HEAD_CELL_ON;
+using ::WIFI_POWER_SCENARIO_VOICE_CALL;
+using ::wifi_radio_combination;
+using ::wifi_radio_combination_matrix;
+using ::wifi_radio_configuration;
+using ::wifi_rate;
+using ::wifi_request_id;
+using ::wifi_ring_buffer_status;
+using ::wifi_roaming_capabilities;
+using ::wifi_roaming_config;
+using ::wifi_rtt_bw;
+using ::WIFI_RTT_BW_10;
+using ::WIFI_RTT_BW_160;
+using ::WIFI_RTT_BW_20;
+using ::WIFI_RTT_BW_320;
+using ::WIFI_RTT_BW_40;
+using ::WIFI_RTT_BW_5;
+using ::WIFI_RTT_BW_80;
+using ::wifi_rtt_capabilities;
+using ::wifi_rtt_config;
+using ::wifi_rtt_preamble;
+using ::WIFI_RTT_PREAMBLE_EHT;
+using ::WIFI_RTT_PREAMBLE_HE;
+using ::WIFI_RTT_PREAMBLE_HT;
+using ::WIFI_RTT_PREAMBLE_LEGACY;
+using ::WIFI_RTT_PREAMBLE_VHT;
+using ::wifi_rtt_responder;
+using ::wifi_rtt_result;
+using ::wifi_rtt_status;
+using ::wifi_rtt_type;
+using ::wifi_rx_packet_fate;
+using ::wifi_rx_report;
+using ::wifi_scan_bucket_spec;
+using ::wifi_scan_cmd_params;
+using ::WIFI_SCAN_FLAG_INTERRUPTED;
+using ::wifi_scan_result;
+using ::WIFI_SUCCESS;
+using ::wifi_tx_packet_fate;
+using ::wifi_tx_report;
+using ::wifi_usable_channel;
+using ::WIFI_USABLE_CHANNEL_FILTER_CELLULAR_COEXISTENCE;
+using ::WIFI_USABLE_CHANNEL_FILTER_CONCURRENCY;
+using ::WLAN_MAC_2_4_BAND;
+using ::WLAN_MAC_5_0_BAND;
+using ::WLAN_MAC_60_0_BAND;
+using ::WLAN_MAC_6_0_BAND;
+
+// APF capabilities supported by the iface.
+struct PacketFilterCapabilities {
+ uint32_t version;
+ uint32_t max_len;
+};
+
+// WARNING: We don't care about the variable sized members of either
+// |wifi_iface_stat|, |wifi_radio_stat| structures. So, using the pragma
+// to escape the compiler warnings regarding this.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wgnu-variable-sized-type-not-at-end"
+// The |wifi_radio_stat.tx_time_per_levels| stats is provided as a pointer in
+// |wifi_radio_stat| structure in the legacy HAL API. Separate that out
+// into a separate return element to avoid passing pointers around.
+struct LinkLayerRadioStats {
+ wifi_radio_stat stats;
+ std::vector<uint32_t> tx_time_per_levels;
+ std::vector<wifi_channel_stat> channel_stats;
+};
+
+struct WifiPeerInfo {
+ wifi_peer_info peer_info;
+ std::vector<wifi_rate_stat> rate_stats;
+};
+
+struct LinkLayerStats {
+ wifi_iface_stat iface;
+ std::vector<LinkLayerRadioStats> radios;
+ std::vector<WifiPeerInfo> peers;
+};
+#pragma GCC diagnostic pop
+
+// The |WLAN_DRIVER_WAKE_REASON_CNT.cmd_event_wake_cnt| and
+// |WLAN_DRIVER_WAKE_REASON_CNT.driver_fw_local_wake_cnt| stats is provided
+// as a pointer in |WLAN_DRIVER_WAKE_REASON_CNT| structure in the legacy HAL
+// API. Separate that out into a separate return elements to avoid passing
+// pointers around.
+struct WakeReasonStats {
+ WLAN_DRIVER_WAKE_REASON_CNT wake_reason_cnt;
+ std::vector<uint32_t> cmd_event_wake_cnt;
+ std::vector<uint32_t> driver_fw_local_wake_cnt;
+};
+
+// NAN response and event callbacks struct.
+struct NanCallbackHandlers {
+ // NotifyResponse invoked to notify the status of the Request.
+ std::function<void(transaction_id, const NanResponseMsg&)> on_notify_response;
+ // Various event callbacks.
+ std::function<void(const NanPublishTerminatedInd&)> on_event_publish_terminated;
+ std::function<void(const NanMatchInd&)> on_event_match;
+ std::function<void(const NanMatchExpiredInd&)> on_event_match_expired;
+ std::function<void(const NanSubscribeTerminatedInd&)> on_event_subscribe_terminated;
+ std::function<void(const NanFollowupInd&)> on_event_followup;
+ std::function<void(const NanDiscEngEventInd&)> on_event_disc_eng_event;
+ std::function<void(const NanDisabledInd&)> on_event_disabled;
+ std::function<void(const NanTCAInd&)> on_event_tca;
+ std::function<void(const NanBeaconSdfPayloadInd&)> on_event_beacon_sdf_payload;
+ std::function<void(const NanDataPathRequestInd&)> on_event_data_path_request;
+ std::function<void(const NanDataPathConfirmInd&)> on_event_data_path_confirm;
+ std::function<void(const NanDataPathEndInd&)> on_event_data_path_end;
+ std::function<void(const NanTransmitFollowupInd&)> on_event_transmit_follow_up;
+ std::function<void(const NanRangeRequestInd&)> on_event_range_request;
+ std::function<void(const NanRangeReportInd&)> on_event_range_report;
+ std::function<void(const NanDataPathScheduleUpdateInd&)> on_event_schedule_update;
+};
+
+// Full scan results contain IE info and are hence passed by reference, to
+// preserve the variable length array member |ie_data|. Callee must not retain
+// the pointer.
+using on_gscan_full_result_callback =
+ std::function<void(wifi_request_id, const wifi_scan_result*, uint32_t)>;
+// These scan results don't contain any IE info, so no need to pass by
+// reference.
+using on_gscan_results_callback =
+ std::function<void(wifi_request_id, const std::vector<wifi_cached_scan_results>&)>;
+
+// Invoked when the rssi value breaches the thresholds set.
+using on_rssi_threshold_breached_callback =
+ std::function<void(wifi_request_id, std::array<uint8_t, ETH_ALEN>, int8_t)>;
+
+// Callback for RTT range request results.
+// Rtt results contain IE info and are hence passed by reference, to
+// preserve the |LCI| and |LCR| pointers. Callee must not retain
+// the pointer.
+using on_rtt_results_callback =
+ std::function<void(wifi_request_id, const std::vector<const wifi_rtt_result*>&)>;
+
+// Callback for ring buffer data.
+using on_ring_buffer_data_callback = std::function<void(
+ const std::string&, const std::vector<uint8_t>&, const wifi_ring_buffer_status&)>;
+
+// Callback for alerts.
+using on_error_alert_callback = std::function<void(int32_t, const std::vector<uint8_t>&)>;
+
+// Callback for subsystem restart
+using on_subsystem_restart_callback = std::function<void(const std::string&)>;
+
+// Struct for the mac info from the legacy HAL. This is a cleaner version
+// of the |wifi_mac_info| & |wifi_iface_info|.
+typedef struct {
+ std::string name;
+ wifi_channel channel;
+} WifiIfaceInfo;
+
+typedef struct {
+ uint32_t wlan_mac_id;
+ /* BIT MASK of BIT(WLAN_MAC*) as represented by wlan_mac_band */
+ uint32_t mac_band;
+ /* Represents the connected Wi-Fi interfaces associated with each MAC */
+ std::vector<WifiIfaceInfo> iface_infos;
+} WifiMacInfo;
+
+// Callback for radio mode change
+using on_radio_mode_change_callback = std::function<void(const std::vector<WifiMacInfo>&)>;
+
+// TWT response and event callbacks struct.
+struct TwtCallbackHandlers {
+ // Callback for TWT setup response
+ std::function<void(const TwtSetupResponse&)> on_setup_response;
+ // Callback for TWT teardown completion
+ std::function<void(const TwtTeardownCompletion&)> on_teardown_completion;
+ // Callback for TWT info frame received event
+ std::function<void(const TwtInfoFrameReceived&)> on_info_frame_received;
+ // Callback for TWT notification from the device
+ std::function<void(const TwtDeviceNotify&)> on_device_notify;
+};
+
+// CHRE response and event callbacks struct.
+struct ChreCallbackHandlers {
+ // Callback for CHRE NAN RTT
+ std::function<void(chre_nan_rtt_state)> on_wifi_chre_nan_rtt_state;
+};
+
+// Cached Scan Results response and event callbacks struct.
+struct CachedScanResultsCallbackHandlers {
+ // Callback for Cached Scan Results
+ std::function<void(wifi_cached_scan_report*)> on_cached_scan_results;
+};
+
+/**
+ * Class that encapsulates all legacy HAL interactions.
+ * This class manages the lifetime of the event loop thread used by legacy HAL.
+ *
+ * Note: There will only be a single instance of this class created in the Wifi
+ * object and will be valid for the lifetime of the process.
+ */
+class WifiLegacyHal {
+ public:
+ WifiLegacyHal(const std::weak_ptr<::android::wifi_system::InterfaceTool> iface_tool,
+ const wifi_hal_fn& fn, bool is_primary);
+ virtual ~WifiLegacyHal() = default;
+
+ // Initialize the legacy HAL function table.
+ virtual wifi_error initialize();
+ // Start the legacy HAL and the event looper thread.
+ virtual wifi_error start();
+ // Deinitialize the legacy HAL and wait for the event loop thread to exit
+ // using a predefined timeout.
+ virtual wifi_error stop(std::unique_lock<std::recursive_mutex>* lock,
+ const std::function<void()>& on_complete_callback);
+ virtual wifi_error waitForDriverReady();
+ // Checks if legacy HAL has successfully started
+ bool isStarted();
+ // Wrappers for all the functions in the legacy HAL function table.
+ virtual std::pair<wifi_error, std::string> getDriverVersion(const std::string& iface_name);
+ virtual std::pair<wifi_error, std::string> getFirmwareVersion(const std::string& iface_name);
+ std::pair<wifi_error, std::vector<uint8_t>> requestDriverMemoryDump(
+ const std::string& iface_name);
+ std::pair<wifi_error, std::vector<uint8_t>> requestFirmwareMemoryDump(
+ const std::string& iface_name);
+ virtual std::pair<wifi_error, uint64_t> getSupportedFeatureSet(const std::string& iface_name);
+ // APF functions.
+ std::pair<wifi_error, PacketFilterCapabilities> getPacketFilterCapabilities(
+ const std::string& iface_name);
+ wifi_error setPacketFilter(const std::string& iface_name, const std::vector<uint8_t>& program);
+ std::pair<wifi_error, std::vector<uint8_t>> readApfPacketFilterData(
+ const std::string& iface_name);
+ // Gscan functions.
+ std::pair<wifi_error, wifi_gscan_capabilities> getGscanCapabilities(
+ const std::string& iface_name);
+ // These API's provides a simplified interface over the legacy Gscan API's:
+ // a) All scan events from the legacy HAL API other than the
+ // |WIFI_SCAN_FAILED| are treated as notification of results.
+ // This method then retrieves the cached scan results from the legacy
+ // HAL API and triggers the externally provided
+ // |on_results_user_callback| on success.
+ // b) |WIFI_SCAN_FAILED| scan event or failure to retrieve cached scan
+ // results
+ // Triggers the externally provided |on_failure_user_callback|.
+ // c) Full scan result event triggers the externally provided
+ // |on_full_result_user_callback|.
+ wifi_error startGscan(const std::string& iface_name, wifi_request_id id,
+ const wifi_scan_cmd_params& params,
+ const std::function<void(wifi_request_id)>& on_failure_callback,
+ const on_gscan_results_callback& on_results_callback,
+ const on_gscan_full_result_callback& on_full_result_callback);
+ wifi_error stopGscan(const std::string& iface_name, wifi_request_id id);
+ std::pair<wifi_error, std::vector<uint32_t>> getValidFrequenciesForBand(
+ const std::string& iface_name, wifi_band band);
+ virtual wifi_error setDfsFlag(const std::string& iface_name, bool dfs_on);
+ // Link layer stats functions.
+ wifi_error enableLinkLayerStats(const std::string& iface_name, bool debug);
+ wifi_error disableLinkLayerStats(const std::string& iface_name);
+ std::pair<wifi_error, LinkLayerStats> getLinkLayerStats(const std::string& iface_name);
+ // RSSI monitor functions.
+ wifi_error startRssiMonitoring(
+ const std::string& iface_name, wifi_request_id id, int8_t max_rssi, int8_t min_rssi,
+ const on_rssi_threshold_breached_callback& on_threshold_breached_callback);
+ wifi_error stopRssiMonitoring(const std::string& iface_name, wifi_request_id id);
+ std::pair<wifi_error, wifi_roaming_capabilities> getRoamingCapabilities(
+ const std::string& iface_name);
+ wifi_error configureRoaming(const std::string& iface_name, const wifi_roaming_config& config);
+ wifi_error enableFirmwareRoaming(const std::string& iface_name, fw_roaming_state_t state);
+ wifi_error configureNdOffload(const std::string& iface_name, bool enable);
+ wifi_error startSendingOffloadedPacket(const std::string& iface_name, int32_t cmd_id,
+ uint16_t ether_type,
+ const std::vector<uint8_t>& ip_packet_data,
+ const std::array<uint8_t, 6>& src_address,
+ const std::array<uint8_t, 6>& dst_address,
+ int32_t period_in_ms);
+ wifi_error stopSendingOffloadedPacket(const std::string& iface_name, uint32_t cmd_id);
+ virtual wifi_error selectTxPowerScenario(const std::string& iface_name,
+ wifi_power_scenario scenario);
+ virtual wifi_error resetTxPowerScenario(const std::string& iface_name);
+ wifi_error setLatencyMode(const std::string& iface_name, wifi_latency_mode mode);
+ wifi_error setThermalMitigationMode(wifi_thermal_mode mode, uint32_t completion_window);
+ wifi_error setDscpToAccessCategoryMapping(uint32_t start, uint32_t end,
+ uint32_t access_category);
+ wifi_error resetDscpToAccessCategoryMapping();
+ // Logger/debug functions.
+ std::pair<wifi_error, uint32_t> getLoggerSupportedFeatureSet(const std::string& iface_name);
+ wifi_error startPktFateMonitoring(const std::string& iface_name);
+ std::pair<wifi_error, std::vector<wifi_tx_report>> getTxPktFates(const std::string& iface_name);
+ std::pair<wifi_error, std::vector<wifi_rx_report>> getRxPktFates(const std::string& iface_name);
+ std::pair<wifi_error, WakeReasonStats> getWakeReasonStats(const std::string& iface_name);
+ wifi_error registerRingBufferCallbackHandler(
+ const std::string& iface_name, const on_ring_buffer_data_callback& on_data_callback);
+ wifi_error deregisterRingBufferCallbackHandler(const std::string& iface_name);
+ virtual wifi_error registerSubsystemRestartCallbackHandler(
+ const on_subsystem_restart_callback& on_restart_callback);
+ std::pair<wifi_error, std::vector<wifi_ring_buffer_status>> getRingBuffersStatus(
+ const std::string& iface_name);
+ wifi_error startRingBufferLogging(const std::string& iface_name, const std::string& ring_name,
+ uint32_t verbose_level, uint32_t max_interval_sec,
+ uint32_t min_data_size);
+ wifi_error getRingBufferData(const std::string& iface_name, const std::string& ring_name);
+ wifi_error registerErrorAlertCallbackHandler(const std::string& iface_name,
+ const on_error_alert_callback& on_alert_callback);
+ wifi_error deregisterErrorAlertCallbackHandler(const std::string& iface_name);
+ // Radio mode functions.
+ virtual wifi_error registerRadioModeChangeCallbackHandler(
+ const std::string& iface_name,
+ const on_radio_mode_change_callback& on_user_change_callback);
+ // RTT functions.
+ wifi_error startRttRangeRequest(const std::string& iface_name, wifi_request_id id,
+ const std::vector<wifi_rtt_config>& rtt_configs,
+ const on_rtt_results_callback& on_results_callback);
+ wifi_error cancelRttRangeRequest(const std::string& iface_name, wifi_request_id id,
+ const std::vector<std::array<uint8_t, ETH_ALEN>>& mac_addrs);
+ std::pair<wifi_error, wifi_rtt_capabilities> getRttCapabilities(const std::string& iface_name);
+ std::pair<wifi_error, wifi_rtt_responder> getRttResponderInfo(const std::string& iface_name);
+ wifi_error enableRttResponder(const std::string& iface_name, wifi_request_id id,
+ const wifi_channel_info& channel_hint, uint32_t max_duration_secs,
+ const wifi_rtt_responder& info);
+ wifi_error disableRttResponder(const std::string& iface_name, wifi_request_id id);
+ wifi_error setRttLci(const std::string& iface_name, wifi_request_id id,
+ const wifi_lci_information& info);
+ wifi_error setRttLcr(const std::string& iface_name, wifi_request_id id,
+ const wifi_lcr_information& info);
+ // NAN functions.
+ virtual wifi_error nanRegisterCallbackHandlers(const std::string& iface_name,
+ const NanCallbackHandlers& callbacks);
+ wifi_error nanEnableRequest(const std::string& iface_name, transaction_id id,
+ const NanEnableRequest& msg);
+ virtual wifi_error nanDisableRequest(const std::string& iface_name, transaction_id id);
+ wifi_error nanPublishRequest(const std::string& iface_name, transaction_id id,
+ const NanPublishRequest& msg);
+ wifi_error nanPublishCancelRequest(const std::string& iface_name, transaction_id id,
+ const NanPublishCancelRequest& msg);
+ wifi_error nanSubscribeRequest(const std::string& iface_name, transaction_id id,
+ const NanSubscribeRequest& msg);
+ wifi_error nanSubscribeCancelRequest(const std::string& iface_name, transaction_id id,
+ const NanSubscribeCancelRequest& msg);
+ wifi_error nanTransmitFollowupRequest(const std::string& iface_name, transaction_id id,
+ const NanTransmitFollowupRequest& msg);
+ wifi_error nanStatsRequest(const std::string& iface_name, transaction_id id,
+ const NanStatsRequest& msg);
+ wifi_error nanConfigRequest(const std::string& iface_name, transaction_id id,
+ const NanConfigRequest& msg);
+ wifi_error nanTcaRequest(const std::string& iface_name, transaction_id id,
+ const NanTCARequest& msg);
+ wifi_error nanBeaconSdfPayloadRequest(const std::string& iface_name, transaction_id id,
+ const NanBeaconSdfPayloadRequest& msg);
+ std::pair<wifi_error, NanVersion> nanGetVersion();
+ wifi_error nanGetCapabilities(const std::string& iface_name, transaction_id id);
+ wifi_error nanDataInterfaceCreate(const std::string& iface_name, transaction_id id,
+ const std::string& data_iface_name);
+ virtual wifi_error nanDataInterfaceDelete(const std::string& iface_name, transaction_id id,
+ const std::string& data_iface_name);
+ wifi_error nanDataRequestInitiator(const std::string& iface_name, transaction_id id,
+ const NanDataPathInitiatorRequest& msg);
+ wifi_error nanDataIndicationResponse(const std::string& iface_name, transaction_id id,
+ const NanDataPathIndicationResponse& msg);
+ wifi_error nanDataEnd(const std::string& iface_name, transaction_id id, uint32_t ndpInstanceId);
+ // AP functions.
+ wifi_error setCountryCode(const std::string& iface_name, const std::array<uint8_t, 2> code);
+
+ // Interface functions.
+ virtual wifi_error createVirtualInterface(const std::string& ifname,
+ wifi_interface_type iftype);
+ virtual wifi_error deleteVirtualInterface(const std::string& ifname);
+ virtual wifi_error getSupportedIfaceName(uint32_t iface_type, std::string& ifname);
+
+ // STA + STA functions
+ virtual wifi_error multiStaSetPrimaryConnection(const std::string& ifname);
+ virtual wifi_error multiStaSetUseCase(wifi_multi_sta_use_case use_case);
+
+ // Coex functions.
+ virtual wifi_error setCoexUnsafeChannels(std::vector<wifi_coex_unsafe_channel> unsafe_channels,
+ uint32_t restrictions);
+
+ wifi_error setVoipMode(const std::string& iface_name, wifi_voip_mode mode);
+
+ wifi_error twtRegisterHandler(const std::string& iface_name,
+ const TwtCallbackHandlers& handler);
+
+ std::pair<wifi_error, TwtCapabilitySet> twtGetCapability(const std::string& iface_name);
+
+ wifi_error twtSetupRequest(const std::string& iface_name, const TwtSetupRequest& msg);
+
+ wifi_error twtTearDownRequest(const std::string& iface_name, const TwtTeardownRequest& msg);
+
+ wifi_error twtInfoFrameRequest(const std::string& iface_name, const TwtInfoFrameRequest& msg);
+
+ std::pair<wifi_error, TwtStats> twtGetStats(const std::string& iface_name, uint8_t configId);
+
+ wifi_error twtClearStats(const std::string& iface_name, uint8_t configId);
+
+ wifi_error setDtimConfig(const std::string& iface_name, uint32_t multiplier);
+
+ // Retrieve the list of usable channels in the requested bands
+ // for the requested modes
+ std::pair<wifi_error, std::vector<wifi_usable_channel>> getUsableChannels(
+ uint32_t band_mask, uint32_t iface_mode_mask, uint32_t filter_mask);
+
+ wifi_error triggerSubsystemRestart();
+
+ wifi_error setIndoorState(bool isIndoor);
+
+ std::pair<wifi_error, wifi_radio_combination_matrix*> getSupportedRadioCombinationsMatrix();
+
+ // CHRE NAN RTT function
+ wifi_error chreNanRttRequest(const std::string& iface_name, bool enable);
+
+ wifi_error chreRegisterHandler(const std::string& iface_name,
+ const ChreCallbackHandlers& handler);
+
+ wifi_error enableWifiTxPowerLimits(const std::string& iface_name, bool enable);
+ wifi_error getWifiCachedScanResults(const std::string& iface_name,
+ const CachedScanResultsCallbackHandlers& handler);
+
+ private:
+ // Retrieve interface handles for all the available interfaces.
+ wifi_error retrieveIfaceHandles();
+ wifi_interface_handle getIfaceHandle(const std::string& iface_name);
+ // Run the legacy HAL event loop thread.
+ void runEventLoop();
+ // Retrieve the cached gscan results to pass the results back to the
+ // external callbacks.
+ 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_;
+ // Opaque handle to be used for all global operations.
+ wifi_handle global_handle_;
+ // Map of interface name to handle that is to be used for all interface
+ // specific operations.
+ std::map<std::string, wifi_interface_handle> iface_name_to_handle_;
+ // Flag to indicate if we have initiated the cleanup of legacy HAL.
+ std::atomic<bool> awaiting_event_loop_termination_;
+ std::condition_variable_any stop_wait_cv_;
+ // Flag to indicate if the legacy HAL has been started.
+ bool is_started_;
+ std::weak_ptr<::android::wifi_system::InterfaceTool> iface_tool_;
+ // Flag to indicate if this HAL is for the primary chip. This is used
+ // in order to avoid some hard-coded behavior used with older HALs,
+ // such as bring wlan0 interface up/down on start/stop HAL.
+ // it may be removed once vendor HALs are updated.
+ bool is_primary_;
+};
+
+} // namespace legacy_hal
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_LEGACY_HAL_H_
diff --git a/wifi/aidl/default/wifi_legacy_hal_factory.cpp b/wifi/aidl/default/wifi_legacy_hal_factory.cpp
new file mode 100644
index 0000000..60f81ed
--- /dev/null
+++ b/wifi/aidl/default/wifi_legacy_hal_factory.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_legacy_hal_factory.h"
+
+#include <android-base/logging.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlmemory.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "wifi_legacy_hal_stubs.h"
+
+namespace {
+static constexpr char kVendorHalsDescPath[] = "/vendor/etc/wifi/vendor_hals";
+static constexpr char kVendorHalsDescExt[] = ".xml";
+static constexpr uint32_t kVendorHalsDescVersion = 1;
+
+bool isDirectory(struct dirent* entryPtr) {
+ bool isDir = false;
+ if (entryPtr->d_type != DT_UNKNOWN && entryPtr->d_type != DT_LNK) {
+ isDir = (entryPtr->d_type == DT_DIR);
+ } else {
+ struct stat entryStat;
+ stat(entryPtr->d_name, &entryStat);
+ isDir = S_ISDIR(entryStat.st_mode);
+ }
+ return isDir;
+}
+
+bool isFileExtension(const char* name, const char* ext) {
+ if (name == NULL) return false;
+ if (ext == NULL) return false;
+
+ size_t extLen = strlen(ext);
+ size_t nameLen = strlen(name);
+
+ if (extLen > nameLen) return false;
+
+ if (strncmp(name + nameLen - extLen, ext, extLen) != 0) return false;
+
+ return true;
+}
+}; // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace legacy_hal {
+
+WifiLegacyHalFactory::WifiLegacyHalFactory(
+ const std::weak_ptr<::android::wifi_system::InterfaceTool> iface_tool)
+ : iface_tool_(iface_tool) {}
+
+std::vector<std::shared_ptr<WifiLegacyHal>> WifiLegacyHalFactory::getHals() {
+ if (legacy_hals_.empty()) {
+ if (!initVendorHalDescriptorFromLinked()) initVendorHalsDescriptorList();
+ for (auto& desc : descs_) {
+ std::shared_ptr<WifiLegacyHal> hal =
+ std::make_shared<WifiLegacyHal>(iface_tool_, desc.fn, desc.primary);
+ legacy_hals_.push_back(hal);
+ }
+ }
+
+ return legacy_hals_;
+}
+
+bool WifiLegacyHalFactory::initVendorHalDescriptorFromLinked() {
+ wifi_hal_lib_desc desc;
+
+ if (!initLinkedHalFunctionTable(&desc.fn)) return false;
+
+ desc.primary = true;
+ desc.handle = NULL;
+ descs_.push_back(desc);
+ return true;
+}
+
+bool WifiLegacyHalFactory::initLinkedHalFunctionTable(wifi_hal_fn* hal_fn) {
+ init_wifi_vendor_hal_func_table_t initfn;
+
+ initfn = (init_wifi_vendor_hal_func_table_t)dlsym(RTLD_DEFAULT,
+ "init_wifi_vendor_hal_func_table");
+ if (!initfn) {
+ LOG(INFO) << "no vendor HAL library linked, will try dynamic load";
+ return false;
+ }
+
+ if (!initHalFuncTableWithStubs(hal_fn)) {
+ LOG(ERROR) << "Can not initialize the basic function pointer table";
+ return false;
+ }
+
+ if (initfn(hal_fn) != WIFI_SUCCESS) {
+ LOG(ERROR) << "Can not initialize the vendor function pointer table";
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Overall structure of the HAL descriptor XML schema
+ *
+ * <?xml version="1.0" encoding="UTF-8"?>
+ * <WifiVendorHal version="1">
+ * <path>/vendor/lib64/libwifi-hal-qcom.so</path>
+ * <primary>1</primary>
+ * </WifiVendorHal>
+ */
+void WifiLegacyHalFactory::initVendorHalsDescriptorList() {
+ xmlDocPtr xml;
+ xmlNodePtr node, cnode;
+ char* version;
+ std::string path;
+ xmlChar* value;
+ wifi_hal_lib_desc desc;
+
+ LOG(INFO) << "processing vendor HALs descriptions in " << kVendorHalsDescPath;
+ DIR* dirPtr = ::opendir(kVendorHalsDescPath);
+ if (dirPtr == NULL) {
+ LOG(ERROR) << "failed to open " << kVendorHalsDescPath;
+ return;
+ }
+ for (struct dirent* entryPtr = ::readdir(dirPtr); entryPtr != NULL;
+ entryPtr = ::readdir(dirPtr)) {
+ if (isDirectory(entryPtr)) continue;
+
+ if (!isFileExtension(entryPtr->d_name, kVendorHalsDescExt))
+ continue; // only process .xml files
+
+ LOG(INFO) << "processing config file: " << entryPtr->d_name;
+
+ std::string fullPath(kVendorHalsDescPath);
+ fullPath.append("/");
+ fullPath.append(entryPtr->d_name);
+ xml = xmlReadFile(fullPath.c_str(), "UTF-8", XML_PARSE_RECOVER);
+ if (!xml) {
+ LOG(ERROR) << "failed to parse: " << entryPtr->d_name << " skipping...";
+ continue;
+ }
+ node = xmlDocGetRootElement(xml);
+ if (!node) {
+ LOG(ERROR) << "empty config file: " << entryPtr->d_name << " skipping...";
+ goto skip;
+ }
+ if (xmlStrcmp(node->name, BAD_CAST "WifiVendorHal")) {
+ LOG(ERROR) << "bad config, root element not WifiVendorHal: " << entryPtr->d_name
+ << " skipping...";
+ goto skip;
+ }
+ version = (char*)xmlGetProp(node, BAD_CAST "version");
+ if (!version || strtoul(version, NULL, 0) != kVendorHalsDescVersion) {
+ LOG(ERROR) << "conf file: " << entryPtr->d_name
+ << "must have version: " << kVendorHalsDescVersion << ", skipping...";
+ goto skip;
+ }
+ cnode = node->children;
+ path.clear();
+ desc.primary = false;
+ while (cnode) {
+ if (!xmlStrcmp(cnode->name, BAD_CAST "path")) {
+ value = xmlNodeListGetString(xml, cnode->children, 1);
+ if (value) path = (char*)value;
+ xmlFree(value);
+ } else if (!xmlStrcmp(cnode->name, BAD_CAST "primary")) {
+ value = xmlNodeListGetString(xml, cnode->children, 1);
+ desc.primary = !xmlStrcmp(value, BAD_CAST "1");
+ xmlFree(value);
+ }
+ cnode = cnode->next;
+ }
+ if (path.empty()) {
+ LOG(ERROR) << "hal library path not provided in: " << entryPtr->d_name
+ << ", skipping...";
+ goto skip;
+ }
+ if (loadVendorHalLib(path, desc)) {
+ if (desc.primary)
+ descs_.insert(descs_.begin(), desc);
+ else
+ descs_.push_back(desc);
+ }
+ skip:
+ xmlFreeDoc(xml);
+ }
+ ::closedir(dirPtr);
+}
+
+bool WifiLegacyHalFactory::loadVendorHalLib(const std::string& path, wifi_hal_lib_desc& desc) {
+ void* h = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
+ init_wifi_vendor_hal_func_table_t initfn;
+ wifi_error res;
+
+ if (!h) {
+ LOG(ERROR) << "failed to open vendor hal library: " << path;
+ return false;
+ }
+ initfn = (init_wifi_vendor_hal_func_table_t)dlsym(h, "init_wifi_vendor_hal_func_table");
+ if (!initfn) {
+ LOG(ERROR) << "init_wifi_vendor_hal_func_table not found in: " << path;
+ goto out_err;
+ }
+
+ if (!initHalFuncTableWithStubs(&desc.fn)) {
+ LOG(ERROR) << "Can not initialize the basic function pointer table";
+ goto out_err;
+ }
+ res = initfn(&desc.fn);
+ if (res != WIFI_SUCCESS) {
+ LOG(ERROR) << "failed to initialize the vendor func table in: " << path
+ << " error: " << res;
+ goto out_err;
+ }
+
+ res = desc.fn.wifi_early_initialize();
+ // vendor HALs which do not implement early_initialize will return
+ // WIFI_ERROR_NOT_SUPPORTED, treat this as success.
+ if (res != WIFI_SUCCESS && res != WIFI_ERROR_NOT_SUPPORTED) {
+ LOG(ERROR) << "early initialization failed in: " << path << " error: " << res;
+ goto out_err;
+ }
+
+ desc.handle = h;
+ return true;
+out_err:
+ dlclose(h);
+ return false;
+}
+
+} // namespace legacy_hal
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi_legacy_hal_factory.h b/wifi/aidl/default/wifi_legacy_hal_factory.h
new file mode 100644
index 0000000..e7b287a
--- /dev/null
+++ b/wifi/aidl/default/wifi_legacy_hal_factory.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_LEGACY_HAL_FACTORY_H_
+#define WIFI_LEGACY_HAL_FACTORY_H_
+
+#include <wifi_system/interface_tool.h>
+
+#include "wifi_legacy_hal.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+// This is in a separate namespace to prevent typename conflicts between
+// the legacy HAL types and the AIDL interface types.
+namespace legacy_hal {
+/**
+ * Class that creates WifiLegacyHal objects for vendor HALs in the system.
+ */
+class WifiLegacyHalFactory {
+ public:
+ WifiLegacyHalFactory(const std::weak_ptr<::android::wifi_system::InterfaceTool> iface_tool);
+ virtual ~WifiLegacyHalFactory() = default;
+
+ std::vector<std::shared_ptr<WifiLegacyHal>> getHals();
+
+ private:
+ typedef struct {
+ wifi_hal_fn fn;
+ bool primary;
+ void* handle;
+ } wifi_hal_lib_desc;
+
+ bool initVendorHalDescriptorFromLinked();
+ void initVendorHalsDescriptorList();
+ bool initLinkedHalFunctionTable(wifi_hal_fn* hal_fn);
+ bool loadVendorHalLib(const std::string& path, wifi_hal_lib_desc& desc);
+
+ std::weak_ptr<::android::wifi_system::InterfaceTool> iface_tool_;
+ std::vector<wifi_hal_lib_desc> descs_;
+ std::vector<std::shared_ptr<WifiLegacyHal>> legacy_hals_;
+};
+
+} // namespace legacy_hal
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_LEGACY_HAL_FACTORY_H_
diff --git a/wifi/aidl/default/wifi_legacy_hal_stubs.cpp b/wifi/aidl/default/wifi_legacy_hal_stubs.cpp
new file mode 100644
index 0000000..cff7f69
--- /dev/null
+++ b/wifi/aidl/default/wifi_legacy_hal_stubs.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_legacy_hal_stubs.h"
+
+// TODO: Remove these stubs from HalTool in libwifi-system.
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace legacy_hal {
+template <typename>
+struct stubFunction;
+
+template <typename R, typename... Args>
+struct stubFunction<R (*)(Args...)> {
+ static constexpr R invoke(Args...) { return WIFI_ERROR_NOT_SUPPORTED; }
+};
+template <typename... Args>
+struct stubFunction<void (*)(Args...)> {
+ static constexpr void invoke(Args...) {}
+};
+
+template <typename T>
+void populateStubFor(T* val) {
+ *val = &stubFunction<T>::invoke;
+}
+
+bool initHalFuncTableWithStubs(wifi_hal_fn* hal_fn) {
+ if (hal_fn == nullptr) {
+ return false;
+ }
+ populateStubFor(&hal_fn->wifi_initialize);
+ populateStubFor(&hal_fn->wifi_wait_for_driver_ready);
+ populateStubFor(&hal_fn->wifi_cleanup);
+ populateStubFor(&hal_fn->wifi_event_loop);
+ populateStubFor(&hal_fn->wifi_get_error_info);
+ populateStubFor(&hal_fn->wifi_get_supported_feature_set);
+ populateStubFor(&hal_fn->wifi_get_concurrency_matrix);
+ populateStubFor(&hal_fn->wifi_set_scanning_mac_oui);
+ populateStubFor(&hal_fn->wifi_get_supported_channels);
+ populateStubFor(&hal_fn->wifi_is_epr_supported);
+ populateStubFor(&hal_fn->wifi_get_ifaces);
+ populateStubFor(&hal_fn->wifi_get_iface_name);
+ populateStubFor(&hal_fn->wifi_set_iface_event_handler);
+ populateStubFor(&hal_fn->wifi_reset_iface_event_handler);
+ populateStubFor(&hal_fn->wifi_start_gscan);
+ populateStubFor(&hal_fn->wifi_stop_gscan);
+ populateStubFor(&hal_fn->wifi_get_cached_gscan_results);
+ populateStubFor(&hal_fn->wifi_set_bssid_hotlist);
+ populateStubFor(&hal_fn->wifi_reset_bssid_hotlist);
+ populateStubFor(&hal_fn->wifi_set_significant_change_handler);
+ populateStubFor(&hal_fn->wifi_reset_significant_change_handler);
+ populateStubFor(&hal_fn->wifi_get_gscan_capabilities);
+ populateStubFor(&hal_fn->wifi_set_link_stats);
+ populateStubFor(&hal_fn->wifi_get_link_stats);
+ populateStubFor(&hal_fn->wifi_clear_link_stats);
+ populateStubFor(&hal_fn->wifi_get_valid_channels);
+ populateStubFor(&hal_fn->wifi_rtt_range_request);
+ populateStubFor(&hal_fn->wifi_rtt_range_cancel);
+ populateStubFor(&hal_fn->wifi_get_rtt_capabilities);
+ populateStubFor(&hal_fn->wifi_rtt_get_responder_info);
+ populateStubFor(&hal_fn->wifi_enable_responder);
+ populateStubFor(&hal_fn->wifi_disable_responder);
+ populateStubFor(&hal_fn->wifi_set_nodfs_flag);
+ populateStubFor(&hal_fn->wifi_start_logging);
+ populateStubFor(&hal_fn->wifi_set_epno_list);
+ populateStubFor(&hal_fn->wifi_reset_epno_list);
+ populateStubFor(&hal_fn->wifi_set_country_code);
+ populateStubFor(&hal_fn->wifi_get_firmware_memory_dump);
+ populateStubFor(&hal_fn->wifi_set_log_handler);
+ populateStubFor(&hal_fn->wifi_reset_log_handler);
+ populateStubFor(&hal_fn->wifi_set_alert_handler);
+ populateStubFor(&hal_fn->wifi_reset_alert_handler);
+ populateStubFor(&hal_fn->wifi_get_firmware_version);
+ populateStubFor(&hal_fn->wifi_get_ring_buffers_status);
+ populateStubFor(&hal_fn->wifi_get_logger_supported_feature_set);
+ populateStubFor(&hal_fn->wifi_get_ring_data);
+ populateStubFor(&hal_fn->wifi_enable_tdls);
+ populateStubFor(&hal_fn->wifi_disable_tdls);
+ populateStubFor(&hal_fn->wifi_get_tdls_status);
+ populateStubFor(&hal_fn->wifi_get_tdls_capabilities);
+ populateStubFor(&hal_fn->wifi_get_driver_version);
+ populateStubFor(&hal_fn->wifi_set_passpoint_list);
+ populateStubFor(&hal_fn->wifi_reset_passpoint_list);
+ populateStubFor(&hal_fn->wifi_set_lci);
+ populateStubFor(&hal_fn->wifi_set_lcr);
+ populateStubFor(&hal_fn->wifi_start_sending_offloaded_packet);
+ populateStubFor(&hal_fn->wifi_stop_sending_offloaded_packet);
+ populateStubFor(&hal_fn->wifi_start_rssi_monitoring);
+ populateStubFor(&hal_fn->wifi_stop_rssi_monitoring);
+ populateStubFor(&hal_fn->wifi_get_wake_reason_stats);
+ populateStubFor(&hal_fn->wifi_configure_nd_offload);
+ populateStubFor(&hal_fn->wifi_get_driver_memory_dump);
+ populateStubFor(&hal_fn->wifi_start_pkt_fate_monitoring);
+ populateStubFor(&hal_fn->wifi_get_tx_pkt_fates);
+ populateStubFor(&hal_fn->wifi_get_rx_pkt_fates);
+ populateStubFor(&hal_fn->wifi_nan_enable_request);
+ populateStubFor(&hal_fn->wifi_nan_disable_request);
+ populateStubFor(&hal_fn->wifi_nan_publish_request);
+ populateStubFor(&hal_fn->wifi_nan_publish_cancel_request);
+ populateStubFor(&hal_fn->wifi_nan_subscribe_request);
+ populateStubFor(&hal_fn->wifi_nan_subscribe_cancel_request);
+ populateStubFor(&hal_fn->wifi_nan_transmit_followup_request);
+ populateStubFor(&hal_fn->wifi_nan_stats_request);
+ populateStubFor(&hal_fn->wifi_nan_config_request);
+ populateStubFor(&hal_fn->wifi_nan_tca_request);
+ populateStubFor(&hal_fn->wifi_nan_beacon_sdf_payload_request);
+ populateStubFor(&hal_fn->wifi_nan_register_handler);
+ populateStubFor(&hal_fn->wifi_nan_get_version);
+ populateStubFor(&hal_fn->wifi_nan_get_capabilities);
+ populateStubFor(&hal_fn->wifi_nan_data_interface_create);
+ populateStubFor(&hal_fn->wifi_nan_data_interface_delete);
+ populateStubFor(&hal_fn->wifi_nan_data_request_initiator);
+ populateStubFor(&hal_fn->wifi_nan_data_indication_response);
+ populateStubFor(&hal_fn->wifi_nan_data_end);
+ populateStubFor(&hal_fn->wifi_get_packet_filter_capabilities);
+ populateStubFor(&hal_fn->wifi_set_packet_filter);
+ populateStubFor(&hal_fn->wifi_read_packet_filter);
+ populateStubFor(&hal_fn->wifi_get_roaming_capabilities);
+ populateStubFor(&hal_fn->wifi_enable_firmware_roaming);
+ populateStubFor(&hal_fn->wifi_configure_roaming);
+ populateStubFor(&hal_fn->wifi_select_tx_power_scenario);
+ populateStubFor(&hal_fn->wifi_reset_tx_power_scenario);
+ 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);
+ populateStubFor(&hal_fn->wifi_map_dscp_access_category);
+ populateStubFor(&hal_fn->wifi_reset_dscp_mapping);
+ populateStubFor(&hal_fn->wifi_set_subsystem_restart_handler);
+ populateStubFor(&hal_fn->wifi_get_supported_iface_name);
+ populateStubFor(&hal_fn->wifi_early_initialize);
+ populateStubFor(&hal_fn->wifi_get_chip_feature_set);
+ populateStubFor(&hal_fn->wifi_multi_sta_set_primary_connection);
+ populateStubFor(&hal_fn->wifi_multi_sta_set_use_case);
+ populateStubFor(&hal_fn->wifi_set_coex_unsafe_channels);
+ populateStubFor(&hal_fn->wifi_set_voip_mode);
+ populateStubFor(&hal_fn->wifi_twt_register_handler);
+ populateStubFor(&hal_fn->wifi_twt_get_capability);
+ populateStubFor(&hal_fn->wifi_twt_setup_request);
+ populateStubFor(&hal_fn->wifi_twt_teardown_request);
+ populateStubFor(&hal_fn->wifi_twt_info_frame_request);
+ populateStubFor(&hal_fn->wifi_twt_get_stats);
+ populateStubFor(&hal_fn->wifi_twt_clear_stats);
+ populateStubFor(&hal_fn->wifi_set_dtim_config);
+ populateStubFor(&hal_fn->wifi_get_usable_channels);
+ populateStubFor(&hal_fn->wifi_trigger_subsystem_restart);
+ populateStubFor(&hal_fn->wifi_set_indoor_state);
+ populateStubFor(&hal_fn->wifi_get_supported_radio_combinations_matrix);
+ populateStubFor(&hal_fn->wifi_nan_rtt_chre_enable_request);
+ populateStubFor(&hal_fn->wifi_nan_rtt_chre_disable_request);
+ populateStubFor(&hal_fn->wifi_chre_register_handler);
+ populateStubFor(&hal_fn->wifi_enable_tx_power_limits);
+ populateStubFor(&hal_fn->wifi_get_cached_scan_results);
+ return true;
+}
+
+} // namespace legacy_hal
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi_legacy_hal_stubs.h b/wifi/aidl/default/wifi_legacy_hal_stubs.h
new file mode 100644
index 0000000..603ecf2
--- /dev/null
+++ b/wifi/aidl/default/wifi_legacy_hal_stubs.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_LEGACY_HAL_STUBS_H_
+#define WIFI_LEGACY_HAL_STUBS_H_
+
+#include <hardware_legacy/wifi_hal.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace legacy_hal {
+
+bool initHalFuncTableWithStubs(wifi_hal_fn* hal_fn);
+} // namespace legacy_hal
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_LEGACY_HAL_STUBS_H_
diff --git a/wifi/aidl/default/wifi_mode_controller.cpp b/wifi/aidl/default/wifi_mode_controller.cpp
new file mode 100644
index 0000000..07cb072
--- /dev/null
+++ b/wifi/aidl/default/wifi_mode_controller.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_mode_controller.h"
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <private/android_filesystem_config.h>
+
+namespace {
+using aidl::android::hardware::wifi::IfaceType;
+using android::wifi_hal::DriverTool;
+
+int convertIfaceTypeToFirmwareMode(IfaceType type) {
+ int mode;
+ switch (type) {
+ case IfaceType::AP:
+ mode = DriverTool::kFirmwareModeAp;
+ break;
+ case IfaceType::P2P:
+ mode = DriverTool::kFirmwareModeP2p;
+ break;
+ case IfaceType::NAN_IFACE:
+ // NAN is exposed in STA mode currently.
+ mode = DriverTool::kFirmwareModeSta;
+ break;
+ case IfaceType::STA:
+ mode = DriverTool::kFirmwareModeSta;
+ break;
+ }
+ return mode;
+}
+} // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace mode_controller {
+
+WifiModeController::WifiModeController() : driver_tool_(new DriverTool) {}
+
+bool WifiModeController::isFirmwareModeChangeNeeded(IfaceType type) {
+ return driver_tool_->IsFirmwareModeChangeNeeded(convertIfaceTypeToFirmwareMode(type));
+}
+
+bool WifiModeController::initialize() {
+ if (!driver_tool_->LoadDriver()) {
+ LOG(ERROR) << "Failed to load WiFi driver";
+ return false;
+ }
+ return true;
+}
+
+bool WifiModeController::changeFirmwareMode(IfaceType type) {
+ if (!driver_tool_->ChangeFirmwareMode(convertIfaceTypeToFirmwareMode(type))) {
+ LOG(ERROR) << "Failed to change firmware mode";
+ return false;
+ }
+ return true;
+}
+
+bool WifiModeController::deinitialize() {
+ if (!driver_tool_->UnloadDriver()) {
+ LOG(ERROR) << "Failed to unload WiFi driver";
+ return false;
+ }
+ return true;
+}
+
+} // namespace mode_controller
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi_mode_controller.h b/wifi/aidl/default/wifi_mode_controller.h
new file mode 100644
index 0000000..1a9fb1a
--- /dev/null
+++ b/wifi/aidl/default/wifi_mode_controller.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_MODE_CONTROLLER_H_
+#define WIFI_MODE_CONTROLLER_H_
+
+#include <aidl/android/hardware/wifi/IWifi.h>
+#include <wifi_hal/driver_tool.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace mode_controller {
+
+/**
+ * Class that encapsulates all firmware mode configuration.
+ * This class will perform the necessary firmware reloads to put the chip in the
+ * required state (essentially a wrapper over DriverTool).
+ */
+class WifiModeController {
+ public:
+ WifiModeController();
+ virtual ~WifiModeController() = default;
+
+ // Checks if a firmware mode change is necessary to support the specified
+ // iface type operations.
+ virtual bool isFirmwareModeChangeNeeded(IfaceType type);
+ virtual bool initialize();
+ // Change the firmware mode to support the specified iface type operations.
+ virtual bool changeFirmwareMode(IfaceType type);
+ // Unload the driver. This should be invoked whenever |IWifi.stop()| is
+ // invoked.
+ virtual bool deinitialize();
+
+ private:
+ std::unique_ptr<::android::wifi_hal::DriverTool> driver_tool_;
+};
+
+} // namespace mode_controller
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_MODE_CONTROLLER_H_
diff --git a/wifi/aidl/default/wifi_nan_iface.cpp b/wifi/aidl/default/wifi_nan_iface.cpp
new file mode 100644
index 0000000..9edef09
--- /dev/null
+++ b/wifi/aidl/default/wifi_nan_iface.cpp
@@ -0,0 +1,751 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_nan_iface.h"
+
+#include <android-base/logging.h>
+
+#include "aidl_return_util.h"
+#include "aidl_struct_util.h"
+#include "wifi_status_util.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+using aidl_return_util::validateAndCall;
+
+WifiNanIface::WifiNanIface(const std::string& ifname, bool is_dedicated_iface,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util)
+ : ifname_(ifname),
+ is_dedicated_iface_(is_dedicated_iface),
+ legacy_hal_(legacy_hal),
+ iface_util_(iface_util),
+ is_valid_(true) {}
+
+std::shared_ptr<WifiNanIface> WifiNanIface::create(
+ const std::string& ifname, bool is_dedicated_iface,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util) {
+ std::shared_ptr<WifiNanIface> ptr = ndk::SharedRefBase::make<WifiNanIface>(
+ ifname, is_dedicated_iface, legacy_hal, iface_util);
+ if (is_dedicated_iface) {
+ // If using a dedicated iface, set the iface up first.
+ if (!iface_util.lock()->setUpState(ifname, true)) {
+ // Fatal failure, invalidate the iface object.
+ ptr->invalidate();
+ return nullptr;
+ }
+ }
+ std::weak_ptr<WifiNanIface> weak_ptr_this(ptr);
+ ptr->setWeakPtr(weak_ptr_this);
+ ptr->registerCallbackHandlers();
+ return ptr;
+}
+
+void WifiNanIface::registerCallbackHandlers() {
+ // Register all the callbacks here. These should be valid for the lifetime
+ // of the object. Whenever the mode changes legacy HAL will remove
+ // all of these callbacks.
+ legacy_hal::NanCallbackHandlers callback_handlers;
+ std::weak_ptr<WifiNanIface> weak_ptr_this = weak_ptr_this_;
+
+ // Callback for response.
+ callback_handlers.on_notify_response = [weak_ptr_this](legacy_hal::transaction_id id,
+ const legacy_hal::NanResponseMsg& msg) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ NanStatus nanStatus;
+ if (!aidl_struct_util::convertLegacyNanResponseHeaderToAidl(msg, &nanStatus)) {
+ LOG(ERROR) << "Failed to convert nan response header";
+ return;
+ }
+
+ switch (msg.response_type) {
+ case legacy_hal::NAN_RESPONSE_ENABLED: {
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyEnableResponse(id, nanStatus).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_RESPONSE_DISABLED: {
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyDisableResponse(id, nanStatus).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_RESPONSE_PUBLISH: {
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyStartPublishResponse(id, nanStatus,
+ msg.body.publish_response.publish_id)
+ .isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_RESPONSE_PUBLISH_CANCEL: {
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyStopPublishResponse(id, nanStatus).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_RESPONSE_TRANSMIT_FOLLOWUP: {
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyTransmitFollowupResponse(id, nanStatus).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_RESPONSE_SUBSCRIBE: {
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyStartSubscribeResponse(
+ id, nanStatus, msg.body.subscribe_response.subscribe_id)
+ .isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_RESPONSE_SUBSCRIBE_CANCEL: {
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyStopSubscribeResponse(id, nanStatus).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_RESPONSE_CONFIG: {
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyConfigResponse(id, nanStatus).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_GET_CAPABILITIES: {
+ NanCapabilities aidl_struct;
+ if (!aidl_struct_util::convertLegacyNanCapabilitiesResponseToAidl(
+ msg.body.nan_capabilities, &aidl_struct)) {
+ LOG(ERROR) << "Failed to convert nan capabilities response";
+ return;
+ }
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyCapabilitiesResponse(id, nanStatus, aidl_struct).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_DP_INTERFACE_CREATE: {
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyCreateDataInterfaceResponse(id, nanStatus).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_DP_INTERFACE_DELETE: {
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyDeleteDataInterfaceResponse(id, nanStatus).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_DP_INITIATOR_RESPONSE: {
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyInitiateDataPathResponse(
+ id, nanStatus,
+ msg.body.data_request_response.ndp_instance_id)
+ .isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_DP_RESPONDER_RESPONSE: {
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyRespondToDataPathIndicationResponse(id, nanStatus)
+ .isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_DP_END: {
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->notifyTerminateDataPathResponse(id, nanStatus).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ break;
+ }
+ case legacy_hal::NAN_RESPONSE_BEACON_SDF_PAYLOAD:
+ /* fall through */
+ case legacy_hal::NAN_RESPONSE_TCA:
+ /* fall through */
+ case legacy_hal::NAN_RESPONSE_STATS:
+ /* fall through */
+ case legacy_hal::NAN_RESPONSE_ERROR:
+ /* fall through */
+ default:
+ LOG(ERROR) << "Unknown or unhandled response type: " << msg.response_type;
+ return;
+ }
+ };
+
+ callback_handlers.on_event_disc_eng_event = [weak_ptr_this](
+ const legacy_hal::NanDiscEngEventInd& msg) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ NanClusterEventInd aidl_struct;
+ // event types defined identically - hence can be cast
+ aidl_struct.eventType = (NanClusterEventType)msg.event_type;
+ aidl_struct.addr = std::array<uint8_t, 6>();
+ std::copy(msg.data.mac_addr.addr, msg.data.mac_addr.addr + 6, std::begin(aidl_struct.addr));
+
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->eventClusterEvent(aidl_struct).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ };
+
+ callback_handlers.on_event_disabled = [weak_ptr_this](const legacy_hal::NanDisabledInd& msg) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ NanStatus status;
+ aidl_struct_util::convertToNanStatus(msg.reason, msg.nan_reason, sizeof(msg.nan_reason),
+ &status);
+
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->eventDisabled(status).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ };
+
+ callback_handlers.on_event_publish_terminated =
+ [weak_ptr_this](const legacy_hal::NanPublishTerminatedInd& msg) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ NanStatus status;
+ aidl_struct_util::convertToNanStatus(msg.reason, msg.nan_reason,
+ sizeof(msg.nan_reason), &status);
+
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->eventPublishTerminated(msg.publish_id, status).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ };
+
+ callback_handlers.on_event_subscribe_terminated =
+ [weak_ptr_this](const legacy_hal::NanSubscribeTerminatedInd& msg) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ NanStatus status;
+ aidl_struct_util::convertToNanStatus(msg.reason, msg.nan_reason,
+ sizeof(msg.nan_reason), &status);
+
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->eventSubscribeTerminated(msg.subscribe_id, status).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ };
+
+ callback_handlers.on_event_match = [weak_ptr_this](const legacy_hal::NanMatchInd& msg) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ NanMatchInd aidl_struct;
+ if (!aidl_struct_util::convertLegacyNanMatchIndToAidl(msg, &aidl_struct)) {
+ LOG(ERROR) << "Failed to convert nan capabilities response";
+ return;
+ }
+
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->eventMatch(aidl_struct).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ };
+
+ callback_handlers.on_event_match_expired = [weak_ptr_this](
+ const legacy_hal::NanMatchExpiredInd& msg) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->eventMatchExpired(msg.publish_subscribe_id, msg.requestor_instance_id)
+ .isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ };
+
+ callback_handlers.on_event_followup = [weak_ptr_this](const legacy_hal::NanFollowupInd& msg) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ NanFollowupReceivedInd aidl_struct;
+ if (!aidl_struct_util::convertLegacyNanFollowupIndToAidl(msg, &aidl_struct)) {
+ LOG(ERROR) << "Failed to convert nan capabilities response";
+ return;
+ }
+
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->eventFollowupReceived(aidl_struct).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ };
+
+ callback_handlers.on_event_transmit_follow_up =
+ [weak_ptr_this](const legacy_hal::NanTransmitFollowupInd& msg) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ NanStatus status;
+ aidl_struct_util::convertToNanStatus(msg.reason, msg.nan_reason,
+ sizeof(msg.nan_reason), &status);
+
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->eventTransmitFollowup(msg.id, status).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ };
+
+ callback_handlers.on_event_data_path_request =
+ [weak_ptr_this](const legacy_hal::NanDataPathRequestInd& msg) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ NanDataPathRequestInd aidl_struct;
+ if (!aidl_struct_util::convertLegacyNanDataPathRequestIndToAidl(msg,
+ &aidl_struct)) {
+ LOG(ERROR) << "Failed to convert nan capabilities response";
+ return;
+ }
+
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->eventDataPathRequest(aidl_struct).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ };
+
+ callback_handlers.on_event_data_path_confirm =
+ [weak_ptr_this](const legacy_hal::NanDataPathConfirmInd& msg) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ NanDataPathConfirmInd aidl_struct;
+ if (!aidl_struct_util::convertLegacyNanDataPathConfirmIndToAidl(msg,
+ &aidl_struct)) {
+ LOG(ERROR) << "Failed to convert nan capabilities response";
+ return;
+ }
+
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->eventDataPathConfirm(aidl_struct).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ };
+
+ callback_handlers.on_event_data_path_end =
+ [weak_ptr_this](const legacy_hal::NanDataPathEndInd& msg) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ for (int i = 0; i < msg.num_ndp_instances; ++i) {
+ if (!callback->eventDataPathTerminated(msg.ndp_instance_id[i]).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ }
+ };
+
+ callback_handlers.on_event_beacon_sdf_payload =
+ [](const legacy_hal::NanBeaconSdfPayloadInd& /* msg */) {
+ LOG(ERROR) << "on_event_beacon_sdf_payload - should not be called";
+ };
+
+ callback_handlers.on_event_range_request = [](const legacy_hal::NanRangeRequestInd& /* msg */) {
+ LOG(ERROR) << "on_event_range_request - should not be called";
+ };
+
+ callback_handlers.on_event_range_report = [](const legacy_hal::NanRangeReportInd& /* msg */) {
+ LOG(ERROR) << "on_event_range_report - should not be called";
+ };
+
+ callback_handlers.on_event_schedule_update =
+ [weak_ptr_this](const legacy_hal::NanDataPathScheduleUpdateInd& msg) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ NanDataPathScheduleUpdateInd aidl_struct;
+ if (!aidl_struct_util::convertLegacyNanDataPathScheduleUpdateIndToAidl(
+ msg, &aidl_struct)) {
+ LOG(ERROR) << "Failed to convert nan capabilities response";
+ return;
+ }
+
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->eventDataPathScheduleUpdate(aidl_struct).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ };
+
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->nanRegisterCallbackHandlers(ifname_, callback_handlers);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to register nan callbacks. Invalidating object";
+ invalidate();
+ }
+
+ // Register for iface state toggle events.
+ iface_util::IfaceEventHandlers event_handlers = {};
+ event_handlers.on_state_toggle_off_on = [weak_ptr_this](const std::string& /* iface_name */) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ // Tell framework that NAN has been disabled.
+ NanStatus status = {NanStatusCode::UNSUPPORTED_CONCURRENCY_NAN_DISABLED, ""};
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->eventDisabled(status).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ };
+ iface_util_.lock()->registerIfaceEventHandlers(ifname_, event_handlers);
+}
+
+void WifiNanIface::setWeakPtr(std::weak_ptr<WifiNanIface> ptr) {
+ weak_ptr_this_ = ptr;
+}
+
+void WifiNanIface::invalidate() {
+ if (!isValid()) {
+ return;
+ }
+ // send commands to HAL to actually disable and destroy interfaces
+ legacy_hal_.lock()->nanDisableRequest(ifname_, 0xFFFF);
+ legacy_hal_.lock()->nanDataInterfaceDelete(ifname_, 0xFFFE, "aware_data0");
+ legacy_hal_.lock()->nanDataInterfaceDelete(ifname_, 0xFFFD, "aware_data1");
+ iface_util_.lock()->unregisterIfaceEventHandlers(ifname_);
+ legacy_hal_.reset();
+ event_cb_handler_.invalidate();
+ is_valid_ = false;
+ if (is_dedicated_iface_) {
+ // If using a dedicated iface, set the iface down.
+ iface_util_.lock()->setUpState(ifname_, false);
+ }
+}
+
+bool WifiNanIface::isValid() {
+ return is_valid_;
+}
+
+std::string WifiNanIface::getName() {
+ return ifname_;
+}
+
+std::set<std::shared_ptr<IWifiNanIfaceEventCallback>> WifiNanIface::getEventCallbacks() {
+ LOG(ERROR) << "Using original getEventCallbacks";
+ return event_cb_handler_.getCallbacks();
+}
+
+ndk::ScopedAStatus WifiNanIface::getName(std::string* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::getNameInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiNanIface::registerEventCallback(
+ const std::shared_ptr<IWifiNanIfaceEventCallback>& callback) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::registerEventCallbackInternal, callback);
+}
+
+ndk::ScopedAStatus WifiNanIface::getCapabilitiesRequest(char16_t in_cmdId) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::getCapabilitiesRequestInternal, in_cmdId);
+}
+
+ndk::ScopedAStatus WifiNanIface::enableRequest(char16_t in_cmdId, const NanEnableRequest& in_msg1,
+ const NanConfigRequestSupplemental& in_msg2) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::enableRequestInternal, in_cmdId, in_msg1, in_msg2);
+}
+
+ndk::ScopedAStatus WifiNanIface::configRequest(char16_t in_cmdId, const NanConfigRequest& in_msg1,
+ const NanConfigRequestSupplemental& in_msg2) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::configRequestInternal, in_cmdId, in_msg1, in_msg2);
+}
+
+ndk::ScopedAStatus WifiNanIface::disableRequest(char16_t in_cmdId) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::disableRequestInternal, in_cmdId);
+}
+
+ndk::ScopedAStatus WifiNanIface::startPublishRequest(char16_t in_cmdId,
+ const NanPublishRequest& in_msg) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::startPublishRequestInternal, in_cmdId, in_msg);
+}
+
+ndk::ScopedAStatus WifiNanIface::stopPublishRequest(char16_t in_cmdId, int8_t in_sessionId) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::stopPublishRequestInternal, in_cmdId, in_sessionId);
+}
+
+ndk::ScopedAStatus WifiNanIface::startSubscribeRequest(char16_t in_cmdId,
+ const NanSubscribeRequest& in_msg) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::startSubscribeRequestInternal, in_cmdId, in_msg);
+}
+
+ndk::ScopedAStatus WifiNanIface::stopSubscribeRequest(char16_t in_cmdId, int8_t in_sessionId) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::stopSubscribeRequestInternal, in_cmdId, in_sessionId);
+}
+
+ndk::ScopedAStatus WifiNanIface::transmitFollowupRequest(char16_t in_cmdId,
+ const NanTransmitFollowupRequest& in_msg) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::transmitFollowupRequestInternal, in_cmdId, in_msg);
+}
+
+ndk::ScopedAStatus WifiNanIface::createDataInterfaceRequest(char16_t in_cmdId,
+ const std::string& in_ifaceName) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::createDataInterfaceRequestInternal, in_cmdId,
+ in_ifaceName);
+}
+
+ndk::ScopedAStatus WifiNanIface::deleteDataInterfaceRequest(char16_t in_cmdId,
+ const std::string& in_ifaceName) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::deleteDataInterfaceRequestInternal, in_cmdId,
+ in_ifaceName);
+}
+
+ndk::ScopedAStatus WifiNanIface::initiateDataPathRequest(char16_t in_cmdId,
+ const NanInitiateDataPathRequest& in_msg) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::initiateDataPathRequestInternal, in_cmdId, in_msg);
+}
+
+ndk::ScopedAStatus WifiNanIface::respondToDataPathIndicationRequest(
+ char16_t in_cmdId, const NanRespondToDataPathIndicationRequest& in_msg) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::respondToDataPathIndicationRequestInternal, in_cmdId,
+ in_msg);
+}
+
+ndk::ScopedAStatus WifiNanIface::terminateDataPathRequest(char16_t in_cmdId,
+ int32_t in_ndpInstanceId) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiNanIface::terminateDataPathRequestInternal, in_cmdId,
+ in_ndpInstanceId);
+}
+
+std::pair<std::string, ndk::ScopedAStatus> WifiNanIface::getNameInternal() {
+ return {ifname_, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiNanIface::registerEventCallbackInternal(
+ const std::shared_ptr<IWifiNanIfaceEventCallback>& callback) {
+ if (!event_cb_handler_.addCallback(callback)) {
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WifiNanIface::getCapabilitiesRequestInternal(char16_t cmd_id) {
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->nanGetCapabilities(ifname_, cmd_id);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiNanIface::enableRequestInternal(char16_t cmd_id,
+ const NanEnableRequest& msg1,
+ const NanConfigRequestSupplemental& msg2) {
+ legacy_hal::NanEnableRequest legacy_msg;
+ if (!aidl_struct_util::convertAidlNanEnableRequestToLegacy(msg1, msg2, &legacy_msg)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->nanEnableRequest(ifname_, cmd_id, legacy_msg);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiNanIface::configRequestInternal(char16_t cmd_id,
+ const NanConfigRequest& msg1,
+ const NanConfigRequestSupplemental& msg2) {
+ legacy_hal::NanConfigRequest legacy_msg;
+ if (!aidl_struct_util::convertAidlNanConfigRequestToLegacy(msg1, msg2, &legacy_msg)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->nanConfigRequest(ifname_, cmd_id, legacy_msg);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiNanIface::disableRequestInternal(char16_t cmd_id) {
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->nanDisableRequest(ifname_, cmd_id);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiNanIface::startPublishRequestInternal(char16_t cmd_id,
+ const NanPublishRequest& msg) {
+ legacy_hal::NanPublishRequest legacy_msg;
+ if (!aidl_struct_util::convertAidlNanPublishRequestToLegacy(msg, &legacy_msg)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->nanPublishRequest(ifname_, cmd_id, legacy_msg);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiNanIface::stopPublishRequestInternal(char16_t cmd_id, int8_t sessionId) {
+ legacy_hal::NanPublishCancelRequest legacy_msg;
+ legacy_msg.publish_id = sessionId;
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->nanPublishCancelRequest(ifname_, cmd_id, legacy_msg);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiNanIface::startSubscribeRequestInternal(char16_t cmd_id,
+ const NanSubscribeRequest& msg) {
+ legacy_hal::NanSubscribeRequest legacy_msg;
+ if (!aidl_struct_util::convertAidlNanSubscribeRequestToLegacy(msg, &legacy_msg)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->nanSubscribeRequest(ifname_, cmd_id, legacy_msg);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiNanIface::stopSubscribeRequestInternal(char16_t cmd_id, int8_t sessionId) {
+ legacy_hal::NanSubscribeCancelRequest legacy_msg;
+ legacy_msg.subscribe_id = sessionId;
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->nanSubscribeCancelRequest(ifname_, cmd_id, legacy_msg);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiNanIface::transmitFollowupRequestInternal(
+ char16_t cmd_id, const NanTransmitFollowupRequest& msg) {
+ legacy_hal::NanTransmitFollowupRequest legacy_msg;
+ if (!aidl_struct_util::convertAidlNanTransmitFollowupRequestToLegacy(msg, &legacy_msg)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->nanTransmitFollowupRequest(ifname_, cmd_id, legacy_msg);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiNanIface::createDataInterfaceRequestInternal(char16_t cmd_id,
+ const std::string& iface_name) {
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->nanDataInterfaceCreate(ifname_, cmd_id, iface_name);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+ndk::ScopedAStatus WifiNanIface::deleteDataInterfaceRequestInternal(char16_t cmd_id,
+ const std::string& iface_name) {
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->nanDataInterfaceDelete(ifname_, cmd_id, iface_name);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+ndk::ScopedAStatus WifiNanIface::initiateDataPathRequestInternal(
+ char16_t cmd_id, const NanInitiateDataPathRequest& msg) {
+ legacy_hal::NanDataPathInitiatorRequest legacy_msg;
+ if (!aidl_struct_util::convertAidlNanDataPathInitiatorRequestToLegacy(msg, &legacy_msg)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->nanDataRequestInitiator(ifname_, cmd_id, legacy_msg);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+ndk::ScopedAStatus WifiNanIface::respondToDataPathIndicationRequestInternal(
+ char16_t cmd_id, const NanRespondToDataPathIndicationRequest& msg) {
+ legacy_hal::NanDataPathIndicationResponse legacy_msg;
+ if (!aidl_struct_util::convertAidlNanDataPathIndicationResponseToLegacy(msg, &legacy_msg)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->nanDataIndicationResponse(ifname_, cmd_id, legacy_msg);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+ndk::ScopedAStatus WifiNanIface::terminateDataPathRequestInternal(char16_t cmd_id,
+ int32_t ndpInstanceId) {
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->nanDataEnd(ifname_, cmd_id, ndpInstanceId);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi_nan_iface.h b/wifi/aidl/default/wifi_nan_iface.h
new file mode 100644
index 0000000..1018905
--- /dev/null
+++ b/wifi/aidl/default/wifi_nan_iface.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_NAN_IFACE_H_
+#define WIFI_NAN_IFACE_H_
+
+#include <aidl/android/hardware/wifi/BnWifiNanIface.h>
+#include <aidl/android/hardware/wifi/IWifiNanIfaceEventCallback.h>
+#include <android-base/macros.h>
+
+#include "aidl_callback_util.h"
+#include "wifi_iface_util.h"
+#include "wifi_legacy_hal.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+/**
+ * AIDL interface object used to control a NAN Iface instance.
+ */
+class WifiNanIface : public BnWifiNanIface {
+ public:
+ WifiNanIface(const std::string& ifname, bool is_dedicated_iface,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util);
+
+ // Factory method - use instead of default constructor.
+ static std::shared_ptr<WifiNanIface> create(
+ const std::string& ifname, bool is_dedicated_iface,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util);
+
+ // Refer to |WifiChip::invalidate()|.
+ void invalidate();
+ bool isValid();
+ std::string getName();
+
+ // AIDL methods exposed.
+ ndk::ScopedAStatus getName(std::string* _aidl_return) override;
+ ndk::ScopedAStatus registerEventCallback(
+ const std::shared_ptr<IWifiNanIfaceEventCallback>& in_callback) override;
+ ndk::ScopedAStatus getCapabilitiesRequest(char16_t in_cmdId) override;
+ ndk::ScopedAStatus enableRequest(char16_t in_cmdId, const NanEnableRequest& in_msg1,
+ const NanConfigRequestSupplemental& in_msg2) override;
+ ndk::ScopedAStatus configRequest(char16_t in_cmdId, const NanConfigRequest& in_msg1,
+ const NanConfigRequestSupplemental& in_msg2) override;
+ ndk::ScopedAStatus disableRequest(char16_t in_cmdId) override;
+ ndk::ScopedAStatus startPublishRequest(char16_t in_cmdId,
+ const NanPublishRequest& in_msg) override;
+ ndk::ScopedAStatus stopPublishRequest(char16_t in_cmdId, int8_t in_sessionId) override;
+ ndk::ScopedAStatus startSubscribeRequest(char16_t in_cmdId,
+ const NanSubscribeRequest& in_msg) override;
+ ndk::ScopedAStatus stopSubscribeRequest(char16_t in_cmdId, int8_t in_sessionId) override;
+ ndk::ScopedAStatus transmitFollowupRequest(char16_t in_cmdId,
+ const NanTransmitFollowupRequest& in_msg) override;
+ ndk::ScopedAStatus createDataInterfaceRequest(char16_t in_cmdId,
+ const std::string& in_ifaceName) override;
+ ndk::ScopedAStatus deleteDataInterfaceRequest(char16_t in_cmdId,
+ const std::string& in_ifaceName) override;
+ ndk::ScopedAStatus initiateDataPathRequest(char16_t in_cmdId,
+ const NanInitiateDataPathRequest& in_msg) override;
+ ndk::ScopedAStatus respondToDataPathIndicationRequest(
+ char16_t in_cmdId, const NanRespondToDataPathIndicationRequest& in_msg) override;
+ ndk::ScopedAStatus terminateDataPathRequest(char16_t in_cmdId,
+ int32_t in_ndpInstanceId) override;
+
+ protected:
+ // Accessible to child class in the gTest suite.
+ void setWeakPtr(std::weak_ptr<WifiNanIface> ptr);
+ void registerCallbackHandlers();
+
+ private:
+ // Corresponding worker functions for the AIDL methods.
+ std::pair<std::string, ndk::ScopedAStatus> getNameInternal();
+ ndk::ScopedAStatus registerEventCallbackInternal(
+ const std::shared_ptr<IWifiNanIfaceEventCallback>& callback);
+ ndk::ScopedAStatus getCapabilitiesRequestInternal(char16_t cmd_id);
+ ndk::ScopedAStatus enableRequestInternal(char16_t cmd_id, const NanEnableRequest& msg1,
+ const NanConfigRequestSupplemental& msg2);
+ ndk::ScopedAStatus configRequestInternal(char16_t cmd_id, const NanConfigRequest& msg1,
+ const NanConfigRequestSupplemental& msg2);
+ ndk::ScopedAStatus disableRequestInternal(char16_t cmd_id);
+ ndk::ScopedAStatus startPublishRequestInternal(char16_t cmd_id, const NanPublishRequest& msg);
+ ndk::ScopedAStatus stopPublishRequestInternal(char16_t cmd_id, int8_t sessionId);
+ ndk::ScopedAStatus startSubscribeRequestInternal(char16_t cmd_id,
+ const NanSubscribeRequest& msg);
+ ndk::ScopedAStatus stopSubscribeRequestInternal(char16_t cmd_id, int8_t sessionId);
+ ndk::ScopedAStatus transmitFollowupRequestInternal(char16_t cmd_id,
+ const NanTransmitFollowupRequest& msg);
+ ndk::ScopedAStatus createDataInterfaceRequestInternal(char16_t cmd_id,
+ const std::string& iface_name);
+ ndk::ScopedAStatus deleteDataInterfaceRequestInternal(char16_t cmd_id,
+ const std::string& iface_name);
+ ndk::ScopedAStatus initiateDataPathRequestInternal(char16_t cmd_id,
+ const NanInitiateDataPathRequest& msg);
+ ndk::ScopedAStatus respondToDataPathIndicationRequestInternal(
+ char16_t cmd_id, const NanRespondToDataPathIndicationRequest& msg);
+ ndk::ScopedAStatus terminateDataPathRequestInternal(char16_t cmd_id, int32_t ndpInstanceId);
+
+ // Overridden in the gTest suite.
+ virtual std::set<std::shared_ptr<IWifiNanIfaceEventCallback>> getEventCallbacks();
+
+ std::string ifname_;
+ bool is_dedicated_iface_;
+ std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
+ std::weak_ptr<iface_util::WifiIfaceUtil> iface_util_;
+ bool is_valid_;
+ std::weak_ptr<WifiNanIface> weak_ptr_this_;
+ aidl_callback_util::AidlCallbackHandler<IWifiNanIfaceEventCallback> event_cb_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(WifiNanIface);
+};
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_NAN_IFACE_H_
diff --git a/wifi/aidl/default/wifi_p2p_iface.cpp b/wifi/aidl/default/wifi_p2p_iface.cpp
new file mode 100644
index 0000000..48ec6d6
--- /dev/null
+++ b/wifi/aidl/default/wifi_p2p_iface.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_p2p_iface.h"
+
+#include <android-base/logging.h>
+
+#include "aidl_return_util.h"
+#include "wifi_status_util.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+using aidl_return_util::validateAndCall;
+
+WifiP2pIface::WifiP2pIface(const std::string& ifname,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal)
+ : ifname_(ifname), legacy_hal_(legacy_hal), is_valid_(true) {}
+
+void WifiP2pIface::invalidate() {
+ legacy_hal_.reset();
+ is_valid_ = false;
+}
+
+bool WifiP2pIface::isValid() {
+ return is_valid_;
+}
+
+std::string WifiP2pIface::getName() {
+ return ifname_;
+}
+
+ndk::ScopedAStatus WifiP2pIface::getName(std::string* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiP2pIface::getNameInternal, _aidl_return);
+}
+
+std::pair<std::string, ndk::ScopedAStatus> WifiP2pIface::getNameInternal() {
+ return {ifname_, ndk::ScopedAStatus::ok()};
+}
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi_p2p_iface.h b/wifi/aidl/default/wifi_p2p_iface.h
new file mode 100644
index 0000000..d17470e
--- /dev/null
+++ b/wifi/aidl/default/wifi_p2p_iface.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_P2P_IFACE_H_
+#define WIFI_P2P_IFACE_H_
+
+#include <aidl/android/hardware/wifi/BnWifiP2pIface.h>
+#include <android-base/macros.h>
+
+#include "wifi_legacy_hal.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+/**
+ * AIDL interface object used to control a P2P Iface instance.
+ */
+class WifiP2pIface : public BnWifiP2pIface {
+ public:
+ WifiP2pIface(const std::string& ifname,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal);
+ // Refer to |WifiChip::invalidate()|.
+ void invalidate();
+ bool isValid();
+ std::string getName();
+
+ // AIDL methods exposed.
+ ndk::ScopedAStatus getName(std::string* _aidl_return) override;
+
+ private:
+ // Corresponding worker functions for the AIDL methods.
+ std::pair<std::string, ndk::ScopedAStatus> getNameInternal();
+
+ std::string ifname_;
+ std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
+ bool is_valid_;
+
+ DISALLOW_COPY_AND_ASSIGN(WifiP2pIface);
+};
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_P2P_IFACE_H_
diff --git a/wifi/aidl/default/wifi_rtt_controller.cpp b/wifi/aidl/default/wifi_rtt_controller.cpp
new file mode 100644
index 0000000..856c3cd
--- /dev/null
+++ b/wifi/aidl/default/wifi_rtt_controller.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_rtt_controller.h"
+
+#include <android-base/logging.h>
+
+#include "aidl_return_util.h"
+#include "aidl_struct_util.h"
+#include "wifi_status_util.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+using aidl_return_util::validateAndCall;
+
+WifiRttController::WifiRttController(const std::string& iface_name,
+ const std::shared_ptr<IWifiStaIface>& bound_iface,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal)
+ : ifname_(iface_name), bound_iface_(bound_iface), legacy_hal_(legacy_hal), is_valid_(true) {}
+
+std::shared_ptr<WifiRttController> WifiRttController::create(
+ const std::string& iface_name, const std::shared_ptr<IWifiStaIface>& bound_iface,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal) {
+ std::shared_ptr<WifiRttController> ptr =
+ ndk::SharedRefBase::make<WifiRttController>(iface_name, bound_iface, legacy_hal);
+ std::weak_ptr<WifiRttController> weak_ptr_this(ptr);
+ ptr->setWeakPtr(weak_ptr_this);
+ return ptr;
+}
+
+void WifiRttController::invalidate() {
+ legacy_hal_.reset();
+ event_callbacks_.clear();
+ is_valid_ = false;
+};
+
+bool WifiRttController::isValid() {
+ return is_valid_;
+}
+
+void WifiRttController::setWeakPtr(std::weak_ptr<WifiRttController> ptr) {
+ weak_ptr_this_ = ptr;
+}
+
+std::vector<std::shared_ptr<IWifiRttControllerEventCallback>>
+WifiRttController::getEventCallbacks() {
+ return event_callbacks_;
+}
+
+std::string WifiRttController::getIfaceName() {
+ return ifname_;
+}
+
+ndk::ScopedAStatus WifiRttController::getBoundIface(std::shared_ptr<IWifiStaIface>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID,
+ &WifiRttController::getBoundIfaceInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiRttController::registerEventCallback(
+ const std::shared_ptr<IWifiRttControllerEventCallback>& callback) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID,
+ &WifiRttController::registerEventCallbackInternal, callback);
+}
+
+ndk::ScopedAStatus WifiRttController::rangeRequest(int32_t in_cmdId,
+ const std::vector<RttConfig>& in_rttConfigs) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID,
+ &WifiRttController::rangeRequestInternal, in_cmdId, in_rttConfigs);
+}
+
+ndk::ScopedAStatus WifiRttController::rangeCancel(int32_t in_cmdId,
+ const std::vector<MacAddress>& in_addrs) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID,
+ &WifiRttController::rangeCancelInternal, in_cmdId, in_addrs);
+}
+
+ndk::ScopedAStatus WifiRttController::getCapabilities(RttCapabilities* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID,
+ &WifiRttController::getCapabilitiesInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiRttController::setLci(int32_t in_cmdId, const RttLciInformation& in_lci) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID,
+ &WifiRttController::setLciInternal, in_cmdId, in_lci);
+}
+
+ndk::ScopedAStatus WifiRttController::setLcr(int32_t in_cmdId, const RttLcrInformation& in_lcr) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID,
+ &WifiRttController::setLcrInternal, in_cmdId, in_lcr);
+}
+
+ndk::ScopedAStatus WifiRttController::getResponderInfo(RttResponder* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID,
+ &WifiRttController::getResponderInfoInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiRttController::enableResponder(int32_t in_cmdId,
+ const WifiChannelInfo& in_channelHint,
+ int32_t in_maxDurationInSeconds,
+ const RttResponder& in_info) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID,
+ &WifiRttController::enableResponderInternal, in_cmdId, in_channelHint,
+ in_maxDurationInSeconds, in_info);
+}
+
+ndk::ScopedAStatus WifiRttController::disableResponder(int32_t in_cmdId) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_RTT_CONTROLLER_INVALID,
+ &WifiRttController::disableResponderInternal, in_cmdId);
+}
+
+std::pair<std::shared_ptr<IWifiStaIface>, ndk::ScopedAStatus>
+WifiRttController::getBoundIfaceInternal() {
+ return {bound_iface_, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiRttController::registerEventCallbackInternal(
+ const std::shared_ptr<IWifiRttControllerEventCallback>& callback) {
+ event_callbacks_.emplace_back(callback);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WifiRttController::rangeRequestInternal(
+ int32_t cmd_id, const std::vector<RttConfig>& rtt_configs) {
+ std::vector<legacy_hal::wifi_rtt_config> legacy_configs;
+ if (!aidl_struct_util::convertAidlVectorOfRttConfigToLegacy(rtt_configs, &legacy_configs)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ std::weak_ptr<WifiRttController> weak_ptr_this = weak_ptr_this_;
+ const auto& on_results_callback =
+ [weak_ptr_this](legacy_hal::wifi_request_id id,
+ const std::vector<const legacy_hal::wifi_rtt_result*>& results) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ std::vector<RttResult> aidl_results;
+ if (!aidl_struct_util::convertLegacyVectorOfRttResultToAidl(results,
+ &aidl_results)) {
+ LOG(ERROR) << "Failed to convert rtt results to AIDL structs";
+ return;
+ }
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->onResults(id, aidl_results).isOk()) {
+ LOG(ERROR) << "Failed to invoke the callback";
+ }
+ }
+ };
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->startRttRangeRequest(
+ ifname_, cmd_id, legacy_configs, on_results_callback);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiRttController::rangeCancelInternal(int32_t cmd_id,
+ const std::vector<MacAddress>& addrs) {
+ std::vector<std::array<uint8_t, ETH_ALEN>> legacy_addrs;
+ for (const auto& addr : addrs) {
+ std::array<uint8_t, ETH_ALEN> addr_array;
+ std::copy_n(addr.data.begin(), ETH_ALEN, addr_array.begin());
+ legacy_addrs.push_back(addr_array);
+ }
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->cancelRttRangeRequest(ifname_, cmd_id, legacy_addrs);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+std::pair<RttCapabilities, ndk::ScopedAStatus> WifiRttController::getCapabilitiesInternal() {
+ legacy_hal::wifi_error legacy_status;
+ legacy_hal::wifi_rtt_capabilities legacy_caps;
+ std::tie(legacy_status, legacy_caps) = legacy_hal_.lock()->getRttCapabilities(ifname_);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ return {RttCapabilities{}, createWifiStatusFromLegacyError(legacy_status)};
+ }
+ RttCapabilities aidl_caps;
+ if (!aidl_struct_util::convertLegacyRttCapabilitiesToAidl(legacy_caps, &aidl_caps)) {
+ return {RttCapabilities{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {aidl_caps, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiRttController::setLciInternal(int32_t cmd_id, const RttLciInformation& lci) {
+ legacy_hal::wifi_lci_information legacy_lci;
+ if (!aidl_struct_util::convertAidlRttLciInformationToLegacy(lci, &legacy_lci)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->setRttLci(ifname_, cmd_id, legacy_lci);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiRttController::setLcrInternal(int32_t cmd_id, const RttLcrInformation& lcr) {
+ legacy_hal::wifi_lcr_information legacy_lcr;
+ if (!aidl_struct_util::convertAidlRttLcrInformationToLegacy(lcr, &legacy_lcr)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->setRttLcr(ifname_, cmd_id, legacy_lcr);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+std::pair<RttResponder, ndk::ScopedAStatus> WifiRttController::getResponderInfoInternal() {
+ legacy_hal::wifi_error legacy_status;
+ legacy_hal::wifi_rtt_responder legacy_responder;
+ std::tie(legacy_status, legacy_responder) = legacy_hal_.lock()->getRttResponderInfo(ifname_);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ return {RttResponder{}, createWifiStatusFromLegacyError(legacy_status)};
+ }
+ RttResponder aidl_responder;
+ if (!aidl_struct_util::convertLegacyRttResponderToAidl(legacy_responder, &aidl_responder)) {
+ return {RttResponder{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {aidl_responder, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiRttController::enableResponderInternal(int32_t cmd_id,
+ const WifiChannelInfo& channel_hint,
+ int32_t max_duration_seconds,
+ const RttResponder& info) {
+ legacy_hal::wifi_channel_info legacy_channel_info;
+ if (!aidl_struct_util::convertAidlWifiChannelInfoToLegacy(channel_hint, &legacy_channel_info)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ legacy_hal::wifi_rtt_responder legacy_responder;
+ if (!aidl_struct_util::convertAidlRttResponderToLegacy(info, &legacy_responder)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->enableRttResponder(
+ ifname_, cmd_id, legacy_channel_info, max_duration_seconds, legacy_responder);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiRttController::disableResponderInternal(int32_t cmd_id) {
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->disableRttResponder(ifname_, cmd_id);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi_rtt_controller.h b/wifi/aidl/default/wifi_rtt_controller.h
new file mode 100644
index 0000000..db690c3
--- /dev/null
+++ b/wifi/aidl/default/wifi_rtt_controller.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_RTT_CONTROLLER_H_
+#define WIFI_RTT_CONTROLLER_H_
+
+#include <aidl/android/hardware/wifi/BnWifiRttController.h>
+#include <aidl/android/hardware/wifi/IWifiRttControllerEventCallback.h>
+#include <aidl/android/hardware/wifi/IWifiStaIface.h>
+#include <android-base/macros.h>
+
+#include "wifi_legacy_hal.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+/**
+ * AIDL interface object used to control all RTT operations.
+ */
+class WifiRttController : public BnWifiRttController {
+ public:
+ WifiRttController(const std::string& iface_name,
+ const std::shared_ptr<IWifiStaIface>& bound_iface,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal);
+ // Factory method - use instead of default constructor.
+ static std::shared_ptr<WifiRttController> create(
+ const std::string& iface_name, const std::shared_ptr<IWifiStaIface>& bound_iface,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal);
+
+ // Refer to |WifiChip::invalidate()|.
+ void invalidate();
+ bool isValid();
+ std::vector<std::shared_ptr<IWifiRttControllerEventCallback>> getEventCallbacks();
+ std::string getIfaceName();
+
+ // AIDL methods exposed.
+ ndk::ScopedAStatus getBoundIface(std::shared_ptr<IWifiStaIface>* _aidl_return) override;
+ ndk::ScopedAStatus registerEventCallback(
+ const std::shared_ptr<IWifiRttControllerEventCallback>& callback) override;
+ ndk::ScopedAStatus rangeRequest(int32_t in_cmdId,
+ const std::vector<RttConfig>& in_rttConfigs) override;
+ ndk::ScopedAStatus rangeCancel(int32_t in_cmdId,
+ const std::vector<MacAddress>& in_addrs) override;
+ ndk::ScopedAStatus getCapabilities(RttCapabilities* _aidl_return) override;
+ ndk::ScopedAStatus setLci(int32_t in_cmdId, const RttLciInformation& in_lci) override;
+ ndk::ScopedAStatus setLcr(int32_t in_cmdId, const RttLcrInformation& in_lcr) override;
+ ndk::ScopedAStatus getResponderInfo(RttResponder* _aidl_return) override;
+ ndk::ScopedAStatus enableResponder(int32_t in_cmdId, const WifiChannelInfo& in_channelHint,
+ int32_t in_maxDurationInSeconds,
+ const RttResponder& in_info) override;
+ ndk::ScopedAStatus disableResponder(int32_t in_cmdId) override;
+
+ private:
+ // Corresponding worker functions for the AIDL methods.
+ std::pair<std::shared_ptr<IWifiStaIface>, ndk::ScopedAStatus> getBoundIfaceInternal();
+ ndk::ScopedAStatus registerEventCallbackInternal(
+ const std::shared_ptr<IWifiRttControllerEventCallback>& callback);
+ ndk::ScopedAStatus rangeRequestInternal(int32_t cmd_id,
+ const std::vector<RttConfig>& rtt_configs);
+ ndk::ScopedAStatus rangeCancelInternal(int32_t cmd_id, const std::vector<MacAddress>& addrs);
+ std::pair<RttCapabilities, ndk::ScopedAStatus> getCapabilitiesInternal();
+ ndk::ScopedAStatus setLciInternal(int32_t cmd_id, const RttLciInformation& lci);
+ ndk::ScopedAStatus setLcrInternal(int32_t cmd_id, const RttLcrInformation& lcr);
+ std::pair<RttResponder, ndk::ScopedAStatus> getResponderInfoInternal();
+ ndk::ScopedAStatus enableResponderInternal(int32_t cmd_id, const WifiChannelInfo& channel_hint,
+ int32_t max_duration_seconds,
+ const RttResponder& info);
+ ndk::ScopedAStatus disableResponderInternal(int32_t cmd_id);
+
+ void setWeakPtr(std::weak_ptr<WifiRttController> ptr);
+
+ std::string ifname_;
+ std::shared_ptr<IWifiStaIface> bound_iface_;
+ std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
+ std::vector<std::shared_ptr<IWifiRttControllerEventCallback>> event_callbacks_;
+ std::weak_ptr<WifiRttController> weak_ptr_this_;
+ bool is_valid_;
+
+ DISALLOW_COPY_AND_ASSIGN(WifiRttController);
+};
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_RTT_CONTROLLER_H_
diff --git a/wifi/aidl/default/wifi_sta_iface.cpp b/wifi/aidl/default/wifi_sta_iface.cpp
new file mode 100644
index 0000000..ce90349
--- /dev/null
+++ b/wifi/aidl/default/wifi_sta_iface.cpp
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_sta_iface.h"
+
+#include <android-base/logging.h>
+
+#include "aidl_return_util.h"
+#include "aidl_struct_util.h"
+#include "wifi_status_util.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+using aidl_return_util::validateAndCall;
+
+WifiStaIface::WifiStaIface(const std::string& ifname,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util)
+ : ifname_(ifname), legacy_hal_(legacy_hal), iface_util_(iface_util), is_valid_(true) {
+ // Turn on DFS channel usage for STA iface.
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->setDfsFlag(ifname_, true);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ LOG(ERROR) << "Failed to set DFS flag; DFS channels may be unavailable.";
+ }
+}
+
+std::shared_ptr<WifiStaIface> WifiStaIface::create(
+ const std::string& ifname, const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util) {
+ std::shared_ptr<WifiStaIface> ptr =
+ ndk::SharedRefBase::make<WifiStaIface>(ifname, legacy_hal, iface_util);
+ std::weak_ptr<WifiStaIface> weak_ptr_this(ptr);
+ ptr->setWeakPtr(weak_ptr_this);
+ return ptr;
+}
+
+void WifiStaIface::invalidate() {
+ legacy_hal_.reset();
+ event_cb_handler_.invalidate();
+ is_valid_ = false;
+}
+
+void WifiStaIface::setWeakPtr(std::weak_ptr<WifiStaIface> ptr) {
+ weak_ptr_this_ = ptr;
+}
+
+bool WifiStaIface::isValid() {
+ return is_valid_;
+}
+
+std::string WifiStaIface::getName() {
+ return ifname_;
+}
+
+std::set<std::shared_ptr<IWifiStaIfaceEventCallback>> WifiStaIface::getEventCallbacks() {
+ return event_cb_handler_.getCallbacks();
+}
+
+ndk::ScopedAStatus WifiStaIface::getName(std::string* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::getNameInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiStaIface::registerEventCallback(
+ const std::shared_ptr<IWifiStaIfaceEventCallback>& in_callback) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::registerEventCallbackInternal, in_callback);
+}
+
+ndk::ScopedAStatus WifiStaIface::getCapabilities(
+ IWifiStaIface::StaIfaceCapabilityMask* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::getCapabilitiesInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiStaIface::getApfPacketFilterCapabilities(
+ StaApfPacketFilterCapabilities* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::getApfPacketFilterCapabilitiesInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiStaIface::installApfPacketFilter(const std::vector<uint8_t>& in_program) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::installApfPacketFilterInternal, in_program);
+}
+
+ndk::ScopedAStatus WifiStaIface::readApfPacketFilterData(std::vector<uint8_t>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::readApfPacketFilterDataInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiStaIface::getBackgroundScanCapabilities(
+ StaBackgroundScanCapabilities* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::getBackgroundScanCapabilitiesInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiStaIface::getValidFrequenciesForBand(WifiBand in_band,
+ std::vector<int32_t>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::getValidFrequenciesForBandInternal, _aidl_return,
+ in_band);
+}
+
+ndk::ScopedAStatus WifiStaIface::startBackgroundScan(int32_t in_cmdId,
+ const StaBackgroundScanParameters& in_params) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::startBackgroundScanInternal, in_cmdId, in_params);
+}
+
+ndk::ScopedAStatus WifiStaIface::stopBackgroundScan(int32_t in_cmdId) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::stopBackgroundScanInternal, in_cmdId);
+}
+
+ndk::ScopedAStatus WifiStaIface::enableLinkLayerStatsCollection(bool in_debug) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::enableLinkLayerStatsCollectionInternal, in_debug);
+}
+
+ndk::ScopedAStatus WifiStaIface::disableLinkLayerStatsCollection() {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::disableLinkLayerStatsCollectionInternal);
+}
+
+ndk::ScopedAStatus WifiStaIface::getLinkLayerStats(StaLinkLayerStats* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::getLinkLayerStatsInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiStaIface::startRssiMonitoring(int32_t in_cmdId, int32_t in_maxRssi,
+ int32_t in_minRssi) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::startRssiMonitoringInternal, in_cmdId, in_maxRssi,
+ in_minRssi);
+}
+
+ndk::ScopedAStatus WifiStaIface::stopRssiMonitoring(int32_t in_cmdId) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::stopRssiMonitoringInternal, in_cmdId);
+}
+
+ndk::ScopedAStatus WifiStaIface::getRoamingCapabilities(StaRoamingCapabilities* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::getRoamingCapabilitiesInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiStaIface::configureRoaming(const StaRoamingConfig& in_config) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::configureRoamingInternal, in_config);
+}
+
+ndk::ScopedAStatus WifiStaIface::setRoamingState(StaRoamingState in_state) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::setRoamingStateInternal, in_state);
+}
+
+ndk::ScopedAStatus WifiStaIface::enableNdOffload(bool in_enable) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::enableNdOffloadInternal, in_enable);
+}
+
+ndk::ScopedAStatus WifiStaIface::startSendingKeepAlivePackets(
+ int32_t in_cmdId, const std::vector<uint8_t>& in_ipPacketData, char16_t in_etherType,
+ const std::array<uint8_t, 6>& in_srcAddress, const std::array<uint8_t, 6>& in_dstAddress,
+ int32_t in_periodInMs) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::startSendingKeepAlivePacketsInternal, in_cmdId,
+ in_ipPacketData, in_etherType, in_srcAddress, in_dstAddress,
+ in_periodInMs);
+}
+
+ndk::ScopedAStatus WifiStaIface::stopSendingKeepAlivePackets(int32_t in_cmdId) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::stopSendingKeepAlivePacketsInternal, in_cmdId);
+}
+
+ndk::ScopedAStatus WifiStaIface::startDebugPacketFateMonitoring() {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::startDebugPacketFateMonitoringInternal);
+}
+
+ndk::ScopedAStatus WifiStaIface::getDebugTxPacketFates(
+ std::vector<WifiDebugTxPacketFateReport>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::getDebugTxPacketFatesInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiStaIface::getDebugRxPacketFates(
+ std::vector<WifiDebugRxPacketFateReport>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::getDebugRxPacketFatesInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiStaIface::setMacAddress(const std::array<uint8_t, 6>& in_mac) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::setMacAddressInternal, in_mac);
+}
+
+ndk::ScopedAStatus WifiStaIface::getFactoryMacAddress(std::array<uint8_t, 6>* _aidl_return) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::getFactoryMacAddressInternal, _aidl_return);
+}
+
+ndk::ScopedAStatus WifiStaIface::setScanMode(bool in_enable) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiStaIface::setScanModeInternal, in_enable);
+}
+
+std::pair<std::string, ndk::ScopedAStatus> WifiStaIface::getNameInternal() {
+ return {ifname_, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiStaIface::registerEventCallbackInternal(
+ const std::shared_ptr<IWifiStaIfaceEventCallback>& callback) {
+ if (!event_cb_handler_.addCallback(callback)) {
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+std::pair<IWifiStaIface::StaIfaceCapabilityMask, ndk::ScopedAStatus>
+WifiStaIface::getCapabilitiesInternal() {
+ legacy_hal::wifi_error legacy_status;
+ uint64_t legacy_feature_set;
+ std::tie(legacy_status, legacy_feature_set) =
+ legacy_hal_.lock()->getSupportedFeatureSet(ifname_);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ return {IWifiStaIface::StaIfaceCapabilityMask{},
+ createWifiStatusFromLegacyError(legacy_status)};
+ }
+ uint32_t legacy_logger_feature_set;
+ std::tie(legacy_status, legacy_logger_feature_set) =
+ legacy_hal_.lock()->getLoggerSupportedFeatureSet(ifname_);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ // some devices don't support querying logger feature set
+ legacy_logger_feature_set = 0;
+ }
+ uint32_t aidl_caps;
+ if (!aidl_struct_util::convertLegacyFeaturesToAidlStaCapabilities(
+ legacy_feature_set, legacy_logger_feature_set, &aidl_caps)) {
+ return {IWifiStaIface::StaIfaceCapabilityMask{},
+ createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {static_cast<IWifiStaIface::StaIfaceCapabilityMask>(aidl_caps),
+ ndk::ScopedAStatus::ok()};
+}
+
+std::pair<StaApfPacketFilterCapabilities, ndk::ScopedAStatus>
+WifiStaIface::getApfPacketFilterCapabilitiesInternal() {
+ legacy_hal::wifi_error legacy_status;
+ legacy_hal::PacketFilterCapabilities legacy_caps;
+ std::tie(legacy_status, legacy_caps) = legacy_hal_.lock()->getPacketFilterCapabilities(ifname_);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ return {StaApfPacketFilterCapabilities{}, createWifiStatusFromLegacyError(legacy_status)};
+ }
+ StaApfPacketFilterCapabilities aidl_caps;
+ if (!aidl_struct_util::convertLegacyApfCapabilitiesToAidl(legacy_caps, &aidl_caps)) {
+ return {StaApfPacketFilterCapabilities{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {aidl_caps, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiStaIface::installApfPacketFilterInternal(
+ const std::vector<uint8_t>& program) {
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->setPacketFilter(ifname_, program);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+std::pair<std::vector<uint8_t>, ndk::ScopedAStatus>
+WifiStaIface::readApfPacketFilterDataInternal() {
+ const std::pair<legacy_hal::wifi_error, std::vector<uint8_t>> legacy_status_and_data =
+ legacy_hal_.lock()->readApfPacketFilterData(ifname_);
+ return {std::move(legacy_status_and_data.second),
+ createWifiStatusFromLegacyError(legacy_status_and_data.first)};
+}
+
+std::pair<StaBackgroundScanCapabilities, ndk::ScopedAStatus>
+WifiStaIface::getBackgroundScanCapabilitiesInternal() {
+ legacy_hal::wifi_error legacy_status;
+ legacy_hal::wifi_gscan_capabilities legacy_caps;
+ std::tie(legacy_status, legacy_caps) = legacy_hal_.lock()->getGscanCapabilities(ifname_);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ return {StaBackgroundScanCapabilities{}, createWifiStatusFromLegacyError(legacy_status)};
+ }
+ StaBackgroundScanCapabilities aidl_caps;
+ if (!aidl_struct_util::convertLegacyGscanCapabilitiesToAidl(legacy_caps, &aidl_caps)) {
+ return {StaBackgroundScanCapabilities{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {aidl_caps, ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::vector<int32_t>, ndk::ScopedAStatus>
+WifiStaIface::getValidFrequenciesForBandInternal(WifiBand band) {
+ static_assert(sizeof(WifiChannelWidthInMhz) == sizeof(int32_t), "Size mismatch");
+ legacy_hal::wifi_error legacy_status;
+ std::vector<uint32_t> valid_frequencies;
+ std::tie(legacy_status, valid_frequencies) = legacy_hal_.lock()->getValidFrequenciesForBand(
+ ifname_, aidl_struct_util::convertAidlWifiBandToLegacy(band));
+ return {std::vector<int32_t>(valid_frequencies.begin(), valid_frequencies.end()),
+ createWifiStatusFromLegacyError(legacy_status)};
+}
+
+ndk::ScopedAStatus WifiStaIface::startBackgroundScanInternal(
+ int32_t cmd_id, const StaBackgroundScanParameters& params) {
+ legacy_hal::wifi_scan_cmd_params legacy_params;
+ if (!aidl_struct_util::convertAidlGscanParamsToLegacy(params, &legacy_params)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ std::weak_ptr<WifiStaIface> weak_ptr_this = weak_ptr_this_;
+ const auto& on_failure_callback = [weak_ptr_this](legacy_hal::wifi_request_id id) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->onBackgroundScanFailure(id).isOk()) {
+ LOG(ERROR) << "Failed to invoke onBackgroundScanFailure callback";
+ }
+ }
+ };
+ const auto& on_results_callback =
+ [weak_ptr_this](legacy_hal::wifi_request_id id,
+ const std::vector<legacy_hal::wifi_cached_scan_results>& results) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ std::vector<StaScanData> aidl_scan_datas;
+ if (!aidl_struct_util::convertLegacyVectorOfCachedGscanResultsToAidl(
+ results, &aidl_scan_datas)) {
+ LOG(ERROR) << "Failed to convert scan results to AIDL structs";
+ return;
+ }
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->onBackgroundScanResults(id, aidl_scan_datas).isOk()) {
+ LOG(ERROR) << "Failed to invoke onBackgroundScanResults callback";
+ }
+ }
+ };
+ const auto& on_full_result_callback = [weak_ptr_this](
+ legacy_hal::wifi_request_id id,
+ const legacy_hal::wifi_scan_result* result,
+ uint32_t buckets_scanned) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ StaScanResult aidl_scan_result;
+ if (!aidl_struct_util::convertLegacyGscanResultToAidl(*result, true, &aidl_scan_result)) {
+ LOG(ERROR) << "Failed to convert full scan results to AIDL structs";
+ return;
+ }
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->onBackgroundFullScanResult(id, buckets_scanned, aidl_scan_result)
+ .isOk()) {
+ LOG(ERROR) << "Failed to invoke onBackgroundFullScanResult callback";
+ }
+ }
+ };
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->startGscan(ifname_, cmd_id, legacy_params, on_failure_callback,
+ on_results_callback, on_full_result_callback);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiStaIface::stopBackgroundScanInternal(int32_t cmd_id) {
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->stopGscan(ifname_, cmd_id);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiStaIface::enableLinkLayerStatsCollectionInternal(bool debug) {
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->enableLinkLayerStats(ifname_, debug);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiStaIface::disableLinkLayerStatsCollectionInternal() {
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->disableLinkLayerStats(ifname_);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+std::pair<StaLinkLayerStats, ndk::ScopedAStatus> WifiStaIface::getLinkLayerStatsInternal() {
+ legacy_hal::wifi_error legacy_status;
+ legacy_hal::LinkLayerStats legacy_stats;
+ std::tie(legacy_status, legacy_stats) = legacy_hal_.lock()->getLinkLayerStats(ifname_);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ return {StaLinkLayerStats{}, createWifiStatusFromLegacyError(legacy_status)};
+ }
+ StaLinkLayerStats aidl_stats;
+ if (!aidl_struct_util::convertLegacyLinkLayerStatsToAidl(legacy_stats, &aidl_stats)) {
+ return {StaLinkLayerStats{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {aidl_stats, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiStaIface::startRssiMonitoringInternal(int32_t cmd_id, int32_t max_rssi,
+ int32_t min_rssi) {
+ std::weak_ptr<WifiStaIface> weak_ptr_this = weak_ptr_this_;
+ const auto& on_threshold_breached_callback =
+ [weak_ptr_this](legacy_hal::wifi_request_id id, std::array<uint8_t, ETH_ALEN> bssid,
+ int8_t rssi) {
+ const auto shared_ptr_this = weak_ptr_this.lock();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->onRssiThresholdBreached(id, bssid, rssi).isOk()) {
+ LOG(ERROR) << "Failed to invoke onRssiThresholdBreached callback";
+ }
+ }
+ };
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->startRssiMonitoring(
+ ifname_, cmd_id, max_rssi, min_rssi, on_threshold_breached_callback);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiStaIface::stopRssiMonitoringInternal(int32_t cmd_id) {
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->stopRssiMonitoring(ifname_, cmd_id);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+std::pair<StaRoamingCapabilities, ndk::ScopedAStatus>
+WifiStaIface::getRoamingCapabilitiesInternal() {
+ legacy_hal::wifi_error legacy_status;
+ legacy_hal::wifi_roaming_capabilities legacy_caps;
+ std::tie(legacy_status, legacy_caps) = legacy_hal_.lock()->getRoamingCapabilities(ifname_);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ return {StaRoamingCapabilities{}, createWifiStatusFromLegacyError(legacy_status)};
+ }
+ StaRoamingCapabilities aidl_caps;
+ if (!aidl_struct_util::convertLegacyRoamingCapabilitiesToAidl(legacy_caps, &aidl_caps)) {
+ return {StaRoamingCapabilities{}, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {aidl_caps, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiStaIface::configureRoamingInternal(const StaRoamingConfig& config) {
+ legacy_hal::wifi_roaming_config legacy_config;
+ if (!aidl_struct_util::convertAidlRoamingConfigToLegacy(config, &legacy_config)) {
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
+ }
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->configureRoaming(ifname_, legacy_config);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiStaIface::setRoamingStateInternal(StaRoamingState state) {
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->enableFirmwareRoaming(
+ ifname_, aidl_struct_util::convertAidlRoamingStateToLegacy(state));
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiStaIface::enableNdOffloadInternal(bool enable) {
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->configureNdOffload(ifname_, enable);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiStaIface::startSendingKeepAlivePacketsInternal(
+ int32_t cmd_id, const std::vector<uint8_t>& ip_packet_data, char16_t ether_type,
+ const std::array<uint8_t, 6>& src_address, const std::array<uint8_t, 6>& dst_address,
+ int32_t period_in_ms) {
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->startSendingOffloadedPacket(
+ ifname_, cmd_id, ether_type, ip_packet_data, src_address, dst_address, period_in_ms);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiStaIface::stopSendingKeepAlivePacketsInternal(int32_t cmd_id) {
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->stopSendingOffloadedPacket(ifname_, cmd_id);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+ndk::ScopedAStatus WifiStaIface::startDebugPacketFateMonitoringInternal() {
+ legacy_hal::wifi_error legacy_status = legacy_hal_.lock()->startPktFateMonitoring(ifname_);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
+std::pair<std::vector<WifiDebugTxPacketFateReport>, ndk::ScopedAStatus>
+WifiStaIface::getDebugTxPacketFatesInternal() {
+ legacy_hal::wifi_error legacy_status;
+ std::vector<legacy_hal::wifi_tx_report> legacy_fates;
+ std::tie(legacy_status, legacy_fates) = legacy_hal_.lock()->getTxPktFates(ifname_);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ return {std::vector<WifiDebugTxPacketFateReport>(),
+ createWifiStatusFromLegacyError(legacy_status)};
+ }
+ std::vector<WifiDebugTxPacketFateReport> aidl_fates;
+ if (!aidl_struct_util::convertLegacyVectorOfDebugTxPacketFateToAidl(legacy_fates,
+ &aidl_fates)) {
+ return {std::vector<WifiDebugTxPacketFateReport>(),
+ createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {aidl_fates, ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::vector<WifiDebugRxPacketFateReport>, ndk::ScopedAStatus>
+WifiStaIface::getDebugRxPacketFatesInternal() {
+ legacy_hal::wifi_error legacy_status;
+ std::vector<legacy_hal::wifi_rx_report> legacy_fates;
+ std::tie(legacy_status, legacy_fates) = legacy_hal_.lock()->getRxPktFates(ifname_);
+ if (legacy_status != legacy_hal::WIFI_SUCCESS) {
+ return {std::vector<WifiDebugRxPacketFateReport>(),
+ createWifiStatusFromLegacyError(legacy_status)};
+ }
+ std::vector<WifiDebugRxPacketFateReport> aidl_fates;
+ if (!aidl_struct_util::convertLegacyVectorOfDebugRxPacketFateToAidl(legacy_fates,
+ &aidl_fates)) {
+ return {std::vector<WifiDebugRxPacketFateReport>(),
+ createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {aidl_fates, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiStaIface::setMacAddressInternal(const std::array<uint8_t, 6>& mac) {
+ bool status = iface_util_.lock()->setMacAddress(ifname_, mac);
+ if (!status) {
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+std::pair<std::array<uint8_t, 6>, ndk::ScopedAStatus> WifiStaIface::getFactoryMacAddressInternal() {
+ std::array<uint8_t, 6> mac = iface_util_.lock()->getFactoryMacAddress(ifname_);
+ if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 && mac[4] == 0 && mac[5] == 0) {
+ return {mac, createWifiStatus(WifiStatusCode::ERROR_UNKNOWN)};
+ }
+ return {mac, ndk::ScopedAStatus::ok()};
+}
+
+ndk::ScopedAStatus WifiStaIface::setScanModeInternal(bool enable) {
+ // OEM's need to implement this on their devices if needed.
+ LOG(WARNING) << "setScanModeInternal(" << enable << ") not supported";
+ return createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED);
+}
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi_sta_iface.h b/wifi/aidl/default/wifi_sta_iface.h
new file mode 100644
index 0000000..8ac3470
--- /dev/null
+++ b/wifi/aidl/default/wifi_sta_iface.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_STA_IFACE_H_
+#define WIFI_STA_IFACE_H_
+
+#include <aidl/android/hardware/wifi/BnWifiStaIface.h>
+#include <aidl/android/hardware/wifi/IWifiStaIfaceEventCallback.h>
+#include <android-base/macros.h>
+
+#include "aidl_callback_util.h"
+#include "wifi_iface_util.h"
+#include "wifi_legacy_hal.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+/**
+ * AIDL interface object used to control a STA Iface instance.
+ */
+class WifiStaIface : public BnWifiStaIface {
+ public:
+ WifiStaIface(const std::string& ifname,
+ const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util);
+
+ // Factory method - use instead of default constructor.
+ static std::shared_ptr<WifiStaIface> create(
+ const std::string& ifname, const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+ const std::weak_ptr<iface_util::WifiIfaceUtil> iface_util);
+
+ // Refer to |WifiChip::invalidate()|.
+ void invalidate();
+ bool isValid();
+ std::set<std::shared_ptr<IWifiStaIfaceEventCallback>> getEventCallbacks();
+ std::string getName();
+
+ // AIDL methods exposed.
+ ndk::ScopedAStatus getName(std::string* _aidl_return) override;
+ ndk::ScopedAStatus registerEventCallback(
+ const std::shared_ptr<IWifiStaIfaceEventCallback>& in_callback) override;
+ ndk::ScopedAStatus getCapabilities(
+ IWifiStaIface::StaIfaceCapabilityMask* _aidl_return) override;
+ ndk::ScopedAStatus getApfPacketFilterCapabilities(
+ StaApfPacketFilterCapabilities* _aidl_return) override;
+ ndk::ScopedAStatus installApfPacketFilter(const std::vector<uint8_t>& in_program) override;
+ ndk::ScopedAStatus readApfPacketFilterData(std::vector<uint8_t>* _aidl_return) override;
+ ndk::ScopedAStatus getBackgroundScanCapabilities(
+ StaBackgroundScanCapabilities* _aidl_return) override;
+ ndk::ScopedAStatus getValidFrequenciesForBand(WifiBand in_band,
+ std::vector<int32_t>* _aidl_return) override;
+ ndk::ScopedAStatus startBackgroundScan(int32_t in_cmdId,
+ const StaBackgroundScanParameters& in_params) override;
+ ndk::ScopedAStatus stopBackgroundScan(int32_t in_cmdId) override;
+ ndk::ScopedAStatus enableLinkLayerStatsCollection(bool in_debug) override;
+ ndk::ScopedAStatus disableLinkLayerStatsCollection() override;
+ ndk::ScopedAStatus getLinkLayerStats(StaLinkLayerStats* _aidl_return) override;
+ ndk::ScopedAStatus startRssiMonitoring(int32_t in_cmdId, int32_t in_maxRssi,
+ int32_t in_minRssi) override;
+ ndk::ScopedAStatus stopRssiMonitoring(int32_t in_cmdId) override;
+ ndk::ScopedAStatus getRoamingCapabilities(StaRoamingCapabilities* _aidl_return) override;
+ ndk::ScopedAStatus configureRoaming(const StaRoamingConfig& in_config) override;
+ ndk::ScopedAStatus setRoamingState(StaRoamingState in_state) override;
+ ndk::ScopedAStatus enableNdOffload(bool in_enable) override;
+ ndk::ScopedAStatus startSendingKeepAlivePackets(int32_t in_cmdId,
+ const std::vector<uint8_t>& in_ipPacketData,
+ char16_t in_etherType,
+ const std::array<uint8_t, 6>& in_srcAddress,
+ const std::array<uint8_t, 6>& in_dstAddress,
+ int32_t in_periodInMs) override;
+ ndk::ScopedAStatus stopSendingKeepAlivePackets(int32_t in_cmdId) override;
+ ndk::ScopedAStatus startDebugPacketFateMonitoring() override;
+ ndk::ScopedAStatus getDebugTxPacketFates(
+ std::vector<WifiDebugTxPacketFateReport>* _aidl_return) override;
+ ndk::ScopedAStatus getDebugRxPacketFates(
+ std::vector<WifiDebugRxPacketFateReport>* _aidl_return) override;
+ ndk::ScopedAStatus setMacAddress(const std::array<uint8_t, 6>& in_mac) override;
+ ndk::ScopedAStatus getFactoryMacAddress(std::array<uint8_t, 6>* _aidl_return) override;
+ ndk::ScopedAStatus setScanMode(bool in_enable) override;
+
+ private:
+ // Corresponding worker functions for the AIDL methods.
+ std::pair<std::string, ndk::ScopedAStatus> getNameInternal();
+ ndk::ScopedAStatus registerEventCallbackInternal(
+ const std::shared_ptr<IWifiStaIfaceEventCallback>& callback);
+ std::pair<IWifiStaIface::StaIfaceCapabilityMask, ndk::ScopedAStatus> getCapabilitiesInternal();
+ std::pair<StaApfPacketFilterCapabilities, ndk::ScopedAStatus>
+ getApfPacketFilterCapabilitiesInternal();
+ ndk::ScopedAStatus installApfPacketFilterInternal(const std::vector<uint8_t>& program);
+ std::pair<std::vector<uint8_t>, ndk::ScopedAStatus> readApfPacketFilterDataInternal();
+ std::pair<StaBackgroundScanCapabilities, ndk::ScopedAStatus>
+ getBackgroundScanCapabilitiesInternal();
+ std::pair<std::vector<int32_t>, ndk::ScopedAStatus> getValidFrequenciesForBandInternal(
+ WifiBand band);
+ ndk::ScopedAStatus startBackgroundScanInternal(int32_t cmd_id,
+ const StaBackgroundScanParameters& params);
+ ndk::ScopedAStatus stopBackgroundScanInternal(int32_t cmd_id);
+ ndk::ScopedAStatus enableLinkLayerStatsCollectionInternal(bool debug);
+ ndk::ScopedAStatus disableLinkLayerStatsCollectionInternal();
+ std::pair<StaLinkLayerStats, ndk::ScopedAStatus> getLinkLayerStatsInternal();
+ ndk::ScopedAStatus startRssiMonitoringInternal(int32_t cmd_id, int32_t max_rssi,
+ int32_t min_rssi);
+ ndk::ScopedAStatus stopRssiMonitoringInternal(int32_t cmd_id);
+ std::pair<StaRoamingCapabilities, ndk::ScopedAStatus> getRoamingCapabilitiesInternal();
+ ndk::ScopedAStatus configureRoamingInternal(const StaRoamingConfig& config);
+ ndk::ScopedAStatus setRoamingStateInternal(StaRoamingState state);
+ ndk::ScopedAStatus enableNdOffloadInternal(bool enable);
+ ndk::ScopedAStatus startSendingKeepAlivePacketsInternal(
+ int32_t cmd_id, const std::vector<uint8_t>& ip_packet_data, char16_t ether_type,
+ const std::array<uint8_t, 6>& src_address, const std::array<uint8_t, 6>& dst_address,
+ int32_t period_in_ms);
+ ndk::ScopedAStatus stopSendingKeepAlivePacketsInternal(int32_t cmd_id);
+ ndk::ScopedAStatus startDebugPacketFateMonitoringInternal();
+ std::pair<std::vector<WifiDebugTxPacketFateReport>, ndk::ScopedAStatus>
+ getDebugTxPacketFatesInternal();
+ std::pair<std::vector<WifiDebugRxPacketFateReport>, ndk::ScopedAStatus>
+ getDebugRxPacketFatesInternal();
+ ndk::ScopedAStatus setMacAddressInternal(const std::array<uint8_t, 6>& mac);
+ std::pair<std::array<uint8_t, 6>, ndk::ScopedAStatus> getFactoryMacAddressInternal();
+ ndk::ScopedAStatus setScanModeInternal(bool enable);
+
+ void setWeakPtr(std::weak_ptr<WifiStaIface> ptr);
+
+ std::string ifname_;
+ std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
+ std::weak_ptr<iface_util::WifiIfaceUtil> iface_util_;
+ std::weak_ptr<WifiStaIface> weak_ptr_this_;
+ bool is_valid_;
+ aidl_callback_util::AidlCallbackHandler<IWifiStaIfaceEventCallback> event_cb_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(WifiStaIface);
+};
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_STA_IFACE_H_
diff --git a/wifi/aidl/default/wifi_status_util.cpp b/wifi/aidl/default/wifi_status_util.cpp
new file mode 100644
index 0000000..25ecd71
--- /dev/null
+++ b/wifi/aidl/default/wifi_status_util.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_status_util.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+
+std::string legacyErrorToString(legacy_hal::wifi_error error) {
+ switch (error) {
+ case legacy_hal::WIFI_SUCCESS:
+ return "SUCCESS";
+ case legacy_hal::WIFI_ERROR_UNINITIALIZED:
+ return "UNINITIALIZED";
+ case legacy_hal::WIFI_ERROR_NOT_AVAILABLE:
+ return "NOT_AVAILABLE";
+ case legacy_hal::WIFI_ERROR_NOT_SUPPORTED:
+ return "NOT_SUPPORTED";
+ case legacy_hal::WIFI_ERROR_INVALID_ARGS:
+ return "INVALID_ARGS";
+ case legacy_hal::WIFI_ERROR_INVALID_REQUEST_ID:
+ return "INVALID_REQUEST_ID";
+ case legacy_hal::WIFI_ERROR_TIMED_OUT:
+ return "TIMED_OUT";
+ case legacy_hal::WIFI_ERROR_TOO_MANY_REQUESTS:
+ return "TOO_MANY_REQUESTS";
+ case legacy_hal::WIFI_ERROR_OUT_OF_MEMORY:
+ return "OUT_OF_MEMORY";
+ case legacy_hal::WIFI_ERROR_BUSY:
+ return "BUSY";
+ case legacy_hal::WIFI_ERROR_UNKNOWN:
+ return "UNKNOWN";
+ default:
+ return "UNKNOWN ERROR";
+ }
+}
+
+ndk::ScopedAStatus createWifiStatus(WifiStatusCode code, const std::string& description) {
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(static_cast<int32_t>(code),
+ description.c_str());
+}
+
+ndk::ScopedAStatus createWifiStatus(WifiStatusCode code) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(code));
+}
+
+ndk::ScopedAStatus createWifiStatusFromLegacyError(legacy_hal::wifi_error error,
+ const std::string& desc) {
+ switch (error) {
+ case legacy_hal::WIFI_ERROR_NONE:
+ return ndk::ScopedAStatus::ok();
+
+ case legacy_hal::WIFI_ERROR_UNINITIALIZED:
+ case legacy_hal::WIFI_ERROR_NOT_AVAILABLE:
+ return createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE, desc);
+
+ case legacy_hal::WIFI_ERROR_NOT_SUPPORTED:
+ return createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED, desc);
+
+ case legacy_hal::WIFI_ERROR_INVALID_ARGS:
+ case legacy_hal::WIFI_ERROR_INVALID_REQUEST_ID:
+ return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS, desc);
+
+ case legacy_hal::WIFI_ERROR_TIMED_OUT:
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN, desc + ", timed out");
+
+ case legacy_hal::WIFI_ERROR_TOO_MANY_REQUESTS:
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN, desc + ", too many requests");
+
+ case legacy_hal::WIFI_ERROR_OUT_OF_MEMORY:
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN, desc + ", out of memory");
+
+ case legacy_hal::WIFI_ERROR_BUSY:
+ return createWifiStatus(WifiStatusCode::ERROR_BUSY);
+
+ case legacy_hal::WIFI_ERROR_UNKNOWN:
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN, "unknown");
+
+ default:
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN, "unknown error");
+ }
+}
+
+ndk::ScopedAStatus createWifiStatusFromLegacyError(legacy_hal::wifi_error error) {
+ return createWifiStatusFromLegacyError(error, "");
+}
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/wifi/aidl/default/wifi_status_util.h b/wifi/aidl/default/wifi_status_util.h
new file mode 100644
index 0000000..633811d
--- /dev/null
+++ b/wifi/aidl/default/wifi_status_util.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WIFI_STATUS_UTIL_H_
+#define WIFI_STATUS_UTIL_H_
+
+#include <aidl/android/hardware/wifi/IWifi.h>
+
+#include "wifi_legacy_hal.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+using ::aidl::android::hardware::wifi::WifiStatusCode;
+
+std::string legacyErrorToString(legacy_hal::wifi_error error);
+ndk::ScopedAStatus createWifiStatus(WifiStatusCode code, const std::string& description);
+ndk::ScopedAStatus createWifiStatus(WifiStatusCode code);
+ndk::ScopedAStatus createWifiStatusFromLegacyError(legacy_hal::wifi_error error,
+ const std::string& description);
+ndk::ScopedAStatus createWifiStatusFromLegacyError(legacy_hal::wifi_error error);
+
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+} // namespace aidl
+
+#endif // WIFI_STATUS_UTIL_H_
diff --git a/wifi/aidl/vts/functional/Android.bp b/wifi/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..1277182
--- /dev/null
+++ b/wifi/aidl/vts/functional/Android.bp
@@ -0,0 +1,169 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+ name: "VtsHalWifiChipTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "wifi_chip_aidl_test.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ "libvndksupport",
+ ],
+ static_libs: [
+ "VtsHalWifiTargetTestUtil",
+ "android.hardware.wifi-V1-ndk",
+ "libwifi-system-iface",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
+
+cc_test {
+ name: "VtsHalWifiStaIfaceTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "wifi_sta_iface_aidl_test.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ "libvndksupport",
+ ],
+ static_libs: [
+ "VtsHalWifiTargetTestUtil",
+ "android.hardware.wifi-V1-ndk",
+ "libwifi-system-iface",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
+
+cc_test {
+ name: "VtsHalWifiApIfaceTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "wifi_ap_iface_aidl_test.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ "libvndksupport",
+ ],
+ static_libs: [
+ "VtsHalWifiTargetTestUtil",
+ "android.hardware.wifi-V1-ndk",
+ "libwifi-system-iface",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
+
+cc_test {
+ name: "VtsHalWifiNanIfaceTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "wifi_nan_iface_aidl_test.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ "libvndksupport",
+ ],
+ static_libs: [
+ "VtsHalWifiTargetTestUtil",
+ "android.hardware.wifi-V1-ndk",
+ "libwifi-system-iface",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
+
+cc_test {
+ name: "VtsHalWifiRttControllerTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "wifi_rtt_controller_aidl_test.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ "libvndksupport",
+ ],
+ static_libs: [
+ "VtsHalWifiTargetTestUtil",
+ "android.hardware.wifi-V1-ndk",
+ "libwifi-system-iface",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
+
+cc_library_static {
+ name: "VtsHalWifiTargetTestUtil",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "wifi_aidl_test_utils.cpp",
+ ],
+ export_include_dirs: [
+ ".",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ "libnativehelper",
+ ],
+ static_libs: [
+ "android.hardware.wifi-V1-ndk",
+ "libwifi-system-iface",
+ ],
+}
diff --git a/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp b/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp
new file mode 100644
index 0000000..6722f36
--- /dev/null
+++ b/wifi/aidl/vts/functional/wifi_aidl_test_utils.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_aidl_test_utils.h"
+
+using ::android::wifi_system::InterfaceTool;
+
+namespace {
+bool findAnyModeSupportingConcurrencyType(IfaceConcurrencyType desired_type,
+ const std::vector<IWifiChip::ChipMode>& modes,
+ int* mode_id) {
+ for (const auto& mode : modes) {
+ for (const auto& combination : mode.availableCombinations) {
+ for (const auto& iface_limit : combination.limits) {
+ const auto& iface_types = iface_limit.types;
+ if (std::find(iface_types.begin(), iface_types.end(), desired_type) !=
+ iface_types.end()) {
+ *mode_id = mode.id;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool configureChipToSupportConcurrencyTypeInternal(const std::shared_ptr<IWifiChip>& wifi_chip,
+ IfaceConcurrencyType type,
+ int* configured_mode_id) {
+ if (!configured_mode_id) {
+ return false;
+ }
+ std::vector<IWifiChip::ChipMode> chip_modes;
+ auto status = wifi_chip->getAvailableModes(&chip_modes);
+ if (!status.isOk()) {
+ return false;
+ }
+ if (!findAnyModeSupportingConcurrencyType(type, chip_modes, configured_mode_id)) {
+ return false;
+ }
+ if (!wifi_chip->configureChip(*configured_mode_id).isOk()) {
+ return false;
+ }
+ return true;
+}
+
+bool configureChipToSupportConcurrencyTypeInternal(const std::shared_ptr<IWifiChip>& wifi_chip,
+ IfaceConcurrencyType type) {
+ int mode_id;
+ return configureChipToSupportConcurrencyTypeInternal(wifi_chip, type, &mode_id);
+}
+} // namespace
+
+bool checkStatusCode(ndk::ScopedAStatus* status, WifiStatusCode expected_code) {
+ if (status == nullptr) {
+ return false;
+ }
+ return status->getServiceSpecificError() == static_cast<int32_t>(expected_code);
+}
+
+std::shared_ptr<IWifi> getWifi(const char* instance_name) {
+ return IWifi::fromBinder(ndk::SpAIBinder(AServiceManager_waitForService(instance_name)));
+}
+
+std::shared_ptr<IWifiChip> getWifiChip(const char* instance_name) {
+ std::shared_ptr<IWifi> wifi = getWifi(instance_name);
+ if (!wifi.get()) {
+ return nullptr;
+ }
+
+ const int retry_interval_ms = 2;
+ const int max_retries = 5;
+ int retry_count = 0;
+ auto status = wifi->start();
+ while (retry_count < max_retries && !status.isOk()) {
+ retry_count++;
+ usleep(retry_interval_ms * 1000);
+ status = wifi->start();
+ }
+ if (!status.isOk()) {
+ return nullptr;
+ }
+
+ std::vector<int> chip_ids = {};
+ status = wifi->getChipIds(&chip_ids);
+ if (!status.isOk() || chip_ids.size() == 0) {
+ return nullptr;
+ }
+ std::shared_ptr<IWifiChip> chip;
+ status = wifi->getChip(chip_ids[0], &chip);
+ if (!status.isOk()) {
+ return nullptr;
+ }
+ return chip;
+}
+
+void setupStaIface(const std::shared_ptr<IWifiStaIface>& iface) {
+ std::string iface_name;
+ auto status = iface->getName(&iface_name);
+ if (status.isOk()) {
+ InterfaceTool iface_tool;
+ iface_tool.SetUpState(iface_name.c_str(), true);
+ }
+}
+
+void setupNanIface(const std::shared_ptr<IWifiNanIface>& iface) {
+ std::string iface_name;
+ auto status = iface->getName(&iface_name);
+ if (status.isOk()) {
+ InterfaceTool iface_tool;
+ iface_tool.SetUpState(iface_name.c_str(), true);
+ }
+}
+
+std::shared_ptr<IWifiStaIface> getWifiStaIface(const char* instance_name) {
+ std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(instance_name);
+ if (!wifi_chip.get()) {
+ return nullptr;
+ }
+ if (!configureChipToSupportConcurrencyTypeInternal(wifi_chip, IfaceConcurrencyType::STA)) {
+ return nullptr;
+ }
+ std::shared_ptr<IWifiStaIface> iface;
+ auto status = wifi_chip->createStaIface(&iface);
+ if (!status.isOk()) {
+ return nullptr;
+ }
+ setupStaIface(iface);
+ return iface;
+}
+
+std::shared_ptr<IWifiNanIface> getWifiNanIface(const char* instance_name) {
+ std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(instance_name);
+ if (!wifi_chip.get()) {
+ return nullptr;
+ }
+ if (!configureChipToSupportConcurrencyTypeInternal(wifi_chip,
+ IfaceConcurrencyType::NAN_IFACE)) {
+ return nullptr;
+ }
+ std::shared_ptr<IWifiNanIface> iface;
+ auto status = wifi_chip->createNanIface(&iface);
+ if (!status.isOk()) {
+ return nullptr;
+ }
+ setupNanIface(iface);
+ return iface;
+}
+
+std::shared_ptr<IWifiApIface> getWifiApIface(const char* instance_name) {
+ std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(instance_name);
+ if (!wifi_chip.get()) {
+ return nullptr;
+ }
+ if (!configureChipToSupportConcurrencyTypeInternal(wifi_chip, IfaceConcurrencyType::AP)) {
+ return nullptr;
+ }
+ std::shared_ptr<IWifiApIface> iface;
+ auto status = wifi_chip->createApIface(&iface);
+ if (!status.isOk()) {
+ return nullptr;
+ }
+ return iface;
+}
+
+std::shared_ptr<IWifiApIface> getBridgedWifiApIface(std::shared_ptr<IWifiChip> wifi_chip) {
+ if (!wifi_chip.get()) {
+ return nullptr;
+ }
+ int mode_id;
+ std::shared_ptr<IWifiApIface> iface;
+ configureChipToSupportConcurrencyTypeInternal(wifi_chip, IfaceConcurrencyType::AP, &mode_id);
+ auto status = wifi_chip->createBridgedApIface(&iface);
+ if (!status.isOk()) {
+ return nullptr;
+ }
+ return iface;
+}
+
+std::shared_ptr<IWifiApIface> getBridgedWifiApIface(const char* instance_name) {
+ std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(instance_name);
+ return getBridgedWifiApIface(wifi_chip);
+}
+
+bool configureChipToSupportConcurrencyType(const std::shared_ptr<IWifiChip>& wifi_chip,
+ IfaceConcurrencyType type, int* configured_mode_id) {
+ return configureChipToSupportConcurrencyTypeInternal(wifi_chip, type, configured_mode_id);
+}
+
+void stopWifiService(const char* instance_name) {
+ std::shared_ptr<IWifi> wifi = getWifi(instance_name);
+ if (wifi != nullptr) {
+ wifi->stop();
+ }
+}
+
+int32_t getChipCapabilities(const std::shared_ptr<IWifiChip>& wifi_chip) {
+ IWifiChip::ChipCapabilityMask caps = {};
+ if (wifi_chip->getCapabilities(&caps).isOk()) {
+ return static_cast<int32_t>(caps);
+ }
+ return 0;
+}
diff --git a/wifi/aidl/vts/functional/wifi_aidl_test_utils.h b/wifi/aidl/vts/functional/wifi_aidl_test_utils.h
new file mode 100644
index 0000000..ad16603
--- /dev/null
+++ b/wifi/aidl/vts/functional/wifi_aidl_test_utils.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <VtsCoreUtil.h>
+
+#include <aidl/android/hardware/wifi/IWifi.h>
+#include <aidl/android/hardware/wifi/IWifiChip.h>
+#include <android/binder_manager.h>
+#include <wifi_system/interface_tool.h>
+
+using aidl::android::hardware::wifi::IfaceConcurrencyType;
+using aidl::android::hardware::wifi::IWifi;
+using aidl::android::hardware::wifi::IWifiApIface;
+using aidl::android::hardware::wifi::IWifiChip;
+using aidl::android::hardware::wifi::IWifiNanIface;
+using aidl::android::hardware::wifi::IWifiStaIface;
+using aidl::android::hardware::wifi::WifiStatusCode;
+
+// Helper functions to obtain references to the various AIDL interface objects.
+std::shared_ptr<IWifi> getWifi(const char* instance_name);
+std::shared_ptr<IWifiChip> getWifiChip(const char* instance_name);
+std::shared_ptr<IWifiStaIface> getWifiStaIface(const char* instance_name);
+std::shared_ptr<IWifiNanIface> getWifiNanIface(const char* instance_name);
+std::shared_ptr<IWifiApIface> getWifiApIface(const char* instance_name);
+std::shared_ptr<IWifiApIface> getBridgedWifiApIface(const char* instance_name);
+std::shared_ptr<IWifiApIface> getBridgedWifiApIface(std::shared_ptr<IWifiChip> wifi_chip);
+// Configure the chip in a mode to support the creation of the provided iface type.
+bool configureChipToSupportConcurrencyType(const std::shared_ptr<IWifiChip>& wifi_chip,
+ IfaceConcurrencyType type, int* configured_mode_id);
+// Used to trigger IWifi.stop() at the end of every test.
+void stopWifiService(const char* instance_name);
+int32_t getChipCapabilities(const std::shared_ptr<IWifiChip>& wifi_chip);
+bool checkStatusCode(ndk::ScopedAStatus* status, WifiStatusCode expected_code);
diff --git a/wifi/aidl/vts/functional/wifi_ap_iface_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_ap_iface_aidl_test.cpp
new file mode 100644
index 0000000..0eaf660
--- /dev/null
+++ b/wifi/aidl/vts/functional/wifi_ap_iface_aidl_test.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Staache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <vector>
+
+#include <VtsCoreUtil.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/wifi/BnWifi.h>
+#include <android/binder_manager.h>
+#include <android/binder_status.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+#include "wifi_aidl_test_utils.h"
+
+using aidl::android::hardware::wifi::IWifiApIface;
+using aidl::android::hardware::wifi::WifiBand;
+
+class WifiApIfaceAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ isBridgedSupport_ = testing::checkSubstringInCommandOutput(
+ "/system/bin/cmd wifi get-softap-supported-features",
+ "wifi_softap_bridged_ap_supported");
+ stopWifiService(getInstanceName());
+ }
+
+ void TearDown() override { stopWifiService(getInstanceName()); }
+
+ protected:
+ bool isBridgedSupport_ = false;
+ const char* getInstanceName() { return GetParam().c_str(); }
+};
+
+/*
+ * SetMacAddress
+ */
+TEST_P(WifiApIfaceAidlTest, SetMacAddress) {
+ std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(getInstanceName());
+ ASSERT_NE(nullptr, wifi_ap_iface.get());
+ std::array<uint8_t, 6> mac = {0x12, 0x22, 0x33, 0x52, 0x10, 0x44};
+ EXPECT_TRUE(wifi_ap_iface->setMacAddress(mac).isOk());
+}
+
+/*
+ * SetCountryCode
+ */
+TEST_P(WifiApIfaceAidlTest, SetCountryCode) {
+ std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(getInstanceName());
+ ASSERT_NE(nullptr, wifi_ap_iface.get());
+
+ const std::array<uint8_t, 2> country_code = {0x55, 0x53};
+ EXPECT_TRUE(wifi_ap_iface->setCountryCode(country_code).isOk());
+}
+
+/*
+ * GetValidFrequenciesForBand
+ * Ensures that we can retrieve valid frequencies for the 2.4 GHz band.
+ */
+TEST_P(WifiApIfaceAidlTest, GetValidFrequenciesForBand) {
+ std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(getInstanceName());
+ ASSERT_NE(nullptr, wifi_ap_iface.get());
+
+ std::vector<int32_t> freqs;
+ EXPECT_TRUE(wifi_ap_iface->getValidFrequenciesForBand(WifiBand::BAND_24GHZ, &freqs).isOk());
+ EXPECT_NE(freqs.size(), 0);
+}
+
+/*
+ * GetFactoryMacAddress
+ */
+TEST_P(WifiApIfaceAidlTest, GetFactoryMacAddress) {
+ std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(getInstanceName());
+ ASSERT_NE(nullptr, wifi_ap_iface.get());
+
+ std::array<uint8_t, 6> mac;
+ EXPECT_TRUE(wifi_ap_iface->getFactoryMacAddress(&mac).isOk());
+ std::array<uint8_t, 6> all_zero_mac = {0, 0, 0, 0, 0, 0};
+ EXPECT_NE(mac, all_zero_mac);
+}
+
+/**
+ * GetBridgedInstances - non-bridged mode
+ */
+TEST_P(WifiApIfaceAidlTest, GetBridgedInstances) {
+ std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(getInstanceName());
+ ASSERT_NE(nullptr, wifi_ap_iface.get());
+
+ std::vector<std::string> instances;
+ EXPECT_TRUE(wifi_ap_iface->getBridgedInstances(&instances).isOk());
+ EXPECT_EQ(instances.size(), 0);
+}
+
+/**
+ * GetBridgedInstances - bridged AP mode.
+ */
+TEST_P(WifiApIfaceAidlTest, GetBridgedInstances_Bridged) {
+ if (!isBridgedSupport_) {
+ GTEST_SKIP() << "Missing Bridged AP support";
+ }
+ std::shared_ptr<IWifiApIface> wifi_ap_iface = getBridgedWifiApIface(getInstanceName());
+ ASSERT_NE(nullptr, wifi_ap_iface.get());
+
+ std::vector<std::string> instances;
+ EXPECT_TRUE(wifi_ap_iface->getBridgedInstances(&instances).isOk());
+ EXPECT_EQ(instances.size(), 2);
+}
+
+/**
+ * ResetToFactoryMacAddress - non-bridged mode
+ */
+TEST_P(WifiApIfaceAidlTest, ResetToFactoryMacAddress) {
+ std::shared_ptr<IWifiApIface> wifi_ap_iface = getWifiApIface(getInstanceName());
+ ASSERT_NE(nullptr, wifi_ap_iface.get());
+ EXPECT_TRUE(wifi_ap_iface->resetToFactoryMacAddress().isOk());
+}
+
+/**
+ * ResetToFactoryMacAddress - bridged AP mode
+ */
+TEST_P(WifiApIfaceAidlTest, ResetToFactoryMacAddress_Bridged) {
+ if (!isBridgedSupport_) {
+ GTEST_SKIP() << "Missing Bridged AP support";
+ }
+ std::shared_ptr<IWifiApIface> wifi_ap_iface = getBridgedWifiApIface(getInstanceName());
+ ASSERT_NE(nullptr, wifi_ap_iface.get());
+ EXPECT_TRUE(wifi_ap_iface->resetToFactoryMacAddress().isOk());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiApIfaceAidlTest);
+INSTANTIATE_TEST_SUITE_P(WifiTest, WifiApIfaceAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IWifi::descriptor)),
+ android::PrintInstanceNameToString);
+
+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/wifi/aidl/vts/functional/wifi_chip_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_chip_aidl_test.cpp
new file mode 100644
index 0000000..0806ed2
--- /dev/null
+++ b/wifi/aidl/vts/functional/wifi_chip_aidl_test.cpp
@@ -0,0 +1,889 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Staache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <numeric>
+#include <vector>
+
+#include <VtsCoreUtil.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/wifi/BnWifi.h>
+#include <aidl/android/hardware/wifi/BnWifiChipEventCallback.h>
+#include <android/binder_manager.h>
+#include <android/binder_status.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+#include "wifi_aidl_test_utils.h"
+
+using aidl::android::hardware::wifi::BnWifiChipEventCallback;
+using aidl::android::hardware::wifi::IfaceType;
+using aidl::android::hardware::wifi::IWifiApIface;
+using aidl::android::hardware::wifi::IWifiChip;
+using aidl::android::hardware::wifi::IWifiNanIface;
+using aidl::android::hardware::wifi::IWifiP2pIface;
+using aidl::android::hardware::wifi::IWifiRttController;
+using aidl::android::hardware::wifi::WifiBand;
+using aidl::android::hardware::wifi::WifiDebugHostWakeReasonStats;
+using aidl::android::hardware::wifi::WifiDebugRingBufferStatus;
+using aidl::android::hardware::wifi::WifiDebugRingBufferVerboseLevel;
+using aidl::android::hardware::wifi::WifiIfaceMode;
+using aidl::android::hardware::wifi::WifiRadioCombinationMatrix;
+using aidl::android::hardware::wifi::WifiStatusCode;
+using aidl::android::hardware::wifi::WifiUsableChannel;
+
+class WifiChipAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ stopWifiService(getInstanceName());
+ wifi_chip_ = getWifiChip(getInstanceName());
+ ASSERT_NE(nullptr, wifi_chip_.get());
+ }
+
+ void TearDown() override { stopWifiService(getInstanceName()); }
+
+ protected:
+ int configureChipForConcurrencyType(IfaceConcurrencyType type) {
+ int mode_id;
+ EXPECT_TRUE(configureChipToSupportConcurrencyType(wifi_chip_, type, &mode_id));
+ return mode_id;
+ }
+
+ std::shared_ptr<IWifiStaIface> configureChipForStaAndGetIface() {
+ std::shared_ptr<IWifiStaIface> iface;
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ EXPECT_TRUE(wifi_chip_->createStaIface(&iface).isOk());
+ EXPECT_NE(nullptr, iface.get());
+ return iface;
+ }
+
+ std::shared_ptr<IWifiP2pIface> configureChipForP2pAndGetIface() {
+ std::shared_ptr<IWifiP2pIface> iface;
+ configureChipForConcurrencyType(IfaceConcurrencyType::P2P);
+ EXPECT_TRUE(wifi_chip_->createP2pIface(&iface).isOk());
+ EXPECT_NE(nullptr, iface.get());
+ return iface;
+ }
+
+ std::shared_ptr<IWifiApIface> configureChipForApAndGetIface() {
+ std::shared_ptr<IWifiApIface> iface;
+ configureChipForConcurrencyType(IfaceConcurrencyType::AP);
+ EXPECT_TRUE(wifi_chip_->createApIface(&iface).isOk());
+ EXPECT_NE(nullptr, iface.get());
+ return iface;
+ }
+
+ std::shared_ptr<IWifiNanIface> configureChipForNanAndGetIface() {
+ std::shared_ptr<IWifiNanIface> iface;
+ configureChipForConcurrencyType(IfaceConcurrencyType::NAN_IFACE);
+ EXPECT_TRUE(wifi_chip_->createNanIface(&iface).isOk());
+ EXPECT_NE(nullptr, iface.get());
+ return iface;
+ }
+
+ std::string getStaIfaceName(const std::shared_ptr<IWifiStaIface>& iface) {
+ std::string iface_name;
+ EXPECT_TRUE(iface->getName(&iface_name).isOk());
+ return iface_name;
+ }
+
+ std::string getP2pIfaceName(const std::shared_ptr<IWifiP2pIface>& iface) {
+ std::string iface_name;
+ EXPECT_TRUE(iface->getName(&iface_name).isOk());
+ return iface_name;
+ }
+
+ std::string getApIfaceName(const std::shared_ptr<IWifiApIface>& iface) {
+ std::string iface_name;
+ EXPECT_TRUE(iface->getName(&iface_name).isOk());
+ return iface_name;
+ }
+
+ std::string getNanIfaceName(const std::shared_ptr<IWifiNanIface>& iface) {
+ std::string iface_name;
+ EXPECT_TRUE(iface->getName(&iface_name).isOk());
+ return iface_name;
+ }
+
+ std::vector<std::shared_ptr<IWifiStaIface>> create2StaIfacesIfPossible() {
+ std::shared_ptr<IWifiStaIface> iface1 = configureChipForStaAndGetIface();
+
+ // Try create a create second iface.
+ std::shared_ptr<IWifiStaIface> iface2;
+ bool add_second_success = wifi_chip_->createStaIface(&iface2).isOk();
+ if (!add_second_success) {
+ return {iface1};
+ }
+ EXPECT_NE(nullptr, iface2.get());
+ return {iface1, iface2};
+ }
+
+ bool hasAnyRingBufferCapabilities(int32_t caps) {
+ return caps &
+ (static_cast<int32_t>(
+ IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_CONNECT_EVENT) |
+ static_cast<int32_t>(IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_POWER_EVENT) |
+ static_cast<int32_t>(
+ IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_WAKELOCK_EVENT) |
+ static_cast<int32_t>(IWifiChip::ChipCapabilityMask::DEBUG_RING_BUFFER_VENDOR_DATA));
+ }
+
+ const char* getInstanceName() { return GetParam().c_str(); }
+
+ std::shared_ptr<IWifiChip> wifi_chip_;
+};
+
+class WifiChipEventCallback : public BnWifiChipEventCallback {
+ public:
+ WifiChipEventCallback() = default;
+
+ ::ndk::ScopedAStatus onChipReconfigureFailure(WifiStatusCode /* status */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onChipReconfigured(int /* modeId */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onDebugErrorAlert(int /* errorCode */,
+ const std::vector<uint8_t>& /* debugData */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onDebugRingBufferDataAvailable(
+ const WifiDebugRingBufferStatus& /* status */,
+ const std::vector<uint8_t>& /* data */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onIfaceAdded(IfaceType /* type */,
+ const std::string& /* name */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onIfaceRemoved(IfaceType /* type */,
+ const std::string& /* name */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onRadioModeChange(
+ const std::vector<RadioModeInfo>& /* radioModeInfos */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+};
+
+/*
+ * RegisterEventCallback
+ *
+ * Note: it is not feasible to test the invocation of the callback function,
+ * since events are triggered internally in the HAL implementation and cannot be
+ * triggered from the test case.
+ */
+TEST_P(WifiChipAidlTest, RegisterEventCallback) {
+ std::shared_ptr<WifiChipEventCallback> callback =
+ ndk::SharedRefBase::make<WifiChipEventCallback>();
+ ASSERT_NE(nullptr, callback.get());
+ EXPECT_TRUE(wifi_chip_->registerEventCallback(callback).isOk());
+}
+
+/*
+ * GetCapabilities
+ */
+TEST_P(WifiChipAidlTest, GetCapabilities) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ int32_t caps = getChipCapabilities(wifi_chip_);
+ EXPECT_NE(static_cast<int32_t>(caps), 0);
+}
+
+/*
+ * GetId
+ */
+TEST_P(WifiChipAidlTest, GetId) {
+ int id;
+ EXPECT_TRUE(wifi_chip_->getId(&id).isOk());
+}
+
+/*
+ * GetAvailableModes
+ */
+TEST_P(WifiChipAidlTest, GetAvailableModes) {
+ std::vector<IWifiChip::ChipMode> modes;
+ EXPECT_TRUE(wifi_chip_->getAvailableModes(&modes).isOk());
+ EXPECT_NE(modes.size(), 0);
+}
+
+/*
+ * GetMode
+ */
+TEST_P(WifiChipAidlTest, GetMode) {
+ int expected_mode = configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ int retrieved_mode;
+ EXPECT_TRUE(wifi_chip_->getMode(&retrieved_mode).isOk());
+ EXPECT_EQ(retrieved_mode, expected_mode);
+}
+
+/*
+ * GetUsableChannels
+ */
+TEST_P(WifiChipAidlTest, GetUsableChannels) {
+ WifiBand band = WifiBand::BAND_24GHZ_5GHZ_6GHZ;
+ uint32_t ifaceModeMask = static_cast<uint32_t>(WifiIfaceMode::IFACE_MODE_P2P_CLIENT) |
+ static_cast<uint32_t>(WifiIfaceMode::IFACE_MODE_P2P_GO);
+ uint32_t filterMask =
+ static_cast<uint32_t>(IWifiChip::UsableChannelFilter::CELLULAR_COEXISTENCE) |
+ static_cast<uint32_t>(IWifiChip::UsableChannelFilter::CONCURRENCY);
+
+ std::vector<WifiUsableChannel> channels;
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ auto status = wifi_chip_->getUsableChannels(
+ band, static_cast<WifiIfaceMode>(ifaceModeMask),
+ static_cast<IWifiChip::UsableChannelFilter>(filterMask), &channels);
+ if (checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
+ GTEST_SKIP() << "getUsableChannels() is not supported by vendor.";
+ }
+ EXPECT_TRUE(status.isOk());
+}
+
+/*
+ * GetSupportedRadioCombinationsMatrix
+ */
+TEST_P(WifiChipAidlTest, GetSupportedRadioCombinationsMatrix) {
+ WifiRadioCombinationMatrix combination_matrix = {};
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ auto status = wifi_chip_->getSupportedRadioCombinationsMatrix(&combination_matrix);
+ if (checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
+ GTEST_SKIP() << "Skipping this test since getSupportedRadioCombinationsMatrix() "
+ "is not supported by vendor.";
+ }
+ EXPECT_TRUE(status.isOk());
+}
+
+/*
+ * SetCountryCode
+ */
+TEST_P(WifiChipAidlTest, SetCountryCode) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ std::array<uint8_t, 2> country_code = {0x55, 0x53};
+ EXPECT_TRUE(wifi_chip_->setCountryCode(country_code).isOk());
+}
+
+/*
+ * SetLatencyMode_normal
+ * Tests the setLatencyMode() API with Latency mode NORMAL.
+ */
+TEST_P(WifiChipAidlTest, SetLatencyMode_normal) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ int32_t caps = getChipCapabilities(wifi_chip_);
+ auto status = wifi_chip_->setLatencyMode(IWifiChip::LatencyMode::NORMAL);
+ if (caps & static_cast<int32_t>(IWifiChip::ChipCapabilityMask::SET_LATENCY_MODE)) {
+ EXPECT_TRUE(status.isOk());
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * SetLatencyMode_low
+ * Tests the setLatencyMode() API with Latency mode LOW.
+ */
+TEST_P(WifiChipAidlTest, SetLatencyMode_low) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ int32_t caps = getChipCapabilities(wifi_chip_);
+ auto status = wifi_chip_->setLatencyMode(IWifiChip::LatencyMode::LOW);
+ if (caps & static_cast<int32_t>(IWifiChip::ChipCapabilityMask::SET_LATENCY_MODE)) {
+ EXPECT_TRUE(status.isOk());
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * SetMultiStaPrimaryConnection
+ *
+ * Only runs if the device supports 2 STA ifaces.
+ */
+TEST_P(WifiChipAidlTest, SetMultiStaPrimaryConnection) {
+ auto ifaces = create2StaIfacesIfPossible();
+ if (ifaces.size() < 2) {
+ GTEST_SKIP() << "Device does not support more than 1 STA concurrently";
+ }
+
+ auto status = wifi_chip_->setMultiStaPrimaryConnection(getStaIfaceName(ifaces[0]));
+ if (!status.isOk()) {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * SetMultiStaUseCase
+ *
+ * Only runs if the device supports 2 STA ifaces.
+ */
+TEST_P(WifiChipAidlTest, setMultiStaUseCase) {
+ auto ifaces = create2StaIfacesIfPossible();
+ if (ifaces.size() < 2) {
+ GTEST_SKIP() << "Device does not support more than 1 STA concurrently";
+ }
+
+ auto status = wifi_chip_->setMultiStaUseCase(
+ IWifiChip::MultiStaUseCase::DUAL_STA_TRANSIENT_PREFER_PRIMARY);
+ if (!status.isOk()) {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * SetCoexUnsafeChannels
+ */
+TEST_P(WifiChipAidlTest, SetCoexUnsafeChannels) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+
+ // Test with an empty vector of CoexUnsafeChannels.
+ std::vector<IWifiChip::CoexUnsafeChannel> vec;
+ IWifiChip::CoexRestriction restrictions = static_cast<IWifiChip::CoexRestriction>(0);
+ auto status = wifi_chip_->setCoexUnsafeChannels(vec, restrictions);
+ if (!status.isOk()) {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+
+ // Test with a non-empty vector of CoexUnsafeChannels.
+ IWifiChip::CoexUnsafeChannel unsafeChannel24Ghz;
+ unsafeChannel24Ghz.band = WifiBand::BAND_24GHZ;
+ unsafeChannel24Ghz.channel = 6;
+ vec.push_back(unsafeChannel24Ghz);
+ IWifiChip::CoexUnsafeChannel unsafeChannel5Ghz;
+ unsafeChannel5Ghz.band = WifiBand::BAND_5GHZ;
+ unsafeChannel5Ghz.channel = 36;
+ vec.push_back(unsafeChannel5Ghz);
+ restrictions = static_cast<IWifiChip::CoexRestriction>(
+ static_cast<int32_t>(IWifiChip::CoexRestriction::WIFI_AWARE) |
+ static_cast<int32_t>(IWifiChip::CoexRestriction::SOFTAP) |
+ static_cast<int32_t>(IWifiChip::CoexRestriction::WIFI_DIRECT));
+
+ status = wifi_chip_->setCoexUnsafeChannels(vec, restrictions);
+ if (!status.isOk()) {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * SelectTxPowerScenario - Body
+ */
+TEST_P(WifiChipAidlTest, SelectTxPowerScenario_body) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ int32_t caps = getChipCapabilities(wifi_chip_);
+ int32_t expected_caps =
+ static_cast<int32_t>(IWifiChip::ChipCapabilityMask::SET_TX_POWER_LIMIT) |
+ static_cast<int32_t>(IWifiChip::ChipCapabilityMask::USE_BODY_HEAD_SAR);
+ auto status = wifi_chip_->selectTxPowerScenario(IWifiChip::TxPowerScenario::ON_BODY_CELL_OFF);
+ if (caps & expected_caps) {
+ EXPECT_TRUE(status.isOk());
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * SelectTxPowerScenario - Voice Call
+ */
+TEST_P(WifiChipAidlTest, SelectTxPowerScenario_voiceCall) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ int32_t caps = getChipCapabilities(wifi_chip_);
+ auto status = wifi_chip_->selectTxPowerScenario(IWifiChip::TxPowerScenario::VOICE_CALL);
+ if (caps & static_cast<int32_t>(IWifiChip::ChipCapabilityMask::SET_TX_POWER_LIMIT)) {
+ EXPECT_TRUE(status.isOk());
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * ResetTxPowerScenario
+ */
+TEST_P(WifiChipAidlTest, ResetTxPowerScenario) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ int32_t caps = getChipCapabilities(wifi_chip_);
+ auto status = wifi_chip_->resetTxPowerScenario();
+ if (caps & static_cast<int32_t>(IWifiChip::ChipCapabilityMask::SET_TX_POWER_LIMIT)) {
+ EXPECT_TRUE(status.isOk());
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * ConfigureChip
+ */
+TEST_P(WifiChipAidlTest, ConfigureChip) {
+ std::vector<IWifiChip::ChipMode> modes;
+ EXPECT_TRUE(wifi_chip_->getAvailableModes(&modes).isOk());
+ EXPECT_NE(modes.size(), 0);
+ for (const auto& mode : modes) {
+ // configureChip() requires a fresh IWifiChip object.
+ wifi_chip_ = getWifiChip(getInstanceName());
+ ASSERT_NE(nullptr, wifi_chip_.get());
+ EXPECT_TRUE(wifi_chip_->configureChip(mode.id).isOk());
+ stopWifiService(getInstanceName());
+ // Sleep for 5 milliseconds between each wifi state toggle.
+ usleep(5000);
+ }
+}
+
+/*
+ * RequestChipDebugInfo
+ */
+TEST_P(WifiChipAidlTest, RequestChipDebugInfo) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ IWifiChip::ChipDebugInfo debug_info = {};
+ EXPECT_TRUE(wifi_chip_->requestChipDebugInfo(&debug_info).isOk());
+ EXPECT_NE(debug_info.driverDescription.size(), 0);
+ EXPECT_NE(debug_info.firmwareDescription.size(), 0);
+}
+
+/*
+ * RequestFirmwareDebugDump
+ */
+TEST_P(WifiChipAidlTest, RequestFirmwareDebugDump) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ int32_t caps = getChipCapabilities(wifi_chip_);
+ std::vector<uint8_t> debug_dump;
+ auto status = wifi_chip_->requestFirmwareDebugDump(&debug_dump);
+ if (caps & static_cast<int32_t>(IWifiChip::ChipCapabilityMask::DEBUG_MEMORY_FIRMWARE_DUMP)) {
+ EXPECT_TRUE(status.isOk());
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * RequestDriverDebugDump
+ */
+TEST_P(WifiChipAidlTest, RequestDriverDebugDump) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ int32_t caps = getChipCapabilities(wifi_chip_);
+ std::vector<uint8_t> debug_dump;
+ auto status = wifi_chip_->requestDriverDebugDump(&debug_dump);
+ if (caps & static_cast<int32_t>(IWifiChip::ChipCapabilityMask::DEBUG_MEMORY_DRIVER_DUMP)) {
+ EXPECT_TRUE(status.isOk());
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * GetDebugRingBuffersStatus
+ */
+TEST_P(WifiChipAidlTest, GetDebugRingBuffersStatus) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ int32_t caps = getChipCapabilities(wifi_chip_);
+ std::vector<WifiDebugRingBufferStatus> ring_buffer_status;
+ auto status = wifi_chip_->getDebugRingBuffersStatus(&ring_buffer_status);
+ if (hasAnyRingBufferCapabilities(caps)) {
+ EXPECT_TRUE(status.isOk());
+ ASSERT_NE(ring_buffer_status.size(), 0);
+ for (const auto& ring_buffer : ring_buffer_status) {
+ EXPECT_NE(ring_buffer.ringName.size(), 0);
+ }
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * GetDebugHostWakeReasonStats
+ */
+TEST_P(WifiChipAidlTest, GetDebugHostWakeReasonStats) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ int32_t caps = getChipCapabilities(wifi_chip_);
+ WifiDebugHostWakeReasonStats wake_reason_stats = {};
+ auto status = wifi_chip_->getDebugHostWakeReasonStats(&wake_reason_stats);
+ if (caps & static_cast<int32_t>(IWifiChip::ChipCapabilityMask::DEBUG_HOST_WAKE_REASON_STATS)) {
+ EXPECT_TRUE(status.isOk());
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * StartLoggingToDebugRingBuffer
+ */
+TEST_P(WifiChipAidlTest, StartLoggingToDebugRingBuffer) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ int32_t caps = getChipCapabilities(wifi_chip_);
+ std::string ring_name;
+ std::vector<WifiDebugRingBufferStatus> ring_buffer_status;
+ auto status = wifi_chip_->getDebugRingBuffersStatus(&ring_buffer_status);
+
+ if (hasAnyRingBufferCapabilities(caps)) {
+ EXPECT_TRUE(status.isOk());
+ ASSERT_NE(ring_buffer_status.size(), 0);
+ ring_name = ring_buffer_status[0].ringName;
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+
+ status = wifi_chip_->startLoggingToDebugRingBuffer(
+ ring_name, WifiDebugRingBufferVerboseLevel::VERBOSE, 5, 1024);
+ if (hasAnyRingBufferCapabilities(caps)) {
+ EXPECT_TRUE(status.isOk());
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * ForceDumpToDebugRingBuffer
+ */
+TEST_P(WifiChipAidlTest, ForceDumpToDebugRingBuffer) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+ int32_t caps = getChipCapabilities(wifi_chip_);
+ std::string ring_name;
+ std::vector<WifiDebugRingBufferStatus> ring_buffer_status;
+ auto status = wifi_chip_->getDebugRingBuffersStatus(&ring_buffer_status);
+
+ if (hasAnyRingBufferCapabilities(caps)) {
+ EXPECT_TRUE(status.isOk());
+ ASSERT_NE(ring_buffer_status.size(), 0);
+ ring_name = ring_buffer_status[0].ringName;
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+
+ status = wifi_chip_->forceDumpToDebugRingBuffer(ring_name);
+ if (hasAnyRingBufferCapabilities(caps)) {
+ EXPECT_TRUE(status.isOk());
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/*
+ * CreateStaIface
+ * Configures the chip in STA mode and creates an iface.
+ */
+TEST_P(WifiChipAidlTest, CreateStaIface) {
+ configureChipForStaAndGetIface();
+}
+
+/*
+ * CreateApIface
+ */
+TEST_P(WifiChipAidlTest, CreateApIface) {
+ configureChipForApAndGetIface();
+}
+
+/*
+ * CreateNanIface
+ */
+TEST_P(WifiChipAidlTest, CreateNanIface) {
+ configureChipForNanAndGetIface();
+}
+
+/*
+ * CreateP2pIface
+ */
+TEST_P(WifiChipAidlTest, CreateP2pIface) {
+ configureChipForNanAndGetIface();
+}
+
+/*
+ * GetStaIfaceNames
+ * Configures the chip in STA mode and ensures that the iface name list is
+ * empty before creating the iface. Then create the iface and ensure that
+ * iface name is returned in the iface name list.
+ */
+TEST_P(WifiChipAidlTest, GetStaIfaceNames) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::STA);
+
+ std::vector<std::string> iface_names;
+ EXPECT_TRUE(wifi_chip_->getP2pIfaceNames(&iface_names).isOk());
+ EXPECT_EQ(iface_names.size(), 0);
+
+ std::shared_ptr<IWifiStaIface> iface;
+ EXPECT_TRUE(wifi_chip_->createStaIface(&iface).isOk());
+ ASSERT_NE(nullptr, iface.get());
+
+ std::string iface_name = getStaIfaceName(iface);
+ EXPECT_TRUE(wifi_chip_->getStaIfaceNames(&iface_names).isOk());
+ EXPECT_EQ(iface_names.size(), 1);
+ EXPECT_EQ(iface_name, iface_names[0]);
+
+ EXPECT_TRUE(wifi_chip_->removeStaIface(iface_name).isOk());
+ EXPECT_TRUE(wifi_chip_->getStaIfaceNames(&iface_names).isOk());
+ EXPECT_EQ(iface_names.size(), 0);
+}
+
+/*
+ * GetP2pIfaceNames
+ */
+TEST_P(WifiChipAidlTest, GetP2pIfaceNames) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::P2P);
+
+ std::vector<std::string> iface_names;
+ EXPECT_TRUE(wifi_chip_->getP2pIfaceNames(&iface_names).isOk());
+ EXPECT_EQ(iface_names.size(), 0);
+
+ std::shared_ptr<IWifiP2pIface> iface;
+ EXPECT_TRUE(wifi_chip_->createP2pIface(&iface).isOk());
+ ASSERT_NE(nullptr, iface.get());
+
+ std::string iface_name = getP2pIfaceName(iface);
+ EXPECT_TRUE(wifi_chip_->getP2pIfaceNames(&iface_names).isOk());
+ EXPECT_EQ(iface_names.size(), 1);
+ EXPECT_EQ(iface_name, iface_names[0]);
+
+ EXPECT_TRUE(wifi_chip_->removeP2pIface(iface_name).isOk());
+ EXPECT_TRUE(wifi_chip_->getP2pIfaceNames(&iface_names).isOk());
+ EXPECT_EQ(iface_names.size(), 0);
+}
+
+/*
+ * GetApIfaceNames
+ */
+TEST_P(WifiChipAidlTest, GetApIfaceNames) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::AP);
+
+ std::vector<std::string> iface_names;
+ EXPECT_TRUE(wifi_chip_->getApIfaceNames(&iface_names).isOk());
+ EXPECT_EQ(iface_names.size(), 0);
+
+ std::shared_ptr<IWifiApIface> iface;
+ EXPECT_TRUE(wifi_chip_->createApIface(&iface).isOk());
+ ASSERT_NE(nullptr, iface.get());
+
+ std::string iface_name = getApIfaceName(iface);
+ EXPECT_TRUE(wifi_chip_->getApIfaceNames(&iface_names).isOk());
+ EXPECT_EQ(iface_names.size(), 1);
+ EXPECT_EQ(iface_name, iface_names[0]);
+
+ EXPECT_TRUE(wifi_chip_->removeApIface(iface_name).isOk());
+ EXPECT_TRUE(wifi_chip_->getApIfaceNames(&iface_names).isOk());
+ EXPECT_EQ(iface_names.size(), 0);
+}
+
+/*
+ * GetNanIfaceNames
+ */
+TEST_P(WifiChipAidlTest, GetNanIfaceNames) {
+ configureChipForConcurrencyType(IfaceConcurrencyType::NAN_IFACE);
+
+ std::vector<std::string> iface_names;
+ EXPECT_TRUE(wifi_chip_->getNanIfaceNames(&iface_names).isOk());
+ EXPECT_EQ(iface_names.size(), 0);
+
+ std::shared_ptr<IWifiNanIface> iface;
+ EXPECT_TRUE(wifi_chip_->createNanIface(&iface).isOk());
+ ASSERT_NE(nullptr, iface.get());
+
+ std::string iface_name = getNanIfaceName(iface);
+ EXPECT_TRUE(wifi_chip_->getNanIfaceNames(&iface_names).isOk());
+ EXPECT_EQ(iface_names.size(), 1);
+ EXPECT_EQ(iface_name, iface_names[0]);
+
+ EXPECT_TRUE(wifi_chip_->removeNanIface(iface_name).isOk());
+ EXPECT_TRUE(wifi_chip_->getNanIfaceNames(&iface_names).isOk());
+ EXPECT_EQ(iface_names.size(), 0);
+}
+
+/*
+ * GetStaIface
+ * Configures the chip in STA mode and creates an iface. Then retrieves
+ * the iface object using its name and ensures that any other name
+ * doesn't retrieve a valid iface object.
+ */
+TEST_P(WifiChipAidlTest, GetStaIface) {
+ std::shared_ptr<IWifiStaIface> iface = configureChipForStaAndGetIface();
+ std::string iface_name = getStaIfaceName(iface);
+
+ std::shared_ptr<IWifiStaIface> retrieved_iface;
+ EXPECT_TRUE(wifi_chip_->getStaIface(iface_name, &retrieved_iface).isOk());
+ EXPECT_NE(nullptr, retrieved_iface.get());
+
+ std::string invalid_name = iface_name + "0";
+ std::shared_ptr<IWifiStaIface> invalid_iface;
+ auto status = wifi_chip_->getStaIface(invalid_name, &invalid_iface);
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_INVALID_ARGS));
+ EXPECT_EQ(nullptr, invalid_iface.get());
+}
+
+/*
+ * GetP2pIface
+ */
+TEST_P(WifiChipAidlTest, GetP2pIface) {
+ std::shared_ptr<IWifiP2pIface> iface = configureChipForP2pAndGetIface();
+ std::string iface_name = getP2pIfaceName(iface);
+
+ std::shared_ptr<IWifiP2pIface> retrieved_iface;
+ EXPECT_TRUE(wifi_chip_->getP2pIface(iface_name, &retrieved_iface).isOk());
+ EXPECT_NE(nullptr, retrieved_iface.get());
+
+ std::string invalid_name = iface_name + "0";
+ std::shared_ptr<IWifiP2pIface> invalid_iface;
+ auto status = wifi_chip_->getP2pIface(invalid_name, &invalid_iface);
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_INVALID_ARGS));
+ EXPECT_EQ(nullptr, invalid_iface.get());
+}
+
+/*
+ * GetApIface
+ */
+TEST_P(WifiChipAidlTest, GetApIface) {
+ std::shared_ptr<IWifiApIface> iface = configureChipForApAndGetIface();
+ std::string iface_name = getApIfaceName(iface);
+
+ std::shared_ptr<IWifiApIface> retrieved_iface;
+ EXPECT_TRUE(wifi_chip_->getApIface(iface_name, &retrieved_iface).isOk());
+ EXPECT_NE(nullptr, retrieved_iface.get());
+
+ std::string invalid_name = iface_name + "0";
+ std::shared_ptr<IWifiApIface> invalid_iface;
+ auto status = wifi_chip_->getApIface(invalid_name, &invalid_iface);
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_INVALID_ARGS));
+ EXPECT_EQ(nullptr, invalid_iface.get());
+}
+
+/*
+ * GetNanIface
+ */
+TEST_P(WifiChipAidlTest, GetNanIface) {
+ std::shared_ptr<IWifiNanIface> iface = configureChipForNanAndGetIface();
+ std::string iface_name = getNanIfaceName(iface);
+
+ std::shared_ptr<IWifiNanIface> retrieved_iface;
+ EXPECT_TRUE(wifi_chip_->getNanIface(iface_name, &retrieved_iface).isOk());
+ EXPECT_NE(nullptr, retrieved_iface.get());
+
+ std::string invalid_name = iface_name + "0";
+ std::shared_ptr<IWifiNanIface> invalid_iface;
+ auto status = wifi_chip_->getNanIface(invalid_name, &invalid_iface);
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_INVALID_ARGS));
+ EXPECT_EQ(nullptr, invalid_iface.get());
+}
+
+/*
+ * RemoveStaIface
+ * Configures the chip in STA mode and creates an iface. Then removes
+ * the iface object using the correct name and ensures that any other
+ * name doesn't remove the iface.
+ */
+TEST_P(WifiChipAidlTest, RemoveStaIface) {
+ std::shared_ptr<IWifiStaIface> iface = configureChipForStaAndGetIface();
+ std::string iface_name = getStaIfaceName(iface);
+
+ std::string invalid_name = iface_name + "0";
+ auto status = wifi_chip_->removeStaIface(invalid_name);
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_INVALID_ARGS));
+ EXPECT_TRUE(wifi_chip_->removeStaIface(iface_name).isOk());
+
+ // No such iface exists now, so this should return failure.
+ EXPECT_FALSE(wifi_chip_->removeStaIface(iface_name).isOk());
+}
+
+/*
+ * RemoveP2pIface
+ */
+TEST_P(WifiChipAidlTest, RemoveP2pIface) {
+ std::shared_ptr<IWifiP2pIface> iface = configureChipForP2pAndGetIface();
+ std::string iface_name = getP2pIfaceName(iface);
+
+ std::string invalid_name = iface_name + "0";
+ auto status = wifi_chip_->removeP2pIface(invalid_name);
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_INVALID_ARGS));
+ EXPECT_TRUE(wifi_chip_->removeP2pIface(iface_name).isOk());
+
+ // No such iface exists now, so this should return failure.
+ EXPECT_FALSE(wifi_chip_->removeP2pIface(iface_name).isOk());
+}
+
+/*
+ * RemoveApIface
+ */
+TEST_P(WifiChipAidlTest, RemoveApIface) {
+ std::shared_ptr<IWifiApIface> iface = configureChipForApAndGetIface();
+ std::string iface_name = getApIfaceName(iface);
+
+ std::string invalid_name = iface_name + "0";
+ auto status = wifi_chip_->removeApIface(invalid_name);
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_INVALID_ARGS));
+ EXPECT_TRUE(wifi_chip_->removeApIface(iface_name).isOk());
+
+ // No such iface exists now, so this should return failure.
+ EXPECT_FALSE(wifi_chip_->removeApIface(iface_name).isOk());
+}
+
+/*
+ * RemoveNanIface
+ */
+TEST_P(WifiChipAidlTest, RemoveNanIface) {
+ std::shared_ptr<IWifiNanIface> iface = configureChipForNanAndGetIface();
+ std::string iface_name = getNanIfaceName(iface);
+
+ std::string invalid_name = iface_name + "0";
+ auto status = wifi_chip_->removeNanIface(invalid_name);
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_INVALID_ARGS));
+ EXPECT_TRUE(wifi_chip_->removeNanIface(iface_name).isOk());
+
+ // No such iface exists now, so this should return failure.
+ EXPECT_FALSE(wifi_chip_->removeNanIface(iface_name).isOk());
+}
+
+/*
+ * CreateRttController
+ */
+TEST_P(WifiChipAidlTest, CreateRttController) {
+ std::shared_ptr<IWifiStaIface> iface = configureChipForStaAndGetIface();
+ std::shared_ptr<IWifiRttController> rtt_controller;
+ auto status = wifi_chip_->createRttController(iface, &rtt_controller);
+ if (status.isOk()) {
+ EXPECT_NE(nullptr, rtt_controller.get());
+ } else {
+ EXPECT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+ }
+}
+
+/**
+ * CreateBridgedApIface & RemoveIfaceInstanceFromBridgedApIface
+ */
+TEST_P(WifiChipAidlTest, CreateBridgedApIfaceAndremoveIfaceInstanceFromBridgedApIfaceTest) {
+ bool isBridgedSupport = testing::checkSubstringInCommandOutput(
+ "/system/bin/cmd wifi get-softap-supported-features",
+ "wifi_softap_bridged_ap_supported");
+ if (!isBridgedSupport) {
+ GTEST_SKIP() << "Missing Bridged AP support";
+ }
+
+ std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(getInstanceName());
+ ASSERT_NE(nullptr, wifi_chip.get());
+ std::shared_ptr<IWifiApIface> wifi_ap_iface = getBridgedWifiApIface(wifi_chip);
+ ASSERT_NE(nullptr, wifi_ap_iface.get());
+
+ std::string br_name;
+ std::vector<std::string> instances;
+ EXPECT_TRUE(wifi_ap_iface->getName(&br_name).isOk());
+ EXPECT_TRUE(wifi_ap_iface->getBridgedInstances(&instances).isOk());
+ EXPECT_EQ(instances.size(), 2);
+
+ std::vector<std::string> instances_after_remove;
+ EXPECT_TRUE(wifi_chip->removeIfaceInstanceFromBridgedApIface(br_name, instances[0]).isOk());
+ EXPECT_TRUE(wifi_ap_iface->getBridgedInstances(&instances_after_remove).isOk());
+ EXPECT_EQ(instances_after_remove.size(), 1);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiChipAidlTest);
+INSTANTIATE_TEST_SUITE_P(WifiTest, WifiChipAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IWifi::descriptor)),
+ android::PrintInstanceNameToString);
+
+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/wifi/aidl/vts/functional/wifi_nan_iface_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_nan_iface_aidl_test.cpp
new file mode 100644
index 0000000..457d57a
--- /dev/null
+++ b/wifi/aidl/vts/functional/wifi_nan_iface_aidl_test.cpp
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Staache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <vector>
+
+#include <VtsCoreUtil.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/wifi/BnWifi.h>
+#include <aidl/android/hardware/wifi/BnWifiNanIfaceEventCallback.h>
+#include <aidl/android/hardware/wifi/NanBandIndex.h>
+#include <android/binder_manager.h>
+#include <android/binder_status.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+#include "wifi_aidl_test_utils.h"
+
+using aidl::android::hardware::wifi::BnWifiNanIfaceEventCallback;
+using aidl::android::hardware::wifi::IWifiNanIface;
+using aidl::android::hardware::wifi::NanBandIndex;
+using aidl::android::hardware::wifi::NanBandSpecificConfig;
+using aidl::android::hardware::wifi::NanCapabilities;
+using aidl::android::hardware::wifi::NanClusterEventInd;
+using aidl::android::hardware::wifi::NanConfigRequest;
+using aidl::android::hardware::wifi::NanConfigRequestSupplemental;
+using aidl::android::hardware::wifi::NanDataPathConfirmInd;
+using aidl::android::hardware::wifi::NanDataPathRequestInd;
+using aidl::android::hardware::wifi::NanDataPathScheduleUpdateInd;
+using aidl::android::hardware::wifi::NanDataPathSecurityType;
+using aidl::android::hardware::wifi::NanEnableRequest;
+using aidl::android::hardware::wifi::NanFollowupReceivedInd;
+using aidl::android::hardware::wifi::NanInitiateDataPathRequest;
+using aidl::android::hardware::wifi::NanMatchAlg;
+using aidl::android::hardware::wifi::NanMatchInd;
+using aidl::android::hardware::wifi::NanPublishRequest;
+using aidl::android::hardware::wifi::NanPublishType;
+using aidl::android::hardware::wifi::NanRespondToDataPathIndicationRequest;
+using aidl::android::hardware::wifi::NanStatus;
+using aidl::android::hardware::wifi::NanStatusCode;
+using aidl::android::hardware::wifi::NanTxType;
+
+#define TIMEOUT_PERIOD 10
+
+class WifiNanIfaceAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ if (!::testing::deviceSupportsFeature("android.hardware.wifi.aware"))
+ GTEST_SKIP() << "Skipping this test since NAN is not supported.";
+ stopWifiService(getInstanceName());
+
+ wifi_nan_iface_ = getWifiNanIface(getInstanceName());
+ ASSERT_NE(nullptr, wifi_nan_iface_.get());
+ std::shared_ptr<WifiNanIfaceEventCallback> callback =
+ ndk::SharedRefBase::make<WifiNanIfaceEventCallback>(*this);
+ EXPECT_TRUE(wifi_nan_iface_->registerEventCallback(callback).isOk());
+ }
+
+ void TearDown() override { stopWifiService(getInstanceName()); }
+
+ // Used as a mechanism to inform the test about data/event callbacks.
+ inline void notify() {
+ std::unique_lock<std::mutex> lock(mtx_);
+ count_++;
+ cv_.notify_one();
+ }
+
+ enum CallbackType {
+ INVALID = -2,
+ ANY_CALLBACK = -1,
+
+ NOTIFY_CAPABILITIES_RESPONSE = 0,
+ NOTIFY_ENABLE_RESPONSE,
+ NOTIFY_CONFIG_RESPONSE,
+ NOTIFY_DISABLE_RESPONSE,
+ NOTIFY_START_PUBLISH_RESPONSE,
+ NOTIFY_STOP_PUBLISH_RESPONSE,
+ NOTIFY_START_SUBSCRIBE_RESPONSE,
+ NOTIFY_STOP_SUBSCRIBE_RESPONSE,
+ NOTIFY_TRANSMIT_FOLLOWUP_RESPONSE,
+ NOTIFY_CREATE_DATA_INTERFACE_RESPONSE,
+ NOTIFY_DELETE_DATA_INTERFACE_RESPONSE,
+ NOTIFY_INITIATE_DATA_PATH_RESPONSE,
+ NOTIFY_RESPOND_TO_DATA_PATH_INDICATION_RESPONSE,
+ NOTIFY_TERMINATE_DATA_PATH_RESPONSE,
+
+ EVENT_CLUSTER_EVENT,
+ EVENT_DISABLED,
+ EVENT_PUBLISH_TERMINATED,
+ EVENT_SUBSCRIBE_TERMINATED,
+ EVENT_MATCH,
+ EVENT_MATCH_EXPIRED,
+ EVENT_FOLLOWUP_RECEIVED,
+ EVENT_TRANSMIT_FOLLOWUP,
+ EVENT_DATA_PATH_REQUEST,
+ EVENT_DATA_PATH_CONFIRM,
+ EVENT_DATA_PATH_TERMINATED,
+ EVENT_DATA_PATH_SCHEDULE_UPDATE,
+ };
+
+ // Test code calls this function to wait for data/event callback.
+ // Must set callbackType = INVALID before calling this function.
+ inline std::cv_status wait(CallbackType waitForCallbackType) {
+ std::unique_lock<std::mutex> lock(mtx_);
+ EXPECT_NE(INVALID, waitForCallbackType);
+
+ 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;
+ if (waitForCallbackType != ANY_CALLBACK && callback_type_ != INVALID &&
+ callback_type_ != waitForCallbackType) {
+ count_--;
+ }
+ }
+ count_--;
+ return status;
+ }
+
+ class WifiNanIfaceEventCallback : public BnWifiNanIfaceEventCallback {
+ public:
+ WifiNanIfaceEventCallback(WifiNanIfaceAidlTest& parent) : parent_(parent){};
+
+ ::ndk::ScopedAStatus eventClusterEvent(const NanClusterEventInd& event) override {
+ parent_.callback_type_ = EVENT_CLUSTER_EVENT;
+ parent_.nan_cluster_event_ind_ = event;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus eventDataPathConfirm(const NanDataPathConfirmInd& event) override {
+ parent_.callback_type_ = EVENT_DATA_PATH_CONFIRM;
+ parent_.nan_data_path_confirm_ind_ = event;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus eventDataPathRequest(const NanDataPathRequestInd& event) override {
+ parent_.callback_type_ = EVENT_DATA_PATH_REQUEST;
+ parent_.nan_data_path_request_ind_ = event;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus eventDataPathScheduleUpdate(
+ const NanDataPathScheduleUpdateInd& event) override {
+ parent_.callback_type_ = EVENT_DATA_PATH_SCHEDULE_UPDATE;
+ parent_.nan_data_path_schedule_update_ind_ = event;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus eventDataPathTerminated(int32_t ndpInstanceId) override {
+ parent_.callback_type_ = EVENT_DATA_PATH_TERMINATED;
+ parent_.ndp_instance_id_ = ndpInstanceId;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus eventDisabled(const NanStatus& status) override {
+ parent_.callback_type_ = EVENT_DISABLED;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus eventFollowupReceived(const NanFollowupReceivedInd& event) override {
+ parent_.callback_type_ = EVENT_FOLLOWUP_RECEIVED;
+ parent_.nan_followup_received_ind_ = event;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus eventMatch(const NanMatchInd& event) override {
+ parent_.callback_type_ = EVENT_MATCH;
+ parent_.nan_match_ind_ = event;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus eventMatchExpired(int8_t discoverySessionId, int32_t peerId) override {
+ parent_.callback_type_ = EVENT_MATCH_EXPIRED;
+ parent_.session_id_ = discoverySessionId;
+ parent_.peer_id_ = peerId;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus eventPublishTerminated(int8_t sessionId,
+ const NanStatus& status) override {
+ parent_.callback_type_ = EVENT_PUBLISH_TERMINATED;
+ parent_.session_id_ = sessionId;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus eventSubscribeTerminated(int8_t sessionId,
+ const NanStatus& status) override {
+ parent_.callback_type_ = EVENT_SUBSCRIBE_TERMINATED;
+ parent_.session_id_ = sessionId;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus eventTransmitFollowup(char16_t id, const NanStatus& status) override {
+ parent_.callback_type_ = EVENT_TRANSMIT_FOLLOWUP;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyCapabilitiesResponse(
+ char16_t id, const NanStatus& status,
+ const NanCapabilities& capabilities) override {
+ parent_.callback_type_ = NOTIFY_CAPABILITIES_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.capabilities_ = capabilities;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyConfigResponse(char16_t id, const NanStatus& status) override {
+ parent_.callback_type_ = NOTIFY_CONFIG_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyCreateDataInterfaceResponse(char16_t id,
+ const NanStatus& status) override {
+ parent_.callback_type_ = NOTIFY_CREATE_DATA_INTERFACE_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyDeleteDataInterfaceResponse(char16_t id,
+ const NanStatus& status) override {
+ parent_.callback_type_ = NOTIFY_DELETE_DATA_INTERFACE_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyDisableResponse(char16_t id, const NanStatus& status) override {
+ parent_.callback_type_ = NOTIFY_DISABLE_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyEnableResponse(char16_t id, const NanStatus& status) override {
+ parent_.callback_type_ = NOTIFY_ENABLE_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyInitiateDataPathResponse(char16_t id, const NanStatus& status,
+ int32_t ndpInstanceId) override {
+ parent_.callback_type_ = NOTIFY_INITIATE_DATA_PATH_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.ndp_instance_id_ = ndpInstanceId;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyRespondToDataPathIndicationResponse(
+ char16_t id, const NanStatus& status) override {
+ parent_.callback_type_ = NOTIFY_RESPOND_TO_DATA_PATH_INDICATION_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyStartPublishResponse(char16_t id, const NanStatus& status,
+ int8_t sessionId) override {
+ parent_.callback_type_ = NOTIFY_START_PUBLISH_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.session_id_ = sessionId;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyStartSubscribeResponse(char16_t id, const NanStatus& status,
+ int8_t sessionId) override {
+ parent_.callback_type_ = NOTIFY_START_SUBSCRIBE_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.session_id_ = sessionId;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyStopPublishResponse(char16_t id,
+ const NanStatus& status) override {
+ parent_.callback_type_ = NOTIFY_STOP_PUBLISH_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyStopSubscribeResponse(char16_t id,
+ const NanStatus& status) override {
+ parent_.callback_type_ = NOTIFY_STOP_SUBSCRIBE_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyTerminateDataPathResponse(char16_t id,
+ const NanStatus& status) override {
+ parent_.callback_type_ = NOTIFY_TERMINATE_DATA_PATH_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus notifyTransmitFollowupResponse(char16_t id,
+ const NanStatus& status) override {
+ parent_.callback_type_ = NOTIFY_TRANSMIT_FOLLOWUP_RESPONSE;
+ parent_.id_ = id;
+ parent_.status_ = status;
+ parent_.notify();
+ return ndk::ScopedAStatus::ok();
+ }
+
+ private:
+ WifiNanIfaceAidlTest& parent_;
+ };
+
+ protected:
+ std::shared_ptr<IWifiNanIface> wifi_nan_iface_;
+ CallbackType callback_type_;
+ uint16_t id_;
+ uint8_t session_id_;
+ uint32_t ndp_instance_id_;
+ uint32_t peer_id_;
+ NanCapabilities capabilities_;
+ NanClusterEventInd nan_cluster_event_ind_;
+ NanDataPathConfirmInd nan_data_path_confirm_ind_;
+ NanDataPathRequestInd nan_data_path_request_ind_;
+ NanDataPathScheduleUpdateInd nan_data_path_schedule_update_ind_;
+ NanFollowupReceivedInd nan_followup_received_ind_;
+ NanMatchInd nan_match_ind_;
+ NanStatus status_;
+
+ const char* getInstanceName() { return GetParam().c_str(); }
+
+ private:
+ // synchronization objects
+ std::mutex mtx_;
+ std::condition_variable cv_;
+ int count_ = 0;
+};
+
+/*
+ * FailOnIfaceInvalid
+ * Ensure that API calls to an interface fail with code ERROR_WIFI_IFACE_INVALID
+ * after wifi is disabled.
+ */
+TEST_P(WifiNanIfaceAidlTest, FailOnIfaceInvalid) {
+ stopWifiService(getInstanceName());
+ sleep(5); // Ensure that all chips/interfaces are invalidated.
+ auto status = wifi_nan_iface_->getCapabilitiesRequest(0);
+ ASSERT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_WIFI_IFACE_INVALID));
+}
+
+/*
+ * EnableRequest - Invalid Args
+ */
+TEST_P(WifiNanIfaceAidlTest, EnableRequest_InvalidArgs) {
+ uint16_t inputCmdId = 10;
+ callback_type_ = INVALID;
+ NanEnableRequest nanEnableRequest = {};
+ NanConfigRequestSupplemental nanConfigRequestSupp = {};
+ auto status =
+ wifi_nan_iface_->enableRequest(inputCmdId, nanEnableRequest, nanConfigRequestSupp);
+ if (!checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
+ ASSERT_TRUE(status.isOk());
+
+ // Wait for a callback.
+ ASSERT_EQ(std::cv_status::no_timeout, wait(NOTIFY_ENABLE_RESPONSE));
+ ASSERT_EQ(NOTIFY_ENABLE_RESPONSE, callback_type_);
+ ASSERT_EQ(id_, inputCmdId);
+ ASSERT_EQ(status_.status, NanStatusCode::INVALID_ARGS);
+ }
+}
+
+/*
+ * ConfigRequest - Invalid Args
+ */
+TEST_P(WifiNanIfaceAidlTest, ConfigRequest_InvalidArgs) {
+ uint16_t inputCmdId = 10;
+ callback_type_ = INVALID;
+ NanConfigRequest nanConfigRequest = {};
+ NanConfigRequestSupplemental nanConfigRequestSupp = {};
+ auto status =
+ wifi_nan_iface_->configRequest(inputCmdId, nanConfigRequest, nanConfigRequestSupp);
+
+ if (!checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
+ ASSERT_TRUE(status.isOk());
+
+ // Wait for a callback.
+ ASSERT_EQ(std::cv_status::no_timeout, wait(NOTIFY_CONFIG_RESPONSE));
+ ASSERT_EQ(NOTIFY_CONFIG_RESPONSE, callback_type_);
+ ASSERT_EQ(id_, inputCmdId);
+ ASSERT_EQ(status_.status, NanStatusCode::INVALID_ARGS);
+ }
+}
+
+/*
+ * EnableRequest - Invalid Args in Shim Conversion
+ */
+TEST_P(WifiNanIfaceAidlTest, EnableRequest_InvalidShimArgs) {
+ uint16_t inputCmdId = 10;
+ NanEnableRequest nanEnableRequest = {};
+ nanEnableRequest.configParams.numberOfPublishServiceIdsInBeacon = -15; // must be > 0
+ NanConfigRequestSupplemental nanConfigRequestSupp = {};
+ auto status =
+ wifi_nan_iface_->enableRequest(inputCmdId, nanEnableRequest, nanConfigRequestSupp);
+ if (!checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
+ ASSERT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_INVALID_ARGS));
+ }
+}
+
+/*
+ * ConfigRequest - Invalid Args in Shim Conversion
+ */
+TEST_P(WifiNanIfaceAidlTest, ConfigRequest_InvalidShimArgs) {
+ uint16_t inputCmdId = 10;
+ NanConfigRequest nanConfigRequest = {};
+ nanConfigRequest.numberOfPublishServiceIdsInBeacon = -15; // must be > 0
+ NanConfigRequestSupplemental nanConfigRequestSupp = {};
+ auto status =
+ wifi_nan_iface_->configRequest(inputCmdId, nanConfigRequest, nanConfigRequestSupp);
+ if (!checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
+ ASSERT_TRUE(checkStatusCode(&status, WifiStatusCode::ERROR_INVALID_ARGS));
+ }
+}
+
+/*
+ * NotifyCapabilitiesResponse
+ */
+TEST_P(WifiNanIfaceAidlTest, NotifyCapabilitiesResponse) {
+ uint16_t inputCmdId = 10;
+ callback_type_ = INVALID;
+ EXPECT_TRUE(wifi_nan_iface_->getCapabilitiesRequest(inputCmdId).isOk());
+
+ // Wait for a callback.
+ ASSERT_EQ(std::cv_status::no_timeout, wait(NOTIFY_CAPABILITIES_RESPONSE));
+ ASSERT_EQ(NOTIFY_CAPABILITIES_RESPONSE, callback_type_);
+ ASSERT_EQ(id_, inputCmdId);
+ ASSERT_EQ(status_.status, NanStatusCode::SUCCESS);
+
+ // Check for reasonable capability values.
+ EXPECT_GT(capabilities_.maxConcurrentClusters, 0);
+ EXPECT_GT(capabilities_.maxPublishes, 0);
+ EXPECT_GT(capabilities_.maxSubscribes, 0);
+ EXPECT_EQ(capabilities_.maxServiceNameLen, 255);
+ EXPECT_EQ(capabilities_.maxMatchFilterLen, 255);
+ EXPECT_GT(capabilities_.maxTotalMatchFilterLen, 255);
+ EXPECT_EQ(capabilities_.maxServiceSpecificInfoLen, 255);
+ EXPECT_GE(capabilities_.maxExtendedServiceSpecificInfoLen, 255);
+ EXPECT_GT(capabilities_.maxNdiInterfaces, 0);
+ EXPECT_GT(capabilities_.maxNdpSessions, 0);
+ EXPECT_GT(capabilities_.maxAppInfoLen, 0);
+ EXPECT_GT(capabilities_.maxQueuedTransmitFollowupMsgs, 0);
+ EXPECT_GT(capabilities_.maxSubscribeInterfaceAddresses, 0);
+ EXPECT_NE(static_cast<int32_t>(capabilities_.supportedCipherSuites), 0);
+}
+
+/*
+ * StartPublishRequest
+ */
+TEST_P(WifiNanIfaceAidlTest, StartPublishRequest) {
+ uint16_t inputCmdId = 10;
+ NanBandSpecificConfig config24 = {};
+ config24.rssiClose = 60;
+ config24.rssiMiddle = 70;
+ config24.rssiCloseProximity = 60;
+ config24.dwellTimeMs = 200;
+ config24.scanPeriodSec = 20;
+ config24.validDiscoveryWindowIntervalVal = false;
+ config24.discoveryWindowIntervalVal = 0;
+
+ NanBandSpecificConfig config5 = {};
+ config5.rssiClose = 60;
+ config5.rssiMiddle = 75;
+ config5.rssiCloseProximity = 60;
+ config5.dwellTimeMs = 200;
+ config5.scanPeriodSec = 20;
+ config5.validDiscoveryWindowIntervalVal = false;
+ config5.discoveryWindowIntervalVal = 0;
+
+ NanEnableRequest req = {};
+ req.operateInBand[static_cast<int32_t>(NanBandIndex::NAN_BAND_24GHZ)] = true;
+ req.operateInBand[static_cast<int32_t>(NanBandIndex::NAN_BAND_5GHZ)] = false;
+ req.hopCountMax = 2;
+ req.configParams.masterPref = 0;
+ req.configParams.disableDiscoveryAddressChangeIndication = true;
+ req.configParams.disableStartedClusterIndication = true;
+ req.configParams.disableJoinedClusterIndication = true;
+ req.configParams.includePublishServiceIdsInBeacon = true;
+ req.configParams.numberOfPublishServiceIdsInBeacon = 0;
+ req.configParams.includeSubscribeServiceIdsInBeacon = true;
+ req.configParams.numberOfSubscribeServiceIdsInBeacon = 0;
+ req.configParams.rssiWindowSize = 8;
+ req.configParams.macAddressRandomizationIntervalSec = 1800;
+ req.configParams.bandSpecificConfig[static_cast<int32_t>(NanBandIndex::NAN_BAND_24GHZ)] =
+ config24;
+ req.configParams.bandSpecificConfig[static_cast<int32_t>(NanBandIndex::NAN_BAND_5GHZ)] =
+ config5;
+
+ req.debugConfigs.validClusterIdVals = true;
+ req.debugConfigs.clusterIdTopRangeVal = 65535;
+ req.debugConfigs.clusterIdBottomRangeVal = 0;
+ req.debugConfigs.validIntfAddrVal = false;
+ req.debugConfigs.validOuiVal = false;
+ req.debugConfigs.ouiVal = 0;
+ req.debugConfigs.validRandomFactorForceVal = false;
+ req.debugConfigs.randomFactorForceVal = 0;
+ req.debugConfigs.validHopCountForceVal = false;
+ req.debugConfigs.hopCountForceVal = 0;
+ req.debugConfigs.validDiscoveryChannelVal = false;
+ req.debugConfigs.discoveryChannelMhzVal[static_cast<int32_t>(NanBandIndex::NAN_BAND_24GHZ)] = 0;
+ req.debugConfigs.discoveryChannelMhzVal[static_cast<int32_t>(NanBandIndex::NAN_BAND_5GHZ)] = 0;
+ req.debugConfigs.validUseBeaconsInBandVal = false;
+ req.debugConfigs.useBeaconsInBandVal[static_cast<int32_t>(NanBandIndex::NAN_BAND_24GHZ)] = true;
+ req.debugConfigs.useBeaconsInBandVal[static_cast<int32_t>(NanBandIndex::NAN_BAND_5GHZ)] = true;
+ req.debugConfigs.validUseSdfInBandVal = false;
+ req.debugConfigs.useSdfInBandVal[static_cast<int32_t>(NanBandIndex::NAN_BAND_24GHZ)] = true;
+ req.debugConfigs.useSdfInBandVal[static_cast<int32_t>(NanBandIndex::NAN_BAND_5GHZ)] = true;
+
+ NanConfigRequestSupplemental nanConfigRequestSupp = {};
+ nanConfigRequestSupp.discoveryBeaconIntervalMs = 20;
+ nanConfigRequestSupp.numberOfSpatialStreamsInDiscovery = 0;
+ nanConfigRequestSupp.enableDiscoveryWindowEarlyTermination = false;
+
+ callback_type_ = INVALID;
+ auto status = wifi_nan_iface_->enableRequest(inputCmdId, req, nanConfigRequestSupp);
+ if (!checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
+ ASSERT_TRUE(status.isOk());
+
+ // Wait for a callback.
+ ASSERT_EQ(std::cv_status::no_timeout, wait(NOTIFY_ENABLE_RESPONSE));
+ ASSERT_EQ(NOTIFY_ENABLE_RESPONSE, callback_type_);
+ ASSERT_EQ(id_, inputCmdId);
+ ASSERT_EQ(status_.status, NanStatusCode::SUCCESS);
+ }
+
+ NanPublishRequest nanPublishRequest = {};
+ nanPublishRequest.baseConfigs.sessionId = 0;
+ nanPublishRequest.baseConfigs.ttlSec = 0;
+ nanPublishRequest.baseConfigs.discoveryWindowPeriod = 1;
+ nanPublishRequest.baseConfigs.discoveryCount = 0;
+ nanPublishRequest.baseConfigs.serviceName = {97};
+ nanPublishRequest.baseConfigs.discoveryMatchIndicator = NanMatchAlg::MATCH_NEVER;
+ nanPublishRequest.baseConfigs.useRssiThreshold = false;
+ nanPublishRequest.baseConfigs.disableDiscoveryTerminationIndication = false;
+ nanPublishRequest.baseConfigs.disableMatchExpirationIndication = true;
+ nanPublishRequest.baseConfigs.disableFollowupReceivedIndication = false;
+ nanPublishRequest.baseConfigs.securityConfig.securityType = NanDataPathSecurityType::OPEN;
+ nanPublishRequest.autoAcceptDataPathRequests = false;
+ nanPublishRequest.publishType = NanPublishType::UNSOLICITED;
+ nanPublishRequest.txType = NanTxType::BROADCAST;
+
+ status = wifi_nan_iface_->startPublishRequest(inputCmdId + 1, nanPublishRequest);
+ if (!checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
+ ASSERT_TRUE(status.isOk());
+
+ // Wait for a callback.
+ ASSERT_EQ(std::cv_status::no_timeout, wait(NOTIFY_START_PUBLISH_RESPONSE));
+ ASSERT_EQ(NOTIFY_START_PUBLISH_RESPONSE, callback_type_);
+ ASSERT_EQ(id_, inputCmdId + 1);
+ ASSERT_EQ(status_.status, NanStatusCode::SUCCESS);
+ }
+}
+
+/*
+ * RespondToDataPathIndicationRequest - Invalid Args
+ */
+TEST_P(WifiNanIfaceAidlTest, RespondToDataPathIndicationRequest_InvalidArgs) {
+ uint16_t inputCmdId = 10;
+ callback_type_ = INVALID;
+ NanRespondToDataPathIndicationRequest nanRespondToDataPathIndicationRequest = {};
+ nanRespondToDataPathIndicationRequest.ifaceName = "AwareInterfaceNameTooLong";
+ auto status = wifi_nan_iface_->respondToDataPathIndicationRequest(
+ inputCmdId, nanRespondToDataPathIndicationRequest);
+
+ if (!checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
+ ASSERT_EQ(status.getServiceSpecificError(),
+ static_cast<int32_t>(WifiStatusCode::ERROR_INVALID_ARGS));
+ }
+}
+
+/*
+ * InitiateDataPathRequest - Invalid Args
+ */
+TEST_P(WifiNanIfaceAidlTest, InitiateDataPathRequest_InvalidArgs) {
+ uint16_t inputCmdId = 10;
+ callback_type_ = INVALID;
+ NanInitiateDataPathRequest nanInitiateDataPathRequest = {};
+ nanInitiateDataPathRequest.ifaceName = "AwareInterfaceNameTooLong";
+ auto status = wifi_nan_iface_->initiateDataPathRequest(inputCmdId, nanInitiateDataPathRequest);
+
+ if (!checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
+ ASSERT_EQ(status.getServiceSpecificError(),
+ static_cast<int32_t>(WifiStatusCode::ERROR_INVALID_ARGS));
+ }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiNanIfaceAidlTest);
+INSTANTIATE_TEST_SUITE_P(WifiTest, WifiNanIfaceAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IWifi::descriptor)),
+ android::PrintInstanceNameToString);
+
+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/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp
new file mode 100644
index 0000000..d763fe6
--- /dev/null
+++ b/wifi/aidl/vts/functional/wifi_rtt_controller_aidl_test.cpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Staache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <vector>
+
+#include <VtsCoreUtil.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/wifi/BnWifi.h>
+#include <aidl/android/hardware/wifi/BnWifiRttControllerEventCallback.h>
+#include <android/binder_manager.h>
+#include <android/binder_status.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+#include "wifi_aidl_test_utils.h"
+
+using aidl::android::hardware::wifi::BnWifiRttControllerEventCallback;
+using aidl::android::hardware::wifi::IWifiRttController;
+using aidl::android::hardware::wifi::RttBw;
+using aidl::android::hardware::wifi::RttCapabilities;
+using aidl::android::hardware::wifi::RttConfig;
+using aidl::android::hardware::wifi::RttPeerType;
+using aidl::android::hardware::wifi::RttPreamble;
+using aidl::android::hardware::wifi::RttResponder;
+using aidl::android::hardware::wifi::RttResult;
+using aidl::android::hardware::wifi::RttType;
+using aidl::android::hardware::wifi::WifiChannelInfo;
+using aidl::android::hardware::wifi::WifiChannelWidthInMhz;
+using aidl::android::hardware::wifi::WifiStatusCode;
+
+class WifiRttControllerAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ if (!::testing::deviceSupportsFeature("android.hardware.wifi.rtt"))
+ GTEST_SKIP() << "Skipping this test since RTT is not supported.";
+ stopWifiService(getInstanceName());
+ wifi_rtt_controller_ = getWifiRttController();
+ ASSERT_NE(nullptr, wifi_rtt_controller_.get());
+
+ // Check RTT support before we run the test.
+ RttCapabilities caps = {};
+ auto status = wifi_rtt_controller_->getCapabilities(&caps);
+ if (checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED)) {
+ GTEST_SKIP() << "Skipping this test since RTT is not supported.";
+ }
+ }
+
+ void TearDown() override { stopWifiService(getInstanceName()); }
+
+ protected:
+ std::shared_ptr<IWifiRttController> getWifiRttController() {
+ std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(getInstanceName());
+ EXPECT_NE(nullptr, wifi_chip.get());
+
+ std::shared_ptr<IWifiStaIface> wifi_sta_iface = getWifiStaIface(getInstanceName());
+ EXPECT_NE(nullptr, wifi_sta_iface.get());
+
+ std::shared_ptr<IWifiRttController> rtt_controller;
+ EXPECT_TRUE(wifi_chip->createRttController(wifi_sta_iface, &rtt_controller).isOk());
+ EXPECT_NE(nullptr, rtt_controller.get());
+ return rtt_controller;
+ }
+
+ std::shared_ptr<IWifiRttController> wifi_rtt_controller_;
+
+ private:
+ const char* getInstanceName() { return GetParam().c_str(); }
+};
+
+class WifiRttControllerEventCallback : public BnWifiRttControllerEventCallback {
+ public:
+ WifiRttControllerEventCallback() = default;
+
+ ::ndk::ScopedAStatus onResults(int /* cmdId */,
+ const std::vector<RttResult>& /* results */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+};
+
+/*
+ * RegisterEventCallback
+ *
+ * Note: it is not feasible to test the invocation of the callback function,
+ * since events are triggered internally in the HAL implementation and cannot be
+ * triggered from the test case.
+ */
+TEST_P(WifiRttControllerAidlTest, RegisterEventCallback) {
+ std::shared_ptr<WifiRttControllerEventCallback> callback =
+ ndk::SharedRefBase::make<WifiRttControllerEventCallback>();
+ ASSERT_NE(nullptr, callback.get());
+ EXPECT_TRUE(wifi_rtt_controller_->registerEventCallback(callback).isOk());
+}
+
+/*
+ * GetCapabilities
+ */
+TEST_P(WifiRttControllerAidlTest, GetCapabilities) {
+ RttCapabilities caps = {};
+ EXPECT_TRUE(wifi_rtt_controller_->getCapabilities(&caps).isOk());
+}
+
+/*
+ * GetResponderInfo
+ */
+TEST_P(WifiRttControllerAidlTest, GetResponderInfo) {
+ RttResponder responder = {};
+ EXPECT_TRUE(wifi_rtt_controller_->getResponderInfo(&responder).isOk());
+}
+
+/*
+ * EnableResponder
+ */
+TEST_P(WifiRttControllerAidlTest, EnableResponder) {
+ int cmdId = 55;
+ WifiChannelInfo channelInfo;
+ channelInfo.width = WifiChannelWidthInMhz::WIDTH_80;
+ channelInfo.centerFreq = 5660;
+ channelInfo.centerFreq0 = 5660;
+ channelInfo.centerFreq1 = 0;
+
+ RttResponder responder = {};
+ EXPECT_TRUE(wifi_rtt_controller_->getResponderInfo(&responder).isOk());
+ EXPECT_TRUE(wifi_rtt_controller_->enableResponder(cmdId, channelInfo, 10, responder).isOk());
+}
+
+/*
+ * Request2SidedRangeMeasurement
+ * Tests the two sided ranging - 802.11mc FTM protocol.
+ */
+TEST_P(WifiRttControllerAidlTest, Request2SidedRangeMeasurement) {
+ RttCapabilities caps = {};
+ EXPECT_TRUE(wifi_rtt_controller_->getCapabilities(&caps).isOk());
+ if (!caps.rttFtmSupported) {
+ GTEST_SKIP() << "Skipping two sided RTT since driver/fw does not support";
+ }
+
+ RttConfig config;
+ config.addr = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}};
+ config.type = RttType::TWO_SIDED;
+ config.peer = RttPeerType::AP;
+ config.channel.width = WifiChannelWidthInMhz::WIDTH_80;
+ config.channel.centerFreq = 5180;
+ config.channel.centerFreq0 = 5210;
+ config.channel.centerFreq1 = 0;
+ config.bw = RttBw::BW_20MHZ;
+ config.preamble = RttPreamble::HT;
+ config.mustRequestLci = false;
+ config.mustRequestLcr = false;
+ config.burstPeriod = 0;
+ config.numBurst = 0;
+ config.numFramesPerBurst = 8;
+ config.numRetriesPerRttFrame = 0;
+ config.numRetriesPerFtmr = 0;
+ config.burstDuration = 9;
+
+ int cmdId = 55;
+ std::vector<RttConfig> configs = {config};
+ EXPECT_TRUE(wifi_rtt_controller_->rangeRequest(cmdId, configs).isOk());
+
+ // Sleep for 2 seconds to wait for driver/firmware to complete RTT.
+ sleep(2);
+}
+
+/*
+ * RangeRequest
+ */
+TEST_P(WifiRttControllerAidlTest, RangeRequest) {
+ RttCapabilities caps = {};
+ EXPECT_TRUE(wifi_rtt_controller_->getCapabilities(&caps).isOk());
+ if (!caps.rttOneSidedSupported) {
+ GTEST_SKIP() << "Skipping one sided RTT since driver/fw does not support";
+ }
+
+ // Get the highest supported preamble.
+ int preamble = 1;
+ int caps_preamble_support = static_cast<int>(caps.preambleSupport);
+ caps_preamble_support >>= 1;
+ while (caps_preamble_support != 0) {
+ caps_preamble_support >>= 1;
+ preamble <<= 1;
+ }
+
+ RttConfig config;
+ config.addr = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}};
+ config.type = RttType::ONE_SIDED;
+ config.peer = RttPeerType::AP;
+ 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 = static_cast<RttPreamble>(preamble);
+ config.mustRequestLci = false;
+ config.mustRequestLcr = false;
+ config.burstPeriod = 0;
+ config.numBurst = 0;
+ config.numFramesPerBurst = 8;
+ config.numRetriesPerRttFrame = 3;
+ config.numRetriesPerFtmr = 3;
+ config.burstDuration = 9;
+
+ int cmdId = 55;
+ std::vector<RttConfig> configs = {config};
+ EXPECT_TRUE(wifi_rtt_controller_->rangeRequest(cmdId, configs).isOk());
+
+ // Sleep for 2 seconds to wait for driver/firmware to complete RTT.
+ sleep(2);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiRttControllerAidlTest);
+INSTANTIATE_TEST_SUITE_P(WifiTest, WifiRttControllerAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IWifi::descriptor)),
+ android::PrintInstanceNameToString);
+
+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/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
new file mode 100644
index 0000000..ef7e274
--- /dev/null
+++ b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Staache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <vector>
+
+#include <VtsCoreUtil.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/wifi/BnWifi.h>
+#include <android/binder_manager.h>
+#include <android/binder_status.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+#include "wifi_aidl_test_utils.h"
+
+using aidl::android::hardware::wifi::IWifi;
+using aidl::android::hardware::wifi::IWifiStaIface;
+using aidl::android::hardware::wifi::MacAddress;
+using aidl::android::hardware::wifi::Ssid;
+using aidl::android::hardware::wifi::StaApfPacketFilterCapabilities;
+using aidl::android::hardware::wifi::StaBackgroundScanCapabilities;
+using aidl::android::hardware::wifi::StaLinkLayerStats;
+using aidl::android::hardware::wifi::StaRoamingCapabilities;
+using aidl::android::hardware::wifi::StaRoamingConfig;
+using aidl::android::hardware::wifi::StaRoamingState;
+using aidl::android::hardware::wifi::WifiBand;
+using aidl::android::hardware::wifi::WifiDebugRxPacketFateReport;
+using aidl::android::hardware::wifi::WifiDebugTxPacketFateReport;
+using aidl::android::hardware::wifi::WifiStatusCode;
+
+class WifiStaIfaceAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ stopWifiService(getInstanceName());
+ wifi_sta_iface_ = getWifiStaIface(getInstanceName());
+ ASSERT_NE(nullptr, wifi_sta_iface_.get());
+ }
+
+ void TearDown() override { stopWifiService(getInstanceName()); }
+
+ protected:
+ bool isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask expected) {
+ IWifiStaIface::StaIfaceCapabilityMask caps = {};
+ EXPECT_TRUE(wifi_sta_iface_->getCapabilities(&caps).isOk());
+ return static_cast<uint32_t>(caps) & static_cast<uint32_t>(expected);
+ }
+
+ ndk::ScopedAStatus createStaIface(std::shared_ptr<IWifiStaIface>* sta_iface) {
+ std::shared_ptr<IWifiChip> wifi_chip = getWifiChip(getInstanceName());
+ EXPECT_NE(nullptr, wifi_chip.get());
+ return wifi_chip->createStaIface(sta_iface);
+ }
+
+ std::shared_ptr<IWifiStaIface> wifi_sta_iface_;
+
+ private:
+ const char* getInstanceName() { return GetParam().c_str(); }
+};
+
+/*
+ * GetFactoryMacAddress
+ * Ensures that calls to getFactoryMacAddress will retrieve a non-zero MAC.
+ */
+TEST_P(WifiStaIfaceAidlTest, GetFactoryMacAddress) {
+ std::array<uint8_t, 6> mac;
+ EXPECT_TRUE(wifi_sta_iface_->getFactoryMacAddress(&mac).isOk());
+ std::array<uint8_t, 6> all_zero_mac = {0, 0, 0, 0, 0, 0};
+ EXPECT_NE(mac, all_zero_mac);
+}
+
+/*
+ * GetCapabilities
+ */
+TEST_P(WifiStaIfaceAidlTest, GetCapabilities) {
+ IWifiStaIface::StaIfaceCapabilityMask caps = {};
+ EXPECT_TRUE(wifi_sta_iface_->getCapabilities(&caps).isOk());
+ EXPECT_NE(static_cast<int32_t>(caps), 0);
+}
+
+/*
+ * GetApfPacketFilterCapabilities
+ */
+TEST_P(WifiStaIfaceAidlTest, GetApfPacketFilterCapabilities) {
+ if (!isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask::APF)) {
+ GTEST_SKIP() << "APF packet filter capabilities are not supported.";
+ }
+ StaApfPacketFilterCapabilities apf_caps = {};
+ EXPECT_TRUE(wifi_sta_iface_->getApfPacketFilterCapabilities(&apf_caps).isOk());
+}
+
+/*
+ * GetBackgroundScanCapabilities
+ */
+TEST_P(WifiStaIfaceAidlTest, GetBackgroundScanCapabilities) {
+ if (!isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask::BACKGROUND_SCAN)) {
+ GTEST_SKIP() << "Background scan capabilities are not supported.";
+ }
+ StaBackgroundScanCapabilities caps = {};
+ EXPECT_TRUE(wifi_sta_iface_->getBackgroundScanCapabilities(&caps).isOk());
+}
+
+/*
+ * GetValidFrequenciesForBand
+ * Ensures that we can retrieve valid frequencies for the 2.4 GHz band.
+ */
+TEST_P(WifiStaIfaceAidlTest, GetValidFrequenciesForBand) {
+ std::vector<int> freqs;
+ EXPECT_TRUE(wifi_sta_iface_->getValidFrequenciesForBand(WifiBand::BAND_24GHZ, &freqs).isOk());
+ EXPECT_NE(freqs.size(), 0);
+}
+
+/*
+ * GetLinkLayerStats
+ * Ensures that calls to getLinkLayerStats will retrieve a non-empty
+ * StaLinkLayerStats after link layer stats collection is enabled.
+ */
+TEST_P(WifiStaIfaceAidlTest, GetLinkLayerStats) {
+ if (!isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask::LINK_LAYER_STATS)) {
+ GTEST_SKIP() << "Skipping this test since link layer stats are not supported.";
+ }
+
+ // Enable link layer stats collection.
+ EXPECT_TRUE(wifi_sta_iface_->enableLinkLayerStatsCollection(true).isOk());
+
+ // Retrieve link layer stats.
+ StaLinkLayerStats link_layer_stats = {};
+ EXPECT_TRUE(wifi_sta_iface_->getLinkLayerStats(&link_layer_stats).isOk());
+ EXPECT_GT(link_layer_stats.timeStampInMs, 0);
+
+ // Try to create a 2nd iface. If successful, it should fill the duty cycle field.
+ std::shared_ptr<IWifiStaIface> iface;
+ auto status = createStaIface(&iface);
+ if (status.isOk()) {
+ EXPECT_GT(link_layer_stats.iface.timeSliceDutyCycleInPercent, 0);
+ }
+
+ // Disable link layer stats collection.
+ EXPECT_TRUE(wifi_sta_iface_->disableLinkLayerStatsCollection().isOk());
+}
+
+/*
+ * SetMacAddress
+ * Ensures that calls to setMacAddress will return successfully.
+ */
+TEST_P(WifiStaIfaceAidlTest, SetMacAddress) {
+ std::array<uint8_t, 6> mac = {0x12, 0x22, 0x33, 0x52, 0x10, 0x41};
+ EXPECT_TRUE(wifi_sta_iface_->setMacAddress(mac).isOk());
+}
+
+/*
+ * SetScanMode
+ */
+TEST_P(WifiStaIfaceAidlTest, SetScanMode) {
+ auto status = wifi_sta_iface_->setScanMode(true);
+ EXPECT_TRUE(status.isOk() || checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+
+ status = wifi_sta_iface_->setScanMode(false);
+ EXPECT_TRUE(status.isOk() || checkStatusCode(&status, WifiStatusCode::ERROR_NOT_SUPPORTED));
+}
+
+/*
+ * LinkLayerStatsCollection
+ */
+TEST_P(WifiStaIfaceAidlTest, LinkLayerStatsCollection) {
+ if (!isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask::LINK_LAYER_STATS)) {
+ GTEST_SKIP() << "Link layer stats collection is not supported.";
+ }
+
+ // Enable link layer stats collection.
+ EXPECT_TRUE(wifi_sta_iface_->enableLinkLayerStatsCollection(true).isOk());
+
+ // Retrieve link layer stats.
+ StaLinkLayerStats link_layer_stats = {};
+ EXPECT_TRUE(wifi_sta_iface_->getLinkLayerStats(&link_layer_stats).isOk());
+
+ // Disable link layer stats collection.
+ EXPECT_TRUE(wifi_sta_iface_->disableLinkLayerStatsCollection().isOk());
+}
+
+/*
+ * RSSIMonitoring
+ * Ensures that calls to startRssiMonitoring and stopRssiMonitoring will fail
+ * if the device is not connected to an AP.
+ */
+TEST_P(WifiStaIfaceAidlTest, RSSIMonitoring) {
+ if (!isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask::RSSI_MONITOR)) {
+ GTEST_SKIP() << "RSSI monitoring is not supported.";
+ }
+
+ const int cmd = 1;
+ const int maxRssi = -50;
+ const int minRssi = -90;
+ // Expected to fail because device is not connected to an AP.
+ EXPECT_FALSE(wifi_sta_iface_->startRssiMonitoring(cmd, maxRssi, minRssi).isOk());
+ EXPECT_FALSE(wifi_sta_iface_->stopRssiMonitoring(cmd).isOk());
+}
+
+/*
+ * RoamingControl
+ */
+TEST_P(WifiStaIfaceAidlTest, RoamingControl) {
+ if (!isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask::CONTROL_ROAMING)) {
+ GTEST_SKIP() << "Roaming control is not supported.";
+ }
+
+ // Retrieve roaming capabilities.
+ StaRoamingCapabilities caps = {};
+ EXPECT_TRUE(wifi_sta_iface_->getRoamingCapabilities(&caps).isOk());
+
+ // Set up roaming configuration based on roaming capabilities.
+ StaRoamingConfig roaming_config = {};
+ if (caps.maxBlocklistSize > 0) {
+ MacAddress block_list_entry;
+ block_list_entry.data = std::array<uint8_t, 6>{{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}};
+ roaming_config.bssidBlocklist = {block_list_entry};
+ }
+ if (caps.maxAllowlistSize > 0) {
+ Ssid allow_list_entry = {};
+ allow_list_entry.data = std::array<uint8_t, 32>{{0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC}};
+ roaming_config.ssidAllowlist = {allow_list_entry};
+ }
+
+ // Configure roaming.
+ EXPECT_TRUE(wifi_sta_iface_->configureRoaming(roaming_config).isOk());
+
+ // Enable roaming.
+ EXPECT_TRUE(wifi_sta_iface_->setRoamingState(StaRoamingState::ENABLED).isOk());
+}
+
+/*
+ * EnableNDOffload
+ */
+TEST_P(WifiStaIfaceAidlTest, EnableNDOffload) {
+ if (!isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask::ND_OFFLOAD)) {
+ GTEST_SKIP() << "ND offload is not supported.";
+ }
+ EXPECT_TRUE(wifi_sta_iface_->enableNdOffload(true).isOk());
+}
+
+/*
+ * PacketFateMonitoring
+ */
+TEST_P(WifiStaIfaceAidlTest, PacketFateMonitoring) {
+ if (!isCapabilitySupported(IWifiStaIface::StaIfaceCapabilityMask::DEBUG_PACKET_FATE)) {
+ GTEST_SKIP() << "Packet fate monitoring is not supported.";
+ }
+
+ // Start packet fate monitoring.
+ EXPECT_TRUE(wifi_sta_iface_->startDebugPacketFateMonitoring().isOk());
+
+ // Retrieve packets.
+ std::vector<WifiDebugRxPacketFateReport> rx_reports;
+ std::vector<WifiDebugTxPacketFateReport> tx_reports;
+ EXPECT_TRUE(wifi_sta_iface_->getDebugRxPacketFates(&rx_reports).isOk());
+ EXPECT_TRUE(wifi_sta_iface_->getDebugTxPacketFates(&tx_reports).isOk());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiStaIfaceAidlTest);
+INSTANTIATE_TEST_SUITE_P(WifiTest, WifiStaIfaceAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IWifi::descriptor)),
+ android::PrintInstanceNameToString);
+
+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/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
index 25a09b4..c7dd584 100644
--- a/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/aidl_api/android.hardware.wifi.supplicant/current/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
@@ -63,4 +63,5 @@
oneway void onWpsEventSuccess();
oneway void onQosPolicyReset();
oneway void onQosPolicyRequest(in int qosPolicyRequestId, in android.hardware.wifi.supplicant.QosPolicyData[] qosPolicyData);
+ oneway void onStateChangedWithAkm(in android.hardware.wifi.supplicant.StaIfaceCallbackState newState, in byte[] bssid, in int id, in byte[] ssid, in boolean filsHlpSent, in android.hardware.wifi.supplicant.KeyMgmtMask keyMgmtMask);
}
diff --git a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
index 0730a8c..c7961fa 100644
--- a/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
+++ b/wifi/supplicant/aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.aidl
@@ -27,6 +27,7 @@
import android.hardware.wifi.supplicant.DppFailureCode;
import android.hardware.wifi.supplicant.DppProgressCode;
import android.hardware.wifi.supplicant.Hs20AnqpData;
+import android.hardware.wifi.supplicant.KeyMgmtMask;
import android.hardware.wifi.supplicant.OsuMethod;
import android.hardware.wifi.supplicant.QosPolicyData;
import android.hardware.wifi.supplicant.StaIfaceCallbackState;
@@ -252,6 +253,8 @@
* event is triggered by a particular network, the |SupplicantNetworkId|,
* |ssid|, |bssid| parameters must indicate the parameters of the network/AP
* which caused this state transition.
+ * <p>
+ * This callback is deprecated from AIDL v2, newer HAL should call onStateChangedWithAkm().
*
* @param newState New State of the interface. This must be one of the |State|
* values above.
@@ -303,4 +306,27 @@
* @param qosPolicyData QoS policies info requested by the AP.
*/
void onQosPolicyRequest(in int qosPolicyRequestId, in QosPolicyData[] qosPolicyData);
+
+ /**
+ * Used to indicate a state change event on this particular iface. If this
+ * event is triggered by a particular network, the |id|,
+ * |ssid|, |bssid| parameters must indicate the parameters of the network/AP
+ * which caused this state transition.
+ *
+ * @param newState New State of the interface. This must be one of the
+ * |StaIfaceCallbackState| values above.
+ * @param bssid BSSID of the corresponding AP which caused this state
+ * change event. This must be zero'ed if this event is not
+ * specific to a particular network.
+ * @param id ID of the corresponding network which caused this
+ * state change event. This must be invalid (-1) if this
+ * event is not specific to a particular network.
+ * @param ssid SSID of the corresponding network which caused this state
+ * change event. This must be empty if this event is not specific
+ * to a particular network.
+ * @param filsHlpSent Whether FILS HLP IEs were included in this association.
+ * @param keyMgmtMask current used key mgmt mask.
+ */
+ void onStateChangedWithAkm(in StaIfaceCallbackState newState, in byte[] bssid, in int id,
+ in byte[] ssid, in boolean filsHlpSent, in KeyMgmtMask keyMgmtMask);
}
diff --git a/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp b/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
index 272a427..d57f539 100644
--- a/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
+++ b/wifi/supplicant/aidl/vts/functional/supplicant_sta_iface_aidl_test.cpp
@@ -206,6 +206,13 @@
QosPolicyData /* qosPolicyData */>&) override {
return ndk::ScopedAStatus::ok();
}
+ ::ndk::ScopedAStatus onStateChangedWithAkm(
+ ::aidl::android::hardware::wifi::supplicant::StaIfaceCallbackState /* newState */,
+ const std::vector<uint8_t>& /* bssid */, int32_t /* id */,
+ const std::vector<uint8_t>& /* ssid */, bool /* filsHlpSent */,
+ ::aidl::android::hardware::wifi::supplicant::KeyMgmtMask /* keyMgmtMask*/) override {
+ return ndk::ScopedAStatus::ok();
+ }
};
class SupplicantStaIfaceAidlTest : public testing::TestWithParam<std::string> {