WifiConfigManager.java revision 50a5e65be9656e369b65cdeea8739ca993d349af
137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines/*
237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * Copyright (C) 2016 The Android Open Source Project
337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines *
437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * Licensed under the Apache License, Version 2.0 (the "License");
5ebe69fe11e48d322045d5949c83283927a0d790bStephen Hines * you may not use this file except in compliance with the License.
637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * You may obtain a copy of the License at
737ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines *
837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines *      http://www.apache.org/licenses/LICENSE-2.0
937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines *
1037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * Unless required by applicable law or agreed to in writing, software
1137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * distributed under the License is distributed on an "AS IS" BASIS,
1237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * See the License for the specific language governing permissions and
1437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * limitations under the License.
1537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines */
1637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines
1737ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinespackage com.android.server.wifi;
1837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines
1937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.app.ActivityManager;
2037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.app.admin.DeviceAdminInfo;
2137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.app.admin.DevicePolicyManagerInternal;
2237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.content.ContentResolver;
2337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.content.Context;
2437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.content.Intent;
2537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.content.pm.ApplicationInfo;
2637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.content.pm.PackageManager;
2737ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.net.IpConfiguration;
2837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.net.ProxyInfo;
2937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.net.StaticIpConfiguration;
3037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.net.wifi.ScanResult;
3137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.net.wifi.WifiConfiguration;
3237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
3337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.net.wifi.WifiEnterpriseConfig;
3437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.net.wifi.WifiInfo;
3537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.net.wifi.WifiManager;
3637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.net.wifi.WifiScanner;
3737ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.os.RemoteException;
3837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.os.UserHandle;
3937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.os.UserManager;
4037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.provider.Settings;
4137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.telephony.TelephonyManager;
4237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.text.TextUtils;
4337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.util.LocalLog;
4437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport android.util.Log;
4537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines
4637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport com.android.internal.R;
4737ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport com.android.internal.annotations.VisibleForTesting;
4837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport com.android.server.LocalServices;
4937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport com.android.server.wifi.WifiConfigStoreLegacy.WifiConfigStoreDataLegacy;
5037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport com.android.server.wifi.util.ScanResultUtil;
5137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport com.android.server.wifi.util.TelephonyUtil;
5237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines
5337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport org.xmlpull.v1.XmlPullParserException;
5437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines
5537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.io.FileDescriptor;
5637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.io.IOException;
5737ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.io.PrintWriter;
5837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.util.ArrayList;
5937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.util.BitSet;
6037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.util.Calendar;
6137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.util.Collection;
6237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.util.Collections;
6337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.util.HashMap;
6437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.util.HashSet;
6537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.util.Iterator;
6637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.util.List;
6737ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.util.Map;
6837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinesimport java.util.Set;
6937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines
7037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines/**
7137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * This class provides the APIs to manage configured Wi-Fi networks.
7237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * It deals with the following:
7337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * - Maintaining a list of configured networks for quick access.
7437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * - Persisting the configurations to store when required.
7537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * - Supporting WifiManager Public API calls:
7637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines *   > addOrUpdateNetwork()
7737ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines *   > removeNetwork()
7837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines *   > enableNetwork()
7937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines *   > disableNetwork()
8037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * - Handle user switching on multi-user devices.
8137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines *
8237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * All network configurations retrieved from this class are copies of the original configuration
8337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * stored in the internal database. So, any updates to the retrieved configuration object are
8437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * meaningless and will not be reflected in the original database.
8537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * This is done on purpose to ensure that only WifiConfigManager can modify configurations stored
8637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * in the internal database. Any configuration updates should be triggered with appropriate helper
8737ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * methods of this class using the configuration's unique networkId.
8837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines *
8937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines * NOTE: These API's are not thread safe and should only be used from WifiStateMachine thread.
9037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines */
9137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hinespublic class WifiConfigManager {
9237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    /**
9337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines     * String used to mask passwords to public interface.
9437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines     */
9537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    @VisibleForTesting
9637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    public static final String PASSWORD_MASK = "*";
9737ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    /**
9837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines     * Package name for SysUI. This is used to lookup the UID of SysUI which is used to allow
9937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines     * Quick settings to modify network configurations.
10037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines     */
10137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    @VisibleForTesting
10237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    public static final String SYSUI_PACKAGE_NAME = "com.android.systemui";
10337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    /**
10437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines     * Network Selection disable reason thresholds. These numbers are used to debounce network
10537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines     * failures before we disable them.
10637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines     * These are indexed using the disable reason constants defined in
107de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar     * {@link android.net.wifi.WifiConfiguration.NetworkSelectionStatus}.
108de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar     */
10937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    @VisibleForTesting
11037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    public static final int[] NETWORK_SELECTION_DISABLE_THRESHOLD = {
11137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            -1, //  threshold for NETWORK_SELECTION_ENABLE
11237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            1,  //  threshold for DISABLED_BAD_LINK
11337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            5,  //  threshold for DISABLED_ASSOCIATION_REJECTION
11437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            5,  //  threshold for DISABLED_AUTHENTICATION_FAILURE
11537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            5,  //  threshold for DISABLED_DHCP_FAILURE
116de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar            5,  //  threshold for DISABLED_DNS_FAILURE
11737ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            1,  //  threshold for DISABLED_WPS_START
11837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            6,  //  threshold for DISABLED_TLS_VERSION_MISMATCH
11937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            1,  //  threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
12037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            1,  //  threshold for DISABLED_NO_INTERNET
12137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            1,  //  threshold for DISABLED_BY_WIFI_MANAGER
12237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            1   //  threshold for DISABLED_BY_USER_SWITCH
12337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    };
12437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    /**
12537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines     * Network Selection disable timeout for each kind of error. After the timeout milliseconds,
12637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines     * enable the network again.
12737ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines     * These are indexed using the disable reason constants defined in
12837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines     * {@link android.net.wifi.WifiConfiguration.NetworkSelectionStatus}.
12937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines     */
13037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    @VisibleForTesting
13137ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines    public static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT_MS = {
13237ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            Integer.MAX_VALUE,  // threshold for NETWORK_SELECTION_ENABLE
13337ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            15 * 60 * 1000,     // threshold for DISABLED_BAD_LINK
13437ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            5 * 60 * 1000,      // threshold for DISABLED_ASSOCIATION_REJECTION
13537ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            5 * 60 * 1000,      // threshold for DISABLED_AUTHENTICATION_FAILURE
13637ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            5 * 60 * 1000,      // threshold for DISABLED_DHCP_FAILURE
13737ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            5 * 60 * 1000,      // threshold for DISABLED_DNS_FAILURE
13837ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            0 * 60 * 1000,      // threshold for DISABLED_WPS_START
13937ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            Integer.MAX_VALUE,  // threshold for DISABLED_TLS_VERSION
14037ed9c199ca639565f6ce88105f9e39e898d82d0Stephen Hines            Integer.MAX_VALUE,  // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
141            Integer.MAX_VALUE,  // threshold for DISABLED_NO_INTERNET
142            Integer.MAX_VALUE,  // threshold for DISABLED_BY_WIFI_MANAGER
143            Integer.MAX_VALUE   // threshold for DISABLED_BY_USER_SWITCH
144    };
145    /**
146     * Max size of scan details to cache in {@link #mScanDetailCaches}.
147     */
148    @VisibleForTesting
149    public static final int SCAN_CACHE_ENTRIES_MAX_SIZE = 192;
150    /**
151     * Once the size of the scan details in the cache {@link #mScanDetailCaches} exceeds
152     * {@link #SCAN_CACHE_ENTRIES_MAX_SIZE}, trim it down to this value so that we have some
153     * buffer time before the next eviction.
154     */
155    @VisibleForTesting
156    public static final int SCAN_CACHE_ENTRIES_TRIM_SIZE = 128;
157    /**
158     * Link networks only if they have less than this number of scan cache entries.
159     */
160    @VisibleForTesting
161    public static final int LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES = 6;
162    /**
163     * Link networks only if the bssid in scan results for the networks match in the first
164     * 16 ASCII chars in the bssid string. For example = "af:de:56;34:15:7"
165     */
166    @VisibleForTesting
167    public static final int LINK_CONFIGURATION_BSSID_MATCH_LENGTH = 16;
168    /**
169     * Flags to be passed in for |canModifyNetwork| to decide if the change is minor and can
170     * bypass the lockdown checks.
171     */
172    private static final boolean ALLOW_LOCKDOWN_CHECK_BYPASS = true;
173    private static final boolean DISALLOW_LOCKDOWN_CHECK_BYPASS = false;
174    /**
175     * Log tag for this class.
176     */
177    private static final String TAG = "WifiConfigManager";
178    /**
179     * Maximum age of scan results that can be used for averaging out RSSI value.
180     */
181    private static final int SCAN_RESULT_MAXIMUM_AGE_MS = 40000;
182    /**
183     * General sorting algorithm of all networks for scanning purposes:
184     * Place the configurations in descending order of their |numAssociation| values. If networks
185     * have the same |numAssociation|, place the configurations with
186     * |lastSeenInQualifiedNetworkSelection| set first.
187     */
188    private static final WifiConfigurationUtil.WifiConfigurationComparator sScanListComparator =
189            new WifiConfigurationUtil.WifiConfigurationComparator() {
190                @Override
191                public int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b) {
192                    if (a.numAssociation != b.numAssociation) {
193                        return Long.compare(b.numAssociation, a.numAssociation);
194                    } else {
195                        boolean isConfigALastSeen =
196                                a.getNetworkSelectionStatus()
197                                        .getSeenInLastQualifiedNetworkSelection();
198                        boolean isConfigBLastSeen =
199                                b.getNetworkSelectionStatus()
200                                        .getSeenInLastQualifiedNetworkSelection();
201                        return Boolean.compare(isConfigBLastSeen, isConfigALastSeen);
202                    }
203                }
204            };
205
206    /**
207     * List of external dependencies for WifiConfigManager.
208     */
209    private final Context mContext;
210    private final FrameworkFacade mFacade;
211    private final Clock mClock;
212    private final UserManager mUserManager;
213    private final BackupManagerProxy mBackupManagerProxy;
214    private final TelephonyManager mTelephonyManager;
215    private final WifiKeyStore mWifiKeyStore;
216    private final WifiConfigStore mWifiConfigStore;
217    private final WifiConfigStoreLegacy mWifiConfigStoreLegacy;
218    /**
219     * Local log used for debugging any WifiConfigManager issues.
220     */
221    private final LocalLog mLocalLog =
222            new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 128 : 256);
223    /**
224     * Map of configured networks with network id as the key.
225     */
226    private final ConfigurationMap mConfiguredNetworks;
227    /**
228     * Stores a map of NetworkId to ScanDetailCache.
229     */
230    private final Map<Integer, ScanDetailCache> mScanDetailCaches;
231    /**
232     * Framework keeps a list of ephemeral SSIDs that where deleted by user,
233     * so as, framework knows not to autoconnect again those SSIDs based on scorer input.
234     * The list is never cleared up.
235     * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field.
236     */
237    private final Set<String> mDeletedEphemeralSSIDs;
238    /**
239     * Flag to indicate if only networks with the same psk should be linked.
240     * TODO(b/30706406): Remove this flag if unused.
241     */
242    private final boolean mOnlyLinkSameCredentialConfigurations;
243    /**
244     * Number of channels to scan for during partial scans initiated while connected.
245     */
246    private final int mMaxNumActiveChannelsForPartialScans;
247    /**
248     * Verbose logging flag. Toggled by developer options.
249     */
250    private boolean mVerboseLoggingEnabled = false;
251    /**
252     * Current logged in user ID.
253     */
254    private int mCurrentUserId = UserHandle.USER_SYSTEM;
255    /**
256     * Flag to indicate that the new user's store has not yet been read since user switch.
257     * Initialize this flag to |true| to trigger a read on the first user unlock after
258     * bootup.
259     */
260    private boolean mPendingUnlockStoreRead = true;
261    /**
262     * This is keeping track of the next network ID to be assigned. Any new networks will be
263     * assigned |mNextNetworkId| as network ID.
264     */
265    private int mNextNetworkId = 0;
266    /**
267     * UID of system UI. This uid is allowed to modify network configurations regardless of which
268     * user is logged in.
269     */
270    private int mSystemUiUid = -1;
271    /**
272     * This is used to remember which network was selected successfully last by an app. This is set
273     * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
274     * This is the only way for an app to request connection to a specific network using the
275     * {@link WifiManager} API's.
276     */
277    private int mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
278    private long mLastSelectedTimeStamp =
279            WifiConfiguration.NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
280
281    /**
282     * Create new instance of WifiConfigManager.
283     */
284    WifiConfigManager(
285            Context context, FrameworkFacade facade, Clock clock, UserManager userManager,
286            TelephonyManager telephonyManager, WifiKeyStore wifiKeyStore,
287            WifiConfigStore wifiConfigStore, WifiConfigStoreLegacy wifiConfigStoreLegacy) {
288        mContext = context;
289        mFacade = facade;
290        mClock = clock;
291        mUserManager = userManager;
292        mBackupManagerProxy = new BackupManagerProxy();
293        mTelephonyManager = telephonyManager;
294        mWifiKeyStore = wifiKeyStore;
295        mWifiConfigStore = wifiConfigStore;
296        mWifiConfigStoreLegacy = wifiConfigStoreLegacy;
297
298        mConfiguredNetworks = new ConfigurationMap(userManager);
299        mScanDetailCaches = new HashMap<>(16, 0.75f);
300        mDeletedEphemeralSSIDs = new HashSet<>();
301
302        mOnlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean(
303                R.bool.config_wifi_only_link_same_credential_configurations);
304        mMaxNumActiveChannelsForPartialScans = mContext.getResources().getInteger(
305                R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels);
306
307        try {
308            mSystemUiUid = mContext.getPackageManager().getPackageUidAsUser(SYSUI_PACKAGE_NAME,
309                    PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
310        } catch (PackageManager.NameNotFoundException e) {
311            Log.e(TAG, "Unable to resolve SystemUI's UID.");
312        }
313    }
314
315    /**
316     * Construct the string to be put in the |creationTime| & |updateTime| elements of
317     * WifiConfiguration from the provided wall clock millis.
318     *
319     * @param wallClockMillis Time in milliseconds to be converted to string.
320     */
321    @VisibleForTesting
322    public static String createDebugTimeStampString(long wallClockMillis) {
323        StringBuilder sb = new StringBuilder();
324        sb.append("time=");
325        Calendar c = Calendar.getInstance();
326        c.setTimeInMillis(wallClockMillis);
327        sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
328        return sb.toString();
329    }
330
331    /**
332     * Enable/disable verbose logging in WifiConfigManager & its helper classes.
333     */
334    public void enableVerboseLogging(int verbose) {
335        if (verbose > 0) {
336            mVerboseLoggingEnabled = true;
337        } else {
338            mVerboseLoggingEnabled = false;
339        }
340        mWifiConfigStore.enableVerboseLogging(mVerboseLoggingEnabled);
341        mWifiKeyStore.enableVerboseLogging(mVerboseLoggingEnabled);
342    }
343
344    /**
345     * Helper method to mask all passwords/keys from the provided WifiConfiguration object. This
346     * is needed when the network configurations are being requested via the public WifiManager
347     * API's.
348     * This currently masks the following elements: psk, wepKeys & enterprise config password.
349     */
350    private void maskPasswordsInWifiConfiguration(WifiConfiguration configuration) {
351        if (!TextUtils.isEmpty(configuration.preSharedKey)) {
352            configuration.preSharedKey = PASSWORD_MASK;
353        }
354        if (configuration.wepKeys != null) {
355            for (int i = 0; i < configuration.wepKeys.length; i++) {
356                if (!TextUtils.isEmpty(configuration.wepKeys[i])) {
357                    configuration.wepKeys[i] = PASSWORD_MASK;
358                }
359            }
360        }
361        if (!TextUtils.isEmpty(configuration.enterpriseConfig.getPassword())) {
362            configuration.enterpriseConfig.setPassword(PASSWORD_MASK);
363        }
364    }
365
366    /**
367     * Helper method to create a copy of the provided internal WifiConfiguration object to be
368     * passed to external modules.
369     *
370     * @param configuration provided WifiConfiguration object.
371     * @param maskPasswords Mask passwords or not.
372     * @return Copy of the WifiConfiguration object.
373     */
374    private WifiConfiguration createExternalWifiConfiguration(
375            WifiConfiguration configuration, boolean maskPasswords) {
376        WifiConfiguration network = new WifiConfiguration(configuration);
377        if (maskPasswords) {
378            maskPasswordsInWifiConfiguration(network);
379        }
380        return network;
381    }
382
383    /**
384     * Fetch the list of currently configured networks maintained in WifiConfigManager.
385     *
386     * This retrieves a copy of the internal configurations maintained by WifiConfigManager and
387     * should be used for any public interfaces.
388     *
389     * @param savedOnly     Retrieve only saved networks.
390     * @param maskPasswords Mask passwords or not.
391     * @return List of WifiConfiguration objects representing the networks.
392     */
393    private List<WifiConfiguration> getConfiguredNetworks(
394            boolean savedOnly, boolean maskPasswords) {
395        List<WifiConfiguration> networks = new ArrayList<>();
396        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
397            if (savedOnly && config.ephemeral) {
398                continue;
399            }
400            networks.add(createExternalWifiConfiguration(config, maskPasswords));
401        }
402        return networks;
403    }
404
405    /**
406     * Retrieves the list of all configured networks with passwords masked.
407     *
408     * @return List of WifiConfiguration objects representing the networks.
409     */
410    public List<WifiConfiguration> getConfiguredNetworks() {
411        return getConfiguredNetworks(false, true);
412    }
413
414    /**
415     * Retrieves the list of all configured networks with the passwords in plaintext.
416     *
417     * WARNING: Don't use this to pass network configurations to external apps. Should only be
418     * sent to system apps/wifi stack, when there is a need for passwords in plaintext.
419     * TODO: Need to understand the current use case of this API.
420     *
421     * @return List of WifiConfiguration objects representing the networks.
422     */
423    public List<WifiConfiguration> getConfiguredNetworksWithPasswords() {
424        return getConfiguredNetworks(false, false);
425    }
426
427    /**
428     * Retrieves the list of all configured networks with the passwords masked.
429     *
430     * @return List of WifiConfiguration objects representing the networks.
431     */
432    public List<WifiConfiguration> getSavedNetworks() {
433        return getConfiguredNetworks(true, true);
434    }
435
436    /**
437     * Retrieves the configured network corresponding to the provided networkId with password
438     * masked.
439     *
440     * @param networkId networkId of the requested network.
441     * @return WifiConfiguration object if found, null otherwise.
442     */
443    public WifiConfiguration getConfiguredNetwork(int networkId) {
444        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
445        if (config == null) {
446            return null;
447        }
448        // Create a new configuration object with the passwords masked to send out to the external
449        // world.
450        return createExternalWifiConfiguration(config, true);
451    }
452
453    /**
454     * Retrieves the configured network corresponding to the provided config key with password
455     * masked.
456     *
457     * @param configKey configKey of the requested network.
458     * @return WifiConfiguration object if found, null otherwise.
459     */
460    public WifiConfiguration getConfiguredNetwork(String configKey) {
461        WifiConfiguration config = getInternalConfiguredNetwork(configKey);
462        if (config == null) {
463            return null;
464        }
465        // Create a new configuration object with the passwords masked to send out to the external
466        // world.
467        return createExternalWifiConfiguration(config, true);
468    }
469
470    /**
471     * Retrieves the configured network corresponding to the provided networkId with password
472     * in plaintext.
473     *
474     * WARNING: Don't use this to pass network configurations to external apps. Should only be
475     * sent to system apps/wifi stack, when there is a need for passwords in plaintext.
476     *
477     * @param networkId networkId of the requested network.
478     * @return WifiConfiguration object if found, null otherwise.
479     */
480    public WifiConfiguration getConfiguredNetworkWithPassword(int networkId) {
481        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
482        if (config == null) {
483            return null;
484        }
485        // Create a new configuration object without the passwords masked to send out to the
486        // external world.
487        return createExternalWifiConfiguration(config, false);
488    }
489
490    /**
491     * Helper method to retrieve all the internal WifiConfiguration objects corresponding to all
492     * the networks in our database.
493     */
494    private Collection<WifiConfiguration> getInternalConfiguredNetworks() {
495        return mConfiguredNetworks.valuesForCurrentUser();
496    }
497
498    /**
499     * Helper method to retrieve the internal WifiConfiguration object corresponding to the
500     * provided configuration in our database.
501     * This first attempts to find the network using the provided network ID in configuration,
502     * else it attempts to find a matching configuration using the configKey.
503     */
504    private WifiConfiguration getInternalConfiguredNetwork(WifiConfiguration config) {
505        WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(config.networkId);
506        if (internalConfig != null) {
507            return internalConfig;
508        }
509        internalConfig = mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey());
510        if (internalConfig == null) {
511            Log.e(TAG, "Cannot find network with networkId " + config.networkId
512                    + " or configKey " + config.configKey());
513        }
514        return internalConfig;
515    }
516
517    /**
518     * Helper method to retrieve the internal WifiConfiguration object corresponding to the
519     * provided network ID in our database.
520     */
521    private WifiConfiguration getInternalConfiguredNetwork(int networkId) {
522        if (networkId == WifiConfiguration.INVALID_NETWORK_ID) {
523            Log.e(TAG, "Looking up network with invalid networkId -1");
524            return null;
525        }
526        WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(networkId);
527        if (internalConfig == null) {
528            Log.e(TAG, "Cannot find network with networkId " + networkId);
529        }
530        return internalConfig;
531    }
532
533    /**
534     * Helper method to retrieve the internal WifiConfiguration object corresponding to the
535     * provided configKey in our database.
536     */
537    private WifiConfiguration getInternalConfiguredNetwork(String configKey) {
538        WifiConfiguration internalConfig =
539                mConfiguredNetworks.getByConfigKeyForCurrentUser(configKey);
540        if (internalConfig == null) {
541            Log.e(TAG, "Cannot find network with configKey " + configKey);
542        }
543        return internalConfig;
544    }
545
546    /**
547     * Method to send out the configured networks change broadcast when a single network
548     * configuration is changed.
549     *
550     * @param network WifiConfiguration corresponding to the network that was changed.
551     * @param reason  The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
552     *                WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
553     */
554    private void sendConfiguredNetworkChangedBroadcast(
555            WifiConfiguration network, int reason) {
556        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
557        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
558        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
559        // Create a new WifiConfiguration with passwords masked before we send it out.
560        WifiConfiguration broadcastNetwork = new WifiConfiguration(network);
561        maskPasswordsInWifiConfiguration(broadcastNetwork);
562        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, broadcastNetwork);
563        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
564        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
565    }
566
567    /**
568     * Method to send out the configured networks change broadcast when multiple network
569     * configurations are changed.
570     */
571    private void sendConfiguredNetworksChangedBroadcast() {
572        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
573        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
574        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
575        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
576    }
577
578    /**
579     * Checks if the app has the permission to override Wi-Fi network configuration or not.
580     *
581     * @param uid uid of the app.
582     * @return true if the app does have the permission, false otherwise.
583     */
584    public boolean checkConfigOverridePermission(int uid) {
585        try {
586            int permission =
587                    mFacade.checkUidPermission(
588                            android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid);
589            return (permission == PackageManager.PERMISSION_GRANTED);
590        } catch (RemoteException e) {
591            Log.e(TAG, "Error checking for permission " + e);
592            return false;
593        }
594    }
595
596    /**
597     * Checks if |uid| has permission to modify the provided configuration.
598     *
599     * @param config         WifiConfiguration object corresponding to the network to be modified.
600     * @param uid            UID of the app requesting the modification.
601     * @param ignoreLockdown Ignore the configuration lockdown checks for connection attempts.
602     */
603    private boolean canModifyNetwork(WifiConfiguration config, int uid, boolean ignoreLockdown) {
604        final DevicePolicyManagerInternal dpmi = LocalServices.getService(
605                DevicePolicyManagerInternal.class);
606
607        final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
608                DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
609
610        // If |uid| corresponds to the device owner, allow all modifications.
611        if (isUidDeviceOwner) {
612            return true;
613        }
614
615        final boolean isCreator = (config.creatorUid == uid);
616
617        // Check if the |uid| holds the |OVERRIDE_CONFIG_WIFI| permission if the caller asks us to
618        // bypass the lockdown checks.
619        if (ignoreLockdown) {
620            return checkConfigOverridePermission(uid);
621        }
622
623        // Check if device has DPM capability. If it has and |dpmi| is still null, then we
624        // treat this case with suspicion and bail out.
625        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)
626                && dpmi == null) {
627            Log.w(TAG, "Error retrieving DPMI service.");
628            return false;
629        }
630
631        // WiFi config lockdown related logic. At this point we know uid is NOT a Device Owner.
632        final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy(
633                config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
634        if (!isConfigEligibleForLockdown) {
635            return isCreator || checkConfigOverridePermission(uid);
636        }
637
638        final ContentResolver resolver = mContext.getContentResolver();
639        final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
640                Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
641        return !isLockdownFeatureEnabled && checkConfigOverridePermission(uid);
642    }
643
644    /**
645     * Method to check if the provided UID belongs to the current foreground user or some other
646     * app (only SysUI today) running on behalf of the user.
647     * This is used to prevent any background user apps from modifying network configurations.
648     *
649     * @param uid uid of the app.
650     * @return true if the UID belongs to the current foreground app or SystemUI, false otherwise.
651     */
652    private boolean doesUidBelongToCurrentUser(int uid) {
653        return (WifiConfigurationUtil.doesUidBelongToAnyProfile(
654                uid, mUserManager.getProfiles(mCurrentUserId)) || (uid == mSystemUiUid));
655    }
656
657    /**
658     * Copy over public elements from an external WifiConfiguration object to the internal
659     * configuration object if element has been set in the provided external WifiConfiguration.
660     * The only exception is the hidden |IpConfiguration| parameters, these need to be copied over
661     * for every update.
662     *
663     * This method updates all elements that are common to both network addition & update.
664     * The following fields of {@link WifiConfiguration} are not copied from external configs:
665     *  > networkId - These are allocated by Wi-Fi stack internally for any new configurations.
666     *  > status - The status needs to be explicitly updated using
667     *             {@link WifiManager#enableNetwork(int, boolean)} or
668     *             {@link WifiManager#disableNetwork(int)}.
669     *
670     * @param internalConfig WifiConfiguration object in our internal map.
671     * @param externalConfig WifiConfiguration object provided from the external API.
672     */
673    private void mergeWithInternalWifiConfiguration(
674            WifiConfiguration internalConfig, WifiConfiguration externalConfig) {
675        if (externalConfig.SSID != null) {
676            internalConfig.SSID = externalConfig.SSID;
677        }
678        if (externalConfig.BSSID != null) {
679            internalConfig.BSSID = externalConfig.BSSID;
680        }
681        internalConfig.hiddenSSID = externalConfig.hiddenSSID;
682        if (externalConfig.preSharedKey != null
683                && !externalConfig.preSharedKey.equals(PASSWORD_MASK)) {
684            internalConfig.preSharedKey = externalConfig.preSharedKey;
685        }
686        // Modify only wep keys are present in the provided configuration. This is a little tricky
687        // because there is no easy way to tell if the app is actually trying to null out the
688        // existing keys or not.
689        if (externalConfig.wepKeys != null) {
690            boolean hasWepKey = false;
691            for (int i = 0; i < internalConfig.wepKeys.length; i++) {
692                if (externalConfig.wepKeys[i] != null
693                        && !externalConfig.wepKeys[i].equals(PASSWORD_MASK)) {
694                    internalConfig.wepKeys[i] = externalConfig.wepKeys[i];
695                    hasWepKey = true;
696                }
697            }
698            if (hasWepKey) {
699                internalConfig.wepTxKeyIndex = externalConfig.wepTxKeyIndex;
700            }
701        }
702        if (externalConfig.FQDN != null) {
703            internalConfig.FQDN = externalConfig.FQDN;
704        }
705        if (externalConfig.providerFriendlyName != null) {
706            internalConfig.providerFriendlyName = externalConfig.providerFriendlyName;
707        }
708        if (externalConfig.roamingConsortiumIds != null) {
709            internalConfig.roamingConsortiumIds = externalConfig.roamingConsortiumIds.clone();
710        }
711
712        // Copy over all the auth/protocol/key mgmt parameters if set.
713        if (externalConfig.allowedAuthAlgorithms != null
714                && !externalConfig.allowedAuthAlgorithms.isEmpty()) {
715            internalConfig.allowedAuthAlgorithms =
716                    (BitSet) externalConfig.allowedAuthAlgorithms.clone();
717        }
718        if (externalConfig.allowedProtocols != null
719                && !externalConfig.allowedProtocols.isEmpty()) {
720            internalConfig.allowedProtocols = (BitSet) externalConfig.allowedProtocols.clone();
721        }
722        if (externalConfig.allowedKeyManagement != null
723                && !externalConfig.allowedKeyManagement.isEmpty()) {
724            internalConfig.allowedKeyManagement =
725                    (BitSet) externalConfig.allowedKeyManagement.clone();
726        }
727        if (externalConfig.allowedPairwiseCiphers != null
728                && !externalConfig.allowedPairwiseCiphers.isEmpty()) {
729            internalConfig.allowedPairwiseCiphers =
730                    (BitSet) externalConfig.allowedPairwiseCiphers.clone();
731        }
732        if (externalConfig.allowedGroupCiphers != null
733                && !externalConfig.allowedGroupCiphers.isEmpty()) {
734            internalConfig.allowedGroupCiphers =
735                    (BitSet) externalConfig.allowedGroupCiphers.clone();
736        }
737
738        // Copy over the |IpConfiguration| parameters if set.
739        if (externalConfig.getIpConfiguration() != null) {
740            IpConfiguration.IpAssignment ipAssignment = externalConfig.getIpAssignment();
741            if (ipAssignment != IpConfiguration.IpAssignment.UNASSIGNED) {
742                internalConfig.setIpAssignment(ipAssignment);
743                if (ipAssignment == IpConfiguration.IpAssignment.STATIC) {
744                    internalConfig.setStaticIpConfiguration(
745                            new StaticIpConfiguration(externalConfig.getStaticIpConfiguration()));
746                }
747            }
748            IpConfiguration.ProxySettings proxySettings = externalConfig.getProxySettings();
749            if (proxySettings != IpConfiguration.ProxySettings.UNASSIGNED) {
750                internalConfig.setProxySettings(proxySettings);
751                if (proxySettings == IpConfiguration.ProxySettings.PAC
752                        || proxySettings == IpConfiguration.ProxySettings.STATIC) {
753                    internalConfig.setHttpProxy(new ProxyInfo(externalConfig.getHttpProxy()));
754                }
755            }
756        }
757
758        // Copy over the |WifiEnterpriseConfig| parameters if set.
759        if (externalConfig.enterpriseConfig != null) {
760            internalConfig.enterpriseConfig =
761                    new WifiEnterpriseConfig(externalConfig.enterpriseConfig);
762        }
763    }
764
765    /**
766     * Set all the exposed defaults in the newly created WifiConfiguration object.
767     * These fields have a default value advertised in our public documentation. The only exception
768     * is the hidden |IpConfiguration| parameters, these have a default value even though they're
769     * hidden.
770     *
771     * @param configuration provided WifiConfiguration object.
772     */
773    private void setDefaultsInWifiConfiguration(WifiConfiguration configuration) {
774        configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
775
776        configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
777        configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
778
779        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
780        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
781
782        configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
783        configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
784
785        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
786        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
787        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
788        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
789
790        configuration.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
791        configuration.setProxySettings(IpConfiguration.ProxySettings.NONE);
792
793        configuration.status = WifiConfiguration.Status.DISABLED;
794        configuration.getNetworkSelectionStatus().setNetworkSelectionStatus(
795                NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
796    }
797
798    /**
799     * Create a new internal WifiConfiguration object by copying over parameters from the provided
800     * external configuration and set defaults for the appropriate parameters.
801     *
802     * @param externalConfig WifiConfiguration object provided from the external API.
803     * @return New WifiConfiguration object with parameters merged from the provided external
804     * configuration.
805     */
806    private WifiConfiguration createNewInternalWifiConfigurationFromExternal(
807            WifiConfiguration externalConfig, int uid) {
808        WifiConfiguration newInternalConfig = new WifiConfiguration();
809
810        // First allocate a new network ID for the configuration.
811        newInternalConfig.networkId = mNextNetworkId++;
812
813        // First set defaults in the new configuration created.
814        setDefaultsInWifiConfiguration(newInternalConfig);
815
816        // Copy over all the public elements from the provided configuration.
817        mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
818
819        // Copy over the hidden configuration parameters. These are the only parameters used by
820        // system apps to indicate some property about the network being added.
821        // These are only copied over for network additions and ignored for network updates.
822        newInternalConfig.requirePMF = externalConfig.requirePMF;
823        newInternalConfig.noInternetAccessExpected = externalConfig.noInternetAccessExpected;
824        newInternalConfig.ephemeral = externalConfig.ephemeral;
825        newInternalConfig.meteredHint = externalConfig.meteredHint;
826        newInternalConfig.useExternalScores = externalConfig.useExternalScores;
827        newInternalConfig.shared = externalConfig.shared;
828
829        // Add debug information for network addition.
830        newInternalConfig.creatorUid = newInternalConfig.lastUpdateUid = uid;
831        newInternalConfig.creatorName = newInternalConfig.lastUpdateName =
832                mContext.getPackageManager().getNameForUid(uid);
833        newInternalConfig.creationTime = newInternalConfig.updateTime =
834                createDebugTimeStampString(mClock.getWallClockMillis());
835
836        return newInternalConfig;
837    }
838
839    /**
840     * Create a new internal WifiConfiguration object by copying over parameters from the provided
841     * external configuration to a copy of the existing internal WifiConfiguration object.
842     *
843     * @param internalConfig WifiConfiguration object in our internal map.
844     * @param externalConfig WifiConfiguration object provided from the external API.
845     * @return Copy of existing WifiConfiguration object with parameters merged from the provided
846     * configuration.
847     */
848    private WifiConfiguration updateExistingInternalWifiConfigurationFromExternal(
849            WifiConfiguration internalConfig, WifiConfiguration externalConfig, int uid) {
850        WifiConfiguration newInternalConfig = new WifiConfiguration(internalConfig);
851
852        // Copy over all the public elements from the provided configuration.
853        mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
854
855        // Add debug information for network update.
856        newInternalConfig.lastUpdateUid = uid;
857        newInternalConfig.lastUpdateName = mContext.getPackageManager().getNameForUid(uid);
858        newInternalConfig.updateTime = createDebugTimeStampString(mClock.getWallClockMillis());
859
860        return newInternalConfig;
861    }
862
863    /**
864     * Add a network or update a network configuration to our database.
865     * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
866     * network configuration. Otherwise, the networkId should refer to an existing configuration.
867     *
868     * @param config provided WifiConfiguration object.
869     * @param uid    UID of the app requesting the network addition/deletion.
870     * @return NetworkUpdateResult object representing status of the update.
871     */
872    private NetworkUpdateResult addOrUpdateNetworkInternal(WifiConfiguration config, int uid) {
873        if (mVerboseLoggingEnabled) {
874            Log.v(TAG, "Adding/Updating network " + config.getPrintableSsid());
875        }
876        WifiConfiguration newInternalConfig = null;
877
878        // First check if we already have a network with the provided network id or configKey.
879        WifiConfiguration existingInternalConfig = getInternalConfiguredNetwork(config);
880        // No existing network found. So, potentially a network add.
881        if (existingInternalConfig == null) {
882            newInternalConfig = createNewInternalWifiConfigurationFromExternal(config, uid);
883            // Since the original config provided may have had an empty
884            // {@link WifiConfiguration#allowedKeyMgmt} field, check again if we already have a
885            // network with the the same configkey.
886            existingInternalConfig = getInternalConfiguredNetwork(newInternalConfig.configKey());
887        }
888        // Existing network found. So, a network update.
889        if (existingInternalConfig != null) {
890            // Check for the app's permission before we let it update this network.
891            if (!canModifyNetwork(existingInternalConfig, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
892                Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
893                        + config.configKey());
894                return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
895            }
896            newInternalConfig =
897                    updateExistingInternalWifiConfigurationFromExternal(
898                            existingInternalConfig, config, uid);
899        }
900
901        // Update the keys for enterprise networks.
902        if (config.enterpriseConfig != null
903                && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
904            if (!(mWifiKeyStore.updateNetworkKeys(newInternalConfig, existingInternalConfig))) {
905                return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
906            }
907        }
908
909        boolean newNetwork = (existingInternalConfig == null);
910        // Reset the |hasEverConnected| flag if the credential parameters changed in this update.
911        boolean hasCredentialChanged =
912                newNetwork || WifiConfigurationUtil.hasCredentialChanged(
913                        existingInternalConfig, newInternalConfig);
914        if (hasCredentialChanged) {
915            newInternalConfig.getNetworkSelectionStatus().setHasEverConnected(false);
916        }
917
918        // Add it to our internal map. This will replace any existing network configuration for
919        // updates.
920        mConfiguredNetworks.put(newInternalConfig);
921
922        if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
923            if (mVerboseLoggingEnabled) {
924                Log.v(TAG, "Removed from ephemeral blacklist: " + config.SSID);
925            }
926        }
927
928        // Stage the backup of the SettingsProvider package which backs this up.
929        mBackupManagerProxy.notifyDataChanged();
930
931        // This is needed to inform IpManager about any IP configuration changes.
932        boolean hasIpChanged =
933                newNetwork || WifiConfigurationUtil.hasIpChanged(
934                        existingInternalConfig, newInternalConfig);
935        boolean hasProxyChanged =
936                newNetwork || WifiConfigurationUtil.hasProxyChanged(
937                        existingInternalConfig, newInternalConfig);
938        NetworkUpdateResult result = new NetworkUpdateResult(hasIpChanged, hasProxyChanged);
939        result.setIsNewNetwork(newNetwork);
940        result.setNetworkId(newInternalConfig.networkId);
941
942        localLog("addOrUpdateNetworkInternal: added/updated config."
943                + " netId=" + newInternalConfig.networkId
944                + " configKey=" + newInternalConfig.configKey()
945                + " uid=" + Integer.toString(newInternalConfig.creatorUid)
946                + " name=" + newInternalConfig.creatorName);
947        return result;
948    }
949
950    /**
951     * Add a network or update a network configuration to our database.
952     * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
953     * network configuration. Otherwise, the networkId should refer to an existing configuration.
954     *
955     * @param config provided WifiConfiguration object.
956     * @param uid    UID of the app requesting the network addition/modification.
957     * @return NetworkUpdateResult object representing status of the update.
958     */
959    public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid) {
960        if (!doesUidBelongToCurrentUser(uid)) {
961            Log.e(TAG, "UID " + uid + " not visible to the current user");
962            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
963        }
964        if (config == null) {
965            Log.e(TAG, "Cannot add/update network with null config");
966            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
967        }
968        NetworkUpdateResult result = addOrUpdateNetworkInternal(config, uid);
969        if (!result.isSuccess()) {
970            Log.e(TAG, "Failed to add/update network " + config.getPrintableSsid());
971            return result;
972        }
973        WifiConfiguration newConfig = getInternalConfiguredNetwork(result.getNetworkId());
974        sendConfiguredNetworkChangedBroadcast(
975                newConfig,
976                result.isNewNetwork()
977                        ? WifiManager.CHANGE_REASON_ADDED
978                        : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
979        // Unless the added network is ephemeral, persist the network update/addition.
980        if (!config.ephemeral) {
981            saveToStore(true);
982        }
983        return result;
984    }
985
986    /**
987     * Removes the specified network configuration from our database.
988     *
989     * @param config provided WifiConfiguration object.
990     * @return true if successful, false otherwise.
991     */
992    private boolean removeNetworkInternal(WifiConfiguration config) {
993        if (mVerboseLoggingEnabled) {
994            Log.v(TAG, "Removing network " + config.getPrintableSsid());
995        }
996        // Remove any associated enterprise keys.
997        if (config.enterpriseConfig != null
998                && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
999            mWifiKeyStore.removeKeys(config.enterpriseConfig);
1000        }
1001
1002        removeConnectChoiceFromAllNetworks(config.configKey());
1003        mConfiguredNetworks.remove(config.networkId);
1004        mScanDetailCaches.remove(config.networkId);
1005        // Stage the backup of the SettingsProvider package which backs this up.
1006        mBackupManagerProxy.notifyDataChanged();
1007
1008        localLog("removeNetworkInternal: removed config."
1009                + " netId=" + config.networkId
1010                + " configKey=" + config.configKey());
1011        return true;
1012    }
1013
1014    /**
1015     * Removes the specified network configuration from our database.
1016     *
1017     * @param networkId network ID of the provided network.
1018     * @param uid       UID of the app requesting the network deletion.
1019     * @return true if successful, false otherwise.
1020     */
1021    public boolean removeNetwork(int networkId, int uid) {
1022        if (!doesUidBelongToCurrentUser(uid)) {
1023            Log.e(TAG, "UID " + uid + " not visible to the current user");
1024            return false;
1025        }
1026        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1027        if (config == null) {
1028            return false;
1029        }
1030        if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
1031            Log.e(TAG, "UID " + uid + " does not have permission to delete configuration "
1032                    + config.configKey());
1033            return false;
1034        }
1035        if (!removeNetworkInternal(config)) {
1036            Log.e(TAG, "Failed to remove network " + config.getPrintableSsid());
1037            return false;
1038        }
1039        if (networkId == mLastSelectedNetworkId) {
1040            clearLastSelectedNetwork();
1041        }
1042        sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
1043        // Unless the removed network is ephemeral, persist the network removal.
1044        if (!config.ephemeral) {
1045            saveToStore(true);
1046        }
1047        return true;
1048    }
1049
1050    /**
1051     * Remove all networks associated with an application.
1052     *
1053     * @param app Application info of the package of networks to remove.
1054     * @return true if all networks removed successfully, false otherwise
1055     */
1056    public boolean removeNetworksForApp(ApplicationInfo app) {
1057        if (app == null || app.packageName == null) {
1058            return false;
1059        }
1060        Log.d(TAG, "Remove all networks for app " + app);
1061        boolean success = true;
1062        WifiConfiguration[] copiedConfigs =
1063                mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1064        for (WifiConfiguration config : copiedConfigs) {
1065            if (app.uid != config.creatorUid || !app.packageName.equals(config.creatorName)) {
1066                continue;
1067            }
1068            localLog("Removing network " + config.SSID
1069                    + ", application \"" + app.packageName + "\" uninstalled"
1070                    + " from user " + UserHandle.getUserId(app.uid));
1071            success &= removeNetwork(config.networkId, app.uid);
1072        }
1073        return success;
1074    }
1075
1076    /**
1077     * Remove all networks associated with a user.
1078     *
1079     * @param userId The identifier of the user which is being removed.
1080     * @return true if all networks removed successfully, false otherwise
1081     */
1082    boolean removeNetworksForUser(int userId) {
1083        Log.d(TAG, "Remove all networks for user " + userId);
1084        boolean success = true;
1085        WifiConfiguration[] copiedConfigs =
1086                mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1087        for (WifiConfiguration config : copiedConfigs) {
1088            if (userId != UserHandle.getUserId(config.creatorUid)) {
1089                continue;
1090            }
1091            success &= removeNetwork(config.networkId, config.creatorUid);
1092            localLog("Removing network " + config.SSID + ", user " + userId + " removed");
1093        }
1094        return success;
1095    }
1096
1097    /**
1098     * Helper method to mark a network enabled for network selection.
1099     */
1100    private void setNetworkSelectionEnabled(NetworkSelectionStatus status) {
1101        status.setNetworkSelectionStatus(
1102                NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
1103        status.setDisableTime(
1104                NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1105        status.setNetworkSelectionDisableReason(NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1106
1107        // Clear out all the disable reason counters.
1108        status.clearDisableReasonCounter();
1109    }
1110
1111    /**
1112     * Helper method to mark a network temporarily disabled for network selection.
1113     */
1114    private void setNetworkSelectionTemporarilyDisabled(
1115            NetworkSelectionStatus status, int disableReason) {
1116        status.setNetworkSelectionStatus(
1117                NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
1118        // Only need a valid time filled in for temporarily disabled networks.
1119        status.setDisableTime(mClock.getElapsedSinceBootMillis());
1120        status.setNetworkSelectionDisableReason(disableReason);
1121    }
1122
1123    /**
1124     * Helper method to mark a network permanently disabled for network selection.
1125     */
1126    private void setNetworkSelectionPermanentlyDisabled(
1127            NetworkSelectionStatus status, int disableReason) {
1128        status.setNetworkSelectionStatus(
1129                NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
1130        status.setDisableTime(
1131                NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1132        status.setNetworkSelectionDisableReason(disableReason);
1133    }
1134
1135    /**
1136     * Helper method to set the publicly exposed status for the network and send out the network
1137     * status change broadcast.
1138     */
1139    private void setNetworkStatus(WifiConfiguration config, int status) {
1140        config.status = status;
1141        sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1142    }
1143
1144    /**
1145     * Sets a network's status (both internal and public) according to the update reason and
1146     * its current state.
1147     *
1148     * This updates the network's {@link WifiConfiguration#mNetworkSelectionStatus} field and the
1149     * public {@link WifiConfiguration#status} field if the network is either enabled or
1150     * permanently disabled.
1151     *
1152     * @param config network to be updated.
1153     * @param reason reason code for update.
1154     * @return true if the input configuration has been updated, false otherwise.
1155     */
1156    private boolean setNetworkSelectionStatus(WifiConfiguration config, int reason) {
1157        NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1158        if (reason < 0 || reason >= NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX) {
1159            Log.e(TAG, "Invalid Network disable reason " + reason);
1160            return false;
1161        }
1162        if (reason == NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
1163            setNetworkSelectionEnabled(networkStatus);
1164            setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
1165        } else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
1166            setNetworkSelectionTemporarilyDisabled(networkStatus, reason);
1167        } else {
1168            setNetworkSelectionPermanentlyDisabled(networkStatus, reason);
1169            setNetworkStatus(config, WifiConfiguration.Status.DISABLED);
1170        }
1171        localLog("setNetworkSelectionStatus: configKey=" + config.configKey()
1172                + " networkStatus=" + networkStatus.getNetworkStatusString() + " disableReason="
1173                + networkStatus.getNetworkDisableReasonString() + " at="
1174                + createDebugTimeStampString(mClock.getWallClockMillis()));
1175        saveToStore(false);
1176        return true;
1177    }
1178
1179    /**
1180     * Update a network's status (both internal and public) according to the update reason and
1181     * its current state.
1182     *
1183     * @param config network to be updated.
1184     * @param reason reason code for update.
1185     * @return true if the input configuration has been updated, false otherwise.
1186     */
1187    private boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) {
1188        NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1189        if (reason != NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
1190            networkStatus.incrementDisableReasonCounter(reason);
1191            // For network disable reasons, we should only update the status if we cross the
1192            // threshold.
1193            int disableReasonCounter = networkStatus.getDisableReasonCounter(reason);
1194            int disableReasonThreshold = NETWORK_SELECTION_DISABLE_THRESHOLD[reason];
1195            if (disableReasonCounter < disableReasonThreshold) {
1196                if (mVerboseLoggingEnabled) {
1197                    Log.v(TAG, "Disable counter for network " + config.getPrintableSsid()
1198                            + " for reason "
1199                            + NetworkSelectionStatus.getNetworkDisableReasonString(reason) + " is "
1200                            + networkStatus.getDisableReasonCounter(reason) + " and threshold is "
1201                            + disableReasonThreshold);
1202                }
1203                return true;
1204            }
1205        }
1206        return setNetworkSelectionStatus(config, reason);
1207    }
1208
1209    /**
1210     * Update a network's status (both internal and public) according to the update reason and
1211     * its current state.
1212     *
1213     * Each network has 2 status:
1214     * 1. NetworkSelectionStatus: This is internal selection status of the network. This is used
1215     * for temporarily disabling a network for Network Selector.
1216     * 2. Status: This is the exposed status for a network. This is mostly set by
1217     * the public API's {@link WifiManager#enableNetwork(int, boolean)} &
1218     * {@link WifiManager#disableNetwork(int)}.
1219     *
1220     * @param networkId network ID of the network that needs the update.
1221     * @param reason    reason to update the network.
1222     * @return true if the input configuration has been updated, false otherwise.
1223     */
1224    public boolean updateNetworkSelectionStatus(int networkId, int reason) {
1225        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1226        if (config == null) {
1227            return false;
1228        }
1229        return updateNetworkSelectionStatus(config, reason);
1230    }
1231
1232    /**
1233     * Attempt to re-enable a network for network selection, if this network was either:
1234     * a) Previously temporarily disabled, but its disable timeout has expired, or
1235     * b) Previously disabled because of a user switch, but is now visible to the current
1236     * user.
1237     *
1238     * @param config configuration for the network to be re-enabled for network selection. The
1239     *               network corresponding to the config must be visible to the current user.
1240     * @return true if the network identified by {@param config} was re-enabled for qualified
1241     * network selection, false otherwise.
1242     */
1243    private boolean tryEnableNetwork(WifiConfiguration config) {
1244        NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1245        if (networkStatus.isNetworkTemporaryDisabled()) {
1246            long timeDifferenceMs =
1247                    mClock.getElapsedSinceBootMillis() - networkStatus.getDisableTime();
1248            int disableReason = networkStatus.getNetworkSelectionDisableReason();
1249            long disableTimeoutMs = NETWORK_SELECTION_DISABLE_TIMEOUT_MS[disableReason];
1250            if (timeDifferenceMs >= disableTimeoutMs) {
1251                return updateNetworkSelectionStatus(
1252                        config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1253            }
1254        } else if (networkStatus.isDisabledByReason(
1255                NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH)) {
1256            return updateNetworkSelectionStatus(
1257                    config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1258        }
1259        return false;
1260    }
1261
1262    /**
1263     * Attempt to re-enable a network for network selection, if this network was either:
1264     * a) Previously temporarily disabled, but its disable timeout has expired, or
1265     * b) Previously disabled because of a user switch, but is now visible to the current
1266     * user.
1267     *
1268     * @param networkId the id of the network to be checked for possible unblock (due to timeout)
1269     * @return true if the network identified by {@param networkId} was re-enabled for qualified
1270     * network selection, false otherwise.
1271     */
1272    public boolean tryEnableNetwork(int networkId) {
1273        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1274        if (config == null) {
1275            return false;
1276        }
1277        return tryEnableNetwork(config);
1278    }
1279
1280    /**
1281     * Enable a network using the public {@link WifiManager#enableNetwork(int, boolean)} API.
1282     *
1283     * @param networkId     network ID of the network that needs the update.
1284     * @param disableOthers Whether to disable all other networks or not. This is used to indicate
1285     *                      that the app requested connection to a specific network.
1286     * @param uid           uid of the app requesting the update.
1287     * @return true if it succeeds, false otherwise
1288     */
1289    public boolean enableNetwork(int networkId, boolean disableOthers, int uid) {
1290        if (mVerboseLoggingEnabled) {
1291            Log.v(TAG, "Enabling network " + networkId + " (disableOthers " + disableOthers + ")");
1292        }
1293        if (!doesUidBelongToCurrentUser(uid)) {
1294            Log.e(TAG, "UID " + uid + " not visible to the current user");
1295            return false;
1296        }
1297        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1298        if (config == null) {
1299            return false;
1300        }
1301        if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
1302            Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1303                    + config.configKey());
1304            return false;
1305        }
1306        if (!updateNetworkSelectionStatus(
1307                networkId, WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE)) {
1308            return false;
1309        }
1310        if (disableOthers) {
1311            setLastSelectedNetwork(networkId);
1312        }
1313        return true;
1314    }
1315
1316    /**
1317     * Disable a network using the public {@link WifiManager#disableNetwork(int)} API.
1318     *
1319     * @param networkId network ID of the network that needs the update.
1320     * @param uid       uid of the app requesting the update.
1321     * @return true if it succeeds, false otherwise
1322     */
1323    public boolean disableNetwork(int networkId, int uid) {
1324        if (mVerboseLoggingEnabled) {
1325            Log.v(TAG, "Disabling network " + networkId);
1326        }
1327        if (!doesUidBelongToCurrentUser(uid)) {
1328            Log.e(TAG, "UID " + uid + " not visible to the current user");
1329            return false;
1330        }
1331        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1332        if (config == null) {
1333            return false;
1334        }
1335        if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
1336            Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1337                    + config.configKey());
1338            return false;
1339        }
1340        if (!updateNetworkSelectionStatus(
1341                networkId, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)) {
1342            return false;
1343        }
1344        if (networkId == mLastSelectedNetworkId) {
1345            clearLastSelectedNetwork();
1346        }
1347        return true;
1348    }
1349
1350    /**
1351     * Checks if the |uid| has the necessary permission to force a connection to a network
1352     * and updates the last connected UID for the provided configuration.
1353     *
1354     * @param networkId network ID corresponding to the network.
1355     * @param uid       uid of the app requesting the connection.
1356     * @return true if |uid| has the necessary permission to trigger explicit connection to the
1357     * network, false otherwise.
1358     * Note: This returns true only for the system settings/sysui app which holds the
1359     * {@link android.Manifest.permission#OVERRIDE_WIFI_CONFIG} permission. We don't want to let
1360     * any other app force connection to a network.
1361     */
1362    public boolean checkAndUpdateLastConnectUid(int networkId, int uid) {
1363        if (mVerboseLoggingEnabled) {
1364            Log.v(TAG, "Update network last connect UID for " + networkId);
1365        }
1366        if (!doesUidBelongToCurrentUser(uid)) {
1367            Log.e(TAG, "UID " + uid + " not visible to the current user");
1368            return false;
1369        }
1370        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1371        if (config == null) {
1372            return false;
1373        }
1374        if (!canModifyNetwork(config, uid, ALLOW_LOCKDOWN_CHECK_BYPASS)) {
1375            Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1376                    + config.configKey());
1377            return false;
1378        }
1379        config.lastConnectUid = uid;
1380        return true;
1381    }
1382
1383    /**
1384     * Updates a network configuration after a successful connection to it.
1385     *
1386     * This method updates the following WifiConfiguration elements:
1387     * 1. Set the |lastConnected| timestamp.
1388     * 2. Increment |numAssociation| counter.
1389     * 3. Clear the disable reason counters in the associated |NetworkSelectionStatus|.
1390     * 4. Set the hasEverConnected| flag in the associated |NetworkSelectionStatus|.
1391     * 5. Sets the status of network as |CURRENT|.
1392     *
1393     * @param networkId network ID corresponding to the network.
1394     * @return true if the network was found, false otherwise.
1395     */
1396    public boolean updateNetworkAfterConnect(int networkId) {
1397        if (mVerboseLoggingEnabled) {
1398            Log.v(TAG, "Update network after connect for " + networkId);
1399        }
1400        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1401        if (config == null) {
1402            return false;
1403        }
1404        config.lastConnected = mClock.getWallClockMillis();
1405        config.numAssociation++;
1406        config.getNetworkSelectionStatus().clearDisableReasonCounter();
1407        config.getNetworkSelectionStatus().setHasEverConnected(true);
1408        setNetworkStatus(config, WifiConfiguration.Status.CURRENT);
1409        saveToStore(false);
1410        return true;
1411    }
1412
1413    /**
1414     * Updates a network configuration after disconnection from it.
1415     *
1416     * This method updates the following WifiConfiguration elements:
1417     * 1. Set the |lastDisConnected| timestamp.
1418     * 2. Sets the status of network back to |ENABLED|.
1419     *
1420     * @param networkId network ID corresponding to the network.
1421     * @return true if the network was found, false otherwise.
1422     */
1423    public boolean updateNetworkAfterDisconnect(int networkId) {
1424        if (mVerboseLoggingEnabled) {
1425            Log.v(TAG, "Update network after disconnect for " + networkId);
1426        }
1427        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1428        if (config == null) {
1429            return false;
1430        }
1431        config.lastDisconnected = mClock.getWallClockMillis();
1432        // If the network hasn't been disabled, mark it back as
1433        // enabled after disconnection.
1434        if (config.status == WifiConfiguration.Status.CURRENT) {
1435            setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
1436        }
1437        saveToStore(false);
1438        return true;
1439    }
1440
1441    /**
1442     * Set default GW MAC address for the provided network.
1443     *
1444     * @param networkId  network ID corresponding to the network.
1445     * @param macAddress MAC address of the gateway to be set.
1446     * @return true if the network was found, false otherwise.
1447     */
1448    public boolean setNetworkDefaultGwMacAddress(int networkId, String macAddress) {
1449        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1450        if (config == null) {
1451            return false;
1452        }
1453        config.defaultGwMacAddress = macAddress;
1454        return true;
1455    }
1456
1457    /**
1458     * Clear the {@link NetworkSelectionStatus#mCandidate},
1459     * {@link NetworkSelectionStatus#mCandidateScore} &
1460     * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
1461     *
1462     * This is invoked by Network Selector at the start of every selection procedure to clear all
1463     * configured networks' scan-result-candidates.
1464     *
1465     * @param networkId network ID corresponding to the network.
1466     * @return true if the network was found, false otherwise.
1467     */
1468    public boolean clearNetworkCandidateScanResult(int networkId) {
1469        if (mVerboseLoggingEnabled) {
1470            Log.v(TAG, "Clear network candidate scan result for " + networkId);
1471        }
1472        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1473        if (config == null) {
1474            return false;
1475        }
1476        config.getNetworkSelectionStatus().setCandidate(null);
1477        config.getNetworkSelectionStatus().setCandidateScore(Integer.MIN_VALUE);
1478        config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(false);
1479        return true;
1480    }
1481
1482    /**
1483     * Set the {@link NetworkSelectionStatus#mCandidate},
1484     * {@link NetworkSelectionStatus#mCandidateScore} &
1485     * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
1486     *
1487     * This is invoked by Network Selector when it sees a network during network selection procedure
1488     * to set the scan result candidate.
1489     *
1490     * @param networkId  network ID corresponding to the network.
1491     * @param scanResult Candidate ScanResult associated with this network.
1492     * @param score      Score assigned to the candidate.
1493     * @return true if the network was found, false otherwise.
1494     */
1495    public boolean setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score) {
1496        if (mVerboseLoggingEnabled) {
1497            Log.v(TAG, "Set network candidate scan result " + scanResult + " for " + networkId);
1498        }
1499        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1500        if (config == null) {
1501            return false;
1502        }
1503        config.getNetworkSelectionStatus().setCandidate(scanResult);
1504        config.getNetworkSelectionStatus().setCandidateScore(score);
1505        config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(true);
1506        return true;
1507    }
1508
1509    /**
1510     * Iterate through all the saved networks and remove the provided configuration from the
1511     * {@link NetworkSelectionStatus#mConnectChoice} from them.
1512     *
1513     * This is invoked when a network is removed from our records.
1514     *
1515     * @param connectChoiceConfigKey ConfigKey corresponding to the network that is being removed.
1516     */
1517    private void removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey) {
1518        if (mVerboseLoggingEnabled) {
1519            Log.v(TAG, "Removing connect choice from all networks " + connectChoiceConfigKey);
1520        }
1521        if (connectChoiceConfigKey == null) {
1522            return;
1523        }
1524        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
1525            WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1526            String connectChoice = status.getConnectChoice();
1527            if (TextUtils.equals(connectChoice, connectChoiceConfigKey)) {
1528                Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID
1529                        + " : " + config.networkId);
1530                clearNetworkConnectChoice(config.networkId);
1531            }
1532        }
1533    }
1534
1535    /**
1536     * Clear the {@link NetworkSelectionStatus#mConnectChoice} &
1537     * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network.
1538     *
1539     * @param networkId network ID corresponding to the network.
1540     * @return true if the network was found, false otherwise.
1541     */
1542    public boolean clearNetworkConnectChoice(int networkId) {
1543        if (mVerboseLoggingEnabled) {
1544            Log.v(TAG, "Clear network connect choice for " + networkId);
1545        }
1546        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1547        if (config == null) {
1548            return false;
1549        }
1550        config.getNetworkSelectionStatus().setConnectChoice(null);
1551        config.getNetworkSelectionStatus().setConnectChoiceTimestamp(
1552                NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1553        saveToStore(false);
1554        return true;
1555    }
1556
1557    /**
1558     * Set the {@link NetworkSelectionStatus#mConnectChoice} &
1559     * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network.
1560     *
1561     * This is invoked by Network Selector when the user overrides the currently connected network
1562     * choice.
1563     *
1564     * @param networkId              network ID corresponding to the network.
1565     * @param connectChoiceConfigKey ConfigKey corresponding to the network which was chosen over
1566     *                               this network.
1567     * @param timestamp              timestamp at which the choice was made.
1568     * @return true if the network was found, false otherwise.
1569     */
1570    public boolean setNetworkConnectChoice(
1571            int networkId, String connectChoiceConfigKey, long timestamp) {
1572        if (mVerboseLoggingEnabled) {
1573            Log.v(TAG, "Set network connect choice " + connectChoiceConfigKey + " for " + networkId);
1574        }
1575        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1576        if (config == null) {
1577            return false;
1578        }
1579        config.getNetworkSelectionStatus().setConnectChoice(connectChoiceConfigKey);
1580        config.getNetworkSelectionStatus().setConnectChoiceTimestamp(timestamp);
1581        saveToStore(false);
1582        return true;
1583    }
1584
1585    /**
1586     * Increments the number of no internet access reports in the provided network.
1587     *
1588     * @param networkId network ID corresponding to the network.
1589     * @return true if the network was found, false otherwise.
1590     */
1591    public boolean incrementNetworkNoInternetAccessReports(int networkId) {
1592        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1593        if (config == null) {
1594            return false;
1595        }
1596        config.numNoInternetAccessReports++;
1597        return true;
1598    }
1599
1600    /**
1601     * Sets the internet access is validated or not in the provided network.
1602     *
1603     * @param networkId network ID corresponding to the network.
1604     * @param validated Whether access is validated or not.
1605     * @return true if the network was found, false otherwise.
1606     */
1607    public boolean setNetworkValidatedInternetAccess(int networkId, boolean validated) {
1608        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1609        if (config == null) {
1610            return false;
1611        }
1612        config.validatedInternetAccess = validated;
1613        config.numNoInternetAccessReports = 0;
1614        saveToStore(false);
1615        return true;
1616    }
1617
1618    /**
1619     * Sets whether the internet access is expected or not in the provided network.
1620     *
1621     * @param networkId network ID corresponding to the network.
1622     * @param expected  Whether access is expected or not.
1623     * @return true if the network was found, false otherwise.
1624     */
1625    public boolean setNetworkNoInternetAccessExpected(int networkId, boolean expected) {
1626        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1627        if (config == null) {
1628            return false;
1629        }
1630        config.noInternetAccessExpected = expected;
1631        return true;
1632    }
1633
1634    /**
1635     * Helper method to clear out the {@link #mNextNetworkId} user/app network selection. This
1636     * is done when either the corresponding network is either removed or disabled.
1637     */
1638    private void clearLastSelectedNetwork() {
1639        if (mVerboseLoggingEnabled) {
1640            Log.v(TAG, "Clearing last selected network");
1641        }
1642        mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
1643        mLastSelectedTimeStamp = NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
1644    }
1645
1646    /**
1647     * Helper method to mark a network as the last selected one by an app/user. This is set
1648     * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
1649     * This is used by network selector to assign a special bonus during network selection.
1650     */
1651    private void setLastSelectedNetwork(int networkId) {
1652        if (mVerboseLoggingEnabled) {
1653            Log.v(TAG, "Setting last selected network to " + networkId);
1654        }
1655        mLastSelectedNetworkId = networkId;
1656        mLastSelectedTimeStamp = mClock.getElapsedSinceBootMillis();
1657    }
1658
1659    /**
1660     * Retrieve the network Id corresponding to the last network that was explicitly selected by
1661     * an app/user.
1662     *
1663     * @return network Id corresponding to the last selected network.
1664     */
1665    public int getLastSelectedNetwork() {
1666        return mLastSelectedNetworkId;
1667    }
1668
1669    /**
1670     * Retrieve the configKey corresponding to the last network that was explicitly selected by
1671     * an app/user.
1672     *
1673     * @return network Id corresponding to the last selected network.
1674     */
1675    public String getLastSelectedNetworkConfigKey() {
1676        if (mLastSelectedNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
1677            return "";
1678        }
1679        WifiConfiguration config = getInternalConfiguredNetwork(mLastSelectedNetworkId);
1680        if (config == null) {
1681            return "";
1682        }
1683        return config.configKey();
1684    }
1685
1686    /**
1687     * Retrieve the time stamp at which a network was explicitly selected by an app/user.
1688     *
1689     * @return timestamp in milliseconds from boot when this was set.
1690     */
1691    public long getLastSelectedTimeStamp() {
1692        return mLastSelectedTimeStamp;
1693    }
1694
1695    /**
1696     * Helper method to get the scan detail cache entry {@link #mScanDetailCaches} for the provided
1697     * network.
1698     *
1699     * @param networkId network ID corresponding to the network.
1700     * @return existing {@link ScanDetailCache} entry if one exists or null.
1701     */
1702    public ScanDetailCache getScanDetailCacheForNetwork(int networkId) {
1703        return mScanDetailCaches.get(networkId);
1704    }
1705
1706    /**
1707     * Helper method to get or create a scan detail cache entry {@link #mScanDetailCaches} for
1708     * the provided network.
1709     *
1710     * @param config configuration corresponding to the the network.
1711     * @return existing {@link ScanDetailCache} entry if one exists or a new instance created for
1712     * this network.
1713     */
1714    private ScanDetailCache getOrCreateScanDetailCacheForNetwork(WifiConfiguration config) {
1715        if (config == null) return null;
1716        ScanDetailCache cache = getScanDetailCacheForNetwork(config.networkId);
1717        if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
1718            cache = new ScanDetailCache(
1719                    config, SCAN_CACHE_ENTRIES_MAX_SIZE, SCAN_CACHE_ENTRIES_TRIM_SIZE);
1720            mScanDetailCaches.put(config.networkId, cache);
1721        }
1722        return cache;
1723    }
1724
1725    /**
1726     * Saves the provided ScanDetail into the corresponding scan detail cache entry
1727     * {@link #mScanDetailCaches} for the provided network.
1728     *
1729     * @param config     configuration corresponding to the the network.
1730     * @param scanDetail new scan detail instance to be saved into the cache.
1731     */
1732    private void saveToScanDetailCacheForNetwork(
1733            WifiConfiguration config, ScanDetail scanDetail) {
1734        ScanResult scanResult = scanDetail.getScanResult();
1735
1736        ScanDetailCache scanDetailCache = getOrCreateScanDetailCacheForNetwork(config);
1737        if (scanDetailCache == null) {
1738            Log.e(TAG, "Could not allocate scan cache for " + config.getPrintableSsid());
1739            return;
1740        }
1741
1742        // Adding a new BSSID
1743        ScanResult result = scanDetailCache.get(scanResult.BSSID);
1744        if (result != null) {
1745            // transfer the black list status
1746            scanResult.blackListTimestamp = result.blackListTimestamp;
1747            scanResult.numIpConfigFailures = result.numIpConfigFailures;
1748            scanResult.numConnection = result.numConnection;
1749        }
1750        if (config.ephemeral) {
1751            // For an ephemeral Wi-Fi config, the ScanResult should be considered
1752            // untrusted.
1753            scanResult.untrusted = true;
1754        }
1755
1756        // Add the scan detail to this network's scan detail cache.
1757        scanDetailCache.put(scanDetail);
1758
1759        // Since we added a scan result to this configuration, re-attempt linking.
1760        // TODO: Do we really need to do this after every scan result?
1761        attemptNetworkLinking(config);
1762    }
1763
1764    /**
1765     * Retrieves a saved network corresponding to the provided scan detail if one exists.
1766     *
1767     * @param scanDetail ScanDetail instance  to use for looking up the network.
1768     * @return WifiConfiguration object representing the network corresponding to the scanDetail,
1769     * null if none exists.
1770     */
1771    private WifiConfiguration getSavedNetworkForScanDetail(ScanDetail scanDetail) {
1772        ScanResult scanResult = scanDetail.getScanResult();
1773        if (scanResult == null) {
1774            Log.e(TAG, "No scan result found in scan detail");
1775            return null;
1776        }
1777        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
1778            if (ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config)) {
1779                if (mVerboseLoggingEnabled) {
1780                    Log.v(TAG, "getSavedNetworkFromScanDetail Found " + config.configKey()
1781                            + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]");
1782                }
1783                return config;
1784            }
1785        }
1786        return null;
1787    }
1788
1789    /**
1790     * Retrieves a saved network corresponding to the provided scan detail if one exists and caches
1791     * the provided |scanDetail| into the corresponding scan detail cache entry
1792     * {@link #mScanDetailCaches} for the retrieved network.
1793     *
1794     * @param scanDetail input a scanDetail from the scan result
1795     * @return WifiConfiguration object representing the network corresponding to the scanDetail,
1796     * null if none exists.
1797     */
1798    public WifiConfiguration getSavedNetworkForScanDetailAndCache(ScanDetail scanDetail) {
1799        WifiConfiguration network = getSavedNetworkForScanDetail(scanDetail);
1800        if (network == null) {
1801            return null;
1802        }
1803        saveToScanDetailCacheForNetwork(network, scanDetail);
1804        // Cache DTIM values parsed from the beacon frame Traffic Indication Map (TIM)
1805        // Information Element (IE), into the associated WifiConfigurations. Most of the
1806        // time there is no TIM IE in the scan result (Probe Response instead of Beacon
1807        // Frame), these scanResult DTIM's are negative and ignored.
1808        // Used for metrics collection.
1809        if (scanDetail.getNetworkDetail() != null
1810                && scanDetail.getNetworkDetail().getDtimInterval() > 0) {
1811            network.dtimInterval = scanDetail.getNetworkDetail().getDtimInterval();
1812        }
1813        return createExternalWifiConfiguration(network, true);
1814    }
1815
1816    /**
1817     * Update the scan detail cache associated with current connected network with latest
1818     * RSSI value in the provided WifiInfo.
1819     * This is invoked when we get an RSSI poll update after connection.
1820     *
1821     * @param info WifiInfo instance pointing to the current connected network.
1822     */
1823    public void updateScanDetailCacheFromWifiInfo(WifiInfo info) {
1824        WifiConfiguration config = getInternalConfiguredNetwork(info.getNetworkId());
1825        ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(info.getNetworkId());
1826        if (config != null && scanDetailCache != null) {
1827            ScanDetail scanDetail = scanDetailCache.getScanDetail(info.getBSSID());
1828            if (scanDetail != null) {
1829                ScanResult result = scanDetail.getScanResult();
1830                long previousSeen = result.seen;
1831                int previousRssi = result.level;
1832                // Update the scan result
1833                scanDetail.setSeen();
1834                result.level = info.getRssi();
1835                // Average the RSSI value
1836                result.averageRssi(previousRssi, previousSeen, SCAN_RESULT_MAXIMUM_AGE_MS);
1837                if (mVerboseLoggingEnabled) {
1838                    Log.v(TAG, "Updating scan detail cache freq=" + result.frequency
1839                            + " BSSID=" + result.BSSID
1840                            + " RSSI=" + result.level
1841                            + " for " + config.configKey());
1842                }
1843            }
1844        }
1845    }
1846
1847    /**
1848     * Helper method to check if the 2 provided networks can be linked or not.
1849     * Networks are considered for linking if:
1850     * 1. Share the same GW MAC address.
1851     * 2. Scan results for the networks have AP's with MAC address which differ only in the last
1852     * nibble.
1853     *
1854     * @param network1         WifiConfiguration corresponding to network 1.
1855     * @param network2         WifiConfiguration corresponding to network 2.
1856     * @param scanDetailCache1 ScanDetailCache entry for network 1.
1857     * @param scanDetailCache1 ScanDetailCache entry for network 2.
1858     * @return true if the networks should be linked, false if the networks should be unlinked.
1859     */
1860    private boolean shouldNetworksBeLinked(
1861            WifiConfiguration network1, WifiConfiguration network2,
1862            ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2) {
1863        // TODO (b/30706406): Link networks only with same passwords if the
1864        // |mOnlyLinkSameCredentialConfigurations| flag is set.
1865        if (mOnlyLinkSameCredentialConfigurations) {
1866            if (!TextUtils.equals(network1.preSharedKey, network2.preSharedKey)) {
1867                if (mVerboseLoggingEnabled) {
1868                    Log.v(TAG, "shouldNetworksBeLinked unlink due to password mismatch");
1869                }
1870                return false;
1871            }
1872        }
1873        if (network1.defaultGwMacAddress != null && network2.defaultGwMacAddress != null) {
1874            // If both default GW are known, link only if they are equal
1875            if (network1.defaultGwMacAddress.equals(network2.defaultGwMacAddress)) {
1876                if (mVerboseLoggingEnabled) {
1877                    Log.v(TAG, "shouldNetworksBeLinked link due to same gw " + network2.SSID
1878                            + " and " + network1.SSID + " GW " + network1.defaultGwMacAddress);
1879                }
1880                return true;
1881            }
1882        } else {
1883            // We do not know BOTH default gateways hence we will try to link
1884            // hoping that WifiConfigurations are indeed behind the same gateway.
1885            // once both WifiConfiguration have been tried and thus once both default gateways
1886            // are known we will revisit the choice of linking them.
1887            if (scanDetailCache1 != null && scanDetailCache2 != null) {
1888                for (String abssid : scanDetailCache1.keySet()) {
1889                    for (String bbssid : scanDetailCache2.keySet()) {
1890                        if (abssid.regionMatches(
1891                                true, 0, bbssid, 0, LINK_CONFIGURATION_BSSID_MATCH_LENGTH)) {
1892                            // If first 16 ASCII characters of BSSID matches,
1893                            // we assume this is a DBDC.
1894                            if (mVerboseLoggingEnabled) {
1895                                Log.v(TAG, "shouldNetworksBeLinked link due to DBDC BSSID match "
1896                                        + network2.SSID + " and " + network1.SSID
1897                                        + " bssida " + abssid + " bssidb " + bbssid);
1898                            }
1899                            return true;
1900                        }
1901                    }
1902                }
1903            }
1904        }
1905        return false;
1906    }
1907
1908    /**
1909     * Helper methods to link 2 networks together.
1910     *
1911     * @param network1 WifiConfiguration corresponding to network 1.
1912     * @param network2 WifiConfiguration corresponding to network 2.
1913     */
1914    private void linkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
1915        if (mVerboseLoggingEnabled) {
1916            Log.v(TAG, "linkNetworks will link " + network2.configKey()
1917                    + " and " + network1.configKey());
1918        }
1919        if (network2.linkedConfigurations == null) {
1920            network2.linkedConfigurations = new HashMap<>();
1921        }
1922        if (network1.linkedConfigurations == null) {
1923            network1.linkedConfigurations = new HashMap<>();
1924        }
1925        // TODO (b/30638473): This needs to become a set instead of map, but it will need
1926        // public interface changes and need some migration of existing store data.
1927        network2.linkedConfigurations.put(network1.configKey(), 1);
1928        network1.linkedConfigurations.put(network2.configKey(), 1);
1929    }
1930
1931    /**
1932     * Helper methods to unlink 2 networks from each other.
1933     *
1934     * @param network1 WifiConfiguration corresponding to network 1.
1935     * @param network2 WifiConfiguration corresponding to network 2.
1936     */
1937    private void unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
1938        if (network2.linkedConfigurations != null
1939                && (network2.linkedConfigurations.get(network1.configKey()) != null)) {
1940            if (mVerboseLoggingEnabled) {
1941                Log.v(TAG, "unlinkNetworks un-link " + network1.configKey()
1942                        + " from " + network2.configKey());
1943            }
1944            network2.linkedConfigurations.remove(network1.configKey());
1945        }
1946        if (network1.linkedConfigurations != null
1947                && (network1.linkedConfigurations.get(network2.configKey()) != null)) {
1948            if (mVerboseLoggingEnabled) {
1949                Log.v(TAG, "unlinkNetworks un-link " + network2.configKey()
1950                        + " from " + network1.configKey());
1951            }
1952            network1.linkedConfigurations.remove(network2.configKey());
1953        }
1954    }
1955
1956    /**
1957     * This method runs through all the saved networks and checks if the provided network can be
1958     * linked with any of them.
1959     *
1960     * @param config WifiConfiguration object corresponding to the network that needs to be
1961     *               checked for potential links.
1962     */
1963    private void attemptNetworkLinking(WifiConfiguration config) {
1964        // Only link WPA_PSK config.
1965        if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
1966            return;
1967        }
1968        ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId);
1969        // Ignore configurations with large number of BSSIDs.
1970        if (scanDetailCache != null
1971                && scanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
1972            return;
1973        }
1974        for (WifiConfiguration linkConfig : getInternalConfiguredNetworks()) {
1975            if (linkConfig.configKey().equals(config.configKey())) {
1976                continue;
1977            }
1978            if (linkConfig.ephemeral) {
1979                continue;
1980            }
1981            // Network Selector will be allowed to dynamically jump from a linked configuration
1982            // to another, hence only link configurations that have WPA_PSK security type.
1983            if (!linkConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
1984                continue;
1985            }
1986            ScanDetailCache linkScanDetailCache =
1987                    getScanDetailCacheForNetwork(linkConfig.networkId);
1988            // Ignore configurations with large number of BSSIDs.
1989            if (linkScanDetailCache != null
1990                    && linkScanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
1991                continue;
1992            }
1993            // Check if the networks should be linked/unlinked.
1994            if (shouldNetworksBeLinked(
1995                    config, linkConfig, scanDetailCache, linkScanDetailCache)) {
1996                linkNetworks(config, linkConfig);
1997            } else {
1998                unlinkNetworks(config, linkConfig);
1999            }
2000        }
2001    }
2002
2003    /**
2004     * Helper method to fetch list of channels for a network from the associated ScanResult's cache
2005     * and add it to the provided channel as long as the size of the set is less than
2006     * |maxChannelSetSize|.
2007     *
2008     * @param channelSet        Channel set holding all the channels for the network.
2009     * @param scanDetailCache   ScanDetailCache entry associated with the network.
2010     * @param nowInMillis       current timestamp to be used for age comparison.
2011     * @param ageInMillis       only consider scan details whose timestamps are earlier than this
2012     *                          value.
2013     * @param maxChannelSetSize Maximum number of channels to be added to the set.
2014     * @return false if the list is full, true otherwise.
2015     */
2016    private boolean addToChannelSetForNetworkFromScanDetailCache(
2017            Set<Integer> channelSet, ScanDetailCache scanDetailCache,
2018            long nowInMillis, long ageInMillis, int maxChannelSetSize) {
2019        if (scanDetailCache != null && scanDetailCache.size() > 0) {
2020            for (ScanDetail scanDetail : scanDetailCache.values()) {
2021                ScanResult result = scanDetail.getScanResult();
2022                boolean valid = (nowInMillis - result.seen) < ageInMillis;
2023                if (mVerboseLoggingEnabled) {
2024                    Log.v(TAG, "fetchChannelSetForNetwork has " + result.BSSID + " freq "
2025                            + result.frequency + " age " + (nowInMillis - result.seen)
2026                            + " ?=" + valid);
2027                }
2028                if (valid) {
2029                    channelSet.add(result.frequency);
2030                }
2031                if (channelSet.size() >= maxChannelSetSize) {
2032                    return false;
2033                }
2034            }
2035        }
2036        return true;
2037    }
2038
2039    /**
2040     * Retrieve a set of channels on which AP's for the provided network was seen using the
2041     * internal ScanResult's cache {@link #mScanDetailCaches}. This is used for initiating partial
2042     * scans for the currently connected network.
2043     *
2044     * @param networkId       network ID corresponding to the network.
2045     * @param ageInMillis     only consider scan details whose timestamps are earlier than this value.
2046     * @param homeChannelFreq frequency of the currently connected network.
2047     * @return Set containing the frequencies on which this network was found, null if the network
2048     * was not found or there are no associated scan details in the cache.
2049     */
2050    public Set<Integer> fetchChannelSetForNetworkForPartialScan(int networkId, long ageInMillis,
2051                                                                int homeChannelFreq) {
2052        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2053        if (config == null) {
2054            return null;
2055        }
2056        ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(networkId);
2057        if (scanDetailCache == null && config.linkedConfigurations == null) {
2058            Log.i(TAG, "No scan detail and linked configs associated with networkId " + networkId);
2059            return null;
2060        }
2061        if (mVerboseLoggingEnabled) {
2062            StringBuilder dbg = new StringBuilder();
2063            dbg.append("fetchChannelSetForNetworkForPartialScan ageInMillis ")
2064                    .append(ageInMillis)
2065                    .append(" for ")
2066                    .append(config.configKey())
2067                    .append(" max ")
2068                    .append(mMaxNumActiveChannelsForPartialScans);
2069            if (scanDetailCache != null) {
2070                dbg.append(" bssids " + scanDetailCache.size());
2071            }
2072            if (config.linkedConfigurations != null) {
2073                dbg.append(" linked " + config.linkedConfigurations.size());
2074            }
2075            Log.v(TAG, dbg.toString());
2076        }
2077        Set<Integer> channelSet = new HashSet<>();
2078
2079        // First add the currently connected network channel.
2080        if (homeChannelFreq > 0) {
2081            channelSet.add(homeChannelFreq);
2082            if (channelSet.size() >= mMaxNumActiveChannelsForPartialScans) {
2083                return channelSet;
2084            }
2085        }
2086
2087        long nowInMillis = mClock.getWallClockMillis();
2088
2089        // Then get channels for the network.
2090        if (!addToChannelSetForNetworkFromScanDetailCache(
2091                channelSet, scanDetailCache, nowInMillis, ageInMillis,
2092                mMaxNumActiveChannelsForPartialScans)) {
2093            return channelSet;
2094        }
2095
2096        // Lastly get channels for linked networks.
2097        if (config.linkedConfigurations != null) {
2098            for (String configKey : config.linkedConfigurations.keySet()) {
2099                WifiConfiguration linkedConfig = getInternalConfiguredNetwork(configKey);
2100                if (linkedConfig == null) {
2101                    continue;
2102                }
2103                ScanDetailCache linkedScanDetailCache =
2104                        getScanDetailCacheForNetwork(linkedConfig.networkId);
2105                if (!addToChannelSetForNetworkFromScanDetailCache(
2106                        channelSet, linkedScanDetailCache, nowInMillis, ageInMillis,
2107                        mMaxNumActiveChannelsForPartialScans)) {
2108                    break;
2109                }
2110            }
2111        }
2112        return channelSet;
2113    }
2114
2115    /**
2116     * Retrieves a list of all the saved networks before enabling disconnected/connected PNO.
2117     *
2118     * PNO network list sent to the firmware has limited size. If there are a lot of saved
2119     * networks, this list will be truncated and we might end up not sending the networks
2120     * with the highest chance of connecting to the firmware.
2121     * So, re-sort the network list based on the frequency of connection to those networks
2122     * and whether it was last seen in the scan results.
2123     *
2124     * TODO (b/30399964): Recalculate the list whenever network status changes.
2125     * @return list of networks with updated priorities.
2126     */
2127    public List<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList() {
2128        List<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>();
2129        List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
2130        // Remove any permanently disabled networks.
2131        Iterator<WifiConfiguration> iter = networks.iterator();
2132        while (iter.hasNext()) {
2133            WifiConfiguration config = iter.next();
2134            if (config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
2135                iter.remove();
2136            }
2137        }
2138        Collections.sort(networks, sScanListComparator);
2139        // Let's use the network list size - 1 as the highest priority and then go down from there.
2140        // So, the most frequently connected network has the highest priority now.
2141        int priority = networks.size() - 1;
2142        for (WifiConfiguration config : networks) {
2143            pnoList.add(WifiConfigurationUtil.createPnoNetwork(config, priority));
2144            priority--;
2145        }
2146        return pnoList;
2147    }
2148
2149    /**
2150     * Retrieves a list of all the saved hidden networks for scans.
2151     *
2152     * Hidden network list sent to the firmware has limited size. If there are a lot of saved
2153     * networks, this list will be truncated and we might end up not sending the networks
2154     * with the highest chance of connecting to the firmware.
2155     * So, re-sort the network list based on the frequency of connection to those networks
2156     * and whether it was last seen in the scan results.
2157     *
2158     * @return list of networks with updated priorities.
2159     */
2160    public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList() {
2161        List<WifiScanner.ScanSettings.HiddenNetwork> hiddenList = new ArrayList<>();
2162        List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
2163        // Remove any permanently disabled networks or non hidden networks.
2164        Iterator<WifiConfiguration> iter = networks.iterator();
2165        while (iter.hasNext()) {
2166            WifiConfiguration config = iter.next();
2167            if (!config.hiddenSSID ||
2168                    config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
2169                iter.remove();
2170            }
2171        }
2172        Collections.sort(networks, sScanListComparator);
2173        // Let's use the network list size - 1 as the highest priority and then go down from there.
2174        // So, the most frequently connected network has the highest priority now.
2175        int priority = networks.size() - 1;
2176        for (WifiConfiguration config : networks) {
2177            hiddenList.add(
2178                    new WifiScanner.ScanSettings.HiddenNetwork(config.SSID));
2179            priority--;
2180        }
2181        return hiddenList;
2182    }
2183
2184    /**
2185     * Check if the provided ephemeral network was deleted by the user or not.
2186     *
2187     * @param ssid caller must ensure that the SSID passed thru this API match
2188     *             the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
2189     * @return true if network was deleted, false otherwise.
2190     */
2191    public boolean wasEphemeralNetworkDeleted(String ssid) {
2192        return mDeletedEphemeralSSIDs.contains(ssid);
2193    }
2194
2195    /**
2196     * Disable an ephemeral SSID for the purpose of network selection.
2197     *
2198     * The only way to "un-disable it" is if the user create a network for that SSID and then
2199     * forget it.
2200     *
2201     * @param ssid caller must ensure that the SSID passed thru this API match
2202     *             the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
2203     * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can
2204     * disconnect if this is the current network.
2205     */
2206    public WifiConfiguration disableEphemeralNetwork(String ssid) {
2207        if (ssid == null) {
2208            return null;
2209        }
2210        WifiConfiguration foundConfig = null;
2211        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2212            if (config.ephemeral && TextUtils.equals(config.SSID, ssid)) {
2213                foundConfig = config;
2214                break;
2215            }
2216        }
2217        mDeletedEphemeralSSIDs.add(ssid);
2218        Log.d(TAG, "Forget ephemeral SSID " + ssid + " num=" + mDeletedEphemeralSSIDs.size());
2219        if (foundConfig != null) {
2220            Log.d(TAG, "Found ephemeral config in disableEphemeralNetwork: "
2221                    + foundConfig.networkId);
2222        }
2223        return foundConfig;
2224    }
2225
2226    /**
2227     * Resets all sim networks state.
2228     */
2229    public void resetSimNetworks() {
2230        if (mVerboseLoggingEnabled) localLog("resetSimNetworks");
2231        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2232            if (TelephonyUtil.isSimConfig(config)) {
2233                String currentIdentity = TelephonyUtil.getSimIdentity(mTelephonyManager,
2234                        config.enterpriseConfig.getEapMethod());
2235                // Update the loaded config
2236                config.enterpriseConfig.setIdentity(currentIdentity);
2237                config.enterpriseConfig.setAnonymousIdentity("");
2238            }
2239        }
2240    }
2241
2242    /**
2243     * Any network using certificates to authenticate access requires unlocked key store; unless
2244     * the certificates can be stored with hardware encryption
2245     *
2246     * @return true if we need an unlocked keystore, false otherwise.
2247     */
2248    public boolean needsUnlockedKeyStore() {
2249        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2250            if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)
2251                    && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
2252                if (mWifiKeyStore.needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
2253                    return true;
2254                }
2255            }
2256        }
2257        return false;
2258    }
2259
2260    /**
2261     * Helper method to perform the following operations during user switch/unlock:
2262     * - Remove private networks of the old user.
2263     * - Load from the new user store file.
2264     * - Save the store files again to migrate any user specific networks from the shared store
2265     *   to user store.
2266     * This method assumes the user store is visible (i.e CE storage is unlocked). So, the caller
2267     * should ensure that the stores are accessible before invocation.
2268     *
2269     * @param userId The identifier of the new foreground user, after the unlock or switch.
2270     */
2271    private void handleUserUnlockOrSwitch(int userId) {
2272        if (mVerboseLoggingEnabled) {
2273            Log.v(TAG, "Loading from store after user switch/unlock for " + userId);
2274        }
2275        // Switch out the user store file.
2276        if (loadFromUserStoreAfterUnlockOrSwitch(userId)) {
2277            saveToStore(true);
2278            mPendingUnlockStoreRead = false;
2279        }
2280    }
2281
2282    /**
2283     * Handles the switch to a different foreground user:
2284     * - Flush the current state to the old user's store file.
2285     * - Switch the user specific store file.
2286     * - Reload the networks from the store files (shared & user).
2287     * - Write the store files to move any user specific private networks from shared store to user
2288     *   store.
2289     *
2290     * Need to be called when {@link com.android.server.SystemService#onSwitchUser(int)} is invoked.
2291     *
2292     * @param userId The identifier of the new foreground user, after the switch.
2293     * @return List of network ID's of all the private networks of the old user which will be
2294     * removed from memory.
2295     */
2296    public Set<Integer> handleUserSwitch(int userId) {
2297        if (mVerboseLoggingEnabled) {
2298            Log.v(TAG, "Handling user switch for " + userId);
2299        }
2300        if (userId == mCurrentUserId) {
2301            Log.w(TAG, "User already in foreground " + userId);
2302            return new HashSet<>();
2303        }
2304        if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
2305            saveToStore(true);
2306        }
2307        // Remove any private networks of the old user before switching the userId.
2308        Set<Integer> removedNetworkIds = clearInternalUserData(mCurrentUserId);
2309        mConfiguredNetworks.setNewUser(userId);
2310        mCurrentUserId = userId;
2311
2312        if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
2313            handleUserUnlockOrSwitch(mCurrentUserId);
2314        } else {
2315            // Cannot read data from new user's CE store file before they log-in.
2316            mPendingUnlockStoreRead = true;
2317            Log.i(TAG, "Waiting for user unlock to load from store");
2318        }
2319        return removedNetworkIds;
2320    }
2321
2322    /**
2323     * Handles the unlock of foreground user. This maybe needed to read the store file if the user's
2324     * CE storage is not visible when {@link #handleUserSwitch(int)} is invoked.
2325     *
2326     * Need to be called when {@link com.android.server.SystemService#onUnlockUser(int)} is invoked.
2327     *
2328     * @param userId The identifier of the user that unlocked.
2329     */
2330    public void handleUserUnlock(int userId) {
2331        if (mVerboseLoggingEnabled) {
2332            Log.v(TAG, "Handling user unlock for " + userId);
2333        }
2334        if (userId == mCurrentUserId && mPendingUnlockStoreRead) {
2335            handleUserUnlockOrSwitch(mCurrentUserId);
2336        }
2337    }
2338
2339    /**
2340     * Handles the stop of foreground user. This is needed to write the store file to flush
2341     * out any pending data before the user's CE store storage is unavailable.
2342     *
2343     * Need to be called when {@link com.android.server.SystemService#onStopUser(int)} is invoked.
2344     *
2345     * @param userId The identifier of the user that stopped.
2346     */
2347    public void handleUserStop(int userId) {
2348        if (userId == mCurrentUserId && mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
2349            saveToStore(true);
2350            clearInternalData();
2351            mCurrentUserId = UserHandle.USER_SYSTEM;
2352        }
2353    }
2354
2355    /**
2356     * Helper method to clear internal databases.
2357     * This method clears the:
2358     *  - List of configured networks.
2359     *  - Map of scan detail caches.
2360     *  - List of deleted ephemeral networks.
2361     */
2362    private void clearInternalData() {
2363        mConfiguredNetworks.clear();
2364        mDeletedEphemeralSSIDs.clear();
2365        mScanDetailCaches.clear();
2366        clearLastSelectedNetwork();
2367    }
2368
2369    /**
2370     * Helper method to clear internal databases of the specified user.
2371     * This method clears the:
2372     *  - Private configured configured networks of the specified user.
2373     *  - Map of scan detail caches.
2374     *  - List of deleted ephemeral networks.
2375     *
2376     * @param userId The identifier of the current foreground user, before the switch.
2377     * @return List of network ID's of all the private networks of the old user which will be
2378     * removed from memory.
2379     */
2380    private Set<Integer> clearInternalUserData(int userId) {
2381        Set<Integer> removedNetworkIds = new HashSet<>();
2382        // Remove any private networks of the old user before switching the userId.
2383        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2384            if (!config.shared && WifiConfigurationUtil.doesUidBelongToAnyProfile(
2385                    config.creatorUid, mUserManager.getProfiles(userId))) {
2386                removedNetworkIds.add(config.networkId);
2387                mConfiguredNetworks.remove(config.networkId);
2388            }
2389        }
2390        mDeletedEphemeralSSIDs.clear();
2391        mScanDetailCaches.clear();
2392        clearLastSelectedNetwork();
2393        return removedNetworkIds;
2394    }
2395
2396    /**
2397     * Helper function to populate the internal (in-memory) data from the retrieved shared store
2398     * (file) data.
2399     *
2400     * @param configurations list of configurations retrieved from store.
2401     */
2402    private void loadInternalDataFromSharedStore(
2403            List<WifiConfiguration> configurations) {
2404        for (WifiConfiguration configuration : configurations) {
2405            configuration.networkId = mNextNetworkId++;
2406            if (mVerboseLoggingEnabled) {
2407                Log.v(TAG, "Adding network from shared store " + configuration.configKey());
2408            }
2409            mConfiguredNetworks.put(configuration);
2410        }
2411    }
2412
2413    /**
2414     * Helper function to populate the internal (in-memory) data from the retrieved user store
2415     * (file) data.
2416     *
2417     * @param configurations        list of configurations retrieved from store.
2418     * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by
2419     *                              the user.
2420     */
2421    private void loadInternalDataFromUserStore(
2422            List<WifiConfiguration> configurations, Set<String> deletedEphemeralSSIDs) {
2423        for (WifiConfiguration configuration : configurations) {
2424            configuration.networkId = mNextNetworkId++;
2425            if (mVerboseLoggingEnabled) {
2426                Log.v(TAG, "Adding network from user store " + configuration.configKey());
2427            }
2428            mConfiguredNetworks.put(configuration);
2429        }
2430        for (String ssid : deletedEphemeralSSIDs) {
2431            mDeletedEphemeralSSIDs.add(ssid);
2432        }
2433    }
2434
2435    /**
2436     * Helper function to populate the internal (in-memory) data from the retrieved stores (file)
2437     * data.
2438     * This method:
2439     * 1. Clears all existing internal data.
2440     * 2. Sends out the networks changed broadcast after loading all the data.
2441     *
2442     * @param sharedConfigurations  list of  network configurations retrieved from shared store.
2443     * @param userConfigurations    list of  network configurations retrieved from user store.
2444     * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by
2445     *                              the user.
2446     */
2447    private void loadInternalData(
2448            List<WifiConfiguration> sharedConfigurations,
2449            List<WifiConfiguration> userConfigurations, Set<String> deletedEphemeralSSIDs) {
2450        // Clear out all the existing in-memory lists and load the lists from what was retrieved
2451        // from the config store.
2452        clearInternalData();
2453        loadInternalDataFromSharedStore(sharedConfigurations);
2454        loadInternalDataFromUserStore(userConfigurations, deletedEphemeralSSIDs);
2455        if (mConfiguredNetworks.sizeForAllUsers() == 0) {
2456            Log.w(TAG, "No stored networks found.");
2457        }
2458        sendConfiguredNetworksChangedBroadcast();
2459    }
2460
2461    /**
2462     * Migrate data from legacy store files. The function performs the following operations:
2463     * 1. Check if the legacy store files are present.
2464     * 2. If yes, read all the data from the store files.
2465     * 3. Save it to the new store files.
2466     * 4. Delete the legacy store file.
2467     *
2468     * @return true if migration was successful or not needed (fresh install), false if it failed.
2469     */
2470    private boolean migrateFromLegacyStore() {
2471        if (mWifiConfigStoreLegacy.areStoresPresent()) {
2472            WifiConfigStoreDataLegacy storeData = mWifiConfigStoreLegacy.read();
2473            Log.d(TAG, "Reading from legacy store completed");
2474            loadInternalData(storeData.getConfigurations(), new ArrayList<WifiConfiguration>(),
2475                    storeData.getDeletedEphemeralSSIDs());
2476            if (!saveToStore(true)) {
2477                return false;
2478            }
2479            mWifiConfigStoreLegacy.removeStores();
2480            Log.d(TAG, "Migration from legacy store completed");
2481        }
2482        return true;
2483    }
2484
2485    /**
2486     * Read the config store and load the in-memory lists from the store data retrieved and sends
2487     * out the networks changed broadcast. This method first checks if there is any data to be
2488     * migrated from legacy store files if the new store files aren't present on the device.
2489     *
2490     * This reads all the network configurations from:
2491     * 1. Shared WifiConfigStore.xml
2492     * 2. User WifiConfigStore.xml
2493     *
2494     * @return true on success, false otherwise.
2495     */
2496    public boolean loadFromStore() {
2497        if (!mWifiConfigStore.areStoresPresent()) {
2498            return migrateFromLegacyStore();
2499        }
2500        WifiConfigStoreData storeData;
2501        try {
2502            storeData = mWifiConfigStore.read();
2503        } catch (IOException e) {
2504            Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e);
2505            return false;
2506        } catch (XmlPullParserException e) {
2507            Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e);
2508            return false;
2509        }
2510        loadInternalData(storeData.getSharedConfigurations(), storeData.getUserConfigurations(),
2511                storeData.getDeletedEphemeralSSIDs());
2512        return true;
2513    }
2514
2515    /**
2516     * Read the user config store and load the in-memory lists from the store data retrieved and
2517     * sends out the networks changed broadcast.
2518     * This should be used for all user switches/unlocks to only load networks from the user
2519     * specific store and avoid reloading the shared networks.
2520     *
2521     * This reads all the network configurations from:
2522     * 1. User WifiConfigStore.xml
2523     *
2524     * @param userId The identifier of the foreground user.
2525     * @return true on success, false otherwise.
2526     */
2527    public boolean loadFromUserStoreAfterUnlockOrSwitch(int userId) {
2528        WifiConfigStoreData storeData;
2529        try {
2530            storeData = mWifiConfigStore.switchUserStoreAndRead(
2531                    mWifiConfigStore.createUserFile(userId));
2532        } catch (IOException e) {
2533            Log.wtf(TAG, "Reading from new store failed. All saved private networks are lost!", e);
2534            return false;
2535        } catch (XmlPullParserException e) {
2536            Log.wtf(TAG, "XML deserialization of store failed. All saved private networks are" +
2537                    "lost!", e);
2538            return false;
2539        }
2540        loadInternalDataFromUserStore(storeData.getUserConfigurations(),
2541                storeData.getDeletedEphemeralSSIDs());
2542        return true;
2543    }
2544
2545    /**
2546     * Save the current snapshot of the in-memory lists to the config store.
2547     *
2548     * @param forceWrite Whether the write needs to be forced or not.
2549     * @return Whether the write was successful or not, this is applicable only for force writes.
2550     */
2551    public boolean saveToStore(boolean forceWrite) {
2552        ArrayList<WifiConfiguration> sharedConfigurations = new ArrayList<>();
2553        ArrayList<WifiConfiguration> userConfigurations = new ArrayList<>();
2554        for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
2555            // Don't persist ephemeral networks to store.
2556            if (!config.ephemeral) {
2557                // We push all shared networks & private networks not belonging to the current
2558                // user to the shared store. Ideally, private networks for other users should
2559                // not even be in memory,
2560                // But, this logic is in place to deal with store migration from N to O
2561                // because all networks were previously stored in a central file. We cannot
2562                // write these private networks to the user specific store until the corresponding
2563                // user logs in.
2564                if (config.shared || !WifiConfigurationUtil.doesUidBelongToAnyProfile(
2565                        config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) {
2566                    sharedConfigurations.add(config);
2567                } else {
2568                    userConfigurations.add(config);
2569                }
2570            }
2571        }
2572        WifiConfigStoreData storeData =
2573                new WifiConfigStoreData(
2574                        sharedConfigurations, userConfigurations, mDeletedEphemeralSSIDs);
2575
2576        try {
2577            mWifiConfigStore.write(forceWrite, storeData);
2578        } catch (IOException e) {
2579            Log.wtf(TAG, "Writing to store failed. Saved networks maybe lost!", e);
2580            return false;
2581        } catch (XmlPullParserException e) {
2582            Log.wtf(TAG, "XML serialization for store failed. Saved networks maybe lost!", e);
2583            return false;
2584        }
2585        return true;
2586    }
2587
2588    /**
2589     * Helper method for logging into local log buffer.
2590     */
2591    private void localLog(String s) {
2592        if (mLocalLog != null) {
2593            mLocalLog.log(s);
2594        }
2595    }
2596
2597    /**
2598     * Dump the local log buffer and other internal state of WifiConfigManager.
2599     */
2600    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2601        pw.println("Dump of WifiConfigManager");
2602        pw.println("WifiConfigManager - Log Begin ----");
2603        mLocalLog.dump(fd, pw, args);
2604        pw.println("WifiConfigManager - Log End ----");
2605        pw.println("WifiConfigManager - Configured networks Begin ----");
2606        for (WifiConfiguration network : getInternalConfiguredNetworks()) {
2607            pw.println(network);
2608        }
2609        pw.println("WifiConfigManager - Configured networks End ----");
2610        pw.println("WifiConfigManager - Next network ID to be allocated " + mNextNetworkId);
2611        pw.println("WifiConfigManager - Last selected network ID " + mLastSelectedNetworkId);
2612    }
2613}
2614