diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2017-06-12 22:17:29 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2017-06-12 22:17:35 +0000 |
commit | 1d094527e82f2e1e4ca1c8883eaf2384ddd7d9dc (patch) | |
tree | 2cf41b954eab88d713ea536427a97d8ba234aa1a | |
parent | 27e0f581a968efc5fec49b761472ebe3649244b9 (diff) | |
parent | 94ea8c951f0dd81563e7ea22f878397e75487746 (diff) |
Merge changes from topic 'osu_provider_api'
* changes:
WifiServiceImpl: add support for retrieving Hotspot 2.0 OSU providers
hotspot2: PasspointManager: add support for retrieving OSU providers info
hotspot2: anqp: OsuProviderInfo: friendly name and service description selection
6 files changed, 355 insertions, 1 deletions
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index 2dff54a50..3306fb608 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -73,6 +73,7 @@ import android.net.wifi.WifiLinkLayerStats; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.LocalOnlyHotspotCallback; import android.net.wifi.WifiScanner; +import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.AsyncTask; import android.os.BatteryStats; @@ -1537,6 +1538,23 @@ public class WifiServiceImpl extends IWifiManager.Stub { } /** + * Returns list of OSU (Online Sign-Up) providers associated with the given Passpoint network. + * + * @param scanResult scanResult of the Passpoint AP + * @return List of {@link OsuProvider} + */ + @Override + public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) { + enforceAccessPermission(); + mLog.trace("getMatchingOsuProviders uid=%").c(Binder.getCallingUid()).flush(); + if (!mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI_PASSPOINT)) { + throw new UnsupportedOperationException("Passpoint not enabled"); + } + return mWifiStateMachine.syncGetMatchingOsuProviders(scanResult, mWifiStateMachineChannel); + } + + /** * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)} * @return the supplicant-assigned identifier for the new or updated * network if the operation succeeds, or {@code -1} if it fails diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java index 89675b62d..06714c72f 100644 --- a/service/java/com/android/server/wifi/WifiStateMachine.java +++ b/service/java/com/android/server/wifi/WifiStateMachine.java @@ -75,6 +75,7 @@ import android.net.wifi.WifiSsid; import android.net.wifi.WpsInfo; import android.net.wifi.WpsResult; import android.net.wifi.WpsResult.Status; +import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.p2p.IWifiP2pManager; import android.os.BatteryStats; @@ -600,6 +601,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss // Get the list of installed Passpoint configurations. static final int CMD_GET_PASSPOINT_CONFIGS = BASE + 108; + // Get the list of OSU providers associated with a Passpoint network. + static final int CMD_GET_MATCHING_OSU_PROVIDERS = BASE + 109; + /* Commands from/to the SupplicantStateTracker */ /* Reset the supplicant state tracker */ static final int CMD_RESET_SUPPLICANT_STATE = BASE + 111; @@ -1874,6 +1878,22 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } /** + * Retrieve a list of {@link OsuProvider} associated with the given AP synchronously. + * + * @param scanResult The scan result of the AP + * @param channel Channel for communicating with the state machine + * @return List of {@link OsuProvider} + */ + public List<OsuProvider> syncGetMatchingOsuProviders(ScanResult scanResult, + AsyncChannel channel) { + Message resultMsg = + channel.sendMessageSynchronously(CMD_GET_MATCHING_OSU_PROVIDERS, scanResult); + List<OsuProvider> providers = (List<OsuProvider>) resultMsg.obj; + resultMsg.recycle(); + return providers; + } + + /** * Add or update a Passpoint configuration synchronously. * * @param channel Channel for communicating with the state machine @@ -3899,6 +3919,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss case CMD_GET_MATCHING_CONFIG: replyToMessage(message, message.what); break; + case CMD_GET_MATCHING_OSU_PROVIDERS: + replyToMessage(message, message.what, new ArrayList<OsuProvider>()); + break; case CMD_IP_CONFIGURATION_SUCCESSFUL: case CMD_IP_CONFIGURATION_LOST: case CMD_IP_REACHABILITY_LOST: @@ -4982,6 +5005,10 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss replyToMessage(message, message.what, mPasspointManager.getMatchingWifiConfig((ScanResult) message.obj)); break; + case CMD_GET_MATCHING_OSU_PROVIDERS: + replyToMessage(message, message.what, + mPasspointManager.getMatchingOsuProviders((ScanResult) message.obj)); + break; case CMD_RECONNECT: mWifiConnectivityManager.forceConnectivityScan(); break; diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java index f892e7051..3695ba35f 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java @@ -33,6 +33,7 @@ import android.graphics.drawable.Icon; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; +import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.UserHandle; import android.text.TextUtils; @@ -47,6 +48,8 @@ import com.android.server.wifi.WifiKeyStore; import com.android.server.wifi.WifiNative; import com.android.server.wifi.hotspot2.anqp.ANQPElement; import com.android.server.wifi.hotspot2.anqp.Constants; +import com.android.server.wifi.hotspot2.anqp.HSOsuProvidersElement; +import com.android.server.wifi.hotspot2.anqp.OsuProviderInfo; import com.android.server.wifi.util.InformationElementUtil; import com.android.server.wifi.util.ScanResultUtil; @@ -439,7 +442,13 @@ public class PasspointManager { InformationElementUtil.getHS2VendorSpecificIE(scanResult.informationElements); // Lookup ANQP data in the cache. - long bssid = Utils.parseMac(scanResult.BSSID); + long bssid; + try { + bssid = Utils.parseMac(scanResult.BSSID); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Invalid BSSID provided in the scan result: " + scanResult.BSSID); + return new HashMap<Constants.ANQPElementType, ANQPElement>(); + } ANQPData anqpEntry = mAnqpCache.getEntry(ANQPNetworkKey.buildKey( scanResult.SSID, bssid, scanResult.hessid, vsa.anqpDomainID)); if (anqpEntry != null) { @@ -480,6 +489,45 @@ public class PasspointManager { } /** + * Return the list of Hosspot 2.0 OSU (Online Sign-Up) providers associated with the given + * AP. + * + * An empty list will be returned when an invalid scan result is provided or no match is found. + * + * @param scanResult The scan result of the AP + * @return List of {@link OsuProvider} + */ + public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) { + if (scanResult == null) { + Log.e(TAG, "Attempt to retrieve OSU providers for a null ScanResult"); + return new ArrayList<OsuProvider>(); + } + if (!scanResult.isPasspointNetwork()) { + Log.e(TAG, "Attempt to retrieve OSU providers for a non-Passpoint AP"); + return new ArrayList<OsuProvider>(); + } + + // Lookup OSU Providers ANQP element. + Map<Constants.ANQPElementType, ANQPElement> anqpElements = getANQPElements(scanResult); + if (!anqpElements.containsKey(Constants.ANQPElementType.HSOSUProviders)) { + return new ArrayList<OsuProvider>(); + } + + HSOsuProvidersElement element = + (HSOsuProvidersElement) anqpElements.get(Constants.ANQPElementType.HSOSUProviders); + List<OsuProvider> providers = new ArrayList<>(); + for (OsuProviderInfo info : element.getProviders()) { + // TODO(b/62256482): include icon data once the icon file retrieval and management + // support is added. + OsuProvider provider = new OsuProvider(element.getOsuSsid(), info.getFriendlyName(), + info.getServiceDescription(), info.getServerUri(), + info.getNetworkAccessIdentifier(), info.getMethodList(), null); + providers.add(provider); + } + return providers; + } + + /** * Dump the current state of PasspointManager to the provided output stream. * * @param pw The output stream to write to diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/OsuProviderInfo.java b/service/java/com/android/server/wifi/hotspot2/anqp/OsuProviderInfo.java index 85b024e50..8952c5a51 100644 --- a/service/java/com/android/server/wifi/hotspot2/anqp/OsuProviderInfo.java +++ b/service/java/com/android/server/wifi/hotspot2/anqp/OsuProviderInfo.java @@ -30,6 +30,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Objects; /** @@ -168,6 +169,28 @@ public class OsuProviderInfo { return Collections.unmodifiableList(mServiceDescriptions); } + /** + * Return the friendly name string from the friendly name list. The string matching + * the default locale will be returned if it is found, otherwise the first name in the list + * will be returned. A null will be returned if the list is empty. + * + * @return friendly name string + */ + public String getFriendlyName() { + return getI18String(mFriendlyNames); + } + + /** + * Return the service description string from the service description list. The string + * matching the default locale will be returned if it is found, otherwise the first element in + * the list will be returned. A null will be returned if the list is empty. + * + * @return service description string + */ + public String getServiceDescription() { + return getI18String(mServiceDescriptions); + } + @Override public boolean equals(Object thatObject) { if (this == thatObject) { @@ -255,4 +278,24 @@ public class OsuProviderInfo { payload.position(payload.position() + length); return subBuffer; } + + /** + * Return the appropriate I18 string value from the list of I18 string values. + * The string matching the default locale will be returned if it is found, otherwise the + * first string in the list will be returned. A null will be returned if the list is empty. + * + * @param i18Strings List of I18 string values + * @return String matching the default locale, null otherwise + */ + private static String getI18String(List<I18Name> i18Strings) { + for (I18Name name : i18Strings) { + if (name.getLanguage().equals(Locale.getDefault().getLanguage())) { + return name.getText(); + } + } + if (i18Strings.size() > 0) { + return i18Strings.get(0).getText(); + } + return null; + } } diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java index 2b871b2db..d2762d022 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java @@ -47,10 +47,13 @@ import static org.mockito.MockitoAnnotations.initMocks; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Icon; +import android.net.Uri; import android.net.wifi.EAPConstants; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; +import android.net.wifi.WifiSsid; +import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.pps.Credential; import android.net.wifi.hotspot2.pps.HomeSp; @@ -70,6 +73,9 @@ import com.android.server.wifi.WifiNative; import com.android.server.wifi.hotspot2.anqp.ANQPElement; import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType; import com.android.server.wifi.hotspot2.anqp.DomainNameElement; +import com.android.server.wifi.hotspot2.anqp.HSOsuProvidersElement; +import com.android.server.wifi.hotspot2.anqp.I18Name; +import com.android.server.wifi.hotspot2.anqp.OsuProviderInfo; import com.android.server.wifi.util.ScanResultUtil; import org.junit.Before; @@ -84,6 +90,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; /** @@ -821,6 +828,93 @@ public class PasspointManagerTest { } /** + * Verify that an empty list will be returned when retrieving OSU providers for an AP with + * null scan result. + * + * @throws Exception + */ + @Test + public void getMatchingOsuProvidersForNullScanResult() throws Exception { + assertTrue(mManager.getMatchingOsuProviders(null).isEmpty()); + } + + /** + * Verify that an empty list will be returned when retrieving OSU providers for an AP with + * invalid BSSID. + * + * @throws Exception + */ + @Test + public void getMatchingOsuProvidersForInvalidBSSID() throws Exception { + ScanResult scanResult = createTestScanResult(); + scanResult.BSSID = "asdfdasfas"; + assertTrue(mManager.getMatchingOsuProviders(scanResult).isEmpty()); + } + + /** + * Verify that an empty list will be returned when retrieving OSU providers for a + * non-Passpoint AP. + * + * @throws Exception + */ + @Test + public void getMatchingOsuProvidersForNonPasspointAP() throws Exception { + ScanResult scanResult = createTestScanResult(); + scanResult.flags = 0; + assertTrue(mManager.getMatchingOsuProviders(scanResult).isEmpty()); + } + + /** + * Verify that an empty list will be returned when no match is found from the ANQP cache. + * + * @throws Exception + */ + @Test + public void getMatchingOsuProviderWithNoMatch() throws Exception { + when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(null); + assertTrue(mManager.getMatchingOsuProviders(createTestScanResult()).isEmpty()); + } + + /** + * Verify that an expected provider list will be returned when a match is found from + * the ANQP cache. + * + * @throws Exception + */ + @Test + public void getMatchingOsuProvidersWithMatch() throws Exception { + // Test data. + WifiSsid osuSsid = WifiSsid.createFromAsciiEncoded("Test SSID"); + String friendlyName = "Test Provider"; + String serviceDescription = "Dummy Service"; + Uri serverUri = Uri.parse("https://test.com"); + String nai = "access.test.com"; + List<Integer> methodList = Arrays.asList(1); + List<I18Name> friendlyNames = Arrays.asList( + new I18Name(Locale.ENGLISH.getLanguage(), Locale.ENGLISH, friendlyName)); + List<I18Name> serviceDescriptions = Arrays.asList( + new I18Name(Locale.ENGLISH.getLanguage(), Locale.ENGLISH, serviceDescription)); + + // Setup OSU providers ANQP element. + List<OsuProviderInfo> providerInfoList = new ArrayList<>(); + providerInfoList.add(new OsuProviderInfo( + friendlyNames, serverUri, methodList, null, nai, serviceDescriptions)); + Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>(); + anqpElementMap.put(ANQPElementType.HSOSUProviders, + new HSOsuProvidersElement(osuSsid, providerInfoList)); + ANQPData entry = new ANQPData(mClock, anqpElementMap); + + // Setup expectation. + OsuProvider provider = new OsuProvider( + osuSsid, friendlyName, serviceDescription, serverUri, nai, methodList, null); + List<OsuProvider> expectedList = new ArrayList<>(); + expectedList.add(provider); + + when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry); + assertEquals(expectedList, mManager.getMatchingOsuProviders(createTestScanResult())); + } + + /** * Verify that the provider list maintained by the PasspointManager after the list is updated * in the data source. * diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTest.java index e20d6d53e..8ef93f061 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/OsuProviderInfoTest.java @@ -25,6 +25,9 @@ import org.junit.Test; import java.net.ProtocolException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; /** * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.OsuProviderInfo}. @@ -80,4 +83,125 @@ public class OsuProviderInfoTest { assertEquals(OsuProviderInfoTestUtil.TEST_OSU_PROVIDER_INFO, OsuProviderInfo.parse(buffer)); } + + /** + * Verify that when a provider contained multiple friendly names in different languages, the + * friendly name that's in default language is returned. + * + * @throws Exception + */ + @Test + public void getFriendlyNameMatchingDefaultLocale() throws Exception { + List<I18Name> friendlyNames = new ArrayList<>(); + Locale defaultLocale = Locale.getDefault(); + Locale nonDefaultLocale = Locale.FRENCH; + if (defaultLocale.equals(nonDefaultLocale)) { + nonDefaultLocale = Locale.ENGLISH; + } + String nonDefaultString = "Non-default"; + String defaultString = "Default"; + friendlyNames.add( + new I18Name(nonDefaultLocale.getLanguage(), nonDefaultLocale, nonDefaultString)); + friendlyNames.add(new I18Name(defaultLocale.getLanguage(), defaultLocale, defaultString)); + OsuProviderInfo providerInfo = + new OsuProviderInfo(friendlyNames, null, null, null, null, null); + assertEquals(defaultString, providerInfo.getFriendlyName()); + } + + /** + * Verify that when a provider contained multiple friendly names where no friendly name + * is in default language, the first name in the list is returned. + * + * @throws Exception + */ + @Test + public void getFriendlyNameNotMatchingDefaultLocale() throws Exception { + List<I18Name> friendlyNames = new ArrayList<>(); + Locale nonDefaultLocale = Locale.FRENCH; + if (nonDefaultLocale.equals(Locale.getDefault())) { + nonDefaultLocale = Locale.ENGLISH; + } + String firstString = "First name"; + String secondString = "Second name"; + friendlyNames.add( + new I18Name(nonDefaultLocale.getLanguage(), nonDefaultLocale, firstString)); + friendlyNames.add( + new I18Name(nonDefaultLocale.getLanguage(), nonDefaultLocale, secondString)); + OsuProviderInfo providerInfo = + new OsuProviderInfo(friendlyNames, null, null, null, null, null); + assertEquals(firstString, providerInfo.getFriendlyName()); + } + + /** + * Verify that null will be returned for a provider containing empty friendly name list. + * + * @throws Exception + */ + @Test + public void getFriendlyNameWithEmptyList() throws Exception { + OsuProviderInfo providerInfo = + new OsuProviderInfo(new ArrayList<I18Name>(), null, null, null, null, null); + assertEquals(null, providerInfo.getFriendlyName()); + } + + /** + * Verify that when a provider contained multiple service descriptions in different languages, + * the service description that's in default language is returned. + * + * @throws Exception + */ + @Test + public void getServiceDescriptionMatchingDefaultLocale() throws Exception { + List<I18Name> serviceDescriptions = new ArrayList<>(); + Locale defaultLocale = Locale.getDefault(); + Locale nonDefaultLocale = Locale.FRENCH; + if (defaultLocale.equals(nonDefaultLocale)) { + nonDefaultLocale = Locale.ENGLISH; + } + String nonDefaultString = "Non-default"; + String defaultString = "Default"; + serviceDescriptions.add( + new I18Name(nonDefaultLocale.getLanguage(), nonDefaultLocale, nonDefaultString)); + serviceDescriptions.add( + new I18Name(defaultLocale.getLanguage(), defaultLocale, defaultString)); + OsuProviderInfo providerInfo = + new OsuProviderInfo(null, null, null, null, null, serviceDescriptions); + assertEquals(defaultString, providerInfo.getServiceDescription()); + } + + /** + * Verify that when a provider contained multiple service descriptions where none of them + * is in default language, the first element in the list is returned. + * + * @throws Exception + */ + @Test + public void getServiceDescriptionNotMatchingDefaultLocale() throws Exception { + List<I18Name> serviceDescriptions = new ArrayList<>(); + Locale nonDefaultLocale = Locale.FRENCH; + if (nonDefaultLocale.equals(Locale.getDefault())) { + nonDefaultLocale = Locale.ENGLISH; + } + String firstString = "First name"; + String secondString = "Second name"; + serviceDescriptions.add( + new I18Name(nonDefaultLocale.getLanguage(), nonDefaultLocale, firstString)); + serviceDescriptions.add( + new I18Name(nonDefaultLocale.getLanguage(), nonDefaultLocale, secondString)); + OsuProviderInfo providerInfo = + new OsuProviderInfo(null, null, null, null, null, serviceDescriptions); + assertEquals(firstString, providerInfo.getServiceDescription()); + } + + /** + * Verify that null will be returned for a provider containing empty friendly name list. + * + * @throws Exception + */ + @Test + public void getServiceDescriptionWithEmptyList() throws Exception { + OsuProviderInfo providerInfo = + new OsuProviderInfo(null, null, null, null, null, new ArrayList<I18Name>()); + assertEquals(null, providerInfo.getServiceDescription()); + } } |