summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Gabriel Biren <gbiren@google.com> 2025-03-21 14:55:18 -0700
committer Android (Google) Code Review <android-gerrit@google.com> 2025-03-21 14:55:18 -0700
commitd0812a43fee56d624b06dda6913402388ce12ac4 (patch)
tree7224a785099545a6bd2427487a3e1ede54c3080e
parente2bb614ad540a06481b8a759624b137db46f5208 (diff)
parent70b1a5f693063d43b13a255a3561a73afeef8c1b (diff)
Merge changes I9f6e080e,Ie4397072 into main
* changes: Implement the remaining USD methods in the framework mainline supplicant wrapper. Add framework implementations for startUsdPublish and startUsdSubscribe.
-rw-r--r--service/java/com/android/server/wifi/mainline_supplicant/MainlineSupplicant.java338
-rw-r--r--service/tests/wifitests/src/com/android/server/wifi/mainline_supplicant/MainlineSupplicantTest.java38
2 files changed, 376 insertions, 0 deletions
diff --git a/service/java/com/android/server/wifi/mainline_supplicant/MainlineSupplicant.java b/service/java/com/android/server/wifi/mainline_supplicant/MainlineSupplicant.java
index 21ed502075..1ac2ad8f64 100644
--- a/service/java/com/android/server/wifi/mainline_supplicant/MainlineSupplicant.java
+++ b/service/java/com/android/server/wifi/mainline_supplicant/MainlineSupplicant.java
@@ -18,6 +18,10 @@ package com.android.server.wifi.mainline_supplicant;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.MacAddress;
+import android.net.wifi.usd.Config;
+import android.net.wifi.usd.PublishConfig;
+import android.net.wifi.usd.SubscribeConfig;
import android.net.wifi.util.Environment;
import android.os.IBinder;
import android.os.RemoteException;
@@ -25,14 +29,18 @@ import android.os.ServiceSpecificException;
import android.system.wifi.mainline_supplicant.IMainlineSupplicant;
import android.system.wifi.mainline_supplicant.IStaInterface;
import android.system.wifi.mainline_supplicant.IStaInterfaceCallback;
+import android.system.wifi.mainline_supplicant.UsdMessageInfo;
+import android.system.wifi.mainline_supplicant.UsdServiceProtoType;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.SupplicantStaIfaceHal;
import com.android.server.wifi.WifiNative;
import com.android.server.wifi.WifiThreadRunner;
import com.android.server.wifi.usd.UsdNativeManager;
import com.android.wifi.flags.Flags;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
@@ -48,6 +56,7 @@ public class MainlineSupplicant {
private static final String TAG = "MainlineSupplicant";
private static final String MAINLINE_SUPPLICANT_SERVICE_NAME = "wifi_mainline_supplicant";
private static final long WAIT_FOR_DEATH_TIMEOUT_MS = 50L;
+ protected static final int DEFAULT_USD_FREQ_MHZ = 2437;
private IMainlineSupplicant mIMainlineSupplicant;
private final Object mLock = new Object();
@@ -360,6 +369,324 @@ public class MainlineSupplicant {
}
}
+ private static byte frameworkToHalUsdTransmissionType(
+ @Config.TransmissionType int transmissionType) {
+ switch (transmissionType) {
+ case Config.TRANSMISSION_TYPE_MULTICAST:
+ return IStaInterface.UsdPublishTransmissionType.MULTICAST;
+ case Config.TRANSMISSION_TYPE_UNICAST:
+ default:
+ return IStaInterface.UsdPublishTransmissionType.UNICAST;
+ }
+ }
+
+ private static byte frameworkToHalUsdProtoType(
+ @Config.ServiceProtoType int protoType) {
+ switch (protoType) {
+ case Config.SERVICE_PROTO_TYPE_GENERIC:
+ return UsdServiceProtoType.GENERIC;
+ case Config.SERVICE_PROTO_TYPE_CSA_MATTER:
+ return UsdServiceProtoType.CSA_MATTER;
+ default:
+ return UsdServiceProtoType.UNKNOWN;
+ }
+ }
+
+ @VisibleForTesting
+ protected static IStaInterface.UsdPublishConfig frameworkToHalUsdPublishConfig(
+ PublishConfig frameworkConfig) {
+ IStaInterface.UsdPublishConfig aidlConfig = new IStaInterface.UsdPublishConfig();
+ // USD publisher is always solicited and unsolicited
+ aidlConfig.publishType = IStaInterface.UsdPublishType.SOLICITED_AND_UNSOLICITED;
+ // FSD is always enabled for USD
+ aidlConfig.isFsd = true;
+ aidlConfig.transmissionType = frameworkToHalUsdTransmissionType(
+ frameworkConfig.getSolicitedTransmissionType());
+ aidlConfig.announcementPeriodMillis = frameworkConfig.getAnnouncementPeriodMillis();
+ aidlConfig.baseConfig = new IStaInterface.UsdBaseConfig();
+ aidlConfig.baseConfig.ttlSec = frameworkConfig.getTtlSeconds();
+ int[] freqs = frameworkConfig.getOperatingFrequenciesMhz();
+ aidlConfig.baseConfig.defaultFreqMhz = (freqs == null || freqs.length == 0)
+ ? DEFAULT_USD_FREQ_MHZ : freqs[0];
+ aidlConfig.baseConfig.freqsMhz = (freqs == null || freqs.length <= 1)
+ ? new int[0] : Arrays.copyOfRange(freqs, 1, freqs.length);
+ aidlConfig.baseConfig.serviceName = Arrays.toString(frameworkConfig.getServiceName());
+ aidlConfig.baseConfig.serviceSpecificInfo =
+ frameworkConfig.getServiceSpecificInfo() != null
+ ? frameworkConfig.getServiceSpecificInfo() : new byte[0];
+ aidlConfig.baseConfig.rxMatchFilter = frameworkConfig.getRxMatchFilterTlv() != null
+ ? frameworkConfig.getRxMatchFilterTlv() : new byte[0];
+ aidlConfig.baseConfig.txMatchFilter = frameworkConfig.getTxMatchFilterTlv() != null
+ ? frameworkConfig.getTxMatchFilterTlv() : new byte[0];
+ aidlConfig.baseConfig.serviceProtoType = frameworkToHalUsdProtoType(
+ frameworkConfig.getServiceProtoType());
+ return aidlConfig;
+ }
+
+ /**
+ * Start a USD publish operation.
+ *
+ * @param ifaceName Name of the interface
+ * @param cmdId An id for this command
+ * @param publishConfig Publish configuration
+ * @return true if successful, false otherwise
+ */
+ public boolean startUsdPublish(@NonNull String ifaceName, int cmdId,
+ @NonNull PublishConfig publishConfig) {
+ synchronized (mLock) {
+ final String methodName = "startUsdPublish";
+ if (ifaceName == null || publishConfig == null) {
+ return false;
+ }
+ IStaInterface iface = getStaIfaceOrLogError(ifaceName, methodName);
+ if (iface == null) {
+ return false;
+ }
+ try {
+ iface.startUsdPublish(cmdId, frameworkToHalUsdPublishConfig(publishConfig));
+ return true;
+ } catch (ServiceSpecificException e) {
+ handleServiceSpecificException(e, methodName);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodName);
+ }
+ return false;
+ }
+ }
+
+ private static byte frameworkToHalUsdSubscribeType(
+ @Config.SubscribeType int subscribeType) {
+ switch (subscribeType) {
+ case Config.SUBSCRIBE_TYPE_ACTIVE:
+ return IStaInterface.UsdSubscribeType.ACTIVE_MODE;
+ case Config.SUBSCRIBE_TYPE_PASSIVE:
+ default:
+ return IStaInterface.UsdSubscribeType.PASSIVE_MODE;
+ }
+ }
+
+ @VisibleForTesting
+ protected static IStaInterface.UsdSubscribeConfig frameworkToHalUsdSubscribeConfig(
+ SubscribeConfig frameworkConfig) {
+ IStaInterface.UsdSubscribeConfig aidlConfig = new IStaInterface.UsdSubscribeConfig();
+ aidlConfig.subscribeType =
+ frameworkToHalUsdSubscribeType(frameworkConfig.getSubscribeType());
+ aidlConfig.queryPeriodMillis = frameworkConfig.getQueryPeriodMillis();
+ aidlConfig.baseConfig = new IStaInterface.UsdBaseConfig();
+ aidlConfig.baseConfig.ttlSec = frameworkConfig.getTtlSeconds();
+ int[] freqs = frameworkConfig.getOperatingFrequenciesMhz();
+ aidlConfig.baseConfig.defaultFreqMhz = (freqs == null || freqs.length == 0)
+ ? DEFAULT_USD_FREQ_MHZ : freqs[0];
+ aidlConfig.baseConfig.freqsMhz = (freqs == null || freqs.length <= 1)
+ ? new int[0] : Arrays.copyOfRange(freqs, 1, freqs.length);
+ aidlConfig.baseConfig.serviceName = Arrays.toString(frameworkConfig.getServiceName());
+ aidlConfig.baseConfig.serviceSpecificInfo =
+ frameworkConfig.getServiceSpecificInfo() != null
+ ? frameworkConfig.getServiceSpecificInfo() : new byte[0];
+ aidlConfig.baseConfig.rxMatchFilter = frameworkConfig.getRxMatchFilterTlv() != null
+ ? frameworkConfig.getRxMatchFilterTlv() : new byte[0];
+ aidlConfig.baseConfig.txMatchFilter = frameworkConfig.getTxMatchFilterTlv() != null
+ ? frameworkConfig.getTxMatchFilterTlv() : new byte[0];
+ aidlConfig.baseConfig.serviceProtoType = frameworkToHalUsdProtoType(
+ frameworkConfig.getServiceProtoType());
+ return aidlConfig;
+ }
+
+ /**
+ * Start a USD subscribe operation.
+ *
+ * @param ifaceName Name of the interface
+ * @param cmdId An id for this command
+ * @param subscribeConfig Subscribe configuration
+ * @return true if successful, false otherwise
+ */
+ public boolean startUsdSubscribe(@NonNull String ifaceName, int cmdId,
+ @NonNull SubscribeConfig subscribeConfig) {
+ synchronized (mLock) {
+ final String methodName = "startUsdSubscribe";
+ if (ifaceName == null || subscribeConfig == null) {
+ return false;
+ }
+ IStaInterface iface = getStaIfaceOrLogError(ifaceName, methodName);
+ if (iface == null) {
+ return false;
+ }
+ try {
+ iface.startUsdSubscribe(cmdId, frameworkToHalUsdSubscribeConfig(subscribeConfig));
+ return true;
+ } catch (ServiceSpecificException e) {
+ handleServiceSpecificException(e, methodName);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodName);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Get the USD capabilities for the interface.
+ *
+ * @param ifaceName Name of the interface
+ * @return UsdCapabilities if available, otherwise null
+ */
+ public @Nullable SupplicantStaIfaceHal.UsdCapabilitiesInternal getUsdCapabilities(
+ @NonNull String ifaceName) {
+ synchronized (mLock) {
+ final String methodName = "getUsdCapabilities";
+ if (ifaceName == null) {
+ return null;
+ }
+ IStaInterface iface = getStaIfaceOrLogError(ifaceName, methodName);
+ if (iface == null) {
+ return null;
+ }
+ try {
+ IStaInterface.UsdCapabilities aidlCaps = iface.getUsdCapabilities();
+ if (aidlCaps == null) {
+ Log.e(TAG, "Received null USD capabilities from the HAL");
+ return null;
+ }
+ return new SupplicantStaIfaceHal.UsdCapabilitiesInternal(
+ aidlCaps.isUsdPublisherSupported,
+ aidlCaps.isUsdSubscriberSupported,
+ aidlCaps.maxLocalSsiLengthBytes,
+ aidlCaps.maxServiceNameLengthBytes,
+ aidlCaps.maxMatchFilterLengthBytes,
+ aidlCaps.maxNumPublishSessions,
+ aidlCaps.maxNumSubscribeSessions);
+ } catch (ServiceSpecificException e) {
+ handleServiceSpecificException(e, methodName);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodName);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Update an ongoing USD publish operation.
+ *
+ * @param ifaceName Name of the interface
+ * @param publishId Publish id for this session
+ * @param ssi Service specific info
+ */
+ public void updateUsdPublish(@NonNull String ifaceName, int publishId,
+ @NonNull byte[] ssi) {
+ synchronized (mLock) {
+ final String methodName = "updateUsdPublish";
+ if (ifaceName == null || ssi == null) {
+ return;
+ }
+ IStaInterface iface = getStaIfaceOrLogError(ifaceName, methodName);
+ if (iface == null) {
+ return;
+ }
+ try {
+ iface.updateUsdPublish(publishId, ssi);
+ } catch (ServiceSpecificException e) {
+ handleServiceSpecificException(e, methodName);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodName);
+ }
+ }
+ }
+
+ /**
+ * Cancel an ongoing USD publish session.
+ *
+ * @param ifaceName Name of the interface
+ * @param publishId Publish id for the session
+ */
+ public void cancelUsdPublish(@NonNull String ifaceName, int publishId) {
+ synchronized (mLock) {
+ final String methodName = "cancelUsdPublish";
+ if (ifaceName == null) {
+ return;
+ }
+ IStaInterface iface = getStaIfaceOrLogError(ifaceName, methodName);
+ if (iface == null) {
+ return;
+ }
+ try {
+ iface.cancelUsdPublish(publishId);
+ } catch (ServiceSpecificException e) {
+ handleServiceSpecificException(e, methodName);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodName);
+ }
+ }
+ }
+
+ /**
+ * Cancel an ongoing USD subscribe session.
+ *
+ * @param ifaceName Name of the interface
+ * @param subscribeId Subscribe id for the session
+ */
+ public void cancelUsdSubscribe(@NonNull String ifaceName, int subscribeId) {
+ synchronized (mLock) {
+ final String methodName = "cancelUsdSubscribe";
+ if (ifaceName == null) {
+ return;
+ }
+ IStaInterface iface = getStaIfaceOrLogError(ifaceName, methodName);
+ if (iface == null) {
+ return;
+ }
+ try {
+ iface.cancelUsdSubscribe(subscribeId);
+ } catch (ServiceSpecificException e) {
+ handleServiceSpecificException(e, methodName);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodName);
+ }
+ }
+ }
+
+ private static UsdMessageInfo createUsdMessageInfo(int ownId, int peerId,
+ MacAddress peerMacAddress, byte[] message) {
+ UsdMessageInfo messageInfo = new UsdMessageInfo();
+ messageInfo.ownId = ownId;
+ messageInfo.peerId = peerId;
+ messageInfo.message = message;
+ messageInfo.peerMacAddress = peerMacAddress.toByteArray();
+ return messageInfo;
+ }
+
+ /**
+ * Send a message to an ongoing USD publish or subscribe session.
+ *
+ * @param ifaceName Name of the interface
+ * @param ownId Id for the session
+ * @param peerId Id for the peer session
+ * @param peerMacAddress Mac address of the peer session
+ * @param message Data to send
+ * @return true if successful, false otherwise
+ */
+ public boolean sendUsdMessage(@NonNull String ifaceName, int ownId, int peerId,
+ @NonNull MacAddress peerMacAddress, @NonNull byte[] message) {
+ synchronized (mLock) {
+ final String methodName = "sendUsdMessage";
+ if (ifaceName == null || peerMacAddress == null || message == null) {
+ return false;
+ }
+ IStaInterface iface = getStaIfaceOrLogError(ifaceName, methodName);
+ if (iface == null) {
+ return false;
+ }
+ try {
+ iface.sendUsdMessage(
+ createUsdMessageInfo(ownId, peerId, peerMacAddress, message));
+ return true;
+ } catch (ServiceSpecificException e) {
+ handleServiceSpecificException(e, methodName);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodName);
+ }
+ return false;
+ }
+ }
+
private void handleServiceSpecificException(ServiceSpecificException e, String methodName) {
Log.e(TAG, methodName + " encountered ServiceSpecificException " + e);
}
@@ -372,6 +699,17 @@ public class MainlineSupplicant {
return true;
}
+ private @Nullable IStaInterface getStaIfaceOrLogError(String ifaceName, String methodName) {
+ synchronized (mLock) {
+ if (!mActiveStaIfaces.containsKey(ifaceName)) {
+ Log.e(TAG, "Unable to call " + methodName + " since iface "
+ + ifaceName + " does not exist");
+ return null;
+ }
+ return mActiveStaIfaces.get(ifaceName);
+ }
+ }
+
private void handleRemoteException(RemoteException e, String methodName) {
synchronized (mLock) {
Log.e(TAG, methodName + " encountered RemoteException " + e);
diff --git a/service/tests/wifitests/src/com/android/server/wifi/mainline_supplicant/MainlineSupplicantTest.java b/service/tests/wifitests/src/com/android/server/wifi/mainline_supplicant/MainlineSupplicantTest.java
index d1c25a5078..2312fc6c33 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/mainline_supplicant/MainlineSupplicantTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/mainline_supplicant/MainlineSupplicantTest.java
@@ -16,7 +16,9 @@
package com.android.server.wifi.mainline_supplicant;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -27,6 +29,8 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.net.wifi.usd.PublishConfig;
+import android.net.wifi.usd.SubscribeConfig;
import android.net.wifi.util.Environment;
import android.os.Handler;
import android.os.IBinder;
@@ -48,6 +52,7 @@ import org.mockito.MockitoAnnotations;
*/
public class MainlineSupplicantTest {
private static final String IFACE_NAME = "wlan0";
+ private static final String USD_SERVICE_NAME = "usd_service";
private @Mock IMainlineSupplicant mIMainlineSupplicantMock;
private @Mock IBinder mIBinderMock;
@@ -176,4 +181,37 @@ public class MainlineSupplicantTest {
// Only the valid remove request should reach have reached the service
verify(mIMainlineSupplicantMock, times(1)).removeStaInterface(anyString());
}
+
+ private void verifyUsdBaseConfigDefaultValues(IStaInterface.UsdBaseConfig baseConfig) {
+ assertNotNull(baseConfig.serviceName);
+ assertNotNull(baseConfig.freqsMhz);
+ assertNotNull(baseConfig.serviceSpecificInfo);
+ assertNotNull(baseConfig.rxMatchFilter);
+ assertNotNull(baseConfig.txMatchFilter);
+ assertEquals(MainlineSupplicant.DEFAULT_USD_FREQ_MHZ, baseConfig.defaultFreqMhz);
+ }
+
+ /**
+ * Verify that the proper default values are assigned during the
+ * USD Publish Config conversion.
+ */
+ @Test
+ public void testUsdPublishConfigConversionDefaultValues() {
+ PublishConfig frameworkConfig = new PublishConfig.Builder(USD_SERVICE_NAME).build();
+ IStaInterface.UsdPublishConfig aidlConfig =
+ MainlineSupplicant.frameworkToHalUsdPublishConfig(frameworkConfig);
+ verifyUsdBaseConfigDefaultValues(aidlConfig.baseConfig);
+ }
+
+ /**
+ * Verify that the proper default values are assigned during the
+ * USD Subscribe Config conversion.
+ */
+ @Test
+ public void testUsdSubscribeConfigConversionDefaultValues() {
+ SubscribeConfig frameworkConfig = new SubscribeConfig.Builder(USD_SERVICE_NAME).build();
+ IStaInterface.UsdSubscribeConfig aidlConfig =
+ MainlineSupplicant.frameworkToHalUsdSubscribeConfig(frameworkConfig);
+ verifyUsdBaseConfigDefaultValues(aidlConfig.baseConfig);
+ }
}