diff options
author | Peter Qiu <zqiu@google.com> | 2016-11-02 14:41:25 -0700 |
---|---|---|
committer | Peter Qiu <zqiu@google.com> | 2016-11-21 23:06:36 +0000 |
commit | c04479601696f56c9cc240d4c7fc49fa99f51825 (patch) | |
tree | 95596aee5c2614353b37fb46b42cc8cc0f3e55cd /service | |
parent | 4365d22b77f31af7567fc12a90786c63cdc5c484 (diff) |
hotspot2: add support for managing Passpoint configurations
Add the initial support to PasspointManager for managing Passpoint
configurations.
More todos:
- add support to WifiKeyStore for installing/removing keys and certificates
in the Passpoint configurations
- add support to WifiConfigStore for loading/storing Passpoint configurations
from/to the persistent storage
Bug: 32618351
Test: frameworks/opt/net/wifi/tests/wifitests/runtests.sh
Test: download/install Boingo passpoint profile and verify configure is installed
using log messages
Merged-In: Id394bcf7514157bfaa3038b3f9f4d1003671d11f
Change-Id: Id394bcf7514157bfaa3038b3f9f4d1003671d11f
Diffstat (limited to 'service')
4 files changed, 198 insertions, 10 deletions
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index a5f132607..bfe6446af 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -91,6 +91,7 @@ public class WifiInjector { private final WifiPermissionsWrapper mWifiPermissionsWrapper; private final WifiPermissionsUtil mWifiPermissionsUtil; private final PasspointManager mPasspointManager; + private final SIMAccessor mSimAccessor; private final boolean mUseRealLogger; @@ -166,7 +167,8 @@ public class WifiInjector { mWifiPermissionsWrapper = new WifiPermissionsWrapper(mContext); mWifiPermissionsUtil = new WifiPermissionsUtil(mWifiPermissionsWrapper, mContext, mSettingsStore, UserManager.get(mContext)); - mPasspointManager = new PasspointManager(mContext, this); + mSimAccessor = new SIMAccessor(mContext); + mPasspointManager = new PasspointManager(mContext, this, mSimAccessor); } /** diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index 420f92366..c70247043 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -85,6 +85,7 @@ import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.AsyncChannel; +import com.android.server.wifi.hotspot2.PasspointManager; import com.android.server.wifi.util.WifiPermissionsUtil; import java.io.BufferedReader; @@ -160,6 +161,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { private WifiPermissionsUtil mWifiPermissionsUtil; private final boolean mPermissionReviewRequired; + private final PasspointManager mPasspointManager; /** * Handles client connections @@ -341,6 +343,8 @@ public class WifiServiceImpl extends IWifiManager.Stub { mPermissionReviewRequired = Build.PERMISSIONS_REVIEW_REQUIRED || context.getResources().getBoolean( com.android.internal.R.bool.config_permissionReviewRequired); + mPasspointManager = mWifiInjector.getPasspointManager(); + enableVerboseLoggingInternal(getVerboseLoggingLevel()); } @@ -1029,8 +1033,8 @@ public class WifiServiceImpl extends IWifiManager.Stub { */ @Override public boolean addPasspointConfiguration(PasspointConfiguration config) { - // TO BE IMPLEMENTED. - return true; + enforceChangePermission(); + return mPasspointManager.addProvider(config); } /** @@ -1041,8 +1045,8 @@ public class WifiServiceImpl extends IWifiManager.Stub { */ @Override public boolean removePasspointConfiguration(String fqdn) { - // TO BE IMPLEMENTED. - return true; + enforceChangePermission(); + return mPasspointManager.removeProvider(fqdn); } /** @@ -1052,8 +1056,8 @@ public class WifiServiceImpl extends IWifiManager.Stub { */ @Override public List<PasspointConfiguration> getPasspointConfigurations() { - // TO BE IMPLEMENTED. - return null; + enforceAccessPermission(); + return mPasspointManager.getProviderConfigs(); } /** diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java index 762917170..2344440f2 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java @@ -29,20 +29,33 @@ import static android.net.wifi.WifiManager.PASSPOINT_WNM_FRAME_RECEIVED_ACTION; import android.content.Context; import android.content.Intent; +import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; +import com.android.server.wifi.IMSIParameter; +import com.android.server.wifi.SIMAccessor; import com.android.server.wifi.WifiInjector; import com.android.server.wifi.anqp.ANQPElement; import com.android.server.wifi.anqp.Constants; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Map; /** * Responsible for managing passpoint networks. */ public class PasspointManager { - private final Context mContext; + private static final String TAG = "PasspointManager"; + private final PasspointEventHandler mHandler; + private final SIMAccessor mSimAccessor; + private final Map<String, PasspointProvider> mProviders; private class CallbackHandler implements PasspointEventHandler.Callbacks { private final Context mContext; @@ -91,9 +104,102 @@ public class PasspointManager { } } - public PasspointManager(Context context, WifiInjector wifiInjector) { - mContext = context; + public PasspointManager(Context context, WifiInjector wifiInjector, SIMAccessor simAccessor) { mHandler = wifiInjector.makePasspointEventHandler(new CallbackHandler(context)); + mSimAccessor = simAccessor; + mProviders = new HashMap<>(); + // TODO(zqiu): load providers from the persistent storage. + } + + /** + * Add or install a Passpoint provider with the given configuration. + * + * Each provider is uniquely identified by its FQDN (Fully Qualified Domain Name). + * In the case when there is an existing configuration with the same base + * domain, a provider with the new configuration will replace the existing provider. + * + * @param config Configuration of the Passpoint provider to be added + * @return true if provider is added, false otherwise + */ + public boolean addProvider(PasspointConfiguration config) { + if (config == null) { + Log.e(TAG, "Configuration not provided"); + return false; + } + if (!config.validate()) { + Log.e(TAG, "Invalid configuration"); + return false; + } + + // Verify IMSI against the IMSI of the installed SIM cards for SIM credential. + if (config.credential.simCredential != null) { + try { + if (mSimAccessor.getMatchingImsis( + new IMSIParameter(config.credential.simCredential.imsi)) == null) { + Log.e(TAG, "IMSI does not match any SIM card"); + return false; + } + } catch (IOException e) { + return false; + } + } + + // TODO(b/32619189): install new key and certificates to the keystore. + + // Detect existing configuration in the same base domain. + PasspointProvider existingProvider = findProviderInSameBaseDomain(config.homeSp.fqdn); + if (existingProvider != null) { + Log.d(TAG, "Replacing configuration for " + existingProvider.getConfig().homeSp.fqdn + + " with " + config.homeSp.fqdn); + // TODO(b/32619189): Remove existing key and certificates from the keystore. + + mProviders.remove(existingProvider.getConfig().homeSp.fqdn); + } + + // TODO(b/32714562): create/use a copy of configuration to avoid others from modifying it, + // since others might still have reference to it? + mProviders.put(config.homeSp.fqdn, new PasspointProvider(config)); + + // TODO(b/31065385): Persist updated providers configuration to the persistent storage. + + return true; + } + + /** + * Remove a Passpoint provider identified by the given FQDN. + * + * @param fqdn The FQDN of the provider to remove + * @return true if a provider is removed, false otherwise + */ + public boolean removeProvider(String fqdn) { + if (!mProviders.containsKey(fqdn)) { + Log.e(TAG, "Config doesn't exist"); + return false; + } + + // TODO(b/32619189): Remove key and certificates from the keystore. + + mProviders.remove(fqdn); + return true; + } + + /** + * Return the installed Passpoint provider configurations. + * + * @return A list of {@link PasspointConfiguration} or null if none is installed + */ + public List<PasspointConfiguration> getProviderConfigs() { + if (mProviders.size() == 0) { + return null; + } + + List<PasspointConfiguration> configs = new ArrayList<>(); + for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) { + // TODO(zqiu): return a copy of the configuration instead, to prevent others from + // modifying it? + configs.add(entry.getValue().getConfig()); + } + return configs; } /** @@ -130,4 +236,45 @@ public class PasspointManager { public boolean queryPasspointIcon(long bssid, String fileName) { return mHandler.requestIcon(bssid, fileName); } + + /** + * Find a provider that have FQDN in the same base domain as the given domain. + * + * @param domain The domain to be compared + * @return {@link PasspointProvider} if a match is found, null otherwise + */ + private PasspointProvider findProviderInSameBaseDomain(String domain) { + for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) { + if (isSameBaseDomain(entry.getKey(), domain)) { + return entry.getValue(); + } + } + return null; + } + + /** + * Check if one domain is the base domain for the other. For example, "test1.test.com" + * and "test.com" should return true. + * + * @param domain1 First domain to be compared + * @param domain2 Second domain to be compared + * @return true if one domain is a base domain for the other, false otherwise. + */ + private static boolean isSameBaseDomain(String domain1, String domain2) { + if (domain1 == null || domain2 == null) { + return false; + } + + List<String> labelList1 = Utils.splitDomain(domain1); + List<String> labelList2 = Utils.splitDomain(domain2); + Iterator<String> l1 = labelList1.iterator(); + Iterator<String> l2 = labelList2.iterator(); + + while (l1.hasNext() && l2.hasNext()) { + if (!TextUtils.equals(l1.next(), l2.next())) { + return false; + } + } + return true; + } } diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java new file mode 100644 index 000000000..2cf8c9f68 --- /dev/null +++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi.hotspot2; + +import android.net.wifi.hotspot2.PasspointConfiguration; + +/** + * Abstraction for Passpoint service provider. This class contains the both static + * Passpoint configuration data and the runtime data (e.g. blacklisted SSIDs, statistics). + */ +public class PasspointProvider { + private final PasspointConfiguration mConfig; + + public PasspointProvider(PasspointConfiguration config) { + mConfig = config; + } + + public PasspointConfiguration getConfig() { + return mConfig; + } +} |