PasspointManager.java revision c56add5be84ea0cf85bc77d5efc0494a68466570
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 17package com.android.server.wifi.hotspot2; 18 19import static android.net.wifi.WifiManager.ACTION_PASSPOINT_DEAUTH_IMMINENT; 20import static android.net.wifi.WifiManager.ACTION_PASSPOINT_ICON; 21import static android.net.wifi.WifiManager.ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION; 22import static android.net.wifi.WifiManager.EXTRA_BSSID_LONG; 23import static android.net.wifi.WifiManager.EXTRA_DELAY; 24import static android.net.wifi.WifiManager.EXTRA_ESS; 25import static android.net.wifi.WifiManager.EXTRA_FILENAME; 26import static android.net.wifi.WifiManager.EXTRA_ICON; 27import static android.net.wifi.WifiManager.EXTRA_SUBSCRIPTION_REMEDIATION_METHOD; 28import static android.net.wifi.WifiManager.EXTRA_URL; 29 30import android.content.Context; 31import android.content.Intent; 32import android.graphics.drawable.Icon; 33import android.net.wifi.ScanResult; 34import android.net.wifi.WifiConfiguration; 35import android.net.wifi.WifiEnterpriseConfig; 36import android.net.wifi.hotspot2.OsuProvider; 37import android.net.wifi.hotspot2.PasspointConfiguration; 38import android.os.UserHandle; 39import android.text.TextUtils; 40import android.util.Log; 41import android.util.Pair; 42 43import com.android.server.wifi.Clock; 44import com.android.server.wifi.SIMAccessor; 45import com.android.server.wifi.WifiConfigManager; 46import com.android.server.wifi.WifiConfigStore; 47import com.android.server.wifi.WifiKeyStore; 48import com.android.server.wifi.WifiNative; 49import com.android.server.wifi.hotspot2.anqp.ANQPElement; 50import com.android.server.wifi.hotspot2.anqp.Constants; 51import com.android.server.wifi.hotspot2.anqp.HSOsuProvidersElement; 52import com.android.server.wifi.hotspot2.anqp.OsuProviderInfo; 53import com.android.server.wifi.util.InformationElementUtil; 54import com.android.server.wifi.util.ScanResultUtil; 55 56import java.io.PrintWriter; 57import java.util.ArrayList; 58import java.util.HashMap; 59import java.util.List; 60import 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 */ 78public 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; 106 CallbackHandler(Context context) { 107 mContext = context; 108 } 109 110 @Override 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 126 @Override 127 public void onIconResponse(long bssid, String fileName, byte[] data) { 128 Intent intent = new Intent(ACTION_PASSPOINT_ICON); 129 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 130 intent.putExtra(EXTRA_BSSID_LONG, bssid); 131 intent.putExtra(EXTRA_FILENAME, fileName); 132 if (data != null) { 133 intent.putExtra(EXTRA_ICON, Icon.createWithData(data, 0, data.length)); 134 } 135 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 136 android.Manifest.permission.ACCESS_WIFI_STATE); 137 } 138 139 @Override 140 public void onWnmFrameReceived(WnmData event) { 141 // %012x HS20-SUBSCRIPTION-REMEDIATION "%u %s", osu_method, url 142 // %012x HS20-DEAUTH-IMMINENT-NOTICE "%u %u %s", code, reauth_delay, url 143 Intent intent; 144 if (event.isDeauthEvent()) { 145 intent = new Intent(ACTION_PASSPOINT_DEAUTH_IMMINENT); 146 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 147 intent.putExtra(EXTRA_BSSID_LONG, event.getBssid()); 148 intent.putExtra(EXTRA_URL, event.getUrl()); 149 intent.putExtra(EXTRA_ESS, event.isEss()); 150 intent.putExtra(EXTRA_DELAY, event.getDelay()); 151 } else { 152 intent = new Intent(ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION); 153 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 154 intent.putExtra(EXTRA_BSSID_LONG, event.getBssid()); 155 intent.putExtra(EXTRA_SUBSCRIPTION_REMEDIATION_METHOD, event.getMethod()); 156 intent.putExtra(EXTRA_URL, event.getUrl()); 157 } 158 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 159 android.Manifest.permission.ACCESS_WIFI_STATE); 160 } 161 } 162 163 /** 164 * Data provider for the Passpoint configuration store data {@link PasspointConfigStoreData}. 165 */ 166 private class DataSourceHandler implements PasspointConfigStoreData.DataSource { 167 @Override 168 public List<PasspointProvider> getProviders() { 169 List<PasspointProvider> providers = new ArrayList<>(); 170 for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) { 171 providers.add(entry.getValue()); 172 } 173 return providers; 174 } 175 176 @Override 177 public void setProviders(List<PasspointProvider> providers) { 178 mProviders.clear(); 179 for (PasspointProvider provider : providers) { 180 mProviders.put(provider.getConfig().getHomeSp().getFqdn(), provider); 181 } 182 } 183 184 @Override 185 public long getProviderIndex() { 186 return mProviderIndex; 187 } 188 189 @Override 190 public void setProviderIndex(long providerIndex) { 191 mProviderIndex = providerIndex; 192 } 193 } 194 195 public PasspointManager(Context context, WifiNative wifiNative, WifiKeyStore keyStore, 196 Clock clock, SIMAccessor simAccessor, PasspointObjectFactory objectFactory, 197 WifiConfigManager wifiConfigManager, WifiConfigStore wifiConfigStore) { 198 mHandler = objectFactory.makePasspointEventHandler(wifiNative, 199 new CallbackHandler(context)); 200 mKeyStore = keyStore; 201 mSimAccessor = simAccessor; 202 mObjectFactory = objectFactory; 203 mProviders = new HashMap<>(); 204 mAnqpCache = objectFactory.makeAnqpCache(clock); 205 mAnqpRequestManager = objectFactory.makeANQPRequestManager(mHandler, clock); 206 mCertVerifier = objectFactory.makeCertificateVerifier(); 207 mWifiConfigManager = wifiConfigManager; 208 mProviderIndex = 0; 209 wifiConfigStore.registerStoreData(objectFactory.makePasspointConfigStoreData( 210 mKeyStore, mSimAccessor, new DataSourceHandler())); 211 sPasspointManager = this; 212 } 213 214 /** 215 * Add or update a Passpoint provider with the given configuration. 216 * 217 * Each provider is uniquely identified by its FQDN (Fully Qualified Domain Name). 218 * In the case when there is an existing configuration with the same FQDN 219 * a provider with the new configuration will replace the existing provider. 220 * 221 * @param config Configuration of the Passpoint provider to be added 222 * @return true if provider is added, false otherwise 223 */ 224 public boolean addOrUpdateProvider(PasspointConfiguration config, int uid) { 225 if (config == null) { 226 Log.e(TAG, "Configuration not provided"); 227 return false; 228 } 229 if (!config.validate()) { 230 Log.e(TAG, "Invalid configuration"); 231 return false; 232 } 233 234 // For Hotspot 2.0 Release 1, the CA Certificate must be trusted by one of the pre-loaded 235 // public CAs in the system key store on the device. Since the provisioning method 236 // for Release 1 is not standardized nor trusted, this is a reasonable restriction 237 // to improve security. The presence of UpdateIdentifier is used to differentiate 238 // between R1 and R2 configuration. 239 if (config.getUpdateIdentifier() == Integer.MIN_VALUE 240 && config.getCredential().getCaCertificate() != null) { 241 try { 242 mCertVerifier.verifyCaCert(config.getCredential().getCaCertificate()); 243 } catch (Exception e) { 244 Log.e(TAG, "Failed to verify CA certificate: " + e.getMessage()); 245 return false; 246 } 247 } 248 249 // Create a provider and install the necessary certificates and keys. 250 PasspointProvider newProvider = mObjectFactory.makePasspointProvider( 251 config, mKeyStore, mSimAccessor, mProviderIndex++, uid); 252 253 if (!newProvider.installCertsAndKeys()) { 254 Log.e(TAG, "Failed to install certificates and keys to keystore"); 255 return false; 256 } 257 258 // Remove existing provider with the same FQDN. 259 if (mProviders.containsKey(config.getHomeSp().getFqdn())) { 260 Log.d(TAG, "Replacing configuration for " + config.getHomeSp().getFqdn()); 261 mProviders.get(config.getHomeSp().getFqdn()).uninstallCertsAndKeys(); 262 mProviders.remove(config.getHomeSp().getFqdn()); 263 } 264 265 mProviders.put(config.getHomeSp().getFqdn(), newProvider); 266 mWifiConfigManager.saveToStore(true /* forceWrite */); 267 Log.d(TAG, "Added/updated Passpoint configuration: " + config.getHomeSp().getFqdn() 268 + " by " + uid); 269 return true; 270 } 271 272 /** 273 * Remove a Passpoint provider identified by the given FQDN. 274 * 275 * @param fqdn The FQDN of the provider to remove 276 * @return true if a provider is removed, false otherwise 277 */ 278 public boolean removeProvider(String fqdn) { 279 if (!mProviders.containsKey(fqdn)) { 280 Log.e(TAG, "Config doesn't exist"); 281 return false; 282 } 283 284 mProviders.get(fqdn).uninstallCertsAndKeys(); 285 mProviders.remove(fqdn); 286 mWifiConfigManager.saveToStore(true /* forceWrite */); 287 Log.d(TAG, "Removed Passpoint configuration: " + fqdn); 288 return true; 289 } 290 291 /** 292 * Return the installed Passpoint provider configurations. 293 * 294 * An empty list will be returned when no provider is installed. 295 * 296 * @return A list of {@link PasspointConfiguration} 297 */ 298 public List<PasspointConfiguration> getProviderConfigs() { 299 List<PasspointConfiguration> configs = new ArrayList<>(); 300 for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) { 301 configs.add(entry.getValue().getConfig()); 302 } 303 return configs; 304 } 305 306 /** 307 * Find the best provider that can provide service through the given AP, which means the 308 * provider contained credential to authenticate with the given AP. 309 * 310 * Here is the current precedence of the matching rule in descending order: 311 * 1. Home Provider 312 * 2. Roaming Provider 313 * 314 * A {code null} will be returned if no matching is found. 315 * 316 * @param scanResult The scan result associated with the AP 317 * @return A pair of {@link PasspointProvider} and match status. 318 */ 319 public Pair<PasspointProvider, PasspointMatch> matchProvider(ScanResult scanResult) { 320 // Retrieve the relevant information elements, mainly Roaming Consortium IE and Hotspot 2.0 321 // Vendor Specific IE. 322 InformationElementUtil.RoamingConsortium roamingConsortium = 323 InformationElementUtil.getRoamingConsortiumIE(scanResult.informationElements); 324 InformationElementUtil.Vsa vsa = InformationElementUtil.getHS2VendorSpecificIE( 325 scanResult.informationElements); 326 327 // Lookup ANQP data in the cache. 328 long bssid; 329 try { 330 bssid = Utils.parseMac(scanResult.BSSID); 331 } catch (IllegalArgumentException e) { 332 Log.e(TAG, "Invalid BSSID provided in the scan result: " + scanResult.BSSID); 333 return null; 334 } 335 ANQPNetworkKey anqpKey = ANQPNetworkKey.buildKey(scanResult.SSID, bssid, scanResult.hessid, 336 vsa.anqpDomainID); 337 ANQPData anqpEntry = mAnqpCache.getEntry(anqpKey); 338 339 if (anqpEntry == null) { 340 mAnqpRequestManager.requestANQPElements(bssid, anqpKey, 341 roamingConsortium.anqpOICount > 0, 342 vsa.hsRelease == NetworkDetail.HSRelease.R2); 343 Log.d(TAG, "ANQP entry not found for: " + anqpKey); 344 return null; 345 } 346 347 Pair<PasspointProvider, PasspointMatch> bestMatch = null; 348 for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) { 349 PasspointProvider provider = entry.getValue(); 350 PasspointMatch matchStatus = provider.match(anqpEntry.getElements()); 351 if (matchStatus == PasspointMatch.HomeProvider) { 352 bestMatch = Pair.create(provider, matchStatus); 353 break; 354 } 355 if (matchStatus == PasspointMatch.RoamingProvider && bestMatch == null) { 356 bestMatch = Pair.create(provider, matchStatus); 357 } 358 } 359 if (bestMatch != null) { 360 Log.d(TAG, String.format("Matched %s to %s as %s", scanResult.SSID, 361 bestMatch.first.getConfig().getHomeSp().getFqdn(), 362 bestMatch.second == PasspointMatch.HomeProvider ? "Home Provider" 363 : "Roaming Provider")); 364 } else { 365 Log.d(TAG, "Match not found for " + scanResult.SSID); 366 } 367 return bestMatch; 368 } 369 370 /** 371 * Add a legacy Passpoint configuration represented by a {@link WifiConfiguration} to the 372 * current {@link PasspointManager}. 373 * 374 * This will not trigger a config store write, since this will be invoked as part of the 375 * configuration migration, the caller will be responsible for triggering store write 376 * after the migration is completed. 377 * 378 * @param config {@link WifiConfiguration} representation of the Passpoint configuration 379 * @return true on success 380 */ 381 public static boolean addLegacyPasspointConfig(WifiConfiguration config) { 382 if (sPasspointManager == null) { 383 Log.e(TAG, "PasspointManager have not been initialized yet"); 384 return false; 385 } 386 Log.d(TAG, "Installing legacy Passpoint configuration: " + config.FQDN); 387 return sPasspointManager.addWifiConfig(config); 388 } 389 390 /** 391 * Sweep the ANQP cache to remove expired entries. 392 */ 393 public void sweepCache() { 394 mAnqpCache.sweep(); 395 } 396 397 /** 398 * Notify the completion of an ANQP request. 399 * TODO(zqiu): currently the notification is done through WifiMonitor, 400 * will no longer be the case once we switch over to use wificond. 401 */ 402 public void notifyANQPDone(AnqpEvent anqpEvent) { 403 mHandler.notifyANQPDone(anqpEvent); 404 } 405 406 /** 407 * Notify the completion of an icon request. 408 * TODO(zqiu): currently the notification is done through WifiMonitor, 409 * will no longer be the case once we switch over to use wificond. 410 */ 411 public void notifyIconDone(IconEvent iconEvent) { 412 mHandler.notifyIconDone(iconEvent); 413 } 414 415 /** 416 * Notify the reception of a Wireless Network Management (WNM) frame. 417 * TODO(zqiu): currently the notification is done through WifiMonitor, 418 * will no longer be the case once we switch over to use wificond. 419 */ 420 public void receivedWnmFrame(WnmData data) { 421 mHandler.notifyWnmFrameReceived(data); 422 } 423 424 /** 425 * Request the specified icon file |fileName| from the specified AP |bssid|. 426 * @return true if the request is sent successfully, false otherwise 427 */ 428 public boolean queryPasspointIcon(long bssid, String fileName) { 429 return mHandler.requestIcon(bssid, fileName); 430 } 431 432 /** 433 * Lookup the ANQP elements associated with the given AP from the cache. An empty map 434 * will be returned if no match found in the cache. 435 * 436 * @param scanResult The scan result associated with the AP 437 * @return Map of ANQP elements 438 */ 439 public Map<Constants.ANQPElementType, ANQPElement> getANQPElements(ScanResult scanResult) { 440 // Retrieve the Hotspot 2.0 Vendor Specific IE. 441 InformationElementUtil.Vsa vsa = 442 InformationElementUtil.getHS2VendorSpecificIE(scanResult.informationElements); 443 444 // Lookup ANQP data in the cache. 445 long bssid; 446 try { 447 bssid = Utils.parseMac(scanResult.BSSID); 448 } catch (IllegalArgumentException e) { 449 Log.e(TAG, "Invalid BSSID provided in the scan result: " + scanResult.BSSID); 450 return new HashMap<Constants.ANQPElementType, ANQPElement>(); 451 } 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 */ 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 if (!scanResult.isPasspointNetwork()) { 476 Log.e(TAG, "Attempt to get matching config for a non-Passpoint AP"); 477 return null; 478 } 479 Pair<PasspointProvider, PasspointMatch> matchedProvider = matchProvider(scanResult); 480 if (matchedProvider == null) { 481 return null; 482 } 483 WifiConfiguration config = matchedProvider.first.getWifiConfig(); 484 config.SSID = ScanResultUtil.createQuotedSSID(scanResult.SSID); 485 if (matchedProvider.second == PasspointMatch.HomeProvider) { 486 config.isHomeProviderNetwork = true; 487 } 488 return config; 489 } 490 491 /** 492 * Return the list of Hosspot 2.0 OSU (Online Sign-Up) providers associated with the given 493 * AP. 494 * 495 * An empty list will be returned when an invalid scan result is provided or no match is found. 496 * 497 * @param scanResult The scan result of the AP 498 * @return List of {@link OsuProvider} 499 */ 500 public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) { 501 if (scanResult == null) { 502 Log.e(TAG, "Attempt to retrieve OSU providers for a null ScanResult"); 503 return new ArrayList<OsuProvider>(); 504 } 505 if (!scanResult.isPasspointNetwork()) { 506 Log.e(TAG, "Attempt to retrieve OSU providers for a non-Passpoint AP"); 507 return new ArrayList<OsuProvider>(); 508 } 509 510 // Lookup OSU Providers ANQP element. 511 Map<Constants.ANQPElementType, ANQPElement> anqpElements = getANQPElements(scanResult); 512 if (!anqpElements.containsKey(Constants.ANQPElementType.HSOSUProviders)) { 513 return new ArrayList<OsuProvider>(); 514 } 515 516 HSOsuProvidersElement element = 517 (HSOsuProvidersElement) anqpElements.get(Constants.ANQPElementType.HSOSUProviders); 518 List<OsuProvider> providers = new ArrayList<>(); 519 for (OsuProviderInfo info : element.getProviders()) { 520 // TODO(b/62256482): include icon data once the icon file retrieval and management 521 // support is added. 522 OsuProvider provider = new OsuProvider(element.getOsuSsid(), info.getFriendlyName(), 523 info.getServiceDescription(), info.getServerUri(), 524 info.getNetworkAccessIdentifier(), info.getMethodList(), null); 525 providers.add(provider); 526 } 527 return providers; 528 } 529 530 /** 531 * Dump the current state of PasspointManager to the provided output stream. 532 * 533 * @param pw The output stream to write to 534 */ 535 public void dump(PrintWriter pw) { 536 pw.println("Dump of PasspointManager"); 537 pw.println("PasspointManager - Providers Begin ---"); 538 for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) { 539 pw.println(entry.getValue()); 540 } 541 pw.println("PasspointManager - Providers End ---"); 542 pw.println("PasspointManager - Next provider ID to be assigned " + mProviderIndex); 543 mAnqpCache.dump(pw); 544 } 545 546 /** 547 * Add a legacy Passpoint configuration represented by a {@link WifiConfiguration}. 548 * 549 * @param wifiConfig {@link WifiConfiguration} representation of the Passpoint configuration 550 * @return true on success 551 */ 552 private boolean addWifiConfig(WifiConfiguration wifiConfig) { 553 if (wifiConfig == null) { 554 return false; 555 } 556 557 // Convert to PasspointConfiguration 558 PasspointConfiguration passpointConfig = 559 PasspointProvider.convertFromWifiConfig(wifiConfig); 560 if (passpointConfig == null) { 561 return false; 562 } 563 564 // Setup aliases for enterprise certificates and key. 565 WifiEnterpriseConfig enterpriseConfig = wifiConfig.enterpriseConfig; 566 String caCertificateAliasSuffix = enterpriseConfig.getCaCertificateAlias(); 567 String clientCertAndKeyAliasSuffix = enterpriseConfig.getClientCertificateAlias(); 568 if (passpointConfig.getCredential().getUserCredential() != null 569 && TextUtils.isEmpty(caCertificateAliasSuffix)) { 570 Log.e(TAG, "Missing CA Certificate for user credential"); 571 return false; 572 } 573 if (passpointConfig.getCredential().getCertCredential() != null) { 574 if (TextUtils.isEmpty(caCertificateAliasSuffix)) { 575 Log.e(TAG, "Missing CA certificate for Certificate credential"); 576 return false; 577 } 578 if (TextUtils.isEmpty(clientCertAndKeyAliasSuffix)) { 579 Log.e(TAG, "Missing client certificate and key for certificate credential"); 580 return false; 581 } 582 } 583 584 // Note that for legacy configuration, the alias for client private key is the same as the 585 // alias for the client certificate. 586 PasspointProvider provider = new PasspointProvider(passpointConfig, mKeyStore, 587 mSimAccessor, mProviderIndex++, wifiConfig.creatorUid, 588 enterpriseConfig.getCaCertificateAlias(), 589 enterpriseConfig.getClientCertificateAlias(), 590 enterpriseConfig.getClientCertificateAlias()); 591 mProviders.put(passpointConfig.getHomeSp().getFqdn(), provider); 592 return true; 593 } 594} 595