diff options
author | 2025-03-21 14:55:18 -0700 | |
---|---|---|
committer | 2025-03-21 14:55:18 -0700 | |
commit | d0812a43fee56d624b06dda6913402388ce12ac4 (patch) | |
tree | 7224a785099545a6bd2427487a3e1ede54c3080e | |
parent | e2bb614ad540a06481b8a759624b137db46f5208 (diff) | |
parent | 70b1a5f693063d43b13a255a3561a73afeef8c1b (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.
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); + } } |