1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi.hotspot2; 18 19 import static android.net.wifi.WifiManager.ACTION_PASSPOINT_DEAUTH_IMMINENT; 20 import static android.net.wifi.WifiManager.ACTION_PASSPOINT_ICON; 21 import static android.net.wifi.WifiManager.ACTION_PASSPOINT_OSU_PROVIDERS_LIST; 22 import static android.net.wifi.WifiManager.ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION; 23 import static android.net.wifi.WifiManager.EXTRA_ANQP_ELEMENT_DATA; 24 import static android.net.wifi.WifiManager.EXTRA_BSSID_LONG; 25 import static android.net.wifi.WifiManager.EXTRA_DELAY; 26 import static android.net.wifi.WifiManager.EXTRA_ESS; 27 import static android.net.wifi.WifiManager.EXTRA_FILENAME; 28 import static android.net.wifi.WifiManager.EXTRA_ICON; 29 import static android.net.wifi.WifiManager.EXTRA_SUBSCRIPTION_REMEDIATION_METHOD; 30 import static android.net.wifi.WifiManager.EXTRA_URL; 31 32 import android.content.Context; 33 import android.content.Intent; 34 import android.graphics.drawable.Icon; 35 import android.net.wifi.ScanResult; 36 import android.net.wifi.WifiConfiguration; 37 import android.net.wifi.WifiEnterpriseConfig; 38 import android.net.wifi.hotspot2.PasspointConfiguration; 39 import android.os.UserHandle; 40 import android.text.TextUtils; 41 import android.util.Log; 42 import android.util.Pair; 43 44 import com.android.server.wifi.Clock; 45 import com.android.server.wifi.SIMAccessor; 46 import com.android.server.wifi.WifiConfigManager; 47 import com.android.server.wifi.WifiConfigStore; 48 import com.android.server.wifi.WifiKeyStore; 49 import com.android.server.wifi.WifiNative; 50 import com.android.server.wifi.hotspot2.anqp.ANQPElement; 51 import com.android.server.wifi.hotspot2.anqp.Constants; 52 import com.android.server.wifi.hotspot2.anqp.RawByteElement; 53 import com.android.server.wifi.util.InformationElementUtil; 54 import com.android.server.wifi.util.ScanResultUtil; 55 56 import java.io.PrintWriter; 57 import java.util.ArrayList; 58 import java.util.HashMap; 59 import java.util.List; 60 import java.util.Map; 61 62 /** 63 * This class provides the APIs to manage Passpoint provider configurations. 64 * It deals with the following: 65 * - Maintaining a list of configured Passpoint providers for provider matching. 66 * - Persisting the providers configurations to store when required. 67 * - matching Passpoint providers based on the scan results 68 * - Supporting WifiManager Public API calls: 69 * > addOrUpdatePasspointConfiguration() 70 * > removePasspointConfiguration() 71 * > getPasspointConfigurations() 72 * 73 * The provider matching requires obtaining additional information from the AP (ANQP elements). 74 * The ANQP elements will be cached using {@link AnqpCache} to avoid unnecessary requests. 75 * 76 * NOTE: These API's are not thread safe and should only be used from WifiStateMachine thread. 77 */ 78 public class PasspointManager { 79 private static final String TAG = "PasspointManager"; 80 81 /** 82 * Handle for the current {@link PasspointManager} instance. This is needed to avoid 83 * circular dependency with the WifiConfigManger, it will be used for adding the 84 * legacy Passpoint configurations. 85 * 86 * This can be eliminated once we can remove the dependency for WifiConfigManager (for 87 * triggering config store write) from this class. 88 */ 89 private static PasspointManager sPasspointManager; 90 91 private final PasspointEventHandler mHandler; 92 private final SIMAccessor mSimAccessor; 93 private final WifiKeyStore mKeyStore; 94 private final PasspointObjectFactory mObjectFactory; 95 private final Map<String, PasspointProvider> mProviders; 96 private final AnqpCache mAnqpCache; 97 private final ANQPRequestManager mAnqpRequestManager; 98 private final WifiConfigManager mWifiConfigManager; 99 private final CertificateVerifier mCertVerifier; 100 101 // Counter used for assigning unique identifier to each provider. 102 private long mProviderIndex; 103 104 private class CallbackHandler implements PasspointEventHandler.Callbacks { 105 private final Context mContext; CallbackHandler(Context context)106 CallbackHandler(Context context) { 107 mContext = context; 108 } 109 110 @Override onANQPResponse(long bssid, Map<Constants.ANQPElementType, ANQPElement> anqpElements)111 public void onANQPResponse(long bssid, 112 Map<Constants.ANQPElementType, ANQPElement> anqpElements) { 113 // Notify request manager for the completion of a request. 114 ANQPNetworkKey anqpKey = 115 mAnqpRequestManager.onRequestCompleted(bssid, anqpElements != null); 116 if (anqpElements == null || anqpKey == null) { 117 // Query failed or the request wasn't originated from us (not tracked by the 118 // request manager). Nothing to be done. 119 return; 120 } 121 122 // Add new entry to the cache. 123 mAnqpCache.addEntry(anqpKey, anqpElements); 124 125 // Broadcast OSU providers info. 126 if (anqpElements.containsKey(Constants.ANQPElementType.HSOSUProviders)) { 127 RawByteElement osuProviders = (RawByteElement) anqpElements.get( 128 Constants.ANQPElementType.HSOSUProviders); 129 Intent intent = new Intent(ACTION_PASSPOINT_OSU_PROVIDERS_LIST); 130 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 131 intent.putExtra(EXTRA_BSSID_LONG, bssid); 132 intent.putExtra(EXTRA_ANQP_ELEMENT_DATA, osuProviders.getPayload()); 133 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 134 android.Manifest.permission.ACCESS_WIFI_STATE); 135 } 136 } 137 138 @Override onIconResponse(long bssid, String fileName, byte[] data)139 public void onIconResponse(long bssid, String fileName, byte[] data) { 140 Intent intent = new Intent(ACTION_PASSPOINT_ICON); 141 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 142 intent.putExtra(EXTRA_BSSID_LONG, bssid); 143 intent.putExtra(EXTRA_FILENAME, fileName); 144 if (data != null) { 145 intent.putExtra(EXTRA_ICON, Icon.createWithData(data, 0, data.length)); 146 } 147 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 148 android.Manifest.permission.ACCESS_WIFI_STATE); 149 } 150 151 @Override onWnmFrameReceived(WnmData event)152 public void onWnmFrameReceived(WnmData event) { 153 // %012x HS20-SUBSCRIPTION-REMEDIATION "%u %s", osu_method, url 154 // %012x HS20-DEAUTH-IMMINENT-NOTICE "%u %u %s", code, reauth_delay, url 155 Intent intent; 156 if (event.isDeauthEvent()) { 157 intent = new Intent(ACTION_PASSPOINT_DEAUTH_IMMINENT); 158 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 159 intent.putExtra(EXTRA_BSSID_LONG, event.getBssid()); 160 intent.putExtra(EXTRA_URL, event.getUrl()); 161 intent.putExtra(EXTRA_ESS, event.isEss()); 162 intent.putExtra(EXTRA_DELAY, event.getDelay()); 163 } else { 164 intent = new Intent(ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION); 165 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 166 intent.putExtra(EXTRA_BSSID_LONG, event.getBssid()); 167 intent.putExtra(EXTRA_SUBSCRIPTION_REMEDIATION_METHOD, event.getMethod()); 168 intent.putExtra(EXTRA_URL, event.getUrl()); 169 } 170 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 171 android.Manifest.permission.ACCESS_WIFI_STATE); 172 } 173 } 174 175 /** 176 * Data provider for the Passpoint configuration store data {@link PasspointConfigStoreData}. 177 */ 178 private class DataSourceHandler implements PasspointConfigStoreData.DataSource { 179 @Override getProviders()180 public List<PasspointProvider> getProviders() { 181 List<PasspointProvider> providers = new ArrayList<>(); 182 for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) { 183 providers.add(entry.getValue()); 184 } 185 return providers; 186 } 187 188 @Override setProviders(List<PasspointProvider> providers)189 public void setProviders(List<PasspointProvider> providers) { 190 mProviders.clear(); 191 for (PasspointProvider provider : providers) { 192 mProviders.put(provider.getConfig().getHomeSp().getFqdn(), provider); 193 } 194 } 195 196 @Override getProviderIndex()197 public long getProviderIndex() { 198 return mProviderIndex; 199 } 200 201 @Override setProviderIndex(long providerIndex)202 public void setProviderIndex(long providerIndex) { 203 mProviderIndex = providerIndex; 204 } 205 } 206 PasspointManager(Context context, WifiNative wifiNative, WifiKeyStore keyStore, Clock clock, SIMAccessor simAccessor, PasspointObjectFactory objectFactory, WifiConfigManager wifiConfigManager, WifiConfigStore wifiConfigStore)207 public PasspointManager(Context context, WifiNative wifiNative, WifiKeyStore keyStore, 208 Clock clock, SIMAccessor simAccessor, PasspointObjectFactory objectFactory, 209 WifiConfigManager wifiConfigManager, WifiConfigStore wifiConfigStore) { 210 mHandler = objectFactory.makePasspointEventHandler(wifiNative, 211 new CallbackHandler(context)); 212 mKeyStore = keyStore; 213 mSimAccessor = simAccessor; 214 mObjectFactory = objectFactory; 215 mProviders = new HashMap<>(); 216 mAnqpCache = objectFactory.makeAnqpCache(clock); 217 mAnqpRequestManager = objectFactory.makeANQPRequestManager(mHandler, clock); 218 mCertVerifier = objectFactory.makeCertificateVerifier(); 219 mWifiConfigManager = wifiConfigManager; 220 mProviderIndex = 0; 221 wifiConfigStore.registerStoreData(objectFactory.makePasspointConfigStoreData( 222 mKeyStore, mSimAccessor, new DataSourceHandler())); 223 sPasspointManager = this; 224 } 225 226 /** 227 * Add or update a Passpoint provider with the given configuration. 228 * 229 * Each provider is uniquely identified by its FQDN (Fully Qualified Domain Name). 230 * In the case when there is an existing configuration with the same FQDN 231 * a provider with the new configuration will replace the existing provider. 232 * 233 * @param config Configuration of the Passpoint provider to be added 234 * @return true if provider is added, false otherwise 235 */ addOrUpdateProvider(PasspointConfiguration config, int uid)236 public boolean addOrUpdateProvider(PasspointConfiguration config, int uid) { 237 if (config == null) { 238 Log.e(TAG, "Configuration not provided"); 239 return false; 240 } 241 if (!config.validate()) { 242 Log.e(TAG, "Invalid configuration"); 243 return false; 244 } 245 246 // For Hotspot 2.0 Release 1, the CA Certificate must be trusted by one of the pre-loaded 247 // public CAs in the system key store on the device. Since the provisioning method 248 // for Release 1 is not standardized nor trusted, this is a reasonable restriction 249 // to improve security. The presence of UpdateIdentifier is used to differentiate 250 // between R1 and R2 configuration. 251 if (config.getUpdateIdentifier() == Integer.MIN_VALUE 252 && config.getCredential().getCaCertificate() != null) { 253 try { 254 mCertVerifier.verifyCaCert(config.getCredential().getCaCertificate()); 255 } catch (Exception e) { 256 Log.e(TAG, "Failed to verify CA certificate: " + e.getMessage()); 257 return false; 258 } 259 } 260 261 // Create a provider and install the necessary certificates and keys. 262 PasspointProvider newProvider = mObjectFactory.makePasspointProvider( 263 config, mKeyStore, mSimAccessor, mProviderIndex++, uid); 264 265 if (!newProvider.installCertsAndKeys()) { 266 Log.e(TAG, "Failed to install certificates and keys to keystore"); 267 return false; 268 } 269 270 // Remove existing provider with the same FQDN. 271 if (mProviders.containsKey(config.getHomeSp().getFqdn())) { 272 Log.d(TAG, "Replacing configuration for " + config.getHomeSp().getFqdn()); 273 mProviders.get(config.getHomeSp().getFqdn()).uninstallCertsAndKeys(); 274 mProviders.remove(config.getHomeSp().getFqdn()); 275 } 276 277 mProviders.put(config.getHomeSp().getFqdn(), newProvider); 278 mWifiConfigManager.saveToStore(true /* forceWrite */); 279 Log.d(TAG, "Added/updated Passpoint configuration: " + config.getHomeSp().getFqdn() 280 + " by " + uid); 281 return true; 282 } 283 284 /** 285 * Remove a Passpoint provider identified by the given FQDN. 286 * 287 * @param fqdn The FQDN of the provider to remove 288 * @return true if a provider is removed, false otherwise 289 */ removeProvider(String fqdn)290 public boolean removeProvider(String fqdn) { 291 if (!mProviders.containsKey(fqdn)) { 292 Log.e(TAG, "Config doesn't exist"); 293 return false; 294 } 295 296 mProviders.get(fqdn).uninstallCertsAndKeys(); 297 mProviders.remove(fqdn); 298 mWifiConfigManager.saveToStore(true /* forceWrite */); 299 Log.d(TAG, "Removed Passpoint configuration: " + fqdn); 300 return true; 301 } 302 303 /** 304 * Return the installed Passpoint provider configurations. 305 * 306 * An empty list will be returned when no provider is installed. 307 * 308 * @return A list of {@link PasspointConfiguration} 309 */ getProviderConfigs()310 public List<PasspointConfiguration> getProviderConfigs() { 311 List<PasspointConfiguration> configs = new ArrayList<>(); 312 for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) { 313 configs.add(entry.getValue().getConfig()); 314 } 315 return configs; 316 } 317 318 /** 319 * Find the best provider that can provide service through the given AP, which means the 320 * provider contained credential to authenticate with the given AP. 321 * 322 * Here is the current precedence of the matching rule in descending order: 323 * 1. Home Provider 324 * 2. Roaming Provider 325 * 326 * A {code null} will be returned if no matching is found. 327 * 328 * @param scanResult The scan result associated with the AP 329 * @return A pair of {@link PasspointProvider} and match status. 330 */ matchProvider(ScanResult scanResult)331 public Pair<PasspointProvider, PasspointMatch> matchProvider(ScanResult scanResult) { 332 // Retrieve the relevant information elements, mainly Roaming Consortium IE and Hotspot 2.0 333 // Vendor Specific IE. 334 InformationElementUtil.RoamingConsortium roamingConsortium = 335 InformationElementUtil.getRoamingConsortiumIE(scanResult.informationElements); 336 InformationElementUtil.Vsa vsa = InformationElementUtil.getHS2VendorSpecificIE( 337 scanResult.informationElements); 338 339 // Lookup ANQP data in the cache. 340 long bssid = Utils.parseMac(scanResult.BSSID); 341 ANQPNetworkKey anqpKey = ANQPNetworkKey.buildKey(scanResult.SSID, bssid, scanResult.hessid, 342 vsa.anqpDomainID); 343 ANQPData anqpEntry = mAnqpCache.getEntry(anqpKey); 344 345 if (anqpEntry == null) { 346 mAnqpRequestManager.requestANQPElements(bssid, anqpKey, 347 roamingConsortium.anqpOICount > 0, 348 vsa.hsRelease == NetworkDetail.HSRelease.R2); 349 Log.d(TAG, "ANQP entry not found for: " + anqpKey); 350 return null; 351 } 352 353 Pair<PasspointProvider, PasspointMatch> bestMatch = null; 354 for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) { 355 PasspointProvider provider = entry.getValue(); 356 PasspointMatch matchStatus = provider.match(anqpEntry.getElements()); 357 if (matchStatus == PasspointMatch.HomeProvider) { 358 bestMatch = Pair.create(provider, matchStatus); 359 break; 360 } 361 if (matchStatus == PasspointMatch.RoamingProvider && bestMatch == null) { 362 bestMatch = Pair.create(provider, matchStatus); 363 } 364 } 365 if (bestMatch != null) { 366 Log.d(TAG, String.format("Matched %s to %s as %s", scanResult.SSID, 367 bestMatch.first.getConfig().getHomeSp().getFqdn(), 368 bestMatch.second == PasspointMatch.HomeProvider ? "Home Provider" 369 : "Roaming Provider")); 370 } else { 371 Log.d(TAG, "Match not found for " + scanResult.SSID); 372 } 373 return bestMatch; 374 } 375 376 /** 377 * Add a legacy Passpoint configuration represented by a {@link WifiConfiguration} to the 378 * current {@link PasspointManager}. 379 * 380 * This will not trigger a config store write, since this will be invoked as part of the 381 * configuration migration, the caller will be responsible for triggering store write 382 * after the migration is completed. 383 * 384 * @param config {@link WifiConfiguration} representation of the Passpoint configuration 385 * @return true on success 386 */ addLegacyPasspointConfig(WifiConfiguration config)387 public static boolean addLegacyPasspointConfig(WifiConfiguration config) { 388 if (sPasspointManager == null) { 389 Log.e(TAG, "PasspointManager have not been initialized yet"); 390 return false; 391 } 392 Log.d(TAG, "Installing legacy Passpoint configuration: " + config.FQDN); 393 return sPasspointManager.addWifiConfig(config); 394 } 395 396 /** 397 * Sweep the ANQP cache to remove expired entries. 398 */ sweepCache()399 public void sweepCache() { 400 mAnqpCache.sweep(); 401 } 402 403 /** 404 * Notify the completion of an ANQP request. 405 * TODO(zqiu): currently the notification is done through WifiMonitor, 406 * will no longer be the case once we switch over to use wificond. 407 */ notifyANQPDone(AnqpEvent anqpEvent)408 public void notifyANQPDone(AnqpEvent anqpEvent) { 409 mHandler.notifyANQPDone(anqpEvent); 410 } 411 412 /** 413 * Notify the completion of an icon request. 414 * TODO(zqiu): currently the notification is done through WifiMonitor, 415 * will no longer be the case once we switch over to use wificond. 416 */ notifyIconDone(IconEvent iconEvent)417 public void notifyIconDone(IconEvent iconEvent) { 418 mHandler.notifyIconDone(iconEvent); 419 } 420 421 /** 422 * Notify the reception of a Wireless Network Management (WNM) frame. 423 * TODO(zqiu): currently the notification is done through WifiMonitor, 424 * will no longer be the case once we switch over to use wificond. 425 */ receivedWnmFrame(WnmData data)426 public void receivedWnmFrame(WnmData data) { 427 mHandler.notifyWnmFrameReceived(data); 428 } 429 430 /** 431 * Request the specified icon file |fileName| from the specified AP |bssid|. 432 * @return true if the request is sent successfully, false otherwise 433 */ queryPasspointIcon(long bssid, String fileName)434 public boolean queryPasspointIcon(long bssid, String fileName) { 435 return mHandler.requestIcon(bssid, fileName); 436 } 437 438 /** 439 * Lookup the ANQP elements associated with the given AP from the cache. An empty map 440 * will be returned if no match found in the cache. 441 * 442 * @param scanResult The scan result associated with the AP 443 * @return Map of ANQP elements 444 */ getANQPElements(ScanResult scanResult)445 public Map<Constants.ANQPElementType, ANQPElement> getANQPElements(ScanResult scanResult) { 446 // Retrieve the Hotspot 2.0 Vendor Specific IE. 447 InformationElementUtil.Vsa vsa = 448 InformationElementUtil.getHS2VendorSpecificIE(scanResult.informationElements); 449 450 // Lookup ANQP data in the cache. 451 long bssid = Utils.parseMac(scanResult.BSSID); 452 ANQPData anqpEntry = mAnqpCache.getEntry(ANQPNetworkKey.buildKey( 453 scanResult.SSID, bssid, scanResult.hessid, vsa.anqpDomainID)); 454 if (anqpEntry != null) { 455 return anqpEntry.getElements(); 456 } 457 return new HashMap<Constants.ANQPElementType, ANQPElement>(); 458 } 459 460 /** 461 * Match the given WiFi AP to an installed Passpoint provider. A {@link WifiConfiguration} 462 * will be generated and returned if a match is found. The returned {@link WifiConfiguration} 463 * will contained all the necessary credentials for connecting to the given WiFi AP. 464 * 465 * A {code null} will be returned if no matching provider is found. 466 * 467 * @param scanResult The scan result of the given AP 468 * @return {@link WifiConfiguration} 469 */ getMatchingWifiConfig(ScanResult scanResult)470 public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) { 471 if (scanResult == null) { 472 Log.e(TAG, "Attempt to get matching config for a null ScanResult"); 473 return null; 474 } 475 Pair<PasspointProvider, PasspointMatch> matchedProvider = matchProvider(scanResult); 476 if (matchedProvider == null) { 477 return null; 478 } 479 WifiConfiguration config = matchedProvider.first.getWifiConfig(); 480 config.SSID = ScanResultUtil.createQuotedSSID(scanResult.SSID); 481 if (matchedProvider.second == PasspointMatch.HomeProvider) { 482 config.isHomeProviderNetwork = true; 483 } 484 return config; 485 } 486 487 /** 488 * Dump the current state of PasspointManager to the provided output stream. 489 * 490 * @param pw The output stream to write to 491 */ dump(PrintWriter pw)492 public void dump(PrintWriter pw) { 493 pw.println("Dump of PasspointManager"); 494 pw.println("PasspointManager - Providers Begin ---"); 495 for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) { 496 pw.println(entry.getValue()); 497 } 498 pw.println("PasspointManager - Providers End ---"); 499 pw.println("PasspointManager - Next provider ID to be assigned " + mProviderIndex); 500 mAnqpCache.dump(pw); 501 } 502 503 /** 504 * Add a legacy Passpoint configuration represented by a {@link WifiConfiguration}. 505 * 506 * @param wifiConfig {@link WifiConfiguration} representation of the Passpoint configuration 507 * @return true on success 508 */ addWifiConfig(WifiConfiguration wifiConfig)509 private boolean addWifiConfig(WifiConfiguration wifiConfig) { 510 if (wifiConfig == null) { 511 return false; 512 } 513 514 // Convert to PasspointConfiguration 515 PasspointConfiguration passpointConfig = 516 PasspointProvider.convertFromWifiConfig(wifiConfig); 517 if (passpointConfig == null) { 518 return false; 519 } 520 521 // Setup aliases for enterprise certificates and key. 522 WifiEnterpriseConfig enterpriseConfig = wifiConfig.enterpriseConfig; 523 String caCertificateAliasSuffix = enterpriseConfig.getCaCertificateAlias(); 524 String clientCertAndKeyAliasSuffix = enterpriseConfig.getClientCertificateAlias(); 525 if (passpointConfig.getCredential().getUserCredential() != null 526 && TextUtils.isEmpty(caCertificateAliasSuffix)) { 527 Log.e(TAG, "Missing CA Certificate for user credential"); 528 return false; 529 } 530 if (passpointConfig.getCredential().getCertCredential() != null) { 531 if (TextUtils.isEmpty(caCertificateAliasSuffix)) { 532 Log.e(TAG, "Missing CA certificate for Certificate credential"); 533 return false; 534 } 535 if (TextUtils.isEmpty(clientCertAndKeyAliasSuffix)) { 536 Log.e(TAG, "Missing client certificate and key for certificate credential"); 537 return false; 538 } 539 } 540 541 // Note that for legacy configuration, the alias for client private key is the same as the 542 // alias for the client certificate. 543 PasspointProvider provider = new PasspointProvider(passpointConfig, mKeyStore, 544 mSimAccessor, mProviderIndex++, wifiConfig.creatorUid, 545 enterpriseConfig.getCaCertificateAlias(), 546 enterpriseConfig.getClientCertificateAlias(), 547 enterpriseConfig.getClientCertificateAlias()); 548 mProviders.put(passpointConfig.getHomeSp().getFqdn(), provider); 549 return true; 550 } 551 } 552