Merge changes from topic "qos-r3-hal-impl" into main

* changes:
  Move downlink-specific QoS processing into an if-block.
  Add method to convert from AIDL QosCharacteristics type to the supplicant equivalent.
diff --git a/wpa_supplicant/aidl/aidl_manager.cpp b/wpa_supplicant/aidl/aidl_manager.cpp
index d0b3ae2..0daa8ef 100644
--- a/wpa_supplicant/aidl/aidl_manager.cpp
+++ b/wpa_supplicant/aidl/aidl_manager.cpp
@@ -43,15 +43,6 @@
 using aidl::android::hardware::wifi::supplicant::KeyMgmtMask;
 
 /**
-  * Check that the AIDL service is running at least the expected version.
-  * Use to avoid the case where the AIDL interface version
-  * is greater than the version implemented by the service.
-  */
-inline int32_t isAidlServiceVersionAtLeast(int32_t expected_version)
-{
-	return expected_version <= aidl_service_version;
-}
-/**
  * Check if the provided |wpa_supplicant| structure represents a P2P iface or
  * not.
  */
@@ -413,6 +404,16 @@
 	instance_ = NULL;
 }
 
+/**
+ * Check that the AIDL service is running at least the expected version.
+ * Use to avoid the case where the AIDL interface version
+ * is greater than the version implemented by the service.
+ */
+int32_t AidlManager::isAidlServiceVersionAtLeast(int32_t expected_version)
+{
+	return expected_version <= aidl_service_version;
+}
+
 int AidlManager::registerAidlService(struct wpa_global *global)
 {
 	// Create the main aidl service object and register it.
diff --git a/wpa_supplicant/aidl/aidl_manager.h b/wpa_supplicant/aidl/aidl_manager.h
index cd9d5f6..b11a760 100644
--- a/wpa_supplicant/aidl/aidl_manager.h
+++ b/wpa_supplicant/aidl/aidl_manager.h
@@ -171,6 +171,7 @@
 				       enum mlo_info_change_reason reason);
 
 	// Methods called from aidl objects.
+	int32_t isAidlServiceVersionAtLeast(int32_t expected_version);
 	void notifyExtRadioWorkStart(struct wpa_supplicant *wpa_s, uint32_t id);
 	void notifyExtRadioWorkTimeout(
 		struct wpa_supplicant *wpa_s, uint32_t id);
diff --git a/wpa_supplicant/aidl/sta_iface.cpp b/wpa_supplicant/aidl/sta_iface.cpp
index bd196c1..a4c2c36 100644
--- a/wpa_supplicant/aidl/sta_iface.cpp
+++ b/wpa_supplicant/aidl/sta_iface.cpp
@@ -2267,9 +2267,73 @@
 	return 0;
 }
 
+inline bool hasOptQosCharField(QosCharacteristics chars, QosCharacteristics::QosCharacteristicsMask field) {
+	return chars.optionalFieldMask & static_cast<uint32_t>(field);
+}
+
+static int parseQosCharacteristics(struct scs_desc_elem *descElem, QosPolicyScsData qosPolicy) {
+	struct qos_characteristics* suppChars = &descElem->qos_char_elem;
+	if (!qosPolicy.QosCharacteristics) {
+		suppChars->available = false;
+		return 0;
+	}
+
+	QosCharacteristics inputChars = qosPolicy.QosCharacteristics.value();
+	suppChars->available = true;
+
+	if (qosPolicy.direction == QosPolicyScsData::LinkDirection::DOWNLINK) {
+		suppChars->direction = SCS_DIRECTION_DOWN;
+	} else if (qosPolicy.direction == QosPolicyScsData::LinkDirection::UPLINK) {
+		suppChars->direction = SCS_DIRECTION_UP;
+	} else {
+		wpa_printf(MSG_ERROR, "Invalid QoS direction: %d", static_cast<int>(qosPolicy.direction));
+		return -1;
+	}
+
+	// Mandatory fields
+	suppChars->min_si = inputChars.minServiceIntervalUs;
+	suppChars->max_si = inputChars.maxServiceIntervalUs;
+	suppChars->min_data_rate = inputChars.minDataRateKbps;
+	suppChars->delay_bound = inputChars.delayBoundUs;
+
+	// Optional fields
+	uint16_t suppMask = 0;
+	if (hasOptQosCharField(inputChars, QosCharacteristics::QosCharacteristicsMask::MAX_MSDU_SIZE)) {
+		suppMask |= SCS_QOS_BIT_MAX_MSDU_SIZE;
+		suppChars->max_msdu_size = inputChars.maxMsduSizeOctets;
+	}
+	if (hasOptQosCharField(inputChars, QosCharacteristics::QosCharacteristicsMask::SERVICE_START_TIME)) {
+		// Client must provide both the service start time and the link ID if this field exists.
+		suppMask |= SCS_QOS_BIT_SERVICE_START_TIME | SCS_QOS_BIT_SERVICE_START_TIME_LINKID;
+		suppChars->service_start_time = inputChars.serviceStartTimeUs;
+		suppChars->service_start_time_link_id = inputChars.serviceStartTimeLinkId;
+	}
+	if (hasOptQosCharField(inputChars, QosCharacteristics::QosCharacteristicsMask::MEAN_DATA_RATE)) {
+		suppMask |= SCS_QOS_BIT_MEAN_DATA_RATE;
+		suppChars->mean_data_rate = inputChars.meanDataRateKbps;
+	}
+	if (hasOptQosCharField(inputChars, QosCharacteristics::QosCharacteristicsMask::BURST_SIZE)) {
+		suppMask |= SCS_QOS_BIT_DELAYED_BOUNDED_BURST_SIZE;
+		suppChars->burst_size = inputChars.burstSizeOctets;
+	}
+	if (hasOptQosCharField(inputChars, QosCharacteristics::QosCharacteristicsMask::MSDU_LIFETIME)) {
+		suppMask |= SCS_QOS_BIT_MSDU_LIFETIME;
+		suppChars->msdu_lifetime = inputChars.msduLifetimeMs;
+	}
+	if (hasOptQosCharField(inputChars, QosCharacteristics::QosCharacteristicsMask::MSDU_DELIVERY_INFO)) {
+		suppMask |= SCS_QOS_BIT_MSDU_DELIVERY_INFO;
+		// Expects the delivery ratio in the lower 4 bits and the count exponent
+		// in the upper 4 bits. See Figure 9-1001aw in the 802.11be spec.
+		suppChars->msdu_delivery_info = inputChars.msduDeliveryInfo.countExponent << 4
+			| (uint8_t) inputChars.msduDeliveryInfo.deliveryRatio;
+	}
+	suppChars->mask = suppMask;
+	return 0;
+}
+
 /**
  * This is a request to the AP (if it supports the feature) to apply the QoS policy
- * on traffic in the Downlink.
+ * on traffic in the Downlink or Uplink direction.
  */
 std::pair<std::vector<QosPolicyScsRequestStatus>, ndk::ScopedAStatus>
 StaIface::addQosPolicyRequestForScsInternal(const std::vector<QosPolicyScsData>& qosPolicyData)
@@ -2288,6 +2352,11 @@
 	}
 	free_up_scs_desc(scs_data);
 
+	// Uplink policies are not supported before AIDL V3.
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	WPA_ASSERT(aidl_manager);
+	bool supportsUplink = aidl_manager->isAidlServiceVersionAtLeast(3);
+
 	/**
 	 * format:
 	 * [scs_id=<decimal number>] [scs_up=<0-7>]
@@ -2323,39 +2392,49 @@
 		}
 
 		status.qosPolicyScsRequestStatusCode = QosPolicyScsRequestStatusCode::INVALID;
-		user_priority = qosPolicyData[i].userPriority;
-		if (user_priority < 0 || user_priority > 7) {
-			wpa_printf(MSG_ERROR,
-				   "Intra-Access user priority invalid %d", user_priority);
+		if (parseQosCharacteristics(&desc_elem, qosPolicyData[i])) {
 			reports.push_back(status);
 			continue;
 		}
 
-		desc_elem.intra_access_priority = user_priority;
-		desc_elem.scs_up_avail = true;
+		// TCLAS elements only need to be processed for downlink policies.
+		QosPolicyScsData::LinkDirection policyDirection = supportsUplink
+			? qosPolicyData[i].direction : QosPolicyScsData::LinkDirection::DOWNLINK;
+		if (policyDirection == QosPolicyScsData::LinkDirection::DOWNLINK) {
+			user_priority = qosPolicyData[i].userPriority;
+			if (user_priority < 0 || user_priority > 7) {
+				wpa_printf(MSG_ERROR,
+					"Intra-Access user priority invalid %d", user_priority);
+				reports.push_back(status);
+				continue;
+			}
 
-		/**
-		 * Supported classifier type 4.
-		 */
-		desc_elem.tclas_elems = (struct tclas_element *) os_malloc(sizeof(struct tclas_element));
-		if (!desc_elem.tclas_elems) {
-			wpa_printf(MSG_ERROR,
-				   "Classifier type4 failed with Bad malloc");
-			reports.push_back(status);
-			continue;
+			desc_elem.intra_access_priority = user_priority;
+			desc_elem.scs_up_avail = true;
+
+			/**
+			* Supported classifier type 4.
+			*/
+			desc_elem.tclas_elems = (struct tclas_element *) os_malloc(sizeof(struct tclas_element));
+			if (!desc_elem.tclas_elems) {
+				wpa_printf(MSG_ERROR,
+					"Classifier type4 failed with Bad malloc");
+				reports.push_back(status);
+				continue;
+			}
+
+			elem = desc_elem.tclas_elems;
+			memset(elem, 0, sizeof(struct tclas_element));
+			elem->classifier_type = 4;
+			if (scs_parse_type4(elem, qosPolicyData[i]) < 0) {
+				os_free(elem);
+				reports.push_back(status);
+				continue;
+			}
+
+			desc_elem.num_tclas_elem = 1;
 		}
 
-		elem = desc_elem.tclas_elems;
-		memset(elem, 0, sizeof(struct tclas_element));
-		elem->classifier_type = 4;
-		if (scs_parse_type4(elem, qosPolicyData[i]) < 0) {
-			os_free(elem);
-			reports.push_back(status);
-			continue;
-		}
-
-		desc_elem.num_tclas_elem = 1;
-
 		/* Reallocate memory to scs_desc_elems to accomodate further policies */
 		new_desc_elems = static_cast<struct scs_desc_elem *>(os_realloc(scs_data->scs_desc_elems,
 				(num_scs_ids + 1) * sizeof(struct scs_desc_elem)));