From 44135417f89f8fc0bb593f184659f7a382771bf2 Mon Sep 17 00:00:00 2001 From: xshu Date: Wed, 16 Jan 2019 14:18:29 -0800 Subject: Persist randomized MAC over forget network Create and save a Map from configKey to the randomized MAC address for each unique network ever created. Bug: 122991465 Test: Unit tests Test: Verified on device with MAC randomization on, add network, examine MAC address, forget network, add the same network again and verify the MAC address is the same as the previous one. Change-Id: Ic703ec6fd3c9ccf36582b32721c0a4d703e66cc7 --- .../server/wifi/RandomizedMacStoreData.java | 117 +++++++++++++++++++++ .../com/android/server/wifi/WifiConfigManager.java | 55 +++++++++- .../java/com/android/server/wifi/WifiInjector.java | 4 +- 3 files changed, 169 insertions(+), 7 deletions(-) create mode 100644 service/java/com/android/server/wifi/RandomizedMacStoreData.java (limited to 'service/java/com/android/server/wifi') diff --git a/service/java/com/android/server/wifi/RandomizedMacStoreData.java b/service/java/com/android/server/wifi/RandomizedMacStoreData.java new file mode 100644 index 000000000..1e4d972ef --- /dev/null +++ b/service/java/com/android/server/wifi/RandomizedMacStoreData.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2019 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; + +import com.android.server.wifi.util.XmlUtil; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * This class performs serialization and parsing of XML data block that contain the mapping + * from configKey to randomized MAC address + * (XML block data inside tag). + */ +public class RandomizedMacStoreData implements WifiConfigStore.StoreData { + private static final String XML_TAG_SECTION_HEADER_MAC_ADDRESS_MAP = "MacAddressMap"; + private static final String XML_TAG_MAC_MAP = "MacMapEntry"; + + private Map mMacMapping; + + RandomizedMacStoreData() {} + + @Override + public void serializeData(XmlSerializer out) + throws XmlPullParserException, IOException { + if (mMacMapping != null) { + XmlUtil.writeNextValue(out, XML_TAG_MAC_MAP, mMacMapping); + } + } + + @Override + public void deserializeData(XmlPullParser in, int outerTagDepth) + throws XmlPullParserException, IOException { + // Ignore empty reads. + if (in == null) { + return; + } + while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) { + String[] valueName = new String[1]; + Object value = XmlUtil.readCurrentValue(in, valueName); + if (valueName[0] == null) { + throw new XmlPullParserException("Missing value name"); + } + switch (valueName[0]) { + case XML_TAG_MAC_MAP: + mMacMapping = (Map) value; + break; + default: + throw new XmlPullParserException("Unknown tag under " + + XML_TAG_SECTION_HEADER_MAC_ADDRESS_MAP + + ": " + valueName[0]); + } + } + } + + @Override + public void resetData() { + mMacMapping = null; + } + + @Override + public boolean hasNewDataToSerialize() { + // always persist. + return true; + } + + @Override + public String getName() { + return XML_TAG_SECTION_HEADER_MAC_ADDRESS_MAP; + } + + @Override + public @WifiConfigStore.StoreFileId int getStoreFileId() { + // Shared general store. + return WifiConfigStore.STORE_FILE_SHARED_GENERAL; + } + + /** + * An empty Map will be returned for null MAC address map. + * + * @return Map of mapping from configKey to the randomized MAC address. + */ + public Map getMacMapping() { + if (mMacMapping == null) { + return new HashMap(); + } + return mMacMapping; + } + + /** + * Sets the data to be stored to file. + * @param macMapping + */ + public void setMacMapping(Map macMapping) { + mMacMapping = macMapping; + } +} + diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java index f9fcbb3aa..4a0d02465 100644 --- a/service/java/com/android/server/wifi/WifiConfigManager.java +++ b/service/java/com/android/server/wifi/WifiConfigManager.java @@ -283,6 +283,14 @@ public class WifiConfigManager { * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field. */ private final Set mDeletedEphemeralSSIDs; + + /** + * Framework keeps a mapping from configKey to the randomized MAC address so that + * when a user forgets a network and thne adds it back, the same randomized MAC address + * will get used. + */ + private final Map mRandomizedMacAddressMapping; + /** * Flag to indicate if only networks with the same psk should be linked. * TODO(b/30706406): Remove this flag if unused. @@ -348,6 +356,7 @@ public class WifiConfigManager { private final NetworkListSharedStoreData mNetworkListSharedStoreData; private final NetworkListUserStoreData mNetworkListUserStoreData; private final DeletedEphemeralSsidsStoreData mDeletedEphemeralSsidsStoreData; + private final RandomizedMacStoreData mRandomizedMacStoreData; // Store the saved network update listener. private OnSavedNetworkUpdateListener mListener = null; @@ -369,6 +378,7 @@ public class WifiConfigManager { NetworkListSharedStoreData networkListSharedStoreData, NetworkListUserStoreData networkListUserStoreData, DeletedEphemeralSsidsStoreData deletedEphemeralSsidsStoreData, + RandomizedMacStoreData randomizedMacStoreData, FrameworkFacade frameworkFacade, Looper looper) { mContext = context; mClock = clock; @@ -384,14 +394,17 @@ public class WifiConfigManager { mConfiguredNetworks = new ConfigurationMap(userManager); mScanDetailCaches = new HashMap<>(16, 0.75f); mDeletedEphemeralSSIDs = new HashSet<>(); + mRandomizedMacAddressMapping = new HashMap<>(); // Register store data for network list and deleted ephemeral SSIDs. mNetworkListSharedStoreData = networkListSharedStoreData; mNetworkListUserStoreData = networkListUserStoreData; mDeletedEphemeralSsidsStoreData = deletedEphemeralSsidsStoreData; + mRandomizedMacStoreData = randomizedMacStoreData; mWifiConfigStore.registerStoreData(mNetworkListSharedStoreData); mWifiConfigStore.registerStoreData(mNetworkListUserStoreData); mWifiConfigStore.registerStoreData(mDeletedEphemeralSsidsStoreData); + mWifiConfigStore.registerStoreData(mRandomizedMacStoreData); mOnlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean( R.bool.config_wifi_only_link_same_credential_configurations); @@ -1025,10 +1038,36 @@ public class WifiConfigManager { mContext.getPackageManager().getNameForUid(uid); newInternalConfig.creationTime = newInternalConfig.updateTime = createDebugTimeStampString(mClock.getWallClockMillis()); + updateRandomizedMacAddress(newInternalConfig); return newInternalConfig; } + /** + * Sets the randomized address for the given configuration from stored map if it exist. + * Otherwise generates a new randomized address and save to the stored map. + * @param config + */ + private void updateRandomizedMacAddress(WifiConfiguration config) { + // Update randomized MAC address according to stored map + final String key = config.configKey(); + // If the key is not found in the current store, then it means this network has never been + // seen before. So add it to store. + if (!mRandomizedMacAddressMapping.containsKey(key)) { + mRandomizedMacAddressMapping.put(key, + config.getOrCreateRandomizedMacAddress().toString()); + } else { // Otherwise read from the store and set the WifiConfiguration + try { + config.setRandomizedMacAddress( + MacAddress.fromString(mRandomizedMacAddressMapping.get(key))); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Error creating randomized MAC address from stored value."); + mRandomizedMacAddressMapping.put(key, + config.getOrCreateRandomizedMacAddress().toString()); + } + } + } + /** * Create a new internal WifiConfiguration object by copying over parameters from the provided * external configuration to a copy of the existing internal WifiConfiguration object. @@ -2818,6 +2857,7 @@ public class WifiConfigManager { localLog("clearInternalData: Clearing all internal data"); mConfiguredNetworks.clear(); mDeletedEphemeralSSIDs.clear(); + mRandomizedMacAddressMapping.clear(); mScanDetailCaches.clear(); clearLastSelectedNetwork(); } @@ -2860,7 +2900,8 @@ public class WifiConfigManager { * @param configurations list of configurations retrieved from store. */ private void loadInternalDataFromSharedStore( - List configurations) { + List configurations, + Map macAddressMapping) { for (WifiConfiguration configuration : configurations) { configuration.networkId = mNextNetworkId++; if (mVerboseLoggingEnabled) { @@ -2872,6 +2913,7 @@ public class WifiConfigManager { Log.e(TAG, "Failed to add network to config map", e); } } + mRandomizedMacAddressMapping.putAll(macAddressMapping); } /** @@ -2914,11 +2956,12 @@ public class WifiConfigManager { */ private void loadInternalData( List sharedConfigurations, - List userConfigurations, Set deletedEphemeralSSIDs) { + List userConfigurations, Set deletedEphemeralSSIDs, + Map macAddressMapping) { // Clear out all the existing in-memory lists and load the lists from what was retrieved // from the config store. clearInternalData(); - loadInternalDataFromSharedStore(sharedConfigurations); + loadInternalDataFromSharedStore(sharedConfigurations, macAddressMapping); loadInternalDataFromUserStore(userConfigurations, deletedEphemeralSSIDs); if (mConfiguredNetworks.sizeForAllUsers() == 0) { Log.w(TAG, "No stored networks found."); @@ -2952,7 +2995,7 @@ public class WifiConfigManager { WifiConfigStoreDataLegacy storeData = mWifiConfigStoreLegacy.read(); Log.d(TAG, "Reading from legacy store completed"); loadInternalData(storeData.getConfigurations(), new ArrayList(), - storeData.getDeletedEphemeralSSIDs()); + storeData.getDeletedEphemeralSSIDs(), mRandomizedMacStoreData.getMacMapping()); // Setup user store for the current user in case it have not setup yet, so that data // owned by the current user will be backed to the user store. @@ -3003,7 +3046,8 @@ public class WifiConfigManager { } loadInternalData(mNetworkListSharedStoreData.getConfigurations(), mNetworkListUserStoreData.getConfigurations(), - mDeletedEphemeralSsidsStoreData.getSsidList()); + mDeletedEphemeralSsidsStoreData.getSsidList(), + mRandomizedMacStoreData.getMacMapping()); return true; } @@ -3093,6 +3137,7 @@ public class WifiConfigManager { mNetworkListSharedStoreData.setConfigurations(sharedConfigurations); mNetworkListUserStoreData.setConfigurations(userConfigurations); mDeletedEphemeralSsidsStoreData.setSsidList(mDeletedEphemeralSSIDs); + mRandomizedMacStoreData.setMacMapping(mRandomizedMacAddressMapping); try { mWifiConfigStore.write(forceWrite); diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 2954608ee..40737861b 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -241,8 +241,8 @@ public class WifiInjector { mWifiKeyStore, mWifiConfigStore, mWifiConfigStoreLegacy, mWifiPermissionsUtil, mWifiPermissionsWrapper, new NetworkListSharedStoreData(mContext), new NetworkListUserStoreData(mContext), - new DeletedEphemeralSsidsStoreData(), mFrameworkFacade, - mWifiCoreHandlerThread.getLooper()); + new DeletedEphemeralSsidsStoreData(), new RandomizedMacStoreData(), + mFrameworkFacade, mWifiCoreHandlerThread.getLooper()); mWifiScoreCard = new WifiScoreCard(mClock, "TODO(b/112196799) seed me properly"); mWifiMetrics.setWifiConfigManager(mWifiConfigManager); mWifiConnectivityHelper = new WifiConnectivityHelper(mWifiNative); -- cgit v1.2.3