WifiConfigManager.java revision 47678c136b1f88be3d04d89aa5898e77fa1862a3
1a0842b40441db5332a5290f941021636b1182761Sol Boucher/*
2a0842b40441db5332a5290f941021636b1182761Sol Boucher * Copyright (C) 2016 The Android Open Source Project
3a0842b40441db5332a5290f941021636b1182761Sol Boucher *
4a0842b40441db5332a5290f941021636b1182761Sol Boucher * Licensed under the Apache License, Version 2.0 (the "License");
5a0842b40441db5332a5290f941021636b1182761Sol Boucher * you may not use this file except in compliance with the License.
6a0842b40441db5332a5290f941021636b1182761Sol Boucher * You may obtain a copy of the License at
7a0842b40441db5332a5290f941021636b1182761Sol Boucher *
8a0842b40441db5332a5290f941021636b1182761Sol Boucher *      http://www.apache.org/licenses/LICENSE-2.0
9a0842b40441db5332a5290f941021636b1182761Sol Boucher *
10a0842b40441db5332a5290f941021636b1182761Sol Boucher * Unless required by applicable law or agreed to in writing, software
11a0842b40441db5332a5290f941021636b1182761Sol Boucher * distributed under the License is distributed on an "AS IS" BASIS,
12a0842b40441db5332a5290f941021636b1182761Sol Boucher * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a0842b40441db5332a5290f941021636b1182761Sol Boucher * See the License for the specific language governing permissions and
14a0842b40441db5332a5290f941021636b1182761Sol Boucher * limitations under the License.
15a0842b40441db5332a5290f941021636b1182761Sol Boucher */
16a0842b40441db5332a5290f941021636b1182761Sol Boucher
17a0842b40441db5332a5290f941021636b1182761Sol Boucherpackage com.android.server.wifi;
18a0842b40441db5332a5290f941021636b1182761Sol Boucher
19a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.app.ActivityManager;
20a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.app.admin.DeviceAdminInfo;
21de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucherimport android.app.admin.DevicePolicyManagerInternal;
22f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucherimport android.content.ContentResolver;
23a97b7d1192e246a5f738991adca37cce282e1382Sol Boucherimport android.content.Context;
24f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucherimport android.content.Intent;
25a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.content.pm.ApplicationInfo;
26a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.content.pm.PackageManager;
27a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.net.IpConfiguration;
28a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.net.ProxyInfo;
29a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.net.StaticIpConfiguration;
30a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.net.wifi.ScanResult;
31a97b7d1192e246a5f738991adca37cce282e1382Sol Boucherimport android.net.wifi.WifiConfiguration;
32a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
33a97b7d1192e246a5f738991adca37cce282e1382Sol Boucherimport android.net.wifi.WifiEnterpriseConfig;
34a97b7d1192e246a5f738991adca37cce282e1382Sol Boucherimport android.net.wifi.WifiInfo;
35a97b7d1192e246a5f738991adca37cce282e1382Sol Boucherimport android.net.wifi.WifiManager;
36de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucherimport android.net.wifi.WifiScanner;
37de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucherimport android.os.Process;
38aa907a3b6637b4f95dbf572d0cf790a70ba3aeb0Sol Boucherimport android.os.UserHandle;
39a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.os.UserManager;
40a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.provider.Settings;
41a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.telephony.TelephonyManager;
42a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.text.TextUtils;
43a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.util.ArraySet;
44a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.util.LocalLog;
45a0842b40441db5332a5290f941021636b1182761Sol Boucherimport android.util.Log;
46a0842b40441db5332a5290f941021636b1182761Sol Boucher
47de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucherimport com.android.internal.R;
48a0842b40441db5332a5290f941021636b1182761Sol Boucherimport com.android.internal.annotations.VisibleForTesting;
49de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucherimport com.android.server.LocalServices;
50a0842b40441db5332a5290f941021636b1182761Sol Boucherimport com.android.server.wifi.WifiConfigStoreLegacy.WifiConfigStoreDataLegacy;
51a0842b40441db5332a5290f941021636b1182761Sol Boucherimport com.android.server.wifi.hotspot2.PasspointManager;
52a0842b40441db5332a5290f941021636b1182761Sol Boucherimport com.android.server.wifi.util.TelephonyUtil;
53a0842b40441db5332a5290f941021636b1182761Sol Boucherimport com.android.server.wifi.util.WifiPermissionsUtil;
54a0842b40441db5332a5290f941021636b1182761Sol Boucherimport com.android.server.wifi.util.WifiPermissionsWrapper;
55a0842b40441db5332a5290f941021636b1182761Sol Boucher
56a0842b40441db5332a5290f941021636b1182761Sol Boucherimport org.xmlpull.v1.XmlPullParserException;
57a0842b40441db5332a5290f941021636b1182761Sol Boucher
58a0842b40441db5332a5290f941021636b1182761Sol Boucherimport java.io.FileDescriptor;
59a0842b40441db5332a5290f941021636b1182761Sol Boucherimport java.io.IOException;
60a0842b40441db5332a5290f941021636b1182761Sol Boucherimport java.io.PrintWriter;
61a0842b40441db5332a5290f941021636b1182761Sol Boucherimport java.util.ArrayList;
62a0842b40441db5332a5290f941021636b1182761Sol Boucherimport java.util.BitSet;
63a0842b40441db5332a5290f941021636b1182761Sol Boucherimport java.util.Calendar;
64a0842b40441db5332a5290f941021636b1182761Sol Boucherimport java.util.Collection;
65a0842b40441db5332a5290f941021636b1182761Sol Boucherimport java.util.Collections;
66a0842b40441db5332a5290f941021636b1182761Sol Boucherimport java.util.HashMap;
67aa907a3b6637b4f95dbf572d0cf790a70ba3aeb0Sol Boucherimport java.util.HashSet;
68a0842b40441db5332a5290f941021636b1182761Sol Boucherimport java.util.Iterator;
69a0842b40441db5332a5290f941021636b1182761Sol Boucherimport java.util.List;
70a0842b40441db5332a5290f941021636b1182761Sol Boucherimport java.util.Map;
71a0842b40441db5332a5290f941021636b1182761Sol Boucherimport java.util.Set;
72a0842b40441db5332a5290f941021636b1182761Sol Boucher
73a0842b40441db5332a5290f941021636b1182761Sol Boucher/**
74a0842b40441db5332a5290f941021636b1182761Sol Boucher * This class provides the APIs to manage configured Wi-Fi networks.
75a0842b40441db5332a5290f941021636b1182761Sol Boucher * It deals with the following:
76a0842b40441db5332a5290f941021636b1182761Sol Boucher * - Maintaining a list of configured networks for quick access.
77a0842b40441db5332a5290f941021636b1182761Sol Boucher * - Persisting the configurations to store when required.
78a0842b40441db5332a5290f941021636b1182761Sol Boucher * - Supporting WifiManager Public API calls:
79a0842b40441db5332a5290f941021636b1182761Sol Boucher *   > addOrUpdateNetwork()
80a0842b40441db5332a5290f941021636b1182761Sol Boucher *   > removeNetwork()
81a0842b40441db5332a5290f941021636b1182761Sol Boucher *   > enableNetwork()
82a0842b40441db5332a5290f941021636b1182761Sol Boucher *   > disableNetwork()
83a0842b40441db5332a5290f941021636b1182761Sol Boucher * - Handle user switching on multi-user devices.
84a0842b40441db5332a5290f941021636b1182761Sol Boucher *
85a0842b40441db5332a5290f941021636b1182761Sol Boucher * All network configurations retrieved from this class are copies of the original configuration
86a0842b40441db5332a5290f941021636b1182761Sol Boucher * stored in the internal database. So, any updates to the retrieved configuration object are
87a0842b40441db5332a5290f941021636b1182761Sol Boucher * meaningless and will not be reflected in the original database.
88a0842b40441db5332a5290f941021636b1182761Sol Boucher * This is done on purpose to ensure that only WifiConfigManager can modify configurations stored
89a0842b40441db5332a5290f941021636b1182761Sol Boucher * in the internal database. Any configuration updates should be triggered with appropriate helper
90a0842b40441db5332a5290f941021636b1182761Sol Boucher * methods of this class using the configuration's unique networkId.
91a0842b40441db5332a5290f941021636b1182761Sol Boucher *
92a0842b40441db5332a5290f941021636b1182761Sol Boucher * NOTE: These API's are not thread safe and should only be used from WifiStateMachine thread.
93aa907a3b6637b4f95dbf572d0cf790a70ba3aeb0Sol Boucher */
94aa907a3b6637b4f95dbf572d0cf790a70ba3aeb0Sol Boucherpublic class WifiConfigManager {
95a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
96a0842b40441db5332a5290f941021636b1182761Sol Boucher     * String used to mask passwords to public interface.
97a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
98a0842b40441db5332a5290f941021636b1182761Sol Boucher    @VisibleForTesting
99a0842b40441db5332a5290f941021636b1182761Sol Boucher    public static final String PASSWORD_MASK = "*";
100a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
101a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Package name for SysUI. This is used to lookup the UID of SysUI which is used to allow
102a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Quick settings to modify network configurations.
103a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
104a0842b40441db5332a5290f941021636b1182761Sol Boucher    @VisibleForTesting
105a0842b40441db5332a5290f941021636b1182761Sol Boucher    public static final String SYSUI_PACKAGE_NAME = "com.android.systemui";
106a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
107a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Network Selection disable reason thresholds. These numbers are used to debounce network
108a0842b40441db5332a5290f941021636b1182761Sol Boucher     * failures before we disable them.
109a0842b40441db5332a5290f941021636b1182761Sol Boucher     * These are indexed using the disable reason constants defined in
110a0842b40441db5332a5290f941021636b1182761Sol Boucher     * {@link android.net.wifi.WifiConfiguration.NetworkSelectionStatus}.
111a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
112a0842b40441db5332a5290f941021636b1182761Sol Boucher    @VisibleForTesting
113a0842b40441db5332a5290f941021636b1182761Sol Boucher    public static final int[] NETWORK_SELECTION_DISABLE_THRESHOLD = {
114a0842b40441db5332a5290f941021636b1182761Sol Boucher            -1, //  threshold for NETWORK_SELECTION_ENABLE
115a0842b40441db5332a5290f941021636b1182761Sol Boucher            1,  //  threshold for DISABLED_BAD_LINK
116a0842b40441db5332a5290f941021636b1182761Sol Boucher            5,  //  threshold for DISABLED_ASSOCIATION_REJECTION
117a0842b40441db5332a5290f941021636b1182761Sol Boucher            5,  //  threshold for DISABLED_AUTHENTICATION_FAILURE
118a0842b40441db5332a5290f941021636b1182761Sol Boucher            5,  //  threshold for DISABLED_DHCP_FAILURE
119a0842b40441db5332a5290f941021636b1182761Sol Boucher            5,  //  threshold for DISABLED_DNS_FAILURE
120a0842b40441db5332a5290f941021636b1182761Sol Boucher            1,  //  threshold for DISABLED_WPS_START
121a0842b40441db5332a5290f941021636b1182761Sol Boucher            6,  //  threshold for DISABLED_TLS_VERSION_MISMATCH
122a0842b40441db5332a5290f941021636b1182761Sol Boucher            1,  //  threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
123a0842b40441db5332a5290f941021636b1182761Sol Boucher            1,  //  threshold for DISABLED_NO_INTERNET
124a0842b40441db5332a5290f941021636b1182761Sol Boucher            1,  //  threshold for DISABLED_BY_WIFI_MANAGER
125a0842b40441db5332a5290f941021636b1182761Sol Boucher            1,  //  threshold for DISABLED_BY_USER_SWITCH
126a0842b40441db5332a5290f941021636b1182761Sol Boucher            1   //  threshold for DISABLED_BY_WRONG_PASSWORD
127a0842b40441db5332a5290f941021636b1182761Sol Boucher    };
128a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
129a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Network Selection disable timeout for each kind of error. After the timeout milliseconds,
130a0842b40441db5332a5290f941021636b1182761Sol Boucher     * enable the network again.
131a0842b40441db5332a5290f941021636b1182761Sol Boucher     * These are indexed using the disable reason constants defined in
132a0842b40441db5332a5290f941021636b1182761Sol Boucher     * {@link android.net.wifi.WifiConfiguration.NetworkSelectionStatus}.
133a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
134a0842b40441db5332a5290f941021636b1182761Sol Boucher    @VisibleForTesting
135a0842b40441db5332a5290f941021636b1182761Sol Boucher    public static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT_MS = {
136a0842b40441db5332a5290f941021636b1182761Sol Boucher            Integer.MAX_VALUE,  // threshold for NETWORK_SELECTION_ENABLE
137a0842b40441db5332a5290f941021636b1182761Sol Boucher            15 * 60 * 1000,     // threshold for DISABLED_BAD_LINK
138a0842b40441db5332a5290f941021636b1182761Sol Boucher            5 * 60 * 1000,      // threshold for DISABLED_ASSOCIATION_REJECTION
139a0842b40441db5332a5290f941021636b1182761Sol Boucher            5 * 60 * 1000,      // threshold for DISABLED_AUTHENTICATION_FAILURE
140a0842b40441db5332a5290f941021636b1182761Sol Boucher            5 * 60 * 1000,      // threshold for DISABLED_DHCP_FAILURE
141a0842b40441db5332a5290f941021636b1182761Sol Boucher            5 * 60 * 1000,      // threshold for DISABLED_DNS_FAILURE
142a0842b40441db5332a5290f941021636b1182761Sol Boucher            0 * 60 * 1000,      // threshold for DISABLED_WPS_START
143a0842b40441db5332a5290f941021636b1182761Sol Boucher            Integer.MAX_VALUE,  // threshold for DISABLED_TLS_VERSION
144a0842b40441db5332a5290f941021636b1182761Sol Boucher            Integer.MAX_VALUE,  // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
145a0842b40441db5332a5290f941021636b1182761Sol Boucher            Integer.MAX_VALUE,  // threshold for DISABLED_NO_INTERNET
146a0842b40441db5332a5290f941021636b1182761Sol Boucher            Integer.MAX_VALUE,  // threshold for DISABLED_BY_WIFI_MANAGER
147a0842b40441db5332a5290f941021636b1182761Sol Boucher            Integer.MAX_VALUE,  // threshold for DISABLED_BY_USER_SWITCH
148a0842b40441db5332a5290f941021636b1182761Sol Boucher            Integer.MAX_VALUE   // threshold for DISABLED_BY_WRONG_PASSWORD
149a0842b40441db5332a5290f941021636b1182761Sol Boucher    };
150a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
151a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Interface for other modules to listen to the saved network updated
152a0842b40441db5332a5290f941021636b1182761Sol Boucher     * events.
153a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
154a0842b40441db5332a5290f941021636b1182761Sol Boucher    public interface OnSavedNetworkUpdateListener {
155a0842b40441db5332a5290f941021636b1182761Sol Boucher        /**
156a0842b40441db5332a5290f941021636b1182761Sol Boucher         * Invoked on saved network being added.
157a0842b40441db5332a5290f941021636b1182761Sol Boucher         */
158a0842b40441db5332a5290f941021636b1182761Sol Boucher        void onSavedNetworkAdded(int networkId);
159a0842b40441db5332a5290f941021636b1182761Sol Boucher        /**
160a0842b40441db5332a5290f941021636b1182761Sol Boucher         * Invoked on saved network being enabled.
161a0842b40441db5332a5290f941021636b1182761Sol Boucher         */
162de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        void onSavedNetworkEnabled(int networkId);
163bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala        /**
164de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher         * Invoked on saved network being permanently disabled.
165de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher         */
166a0842b40441db5332a5290f941021636b1182761Sol Boucher        void onSavedNetworkPermanentlyDisabled(int networkId);
167a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        /**
168a0842b40441db5332a5290f941021636b1182761Sol Boucher         * Invoked on saved network being removed.
169a0842b40441db5332a5290f941021636b1182761Sol Boucher         */
170a0842b40441db5332a5290f941021636b1182761Sol Boucher        void onSavedNetworkRemoved(int networkId);
171a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        /**
172a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher         * Invoked on saved network being temporarily disabled.
173a0842b40441db5332a5290f941021636b1182761Sol Boucher         */
174a0842b40441db5332a5290f941021636b1182761Sol Boucher        void onSavedNetworkTemporarilyDisabled(int networkId);
175de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        /**
176a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher         * Invoked on saved network being updated.
177de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher         */
178a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        void onSavedNetworkUpdated(int networkId);
179a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    }
180a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
181a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Max size of scan details to cache in {@link #mScanDetailCaches}.
182a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     */
183a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    @VisibleForTesting
184a0842b40441db5332a5290f941021636b1182761Sol Boucher    public static final int SCAN_CACHE_ENTRIES_MAX_SIZE = 192;
185a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
186a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * Once the size of the scan details in the cache {@link #mScanDetailCaches} exceeds
187de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * {@link #SCAN_CACHE_ENTRIES_MAX_SIZE}, trim it down to this value so that we have some
188a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * buffer time before the next eviction.
189a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     */
190a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    @VisibleForTesting
191a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    public static final int SCAN_CACHE_ENTRIES_TRIM_SIZE = 128;
192a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    /**
193a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * Link networks only if they have less than this number of scan cache entries.
194a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     */
195de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    @VisibleForTesting
196de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    public static final int LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES = 6;
197de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    /**
198a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * Link networks only if the bssid in scan results for the networks match in the first
199a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * 16 ASCII chars in the bssid string. For example = "af:de:56;34:15:7"
200a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
201984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    @VisibleForTesting
202984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    public static final int LINK_CONFIGURATION_BSSID_MATCH_LENGTH = 16;
203984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    /**
204a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Flags to be passed in for |canModifyNetwork| to decide if the change is minor and can
205a0842b40441db5332a5290f941021636b1182761Sol Boucher     * bypass the lockdown checks.
206a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
207a0842b40441db5332a5290f941021636b1182761Sol Boucher    private static final boolean ALLOW_LOCKDOWN_CHECK_BYPASS = true;
208a0842b40441db5332a5290f941021636b1182761Sol Boucher    private static final boolean DISALLOW_LOCKDOWN_CHECK_BYPASS = false;
209a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
210a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Log tag for this class.
211a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
212a0842b40441db5332a5290f941021636b1182761Sol Boucher    private static final String TAG = "WifiConfigManager";
213a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
214a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Maximum age of scan results that can be used for averaging out RSSI value.
215a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
216a0842b40441db5332a5290f941021636b1182761Sol Boucher    private static final int SCAN_RESULT_MAXIMUM_AGE_MS = 40000;
217a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
218772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher     * General sorting algorithm of all networks for scanning purposes:
219a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Place the configurations in descending order of their |numAssociation| values. If networks
220a0842b40441db5332a5290f941021636b1182761Sol Boucher     * have the same |numAssociation|, place the configurations with
221a0842b40441db5332a5290f941021636b1182761Sol Boucher     * |lastSeenInQualifiedNetworkSelection| set first.
222a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
223a0842b40441db5332a5290f941021636b1182761Sol Boucher    private static final WifiConfigurationUtil.WifiConfigurationComparator sScanListComparator =
224a0842b40441db5332a5290f941021636b1182761Sol Boucher            new WifiConfigurationUtil.WifiConfigurationComparator() {
225a0842b40441db5332a5290f941021636b1182761Sol Boucher                @Override
226a0842b40441db5332a5290f941021636b1182761Sol Boucher                public int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b) {
22750f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher                    if (a.numAssociation != b.numAssociation) {
22850f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher                        return Long.compare(b.numAssociation, a.numAssociation);
229a0842b40441db5332a5290f941021636b1182761Sol Boucher                    } else {
230a0842b40441db5332a5290f941021636b1182761Sol Boucher                        boolean isConfigALastSeen =
231a0842b40441db5332a5290f941021636b1182761Sol Boucher                                a.getNetworkSelectionStatus()
232a0842b40441db5332a5290f941021636b1182761Sol Boucher                                        .getSeenInLastQualifiedNetworkSelection();
233a0842b40441db5332a5290f941021636b1182761Sol Boucher                        boolean isConfigBLastSeen =
234bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala                                b.getNetworkSelectionStatus()
235a0842b40441db5332a5290f941021636b1182761Sol Boucher                                        .getSeenInLastQualifiedNetworkSelection();
236a0842b40441db5332a5290f941021636b1182761Sol Boucher                        return Boolean.compare(isConfigBLastSeen, isConfigALastSeen);
237a0842b40441db5332a5290f941021636b1182761Sol Boucher                    }
238a0842b40441db5332a5290f941021636b1182761Sol Boucher                }
239a0842b40441db5332a5290f941021636b1182761Sol Boucher            };
240a0842b40441db5332a5290f941021636b1182761Sol Boucher
241a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    /**
242a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * List of external dependencies for WifiConfigManager.
243a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
244a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final Context mContext;
245a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    private final Clock mClock;
246a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final UserManager mUserManager;
247a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    private final BackupManagerProxy mBackupManagerProxy;
248a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final TelephonyManager mTelephonyManager;
249a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final WifiKeyStore mWifiKeyStore;
250a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final WifiConfigStore mWifiConfigStore;
251a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final WifiConfigStoreLegacy mWifiConfigStoreLegacy;
252a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final WifiPermissionsUtil mWifiPermissionsUtil;
253a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final WifiPermissionsWrapper mWifiPermissionsWrapper;
254de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    /**
255a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * Local log used for debugging any WifiConfigManager issues.
256a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
257a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final LocalLog mLocalLog =
258a0842b40441db5332a5290f941021636b1182761Sol Boucher            new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 128 : 256);
259a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
260a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Map of configured networks with network id as the key.
261de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     */
262de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    private final ConfigurationMap mConfiguredNetworks;
263de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    /**
264de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * Stores a map of NetworkId to ScanDetailCache.
265a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
266a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final Map<Integer, ScanDetailCache> mScanDetailCaches;
267a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
268a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Framework keeps a list of ephemeral SSIDs that where deleted by user,
269984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * so as, framework knows not to autoconnect again those SSIDs based on scorer input.
270a0842b40441db5332a5290f941021636b1182761Sol Boucher     * The list is never cleared up.
271a0842b40441db5332a5290f941021636b1182761Sol Boucher     * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field.
272a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
273a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final Set<String> mDeletedEphemeralSSIDs;
274a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
275a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Flag to indicate if only networks with the same psk should be linked.
276a0842b40441db5332a5290f941021636b1182761Sol Boucher     * TODO(b/30706406): Remove this flag if unused.
277a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
278a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final boolean mOnlyLinkSameCredentialConfigurations;
279a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
280a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Number of channels to scan for during partial scans initiated while connected.
281a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
282a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final int mMaxNumActiveChannelsForPartialScans;
283a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
284a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Verbose logging flag. Toggled by developer options.
285a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
286a0842b40441db5332a5290f941021636b1182761Sol Boucher    private boolean mVerboseLoggingEnabled = false;
287a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
288a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * Current logged in user ID.
289a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
290a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    private int mCurrentUserId = UserHandle.USER_SYSTEM;
291a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
292a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Flag to indicate that the new user's store has not yet been read since user switch.
293a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Initialize this flag to |true| to trigger a read on the first user unlock after
294a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * bootup.
295984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     */
296a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    private boolean mPendingUnlockStoreRead = true;
297de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    /**
298de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * Flag to indicate if we have performed a read from store at all. This is used to gate
299de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * any user unlock/switch operations until we read the store (Will happen if wifi is disabled
300bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala     * when user updates from N to O).
301a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     */
302a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    private boolean mPendingStoreRead = true;
303984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    /**
304a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * Flag to indicate if the user unlock was deferred until the store load occurs.
305a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
306a0842b40441db5332a5290f941021636b1182761Sol Boucher    private boolean mDeferredUserUnlockRead = false;
307a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
3082569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher     * Flag to indicate if SIM is present.
3092569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher     */
3102569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher    private boolean mSimPresent = false;
3112569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher    /**
312a0842b40441db5332a5290f941021636b1182761Sol Boucher     * This is keeping track of the next network ID to be assigned. Any new networks will be
313a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * assigned |mNextNetworkId| as network ID.
314a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     */
315a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    private int mNextNetworkId = 0;
316a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    /**
317a0842b40441db5332a5290f941021636b1182761Sol Boucher     * UID of system UI. This uid is allowed to modify network configurations regardless of which
318a0842b40441db5332a5290f941021636b1182761Sol Boucher     * user is logged in.
319a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     */
320984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    private int mSystemUiUid = -1;
321a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
322a0842b40441db5332a5290f941021636b1182761Sol Boucher     * This is used to remember which network was selected successfully last by an app. This is set
323a0842b40441db5332a5290f941021636b1182761Sol Boucher     * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
324a0842b40441db5332a5290f941021636b1182761Sol Boucher     * This is the only way for an app to request connection to a specific network using the
325a0842b40441db5332a5290f941021636b1182761Sol Boucher     * {@link WifiManager} API's.
326a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
327a0842b40441db5332a5290f941021636b1182761Sol Boucher    private int mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
328a0842b40441db5332a5290f941021636b1182761Sol Boucher    private long mLastSelectedTimeStamp =
329a0842b40441db5332a5290f941021636b1182761Sol Boucher            WifiConfiguration.NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
330a0842b40441db5332a5290f941021636b1182761Sol Boucher
331a0842b40441db5332a5290f941021636b1182761Sol Boucher    // Store data for network list and deleted ephemeral SSID list.  Used for serializing
332a0842b40441db5332a5290f941021636b1182761Sol Boucher    // parsing data to/from the config store.
333a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final NetworkListStoreData mNetworkListStoreData;
334a0842b40441db5332a5290f941021636b1182761Sol Boucher    private final DeletedEphemeralSsidsStoreData mDeletedEphemeralSsidsStoreData;
335a0842b40441db5332a5290f941021636b1182761Sol Boucher
336a0842b40441db5332a5290f941021636b1182761Sol Boucher    // Store the saved network update listener.
337a0842b40441db5332a5290f941021636b1182761Sol Boucher    private OnSavedNetworkUpdateListener mListener = null;
338a0842b40441db5332a5290f941021636b1182761Sol Boucher
339a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
340a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Create new instance of WifiConfigManager.
341a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
342a0842b40441db5332a5290f941021636b1182761Sol Boucher    WifiConfigManager(
343a0842b40441db5332a5290f941021636b1182761Sol Boucher            Context context, Clock clock, UserManager userManager,
344a0842b40441db5332a5290f941021636b1182761Sol Boucher            TelephonyManager telephonyManager, WifiKeyStore wifiKeyStore,
345a0842b40441db5332a5290f941021636b1182761Sol Boucher            WifiConfigStore wifiConfigStore, WifiConfigStoreLegacy wifiConfigStoreLegacy,
346a0842b40441db5332a5290f941021636b1182761Sol Boucher            WifiPermissionsUtil wifiPermissionsUtil,
347a0842b40441db5332a5290f941021636b1182761Sol Boucher            WifiPermissionsWrapper wifiPermissionsWrapper,
348a0842b40441db5332a5290f941021636b1182761Sol Boucher            NetworkListStoreData networkListStoreData,
349a0842b40441db5332a5290f941021636b1182761Sol Boucher            DeletedEphemeralSsidsStoreData deletedEphemeralSsidsStoreData) {
350a0842b40441db5332a5290f941021636b1182761Sol Boucher        mContext = context;
351a0842b40441db5332a5290f941021636b1182761Sol Boucher        mClock = clock;
352a0842b40441db5332a5290f941021636b1182761Sol Boucher        mUserManager = userManager;
353a0842b40441db5332a5290f941021636b1182761Sol Boucher        mBackupManagerProxy = new BackupManagerProxy();
354a0842b40441db5332a5290f941021636b1182761Sol Boucher        mTelephonyManager = telephonyManager;
355a0842b40441db5332a5290f941021636b1182761Sol Boucher        mWifiKeyStore = wifiKeyStore;
356a0842b40441db5332a5290f941021636b1182761Sol Boucher        mWifiConfigStore = wifiConfigStore;
357de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        mWifiConfigStoreLegacy = wifiConfigStoreLegacy;
358a0842b40441db5332a5290f941021636b1182761Sol Boucher        mWifiPermissionsUtil = wifiPermissionsUtil;
359a0842b40441db5332a5290f941021636b1182761Sol Boucher        mWifiPermissionsWrapper = wifiPermissionsWrapper;
360a0842b40441db5332a5290f941021636b1182761Sol Boucher
361a0842b40441db5332a5290f941021636b1182761Sol Boucher        mConfiguredNetworks = new ConfigurationMap(userManager);
362a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        mScanDetailCaches = new HashMap<>(16, 0.75f);
363a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        mDeletedEphemeralSSIDs = new HashSet<>();
364a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
365a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        // Register store data for network list and deleted ephemeral SSIDs.
366a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        mNetworkListStoreData = networkListStoreData;
367a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        mDeletedEphemeralSsidsStoreData = deletedEphemeralSsidsStoreData;
368a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        mWifiConfigStore.registerStoreData(mNetworkListStoreData);
369a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        mWifiConfigStore.registerStoreData(mDeletedEphemeralSsidsStoreData);
370a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
371a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        mOnlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean(
372a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                R.bool.config_wifi_only_link_same_credential_configurations);
373a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        mMaxNumActiveChannelsForPartialScans = mContext.getResources().getInteger(
374a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels);
375a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
376a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        try {
377a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            mSystemUiUid = mContext.getPackageManager().getPackageUidAsUser(SYSUI_PACKAGE_NAME,
378984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher                    PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
379a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        } catch (PackageManager.NameNotFoundException e) {
380a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            Log.e(TAG, "Unable to resolve SystemUI's UID.");
381a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        }
382bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala    }
383bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala
384984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    /**
385984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * Construct the string to be put in the |creationTime| & |updateTime| elements of
386984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * WifiConfiguration from the provided wall clock millis.
387984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     *
388984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * @param wallClockMillis Time in milliseconds to be converted to string.
389984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     */
390984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    @VisibleForTesting
391984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    public static String createDebugTimeStampString(long wallClockMillis) {
392984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        StringBuilder sb = new StringBuilder();
393a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        sb.append("time=");
394a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        Calendar c = Calendar.getInstance();
395a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        c.setTimeInMillis(wallClockMillis);
396a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
397984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        return sb.toString();
398984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    }
399984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
400984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    /**
401984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * Enable/disable verbose logging in WifiConfigManager & its helper classes.
402984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     */
403bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala    public void enableVerboseLogging(int verbose) {
404984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        if (verbose > 0) {
405984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            mVerboseLoggingEnabled = true;
406984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        } else {
407984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            mVerboseLoggingEnabled = false;
408984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        }
409bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala        mWifiConfigStore.enableVerboseLogging(mVerboseLoggingEnabled);
410984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        mWifiKeyStore.enableVerboseLogging(mVerboseLoggingEnabled);
411a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    }
412a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
413a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    /**
414a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * Helper method to mask all passwords/keys from the provided WifiConfiguration object. This
415a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * is needed when the network configurations are being requested via the public WifiManager
416a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * API's.
417a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * This currently masks the following elements: psk, wepKeys & enterprise config password.
418a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     */
419a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    private void maskPasswordsInWifiConfiguration(WifiConfiguration configuration) {
420a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        if (!TextUtils.isEmpty(configuration.preSharedKey)) {
421a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            configuration.preSharedKey = PASSWORD_MASK;
422984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        }
423de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (configuration.wepKeys != null) {
424de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            for (int i = 0; i < configuration.wepKeys.length; i++) {
425de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                if (!TextUtils.isEmpty(configuration.wepKeys[i])) {
426a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                    configuration.wepKeys[i] = PASSWORD_MASK;
427a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                }
428de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            }
429de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        }
430de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (!TextUtils.isEmpty(configuration.enterpriseConfig.getPassword())) {
431a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            configuration.enterpriseConfig.setPassword(PASSWORD_MASK);
432a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        }
433a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    }
434984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
435a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    /**
436a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Helper method to create a copy of the provided internal WifiConfiguration object to be
437a0842b40441db5332a5290f941021636b1182761Sol Boucher     * passed to external modules.
438a0842b40441db5332a5290f941021636b1182761Sol Boucher     *
439a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @param configuration provided WifiConfiguration object.
440a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * @param maskPasswords Mask passwords or not.
441a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * @return Copy of the WifiConfiguration object.
442a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     */
443a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    private WifiConfiguration createExternalWifiConfiguration(
444a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            WifiConfiguration configuration, boolean maskPasswords) {
445a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        WifiConfiguration network = new WifiConfiguration(configuration);
446a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        if (maskPasswords) {
447a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            maskPasswordsInWifiConfiguration(network);
448984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        }
449de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        return network;
450de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    }
451de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher
452a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    /**
453a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * Fetch the list of currently configured networks maintained in WifiConfigManager.
454de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     *
455de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * This retrieves a copy of the internal configurations maintained by WifiConfigManager and
456de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * should be used for any public interfaces.
457a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     *
458a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * @param savedOnly     Retrieve only saved networks.
459a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * @param maskPasswords Mask passwords or not.
460984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * @return List of WifiConfiguration objects representing the networks.
461a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     */
462a0842b40441db5332a5290f941021636b1182761Sol Boucher    private List<WifiConfiguration> getConfiguredNetworks(
463a0842b40441db5332a5290f941021636b1182761Sol Boucher            boolean savedOnly, boolean maskPasswords) {
464a0842b40441db5332a5290f941021636b1182761Sol Boucher        List<WifiConfiguration> networks = new ArrayList<>();
465a0842b40441db5332a5290f941021636b1182761Sol Boucher        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
466a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            if (savedOnly && config.ephemeral) {
467a0842b40441db5332a5290f941021636b1182761Sol Boucher                continue;
468a0842b40441db5332a5290f941021636b1182761Sol Boucher            }
469a0842b40441db5332a5290f941021636b1182761Sol Boucher            networks.add(createExternalWifiConfiguration(config, maskPasswords));
470a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        }
471a0842b40441db5332a5290f941021636b1182761Sol Boucher        return networks;
472a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
473a0842b40441db5332a5290f941021636b1182761Sol Boucher
474a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
475a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Retrieves the list of all configured networks with passwords masked.
476a0842b40441db5332a5290f941021636b1182761Sol Boucher     *
477a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @return List of WifiConfiguration objects representing the networks.
478a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
479a0842b40441db5332a5290f941021636b1182761Sol Boucher    public List<WifiConfiguration> getConfiguredNetworks() {
480a0842b40441db5332a5290f941021636b1182761Sol Boucher        return getConfiguredNetworks(false, true);
481a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
482a0842b40441db5332a5290f941021636b1182761Sol Boucher
483a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
484a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Retrieves the list of all configured networks with the passwords in plaintext.
485a0842b40441db5332a5290f941021636b1182761Sol Boucher     *
486a0842b40441db5332a5290f941021636b1182761Sol Boucher     * WARNING: Don't use this to pass network configurations to external apps. Should only be
487a0842b40441db5332a5290f941021636b1182761Sol Boucher     * sent to system apps/wifi stack, when there is a need for passwords in plaintext.
488a0842b40441db5332a5290f941021636b1182761Sol Boucher     * TODO: Need to understand the current use case of this API.
489a0842b40441db5332a5290f941021636b1182761Sol Boucher     *
490a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @return List of WifiConfiguration objects representing the networks.
491a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
492de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    public List<WifiConfiguration> getConfiguredNetworksWithPasswords() {
493a0842b40441db5332a5290f941021636b1182761Sol Boucher        return getConfiguredNetworks(false, false);
494a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
495de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher
496de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    /**
497de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * Retrieves the list of all configured networks with the passwords masked.
498de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     *
499a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @return List of WifiConfiguration objects representing the networks.
500a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
501a0842b40441db5332a5290f941021636b1182761Sol Boucher    public List<WifiConfiguration> getSavedNetworks() {
502a0842b40441db5332a5290f941021636b1182761Sol Boucher        return getConfiguredNetworks(true, true);
503de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    }
504de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher
505de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    /**
506de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * Retrieves the configured network corresponding to the provided networkId with password
507de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * masked.
508de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     *
509de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * @param networkId networkId of the requested network.
510de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * @return WifiConfiguration object if found, null otherwise.
511de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     */
512de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    public WifiConfiguration getConfiguredNetwork(int networkId) {
513de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
514de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (config == null) {
515984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            return null;
516984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        }
517984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        // Create a new configuration object with the passwords masked to send out to the external
518984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        // world.
519984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        return createExternalWifiConfiguration(config, true);
520984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    }
521984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
522984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    /**
523984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * Retrieves the configured network corresponding to the provided config key with password
524984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * masked.
525984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     *
526984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * @param configKey configKey of the requested network.
527984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * @return WifiConfiguration object if found, null otherwise.
528984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     */
529984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    public WifiConfiguration getConfiguredNetwork(String configKey) {
530de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        WifiConfiguration config = getInternalConfiguredNetwork(configKey);
531de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (config == null) {
532de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            return null;
533de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        }
534de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        // Create a new configuration object with the passwords masked to send out to the external
535de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        // world.
536de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        return createExternalWifiConfiguration(config, true);
537de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    }
538984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
539de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    /**
540de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * Retrieves the configured network corresponding to the provided networkId with password
541984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * in plaintext.
542984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     *
543984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * WARNING: Don't use this to pass network configurations to external apps. Should only be
544984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * sent to system apps/wifi stack, when there is a need for passwords in plaintext.
545bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala     *
546bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala     * @param networkId networkId of the requested network.
547984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * @return WifiConfiguration object if found, null otherwise.
548984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     */
549984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    public WifiConfiguration getConfiguredNetworkWithPassword(int networkId) {
550984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
551984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        if (config == null) {
552984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            return null;
553984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        }
554984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        // Create a new configuration object without the passwords masked to send out to the
555984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        // external world.
556de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        return createExternalWifiConfiguration(config, false);
557de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    }
558de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher
559de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    /**
560984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * Helper method to retrieve all the internal WifiConfiguration objects corresponding to all
561984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * the networks in our database.
562984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     */
563984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    private Collection<WifiConfiguration> getInternalConfiguredNetworks() {
564984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        return mConfiguredNetworks.valuesForCurrentUser();
565984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    }
566bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala
567984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    /**
568984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * Helper method to retrieve the internal WifiConfiguration object corresponding to the
569984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * provided configuration in our database.
570984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * This first attempts to find the network using the provided network ID in configuration,
571984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * else it attempts to find a matching configuration using the configKey.
572bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala     */
573984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    private WifiConfiguration getInternalConfiguredNetwork(WifiConfiguration config) {
574de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(config.networkId);
575de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (internalConfig != null) {
576de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            return internalConfig;
577de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        }
578de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        internalConfig = mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey());
579de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (internalConfig == null) {
580de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            Log.e(TAG, "Cannot find network with networkId " + config.networkId
581de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                    + " or configKey " + config.configKey());
582de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        }
583de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        return internalConfig;
584de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    }
585de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher
586de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    /**
587de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * Helper method to retrieve the internal WifiConfiguration object corresponding to the
588de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * provided network ID in our database.
589de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     */
590de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    private WifiConfiguration getInternalConfiguredNetwork(int networkId) {
591de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (networkId == WifiConfiguration.INVALID_NETWORK_ID) {
592de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            Log.w(TAG, "Looking up network with invalid networkId -1");
593de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            return null;
594de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        }
595de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(networkId);
596de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (internalConfig == null) {
597de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            Log.e(TAG, "Cannot find network with networkId " + networkId);
598de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        }
599a0842b40441db5332a5290f941021636b1182761Sol Boucher        return internalConfig;
600de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    }
601a0842b40441db5332a5290f941021636b1182761Sol Boucher
602a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
603a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Helper method to retrieve the internal WifiConfiguration object corresponding to the
604a0842b40441db5332a5290f941021636b1182761Sol Boucher     * provided configKey in our database.
605a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
606a0842b40441db5332a5290f941021636b1182761Sol Boucher    private WifiConfiguration getInternalConfiguredNetwork(String configKey) {
607a0842b40441db5332a5290f941021636b1182761Sol Boucher        WifiConfiguration internalConfig =
608a0842b40441db5332a5290f941021636b1182761Sol Boucher                mConfiguredNetworks.getByConfigKeyForCurrentUser(configKey);
609a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        if (internalConfig == null) {
610a0842b40441db5332a5290f941021636b1182761Sol Boucher            Log.e(TAG, "Cannot find network with configKey " + configKey);
611a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
612a0842b40441db5332a5290f941021636b1182761Sol Boucher        return internalConfig;
613a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
614a0842b40441db5332a5290f941021636b1182761Sol Boucher
615a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
616a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Method to send out the configured networks change broadcast when a single network
617a0842b40441db5332a5290f941021636b1182761Sol Boucher     * configuration is changed.
618a0842b40441db5332a5290f941021636b1182761Sol Boucher     *
619a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @param network WifiConfiguration corresponding to the network that was changed.
620a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @param reason  The reason for the change, should be one of WifiManager.CHANGE_REASON_ADDED,
621a0842b40441db5332a5290f941021636b1182761Sol Boucher     *                WifiManager.CHANGE_REASON_REMOVED, or WifiManager.CHANGE_REASON_CHANGE.
622a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
623a0842b40441db5332a5290f941021636b1182761Sol Boucher    private void sendConfiguredNetworkChangedBroadcast(
624a0842b40441db5332a5290f941021636b1182761Sol Boucher            WifiConfiguration network, int reason) {
625a0842b40441db5332a5290f941021636b1182761Sol Boucher        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
626a0842b40441db5332a5290f941021636b1182761Sol Boucher        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
627a0842b40441db5332a5290f941021636b1182761Sol Boucher        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
628a0842b40441db5332a5290f941021636b1182761Sol Boucher        // Create a new WifiConfiguration with passwords masked before we send it out.
629a0842b40441db5332a5290f941021636b1182761Sol Boucher        WifiConfiguration broadcastNetwork = new WifiConfiguration(network);
630a0842b40441db5332a5290f941021636b1182761Sol Boucher        maskPasswordsInWifiConfiguration(broadcastNetwork);
631a0842b40441db5332a5290f941021636b1182761Sol Boucher        intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, broadcastNetwork);
632a0842b40441db5332a5290f941021636b1182761Sol Boucher        intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
633a0842b40441db5332a5290f941021636b1182761Sol Boucher        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
634a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
635de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher
636de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    /**
637de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * Method to send out the configured networks change broadcast when multiple network
638de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * configurations are changed.
639de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     */
640de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    private void sendConfiguredNetworksChangedBroadcast() {
641de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
642a0842b40441db5332a5290f941021636b1182761Sol Boucher        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
643a0842b40441db5332a5290f941021636b1182761Sol Boucher        intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
644a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
645a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    }
646a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
647a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    /**
648a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * Checks if |uid| has permission to modify the provided configuration.
649a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     *
650a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * @param config         WifiConfiguration object corresponding to the network to be modified.
651a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * @param uid            UID of the app requesting the modification.
652a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * @param ignoreLockdown Ignore the configuration lockdown checks for connection attempts.
653de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     */
654a0842b40441db5332a5290f941021636b1182761Sol Boucher    private boolean canModifyNetwork(WifiConfiguration config, int uid, boolean ignoreLockdown) {
655de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        // System internals can always update networks; they're typically only
656de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        // making meteredHint or meteredOverride changes
657a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (uid == Process.SYSTEM_UID) {
658a0842b40441db5332a5290f941021636b1182761Sol Boucher            return true;
659a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
660a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
661a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        // Passpoint configurations are generated and managed by PasspointManager. They can be
662a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        // added by either PasspointNetworkEvaluator (for auto connection) or Settings app
663de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        // (for manual connection), and need to be removed once the connection is completed.
664de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        // Since it is "owned" by us, so always allow us to modify them.
665de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (config.isPasspoint() && uid == Process.WIFI_UID) {
666bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala            return true;
667a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        }
668a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
669a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        // EAP-SIM/AKA/AKA' network needs framework to update the anonymous identity provided
670a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        // by authenticator back to the WifiConfiguration object.
671a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        // Since it is "owned" by us, so always allow us to modify them.
672984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        if (config.enterpriseConfig != null
673a0842b40441db5332a5290f941021636b1182761Sol Boucher                && uid == Process.WIFI_UID
674a0842b40441db5332a5290f941021636b1182761Sol Boucher                && TelephonyUtil.isSimEapMethod(config.enterpriseConfig.getEapMethod())) {
675a0842b40441db5332a5290f941021636b1182761Sol Boucher            return true;
676a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
677a0842b40441db5332a5290f941021636b1182761Sol Boucher
678a0842b40441db5332a5290f941021636b1182761Sol Boucher        final DevicePolicyManagerInternal dpmi = LocalServices.getService(
679a0842b40441db5332a5290f941021636b1182761Sol Boucher                DevicePolicyManagerInternal.class);
680a0842b40441db5332a5290f941021636b1182761Sol Boucher
681a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
682a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
683a0842b40441db5332a5290f941021636b1182761Sol Boucher
684a0842b40441db5332a5290f941021636b1182761Sol Boucher        // If |uid| corresponds to the device owner, allow all modifications.
685a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (isUidDeviceOwner) {
686a0842b40441db5332a5290f941021636b1182761Sol Boucher            return true;
687a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
688a0842b40441db5332a5290f941021636b1182761Sol Boucher
689a0842b40441db5332a5290f941021636b1182761Sol Boucher        final boolean isCreator = (config.creatorUid == uid);
690a0842b40441db5332a5290f941021636b1182761Sol Boucher
691a0842b40441db5332a5290f941021636b1182761Sol Boucher        // Check if the |uid| holds the |NETWORK_SETTINGS| permission if the caller asks us to
692a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        // bypass the lockdown checks.
693a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (ignoreLockdown) {
694a0842b40441db5332a5290f941021636b1182761Sol Boucher            return mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
695a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
696a0842b40441db5332a5290f941021636b1182761Sol Boucher
697a0842b40441db5332a5290f941021636b1182761Sol Boucher        // Check if device has DPM capability. If it has and |dpmi| is still null, then we
698a0842b40441db5332a5290f941021636b1182761Sol Boucher        // treat this case with suspicion and bail out.
699a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)
700a0842b40441db5332a5290f941021636b1182761Sol Boucher                && dpmi == null) {
701a0842b40441db5332a5290f941021636b1182761Sol Boucher            Log.w(TAG, "Error retrieving DPMI service.");
702a0842b40441db5332a5290f941021636b1182761Sol Boucher            return false;
703de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        }
704de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher
705de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        // WiFi config lockdown related logic. At this point we know uid is NOT a Device Owner.
706de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy(
707de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
708de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (!isConfigEligibleForLockdown) {
709a0842b40441db5332a5290f941021636b1182761Sol Boucher            return isCreator || mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
710a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
711de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher
712de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        final ContentResolver resolver = mContext.getContentResolver();
713bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala        final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
714a0842b40441db5332a5290f941021636b1182761Sol Boucher                Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
715a0842b40441db5332a5290f941021636b1182761Sol Boucher        return !isLockdownFeatureEnabled
716a0842b40441db5332a5290f941021636b1182761Sol Boucher                && mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
717a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
718a0842b40441db5332a5290f941021636b1182761Sol Boucher
719a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
720a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Check if the given UID belongs to the current foreground user. This is
721a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * used to prevent apps running in background users from modifying network
722a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * configurations.
723a0842b40441db5332a5290f941021636b1182761Sol Boucher     * <p>
724a0842b40441db5332a5290f941021636b1182761Sol Boucher     * UIDs belonging to system internals (such as SystemUI) are always allowed,
725a0842b40441db5332a5290f941021636b1182761Sol Boucher     * since they always run as {@link UserHandle#USER_SYSTEM}.
726984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     *
727984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * @param uid uid of the app.
728984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * @return true if the given UID belongs to the current foreground user,
729984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     *         otherwise false.
730984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     */
731984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    private boolean doesUidBelongToCurrentUser(int uid) {
732984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        if (uid == android.os.Process.SYSTEM_UID || uid == mSystemUiUid) {
733984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            return true;
734bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala        } else {
735984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            return WifiConfigurationUtil.doesUidBelongToAnyProfile(
736984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher                    uid, mUserManager.getProfiles(mCurrentUserId));
737a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
738a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
739bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala
740bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala    /**
741bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala     * Copy over public elements from an external WifiConfiguration object to the internal
742a0842b40441db5332a5290f941021636b1182761Sol Boucher     * configuration object if element has been set in the provided external WifiConfiguration.
743a0842b40441db5332a5290f941021636b1182761Sol Boucher     * The only exception is the hidden |IpConfiguration| parameters, these need to be copied over
744a0842b40441db5332a5290f941021636b1182761Sol Boucher     * for every update.
745a0842b40441db5332a5290f941021636b1182761Sol Boucher     *
746a0842b40441db5332a5290f941021636b1182761Sol Boucher     * This method updates all elements that are common to both network addition & update.
747a0842b40441db5332a5290f941021636b1182761Sol Boucher     * The following fields of {@link WifiConfiguration} are not copied from external configs:
748a0842b40441db5332a5290f941021636b1182761Sol Boucher     *  > networkId - These are allocated by Wi-Fi stack internally for any new configurations.
749a0842b40441db5332a5290f941021636b1182761Sol Boucher     *  > status - The status needs to be explicitly updated using
750a0842b40441db5332a5290f941021636b1182761Sol Boucher     *             {@link WifiManager#enableNetwork(int, boolean)} or
751de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     *             {@link WifiManager#disableNetwork(int)}.
752a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     *
753a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * @param internalConfig WifiConfiguration object in our internal map.
754de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * @param externalConfig WifiConfiguration object provided from the external API.
755de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     */
756de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    private void mergeWithInternalWifiConfiguration(
757984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            WifiConfiguration internalConfig, WifiConfiguration externalConfig) {
758a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (externalConfig.SSID != null) {
759a0842b40441db5332a5290f941021636b1182761Sol Boucher            internalConfig.SSID = externalConfig.SSID;
760a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
761a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (externalConfig.BSSID != null) {
762a0842b40441db5332a5290f941021636b1182761Sol Boucher            internalConfig.BSSID = externalConfig.BSSID.toLowerCase();
763a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
764a0842b40441db5332a5290f941021636b1182761Sol Boucher        internalConfig.hiddenSSID = externalConfig.hiddenSSID;
765a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (externalConfig.preSharedKey != null
766a0842b40441db5332a5290f941021636b1182761Sol Boucher                && !externalConfig.preSharedKey.equals(PASSWORD_MASK)) {
767a0842b40441db5332a5290f941021636b1182761Sol Boucher            internalConfig.preSharedKey = externalConfig.preSharedKey;
768a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
769a0842b40441db5332a5290f941021636b1182761Sol Boucher        // Modify only wep keys are present in the provided configuration. This is a little tricky
770a0842b40441db5332a5290f941021636b1182761Sol Boucher        // because there is no easy way to tell if the app is actually trying to null out the
771a0842b40441db5332a5290f941021636b1182761Sol Boucher        // existing keys or not.
772a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (externalConfig.wepKeys != null) {
773a0842b40441db5332a5290f941021636b1182761Sol Boucher            boolean hasWepKey = false;
774a0842b40441db5332a5290f941021636b1182761Sol Boucher            for (int i = 0; i < internalConfig.wepKeys.length; i++) {
775a0842b40441db5332a5290f941021636b1182761Sol Boucher                if (externalConfig.wepKeys[i] != null
776a0842b40441db5332a5290f941021636b1182761Sol Boucher                        && !externalConfig.wepKeys[i].equals(PASSWORD_MASK)) {
777a0842b40441db5332a5290f941021636b1182761Sol Boucher                    internalConfig.wepKeys[i] = externalConfig.wepKeys[i];
778a0842b40441db5332a5290f941021636b1182761Sol Boucher                    hasWepKey = true;
779a0842b40441db5332a5290f941021636b1182761Sol Boucher                }
780a0842b40441db5332a5290f941021636b1182761Sol Boucher            }
781bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala            if (hasWepKey) {
782bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala                internalConfig.wepTxKeyIndex = externalConfig.wepTxKeyIndex;
783bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala            }
784a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
785a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (externalConfig.FQDN != null) {
786a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            internalConfig.FQDN = externalConfig.FQDN;
787984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        }
788a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (externalConfig.providerFriendlyName != null) {
789a0842b40441db5332a5290f941021636b1182761Sol Boucher            internalConfig.providerFriendlyName = externalConfig.providerFriendlyName;
790a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
791a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (externalConfig.roamingConsortiumIds != null) {
792a0842b40441db5332a5290f941021636b1182761Sol Boucher            internalConfig.roamingConsortiumIds = externalConfig.roamingConsortiumIds.clone();
793a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
794a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
795a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        // Copy over all the auth/protocol/key mgmt parameters if set.
796a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        if (externalConfig.allowedAuthAlgorithms != null
797a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                && !externalConfig.allowedAuthAlgorithms.isEmpty()) {
798a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            internalConfig.allowedAuthAlgorithms =
799a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                    (BitSet) externalConfig.allowedAuthAlgorithms.clone();
800a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        }
801a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        if (externalConfig.allowedProtocols != null
802a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                && !externalConfig.allowedProtocols.isEmpty()) {
803a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            internalConfig.allowedProtocols = (BitSet) externalConfig.allowedProtocols.clone();
804a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        }
805bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala        if (externalConfig.allowedKeyManagement != null
806bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala                && !externalConfig.allowedKeyManagement.isEmpty()) {
807984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            internalConfig.allowedKeyManagement =
808984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher                    (BitSet) externalConfig.allowedKeyManagement.clone();
809984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        }
810984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        if (externalConfig.allowedPairwiseCiphers != null
811984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher                && !externalConfig.allowedPairwiseCiphers.isEmpty()) {
812bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala            internalConfig.allowedPairwiseCiphers =
813bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala                    (BitSet) externalConfig.allowedPairwiseCiphers.clone();
814bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala        }
815a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        if (externalConfig.allowedGroupCiphers != null
816984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher                && !externalConfig.allowedGroupCiphers.isEmpty()) {
817984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            internalConfig.allowedGroupCiphers =
818984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher                    (BitSet) externalConfig.allowedGroupCiphers.clone();
819984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        }
820984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
821984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        // Copy over the |IpConfiguration| parameters if set.
822984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        if (externalConfig.getIpConfiguration() != null) {
823984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            IpConfiguration.IpAssignment ipAssignment = externalConfig.getIpAssignment();
824a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            if (ipAssignment != IpConfiguration.IpAssignment.UNASSIGNED) {
825a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                internalConfig.setIpAssignment(ipAssignment);
826a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                if (ipAssignment == IpConfiguration.IpAssignment.STATIC) {
827a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                    internalConfig.setStaticIpConfiguration(
828984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher                            new StaticIpConfiguration(externalConfig.getStaticIpConfiguration()));
829984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher                }
830984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            }
831984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            IpConfiguration.ProxySettings proxySettings = externalConfig.getProxySettings();
832984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            if (proxySettings != IpConfiguration.ProxySettings.UNASSIGNED) {
833a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                internalConfig.setProxySettings(proxySettings);
834a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                if (proxySettings == IpConfiguration.ProxySettings.PAC
835a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                        || proxySettings == IpConfiguration.ProxySettings.STATIC) {
836984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher                    internalConfig.setHttpProxy(new ProxyInfo(externalConfig.getHttpProxy()));
837984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher                }
838984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            }
839984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        }
840984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
841984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        // Copy over the |WifiEnterpriseConfig| parameters if set.
842a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        if (externalConfig.enterpriseConfig != null) {
843984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher            internalConfig.enterpriseConfig.copyFromExternal(
844984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher                    externalConfig.enterpriseConfig, PASSWORD_MASK);
845984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        }
846984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
847984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        // Copy over any metered information.
848984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        internalConfig.meteredHint = externalConfig.meteredHint;
849984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        internalConfig.meteredOverride = externalConfig.meteredOverride;
850984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    }
851984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
852984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    /**
853984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * Set all the exposed defaults in the newly created WifiConfiguration object.
854984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * These fields have a default value advertised in our public documentation. The only exception
855984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * is the hidden |IpConfiguration| parameters, these have a default value even though they're
856984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * hidden.
857a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     *
858a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * @param configuration provided WifiConfiguration object.
859984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     */
860984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    private void setDefaultsInWifiConfiguration(WifiConfiguration configuration) {
8617d71703202c2fba36653293eba2772745db46c00Sol Boucher        configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
8627d71703202c2fba36653293eba2772745db46c00Sol Boucher
8637d71703202c2fba36653293eba2772745db46c00Sol Boucher        configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
864984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
865984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
866984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
867984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
868984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
869984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
870984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
871984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
872984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
873984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
874a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
875a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
876a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
877a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        configuration.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
878de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        configuration.setProxySettings(IpConfiguration.ProxySettings.NONE);
879de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher
880de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        configuration.status = WifiConfiguration.Status.DISABLED;
881de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        configuration.getNetworkSelectionStatus().setNetworkSelectionStatus(
882984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher                NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
883984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    }
884984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
885984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    /**
8867d71703202c2fba36653293eba2772745db46c00Sol Boucher     * Create a new internal WifiConfiguration object by copying over parameters from the provided
887984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * external configuration and set defaults for the appropriate parameters.
888984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     *
889984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * @param externalConfig WifiConfiguration object provided from the external API.
890984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * @return New WifiConfiguration object with parameters merged from the provided external
891984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * configuration.
892984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     */
893984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    private WifiConfiguration createNewInternalWifiConfigurationFromExternal(
8947d71703202c2fba36653293eba2772745db46c00Sol Boucher            WifiConfiguration externalConfig, int uid) {
8957d71703202c2fba36653293eba2772745db46c00Sol Boucher        WifiConfiguration newInternalConfig = new WifiConfiguration();
8967d71703202c2fba36653293eba2772745db46c00Sol Boucher
897984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        // First allocate a new network ID for the configuration.
898984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        newInternalConfig.networkId = mNextNetworkId++;
899984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
900984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        // First set defaults in the new configuration created.
901de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        setDefaultsInWifiConfiguration(newInternalConfig);
902de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher
903984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        // Copy over all the public elements from the provided configuration.
904984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
905984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
906984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        // Copy over the hidden configuration parameters. These are the only parameters used by
907984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        // system apps to indicate some property about the network being added.
908bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala        // These are only copied over for network additions and ignored for network updates.
909984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        newInternalConfig.requirePMF = externalConfig.requirePMF;
910984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        newInternalConfig.noInternetAccessExpected = externalConfig.noInternetAccessExpected;
911984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        newInternalConfig.ephemeral = externalConfig.ephemeral;
912984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        newInternalConfig.useExternalScores = externalConfig.useExternalScores;
913984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        newInternalConfig.shared = externalConfig.shared;
914984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
915de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        // Add debug information for network addition.
916984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher        newInternalConfig.creatorUid = newInternalConfig.lastUpdateUid = uid;
917de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        newInternalConfig.creatorName = newInternalConfig.lastUpdateName =
918de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                mContext.getPackageManager().getNameForUid(uid);
919de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        newInternalConfig.creationTime = newInternalConfig.updateTime =
920de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                createDebugTimeStampString(mClock.getWallClockMillis());
921a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
922a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        return newInternalConfig;
923a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    }
924984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher
925984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher    /**
926984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * Create a new internal WifiConfiguration object by copying over parameters from the provided
927984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * external configuration to a copy of the existing internal WifiConfiguration object.
928984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     *
929984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * @param internalConfig WifiConfiguration object in our internal map.
930984a086412a94ebea1bd9af8cd8bbf4afab38034Sol Boucher     * @param externalConfig WifiConfiguration object provided from the external API.
931a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * @return Copy of existing WifiConfiguration object with parameters merged from the provided
932a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * configuration.
933a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     */
934a0842b40441db5332a5290f941021636b1182761Sol Boucher    private WifiConfiguration updateExistingInternalWifiConfigurationFromExternal(
935a0842b40441db5332a5290f941021636b1182761Sol Boucher            WifiConfiguration internalConfig, WifiConfiguration externalConfig, int uid) {
936a0842b40441db5332a5290f941021636b1182761Sol Boucher        WifiConfiguration newInternalConfig = new WifiConfiguration(internalConfig);
937a0842b40441db5332a5290f941021636b1182761Sol Boucher
938a0842b40441db5332a5290f941021636b1182761Sol Boucher        // Copy over all the public elements from the provided configuration.
939a0842b40441db5332a5290f941021636b1182761Sol Boucher        mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
940a0842b40441db5332a5290f941021636b1182761Sol Boucher
941a0842b40441db5332a5290f941021636b1182761Sol Boucher        // Add debug information for network update.
94280cda8dae801f09c2ce928d0f3feb8a569939fc2Sol Boucher        newInternalConfig.lastUpdateUid = uid;
943415cf3ee18640ad0799e5c9a738afd1d19c14883Sol Boucher        newInternalConfig.lastUpdateName = mContext.getPackageManager().getNameForUid(uid);
944a0842b40441db5332a5290f941021636b1182761Sol Boucher        newInternalConfig.updateTime = createDebugTimeStampString(mClock.getWallClockMillis());
945a0842b40441db5332a5290f941021636b1182761Sol Boucher
946a0842b40441db5332a5290f941021636b1182761Sol Boucher        return newInternalConfig;
947a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
948a0842b40441db5332a5290f941021636b1182761Sol Boucher
949a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
950a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Add a network or update a network configuration to our database.
951a0842b40441db5332a5290f941021636b1182761Sol Boucher     * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
95280cda8dae801f09c2ce928d0f3feb8a569939fc2Sol Boucher     * network configuration. Otherwise, the networkId should refer to an existing configuration.
953415cf3ee18640ad0799e5c9a738afd1d19c14883Sol Boucher     *
954a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @param config provided WifiConfiguration object.
955a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @param uid    UID of the app requesting the network addition/deletion.
956a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @return NetworkUpdateResult object representing status of the update.
957a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
958a0842b40441db5332a5290f941021636b1182761Sol Boucher    private NetworkUpdateResult addOrUpdateNetworkInternal(WifiConfiguration config, int uid) {
959a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (mVerboseLoggingEnabled) {
960a0842b40441db5332a5290f941021636b1182761Sol Boucher            Log.v(TAG, "Adding/Updating network " + config.getPrintableSsid());
961a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
962a0842b40441db5332a5290f941021636b1182761Sol Boucher        WifiConfiguration newInternalConfig = null;
963a0842b40441db5332a5290f941021636b1182761Sol Boucher
964a0842b40441db5332a5290f941021636b1182761Sol Boucher        // First check if we already have a network with the provided network id or configKey.
965a0842b40441db5332a5290f941021636b1182761Sol Boucher        WifiConfiguration existingInternalConfig = getInternalConfiguredNetwork(config);
966a0842b40441db5332a5290f941021636b1182761Sol Boucher        // No existing network found. So, potentially a network add.
967a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (existingInternalConfig == null) {
968a0842b40441db5332a5290f941021636b1182761Sol Boucher            if (!WifiConfigurationUtil.validate(config, WifiConfigurationUtil.VALIDATE_FOR_ADD)) {
969a0842b40441db5332a5290f941021636b1182761Sol Boucher                Log.e(TAG, "Cannot add network with invalid config");
970a0842b40441db5332a5290f941021636b1182761Sol Boucher                return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
971a0842b40441db5332a5290f941021636b1182761Sol Boucher            }
972a0842b40441db5332a5290f941021636b1182761Sol Boucher            newInternalConfig = createNewInternalWifiConfigurationFromExternal(config, uid);
973a0842b40441db5332a5290f941021636b1182761Sol Boucher            // Since the original config provided may have had an empty
974a0842b40441db5332a5290f941021636b1182761Sol Boucher            // {@link WifiConfiguration#allowedKeyMgmt} field, check again if we already have a
975a0842b40441db5332a5290f941021636b1182761Sol Boucher            // network with the the same configkey.
976a0842b40441db5332a5290f941021636b1182761Sol Boucher            existingInternalConfig = getInternalConfiguredNetwork(newInternalConfig.configKey());
977a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
978a0842b40441db5332a5290f941021636b1182761Sol Boucher        // Existing network found. So, a network update.
9792569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher        if (existingInternalConfig != null) {
9802569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher            if (!WifiConfigurationUtil.validate(
9812569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher                    config, WifiConfigurationUtil.VALIDATE_FOR_UPDATE)) {
9822569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher                Log.e(TAG, "Cannot update network with invalid config");
9832569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher                return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
9842569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher            }
9852569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher            // Check for the app's permission before we let it update this network.
9862569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher            if (!canModifyNetwork(existingInternalConfig, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
9872569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher                Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
9882569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher                        + config.configKey());
9892569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher                return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
9902569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher            }
9912569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher            newInternalConfig =
9922569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher                    updateExistingInternalWifiConfigurationFromExternal(
9932569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher                            existingInternalConfig, config, uid);
9942569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher        }
9952569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher
9962569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher        // Only add networks with proxy settings if the user has permission to
9972569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher        if (WifiConfigurationUtil.hasProxyChanged(existingInternalConfig, newInternalConfig)
9982569329d6cff25bfe9941df539df14a0aeb4c4f4Sol Boucher                && !canModifyProxySettings(uid)) {
999a0842b40441db5332a5290f941021636b1182761Sol Boucher            Log.e(TAG, "UID " + uid + " does not have permission to modify proxy Settings "
1000a0842b40441db5332a5290f941021636b1182761Sol Boucher                    + config.configKey() + ". Must have NETWORK_SETTINGS,"
1001a0842b40441db5332a5290f941021636b1182761Sol Boucher                    + " or be device or profile owner.");
1002a0842b40441db5332a5290f941021636b1182761Sol Boucher            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1003a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
1004a0842b40441db5332a5290f941021636b1182761Sol Boucher
1005a0842b40441db5332a5290f941021636b1182761Sol Boucher        // Update the keys for non-Passpoint enterprise networks.  For Passpoint, the certificates
1006a0842b40441db5332a5290f941021636b1182761Sol Boucher        // and keys are installed at the time the provider is installed.
1007a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (config.enterpriseConfig != null
1008a0842b40441db5332a5290f941021636b1182761Sol Boucher                && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE
1009a0842b40441db5332a5290f941021636b1182761Sol Boucher                && !config.isPasspoint()) {
1010a0842b40441db5332a5290f941021636b1182761Sol Boucher            if (!(mWifiKeyStore.updateNetworkKeys(newInternalConfig, existingInternalConfig))) {
1011a0842b40441db5332a5290f941021636b1182761Sol Boucher                return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
101250f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher            }
101350f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher        }
101450f5b019ba3f333a09a1beb9667fd7290082dc31Sol Boucher
1015a0842b40441db5332a5290f941021636b1182761Sol Boucher        boolean newNetwork = (existingInternalConfig == null);
1016a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        // This is needed to inform IpManager about any IP configuration changes.
1017a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        boolean hasIpChanged =
1018a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                newNetwork || WifiConfigurationUtil.hasIpChanged(
1019a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                        existingInternalConfig, newInternalConfig);
1020a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        boolean hasProxyChanged =
1021a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                newNetwork || WifiConfigurationUtil.hasProxyChanged(
1022a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                        existingInternalConfig, newInternalConfig);
1023a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        // Reset the |hasEverConnected| flag if the credential parameters changed in this update.
1024a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        boolean hasCredentialChanged =
1025a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                newNetwork || WifiConfigurationUtil.hasCredentialChanged(
1026a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                        existingInternalConfig, newInternalConfig);
1027a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        if (hasCredentialChanged) {
1028a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            newInternalConfig.getNetworkSelectionStatus().setHasEverConnected(false);
1029a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        }
1030a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
1031a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        // Add it to our internal map. This will replace any existing network configuration for
1032a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        // updates.
1033a0842b40441db5332a5290f941021636b1182761Sol Boucher        try {
1034a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            mConfiguredNetworks.put(newInternalConfig);
1035a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        } catch (IllegalArgumentException e) {
1036a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            Log.e(TAG, "Failed to add network to config map", e);
1037a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1038a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        }
1039a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
1040a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
1041a0842b40441db5332a5290f941021636b1182761Sol Boucher            if (mVerboseLoggingEnabled) {
1042a0842b40441db5332a5290f941021636b1182761Sol Boucher                Log.v(TAG, "Removed from ephemeral blacklist: " + config.SSID);
1043a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher            }
1044a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        }
1045a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
1046a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        // Stage the backup of the SettingsProvider package which backs this up.
1047a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        mBackupManagerProxy.notifyDataChanged();
1048a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
1049a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        NetworkUpdateResult result =
1050a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                new NetworkUpdateResult(hasIpChanged, hasProxyChanged, hasCredentialChanged);
1051a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        result.setIsNewNetwork(newNetwork);
1052a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        result.setNetworkId(newInternalConfig.networkId);
1053a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
1054a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        localLog("addOrUpdateNetworkInternal: added/updated config."
1055a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                + " netId=" + newInternalConfig.networkId
1056a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                + " configKey=" + newInternalConfig.configKey()
1057a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                + " uid=" + Integer.toString(newInternalConfig.creatorUid)
1058a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                + " name=" + newInternalConfig.creatorName);
1059a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        return result;
1060a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    }
1061a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
1062a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher    /**
1063a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * Add a network or update a network configuration to our database.
1064a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher     * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
1065a0842b40441db5332a5290f941021636b1182761Sol Boucher     * network configuration. Otherwise, the networkId should refer to an existing configuration.
1066a0842b40441db5332a5290f941021636b1182761Sol Boucher     *
1067de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * @param config provided WifiConfiguration object.
1068de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * @param uid    UID of the app requesting the network addition/modification.
1069a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @return NetworkUpdateResult object representing status of the update.
1070a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
1071de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid) {
1072de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (!doesUidBelongToCurrentUser(uid)) {
1073de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            Log.e(TAG, "UID " + uid + " not visible to the current user");
1074de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1075de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        }
1076de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (config == null) {
1077171362f843c1e7623f29db1781176f85b1f74815Eino-Ville Talvala            Log.e(TAG, "Cannot add/update network with null config");
1078de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1079de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        }
1080de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (mPendingStoreRead) {
1081de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            Log.e(TAG, "Cannot add/update network before store is read!");
1082415cf3ee18640ad0799e5c9a738afd1d19c14883Sol Boucher            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
1083415cf3ee18640ad0799e5c9a738afd1d19c14883Sol Boucher        }
1084415cf3ee18640ad0799e5c9a738afd1d19c14883Sol Boucher        NetworkUpdateResult result = addOrUpdateNetworkInternal(config, uid);
1085de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (!result.isSuccess()) {
1086de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            Log.e(TAG, "Failed to add/update network " + config.getPrintableSsid());
1087de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            return result;
1088de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        }
1089a0842b40441db5332a5290f941021636b1182761Sol Boucher        WifiConfiguration newConfig = getInternalConfiguredNetwork(result.getNetworkId());
1090de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        sendConfiguredNetworkChangedBroadcast(
1091de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                newConfig,
1092de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                result.isNewNetwork()
1093de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                        ? WifiManager.CHANGE_REASON_ADDED
1094de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                        : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1095de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        // Unless the added network is ephemeral or Passpoint, persist the network update/addition.
1096de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (!config.ephemeral && !config.isPasspoint()) {
1097de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            saveToStore(true);
1098de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            if (mListener != null) {
1099de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                if (result.isNewNetwork()) {
1100de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                    mListener.onSavedNetworkAdded(newConfig.networkId);
1101de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                } else {
1102de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                    mListener.onSavedNetworkUpdated(newConfig.networkId);
1103de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                }
1104de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            }
1105de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        }
1106de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        return result;
1107de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher    }
1108772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher
1109772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher    /**
1110772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher     * Removes the specified network configuration from our database.
1111de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     *
1112de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * @param config provided WifiConfiguration object.
1113de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     * @return true if successful, false otherwise.
1114de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher     */
1115a0842b40441db5332a5290f941021636b1182761Sol Boucher    private boolean removeNetworkInternal(WifiConfiguration config) {
1116a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (mVerboseLoggingEnabled) {
1117a0842b40441db5332a5290f941021636b1182761Sol Boucher            Log.v(TAG, "Removing network " + config.getPrintableSsid());
1118a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
1119a0842b40441db5332a5290f941021636b1182761Sol Boucher        // Remove any associated enterprise keys for non-Passpoint networks.
1120a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (!config.isPasspoint() && config.enterpriseConfig != null
1121a0842b40441db5332a5290f941021636b1182761Sol Boucher                && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
1122a0842b40441db5332a5290f941021636b1182761Sol Boucher            mWifiKeyStore.removeKeys(config.enterpriseConfig);
1123a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
1124a0842b40441db5332a5290f941021636b1182761Sol Boucher
1125a0842b40441db5332a5290f941021636b1182761Sol Boucher        removeConnectChoiceFromAllNetworks(config.configKey());
1126a0842b40441db5332a5290f941021636b1182761Sol Boucher        mConfiguredNetworks.remove(config.networkId);
1127a0842b40441db5332a5290f941021636b1182761Sol Boucher        mScanDetailCaches.remove(config.networkId);
1128a0842b40441db5332a5290f941021636b1182761Sol Boucher        // Stage the backup of the SettingsProvider package which backs this up.
1129a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        mBackupManagerProxy.notifyDataChanged();
1130a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher
1131a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        localLog("removeNetworkInternal: removed config."
1132a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                + " netId=" + config.networkId
1133a0842b40441db5332a5290f941021636b1182761Sol Boucher                + " configKey=" + config.configKey());
1134a0842b40441db5332a5290f941021636b1182761Sol Boucher        return true;
1135a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
1136a0842b40441db5332a5290f941021636b1182761Sol Boucher
1137a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
1138a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Removes the specified network configuration from our database.
1139a0842b40441db5332a5290f941021636b1182761Sol Boucher     *
1140a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @param networkId network ID of the provided network.
1141a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @param uid       UID of the app requesting the network deletion.
1142a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @return true if successful, false otherwise.
1143a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
1144a0842b40441db5332a5290f941021636b1182761Sol Boucher    public boolean removeNetwork(int networkId, int uid) {
1145a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (!doesUidBelongToCurrentUser(uid)) {
1146a0842b40441db5332a5290f941021636b1182761Sol Boucher            Log.e(TAG, "UID " + uid + " not visible to the current user");
114780cda8dae801f09c2ce928d0f3feb8a569939fc2Sol Boucher            return false;
114880cda8dae801f09c2ce928d0f3feb8a569939fc2Sol Boucher        }
114980cda8dae801f09c2ce928d0f3feb8a569939fc2Sol Boucher        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
115080cda8dae801f09c2ce928d0f3feb8a569939fc2Sol Boucher        if (config == null) {
1151a0842b40441db5332a5290f941021636b1182761Sol Boucher            return false;
1152a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
1153a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
1154a0842b40441db5332a5290f941021636b1182761Sol Boucher            Log.e(TAG, "UID " + uid + " does not have permission to delete configuration "
1155de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher                    + config.configKey());
1156de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            return false;
1157de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        }
1158de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (!removeNetworkInternal(config)) {
1159de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            Log.e(TAG, "Failed to remove network " + config.getPrintableSsid());
1160de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            return false;
1161de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        }
1162de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher        if (networkId == mLastSelectedNetworkId) {
1163de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            clearLastSelectedNetwork();
1164772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher        }
1165772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher        sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
116680cda8dae801f09c2ce928d0f3feb8a569939fc2Sol Boucher        // Unless the removed network is ephemeral or Passpoint, persist the network removal.
116780cda8dae801f09c2ce928d0f3feb8a569939fc2Sol Boucher        if (!config.ephemeral && !config.isPasspoint()) {
116880cda8dae801f09c2ce928d0f3feb8a569939fc2Sol Boucher            saveToStore(true);
116980cda8dae801f09c2ce928d0f3feb8a569939fc2Sol Boucher            if (mListener != null) mListener.onSavedNetworkRemoved(networkId);
1170a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
1171a0842b40441db5332a5290f941021636b1182761Sol Boucher        return true;
1172415cf3ee18640ad0799e5c9a738afd1d19c14883Sol Boucher    }
1173415cf3ee18640ad0799e5c9a738afd1d19c14883Sol Boucher
1174415cf3ee18640ad0799e5c9a738afd1d19c14883Sol Boucher    /**
1175415cf3ee18640ad0799e5c9a738afd1d19c14883Sol Boucher     * Remove all networks associated with an application.
1176415cf3ee18640ad0799e5c9a738afd1d19c14883Sol Boucher     *
1177a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @param app Application info of the package of networks to remove.
1178a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @return the {@link Set} of networks that were removed by this call. Networks which matched
1179a0842b40441db5332a5290f941021636b1182761Sol Boucher     *         but failed to remove are omitted from this set.
1180a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
1181a0842b40441db5332a5290f941021636b1182761Sol Boucher    public Set<Integer> removeNetworksForApp(ApplicationInfo app) {
1182a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (app == null || app.packageName == null) {
1183a0842b40441db5332a5290f941021636b1182761Sol Boucher            return Collections.<Integer>emptySet();
1184a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
1185a0842b40441db5332a5290f941021636b1182761Sol Boucher        Log.d(TAG, "Remove all networks for app " + app);
1186a0842b40441db5332a5290f941021636b1182761Sol Boucher        Set<Integer> removedNetworks = new ArraySet<>();
1187a0842b40441db5332a5290f941021636b1182761Sol Boucher        WifiConfiguration[] copiedConfigs =
1188a0842b40441db5332a5290f941021636b1182761Sol Boucher                mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1189a0842b40441db5332a5290f941021636b1182761Sol Boucher        for (WifiConfiguration config : copiedConfigs) {
1190a0842b40441db5332a5290f941021636b1182761Sol Boucher            if (app.uid != config.creatorUid || !app.packageName.equals(config.creatorName)) {
1191a0842b40441db5332a5290f941021636b1182761Sol Boucher                continue;
1192a0842b40441db5332a5290f941021636b1182761Sol Boucher            }
1193a0842b40441db5332a5290f941021636b1182761Sol Boucher            localLog("Removing network " + config.SSID
1194a0842b40441db5332a5290f941021636b1182761Sol Boucher                    + ", application \"" + app.packageName + "\" uninstalled"
1195a0842b40441db5332a5290f941021636b1182761Sol Boucher                    + " from user " + UserHandle.getUserId(app.uid));
1196a0842b40441db5332a5290f941021636b1182761Sol Boucher            if (removeNetwork(config.networkId, mSystemUiUid)) {
1197a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher                removedNetworks.add(config.networkId);
1198a0842b40441db5332a5290f941021636b1182761Sol Boucher            }
1199a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        }
1200a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        return removedNetworks;
1201772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher    }
1202772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher
1203772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher    /**
1204a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Remove all networks associated with a user.
1205a0842b40441db5332a5290f941021636b1182761Sol Boucher     *
1206772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher     * @param userId The identifier of the user which is being removed.
1207a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @return the {@link Set} of networks that were removed by this call. Networks which matched
1208772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher     *         but failed to remove are omitted from this set.
1209a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
1210772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher    Set<Integer> removeNetworksForUser(int userId) {
1211a0842b40441db5332a5290f941021636b1182761Sol Boucher        Log.d(TAG, "Remove all networks for user " + userId);
1212772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher        Set<Integer> removedNetworks = new ArraySet<>();
1213a0842b40441db5332a5290f941021636b1182761Sol Boucher        WifiConfiguration[] copiedConfigs =
1214772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher                mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1215a97b7d1192e246a5f738991adca37cce282e1382Sol Boucher        for (WifiConfiguration config : copiedConfigs) {
1216772fcce3e51a9c7d33df6a9c278a908ac6902880Sol Boucher            if (userId != UserHandle.getUserId(config.creatorUid)) {
1217a0842b40441db5332a5290f941021636b1182761Sol Boucher                continue;
1218a0842b40441db5332a5290f941021636b1182761Sol Boucher            }
1219a0842b40441db5332a5290f941021636b1182761Sol Boucher            localLog("Removing network " + config.SSID + ", user " + userId + " removed");
1220a0842b40441db5332a5290f941021636b1182761Sol Boucher            if (removeNetwork(config.networkId, mSystemUiUid)) {
1221a0842b40441db5332a5290f941021636b1182761Sol Boucher                removedNetworks.add(config.networkId);
1222a0842b40441db5332a5290f941021636b1182761Sol Boucher            }
12237e0d39bf7b6e0f0df606e3f6c15f673f70fed3f7Sol Boucher        }
1224a0842b40441db5332a5290f941021636b1182761Sol Boucher        return removedNetworks;
1225a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
1226a0842b40441db5332a5290f941021636b1182761Sol Boucher
1227a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
1228a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Iterates through the internal list of configured networks and removes any ephemeral or
1229a0842b40441db5332a5290f941021636b1182761Sol Boucher     * passpoint network configurations which are transient in nature.
1230a0842b40441db5332a5290f941021636b1182761Sol Boucher     *
1231a0842b40441db5332a5290f941021636b1182761Sol Boucher     * @return true if a network was removed, false otherwise.
1232a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
1233a0842b40441db5332a5290f941021636b1182761Sol Boucher    public boolean removeAllEphemeralOrPasspointConfiguredNetworks() {
1234a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (mVerboseLoggingEnabled) {
1235a0842b40441db5332a5290f941021636b1182761Sol Boucher            Log.v(TAG, "Removing all passpoint or ephemeral configured networks");
1236a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
1237a0842b40441db5332a5290f941021636b1182761Sol Boucher        boolean didRemove = false;
1238a0842b40441db5332a5290f941021636b1182761Sol Boucher        WifiConfiguration[] copiedConfigs =
1239a0842b40441db5332a5290f941021636b1182761Sol Boucher                mConfiguredNetworks.valuesForAllUsers().toArray(new WifiConfiguration[0]);
1240a0842b40441db5332a5290f941021636b1182761Sol Boucher        for (WifiConfiguration config : copiedConfigs) {
1241a0842b40441db5332a5290f941021636b1182761Sol Boucher            if (config.isPasspoint()) {
1242a0842b40441db5332a5290f941021636b1182761Sol Boucher                Log.d(TAG, "Removing passpoint network config " + config.configKey());
1243a0842b40441db5332a5290f941021636b1182761Sol Boucher                removeNetwork(config.networkId, mSystemUiUid);
1244a0842b40441db5332a5290f941021636b1182761Sol Boucher                didRemove = true;
1245a0842b40441db5332a5290f941021636b1182761Sol Boucher            } else if (config.ephemeral) {
1246a0842b40441db5332a5290f941021636b1182761Sol Boucher                Log.d(TAG, "Removing ephemeral network config " + config.configKey());
1247a0842b40441db5332a5290f941021636b1182761Sol Boucher                removeNetwork(config.networkId, mSystemUiUid);
1248a0842b40441db5332a5290f941021636b1182761Sol Boucher                didRemove = true;
1249a0842b40441db5332a5290f941021636b1182761Sol Boucher            }
1250a0842b40441db5332a5290f941021636b1182761Sol Boucher        }
1251a0842b40441db5332a5290f941021636b1182761Sol Boucher        return didRemove;
1252a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
1253a0842b40441db5332a5290f941021636b1182761Sol Boucher
1254a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
1255a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Helper method to mark a network enabled for network selection.
1256a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
1257a0842b40441db5332a5290f941021636b1182761Sol Boucher    private void setNetworkSelectionEnabled(WifiConfiguration config) {
1258a0842b40441db5332a5290f941021636b1182761Sol Boucher        NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1259a0842b40441db5332a5290f941021636b1182761Sol Boucher        status.setNetworkSelectionStatus(
1260a0842b40441db5332a5290f941021636b1182761Sol Boucher                NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
1261a0842b40441db5332a5290f941021636b1182761Sol Boucher        status.setDisableTime(
1262a0842b40441db5332a5290f941021636b1182761Sol Boucher                NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1263a0842b40441db5332a5290f941021636b1182761Sol Boucher        status.setNetworkSelectionDisableReason(NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1264a0842b40441db5332a5290f941021636b1182761Sol Boucher
1265a0842b40441db5332a5290f941021636b1182761Sol Boucher        // Clear out all the disable reason counters.
1266a0842b40441db5332a5290f941021636b1182761Sol Boucher        status.clearDisableReasonCounter();
1267a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (mListener != null) mListener.onSavedNetworkEnabled(config.networkId);
1268a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
1269a0842b40441db5332a5290f941021636b1182761Sol Boucher
1270a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
1271a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Helper method to mark a network temporarily disabled for network selection.
1272a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
1273a0842b40441db5332a5290f941021636b1182761Sol Boucher    private void setNetworkSelectionTemporarilyDisabled(
1274a0842b40441db5332a5290f941021636b1182761Sol Boucher            WifiConfiguration config, int disableReason) {
1275a0842b40441db5332a5290f941021636b1182761Sol Boucher        NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1276a0842b40441db5332a5290f941021636b1182761Sol Boucher        status.setNetworkSelectionStatus(
1277a0842b40441db5332a5290f941021636b1182761Sol Boucher                NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
1278a0842b40441db5332a5290f941021636b1182761Sol Boucher        // Only need a valid time filled in for temporarily disabled networks.
1279a0842b40441db5332a5290f941021636b1182761Sol Boucher        status.setDisableTime(mClock.getElapsedSinceBootMillis());
1280a0842b40441db5332a5290f941021636b1182761Sol Boucher        status.setNetworkSelectionDisableReason(disableReason);
1281a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (mListener != null) mListener.onSavedNetworkTemporarilyDisabled(config.networkId);
1282a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
1283a0842b40441db5332a5290f941021636b1182761Sol Boucher
1284a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
1285a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Helper method to mark a network permanently disabled for network selection.
1286a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
1287a0842b40441db5332a5290f941021636b1182761Sol Boucher    private void setNetworkSelectionPermanentlyDisabled(
1288de48004068f8c16f9a56c60b0ed2485a67687b4bSol Boucher            WifiConfiguration config, int disableReason) {
1289a0842b40441db5332a5290f941021636b1182761Sol Boucher        NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1290a0842b40441db5332a5290f941021636b1182761Sol Boucher        status.setNetworkSelectionStatus(
1291a0842b40441db5332a5290f941021636b1182761Sol Boucher                NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
1292a0842b40441db5332a5290f941021636b1182761Sol Boucher        status.setDisableTime(
1293a0842b40441db5332a5290f941021636b1182761Sol Boucher                NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1294a0842b40441db5332a5290f941021636b1182761Sol Boucher        status.setNetworkSelectionDisableReason(disableReason);
1295a0842b40441db5332a5290f941021636b1182761Sol Boucher        if (mListener != null) mListener.onSavedNetworkPermanentlyDisabled(config.networkId);
1296a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
1297a0842b40441db5332a5290f941021636b1182761Sol Boucher
1298a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
1299a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Helper method to set the publicly exposed status for the network and send out the network
1300a0842b40441db5332a5290f941021636b1182761Sol Boucher     * status change broadcast.
1301a0842b40441db5332a5290f941021636b1182761Sol Boucher     */
1302a0842b40441db5332a5290f941021636b1182761Sol Boucher    private void setNetworkStatus(WifiConfiguration config, int status) {
1303a0842b40441db5332a5290f941021636b1182761Sol Boucher        config.status = status;
1304a0842b40441db5332a5290f941021636b1182761Sol Boucher        sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE);
1305a0842b40441db5332a5290f941021636b1182761Sol Boucher    }
1306a0842b40441db5332a5290f941021636b1182761Sol Boucher
1307a0842b40441db5332a5290f941021636b1182761Sol Boucher    /**
1308a0842b40441db5332a5290f941021636b1182761Sol Boucher     * Sets a network's status (both internal and public) according to the update reason and
1309a0842b40441db5332a5290f941021636b1182761Sol Boucher     * its current state.
1310a0842b40441db5332a5290f941021636b1182761Sol Boucher     *
1311a0842b40441db5332a5290f941021636b1182761Sol Boucher     * This updates the network's {@link WifiConfiguration#mNetworkSelectionStatus} field and the
1312a0842b40441db5332a5290f941021636b1182761Sol Boucher     * public {@link WifiConfiguration#status} field if the network is either enabled or
1313f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher     * permanently disabled.
1314f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher     *
1315f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher     * @param config network to be updated.
1316f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher     * @param reason reason code for update.
1317f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher     * @return true if the input configuration has been updated, false otherwise.
1318f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher     */
1319f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher    private boolean setNetworkSelectionStatus(WifiConfiguration config, int reason) {
1320f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher        NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1321f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher        if (reason < 0 || reason >= NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX) {
1322f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher            Log.e(TAG, "Invalid Network disable reason " + reason);
1323f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher            return false;
1324f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher        }
1325f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher        if (reason == NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
1326f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher            setNetworkSelectionEnabled(config);
1327f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher            setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
1328f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher        } else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
1329f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher            setNetworkSelectionTemporarilyDisabled(config, reason);
1330f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher        } else {
1331f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher            setNetworkSelectionPermanentlyDisabled(config, reason);
1332f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher            setNetworkStatus(config, WifiConfiguration.Status.DISABLED);
1333f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher        }
1334f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher        localLog("setNetworkSelectionStatus: configKey=" + config.configKey()
1335f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher                + " networkStatus=" + networkStatus.getNetworkStatusString() + " disableReason="
1336a0842b40441db5332a5290f941021636b1182761Sol Boucher                + networkStatus.getNetworkDisableReasonString() + " at="
1337415cf3ee18640ad0799e5c9a738afd1d19c14883Sol Boucher                + createDebugTimeStampString(mClock.getWallClockMillis()));
1338a0842b40441db5332a5290f941021636b1182761Sol Boucher        saveToStore(false);
1339f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher        return true;
1340f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher    }
1341f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher
1342f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher    /**
1343f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher     * Update a network's status (both internal and public) according to the update reason and
1344f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher     * its current state.
1345f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher     *
1346f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher     * @param config network to be updated.
1347f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher     * @param reason reason code for update.
1348f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher     * @return true if the input configuration has been updated, false otherwise.
1349f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher     */
1350f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher    private boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) {
1351f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher        NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1352f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher        if (reason != NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
1353f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher            networkStatus.incrementDisableReasonCounter(reason);
1354f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher            // For network disable reasons, we should only update the status if we cross the
1355f9feab9a826e5b33d811e757bdfdbfa0738fcfa5Sol Boucher            // threshold.
1356a0842b40441db5332a5290f941021636b1182761Sol Boucher            int disableReasonCounter = networkStatus.getDisableReasonCounter(reason);
1357a0842b40441db5332a5290f941021636b1182761Sol Boucher            int disableReasonThreshold = NETWORK_SELECTION_DISABLE_THRESHOLD[reason];
1358a0842b40441db5332a5290f941021636b1182761Sol Boucher            if (disableReasonCounter < disableReasonThreshold) {
1359a0842b40441db5332a5290f941021636b1182761Sol Boucher                if (mVerboseLoggingEnabled) {
1360a0842b40441db5332a5290f941021636b1182761Sol Boucher                    Log.v(TAG, "Disable counter for network " + config.getPrintableSsid()
1361a0842b40441db5332a5290f941021636b1182761Sol Boucher                            + " for reason "
1362a0842b40441db5332a5290f941021636b1182761Sol Boucher                            + NetworkSelectionStatus.getNetworkDisableReasonString(reason) + " is "
1363a0842b40441db5332a5290f941021636b1182761Sol Boucher                            + networkStatus.getDisableReasonCounter(reason) + " and threshold is "
1364a0842b40441db5332a5290f941021636b1182761Sol Boucher                            + disableReasonThreshold);
1365a0842b40441db5332a5290f941021636b1182761Sol Boucher                }
1366a0842b40441db5332a5290f941021636b1182761Sol Boucher                return true;
1367            }
1368        }
1369        return setNetworkSelectionStatus(config, reason);
1370    }
1371
1372    /**
1373     * Update a network's status (both internal and public) according to the update reason and
1374     * its current state.
1375     *
1376     * Each network has 2 status:
1377     * 1. NetworkSelectionStatus: This is internal selection status of the network. This is used
1378     * for temporarily disabling a network for Network Selector.
1379     * 2. Status: This is the exposed status for a network. This is mostly set by
1380     * the public API's {@link WifiManager#enableNetwork(int, boolean)} &
1381     * {@link WifiManager#disableNetwork(int)}.
1382     *
1383     * @param networkId network ID of the network that needs the update.
1384     * @param reason    reason to update the network.
1385     * @return true if the input configuration has been updated, false otherwise.
1386     */
1387    public boolean updateNetworkSelectionStatus(int networkId, int reason) {
1388        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1389        if (config == null) {
1390            return false;
1391        }
1392        return updateNetworkSelectionStatus(config, reason);
1393    }
1394
1395    /**
1396     * Update whether a network is currently not recommended by {@link RecommendedNetworkEvaluator}.
1397     *
1398     * @param networkId network ID of the network to be updated
1399     * @param notRecommended whether this network is not recommended
1400     * @return true if the network is updated, false otherwise
1401     */
1402    public boolean updateNetworkNotRecommended(int networkId, boolean notRecommended) {
1403        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1404        if (config == null) {
1405            return false;
1406        }
1407
1408        config.getNetworkSelectionStatus().setNotRecommended(notRecommended);
1409        if (mVerboseLoggingEnabled) {
1410            localLog("updateNetworkRecommendation: configKey=" + config.configKey()
1411                    + " notRecommended=" + notRecommended);
1412        }
1413        saveToStore(false);
1414        return true;
1415    }
1416
1417    /**
1418     * Attempt to re-enable a network for network selection, if this network was either:
1419     * a) Previously temporarily disabled, but its disable timeout has expired, or
1420     * b) Previously disabled because of a user switch, but is now visible to the current
1421     * user.
1422     *
1423     * @param config configuration for the network to be re-enabled for network selection. The
1424     *               network corresponding to the config must be visible to the current user.
1425     * @return true if the network identified by {@param config} was re-enabled for qualified
1426     * network selection, false otherwise.
1427     */
1428    private boolean tryEnableNetwork(WifiConfiguration config) {
1429        NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
1430        if (networkStatus.isNetworkTemporaryDisabled()) {
1431            long timeDifferenceMs =
1432                    mClock.getElapsedSinceBootMillis() - networkStatus.getDisableTime();
1433            int disableReason = networkStatus.getNetworkSelectionDisableReason();
1434            long disableTimeoutMs = NETWORK_SELECTION_DISABLE_TIMEOUT_MS[disableReason];
1435            if (timeDifferenceMs >= disableTimeoutMs) {
1436                return updateNetworkSelectionStatus(
1437                        config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1438            }
1439        } else if (networkStatus.isDisabledByReason(
1440                NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH)) {
1441            return updateNetworkSelectionStatus(
1442                    config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
1443        }
1444        return false;
1445    }
1446
1447    /**
1448     * Attempt to re-enable a network for network selection, if this network was either:
1449     * a) Previously temporarily disabled, but its disable timeout has expired, or
1450     * b) Previously disabled because of a user switch, but is now visible to the current
1451     * user.
1452     *
1453     * @param networkId the id of the network to be checked for possible unblock (due to timeout)
1454     * @return true if the network identified by {@param networkId} was re-enabled for qualified
1455     * network selection, false otherwise.
1456     */
1457    public boolean tryEnableNetwork(int networkId) {
1458        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1459        if (config == null) {
1460            return false;
1461        }
1462        return tryEnableNetwork(config);
1463    }
1464
1465    /**
1466     * Enable a network using the public {@link WifiManager#enableNetwork(int, boolean)} API.
1467     *
1468     * @param networkId     network ID of the network that needs the update.
1469     * @param disableOthers Whether to disable all other networks or not. This is used to indicate
1470     *                      that the app requested connection to a specific network.
1471     * @param uid           uid of the app requesting the update.
1472     * @return true if it succeeds, false otherwise
1473     */
1474    public boolean enableNetwork(int networkId, boolean disableOthers, int uid) {
1475        if (mVerboseLoggingEnabled) {
1476            Log.v(TAG, "Enabling network " + networkId + " (disableOthers " + disableOthers + ")");
1477        }
1478        if (!doesUidBelongToCurrentUser(uid)) {
1479            Log.e(TAG, "UID " + uid + " not visible to the current user");
1480            return false;
1481        }
1482        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1483        if (config == null) {
1484            return false;
1485        }
1486        if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
1487            Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1488                    + config.configKey());
1489            return false;
1490        }
1491        if (!updateNetworkSelectionStatus(
1492                networkId, WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE)) {
1493            return false;
1494        }
1495        if (disableOthers) {
1496            setLastSelectedNetwork(networkId);
1497        }
1498        saveToStore(true);
1499        return true;
1500    }
1501
1502    /**
1503     * Disable a network using the public {@link WifiManager#disableNetwork(int)} API.
1504     *
1505     * @param networkId network ID of the network that needs the update.
1506     * @param uid       uid of the app requesting the update.
1507     * @return true if it succeeds, false otherwise
1508     */
1509    public boolean disableNetwork(int networkId, int uid) {
1510        if (mVerboseLoggingEnabled) {
1511            Log.v(TAG, "Disabling network " + networkId);
1512        }
1513        if (!doesUidBelongToCurrentUser(uid)) {
1514            Log.e(TAG, "UID " + uid + " not visible to the current user");
1515            return false;
1516        }
1517        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1518        if (config == null) {
1519            return false;
1520        }
1521        if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
1522            Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1523                    + config.configKey());
1524            return false;
1525        }
1526        if (!updateNetworkSelectionStatus(
1527                networkId, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)) {
1528            return false;
1529        }
1530        if (networkId == mLastSelectedNetworkId) {
1531            clearLastSelectedNetwork();
1532        }
1533        saveToStore(true);
1534        return true;
1535    }
1536
1537    /**
1538     * Checks if the |uid| has the necessary permission to force a connection to a network
1539     * and updates the last connected UID for the provided configuration.
1540     *
1541     * @param networkId network ID corresponding to the network.
1542     * @param uid       uid of the app requesting the connection.
1543     * @return true if |uid| has the necessary permission to trigger explicit connection to the
1544     * network, false otherwise.
1545     * Note: This returns true only for the system settings/sysui app which holds the
1546     * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. We don't want to let
1547     * any other app force connection to a network.
1548     */
1549    public boolean checkAndUpdateLastConnectUid(int networkId, int uid) {
1550        if (mVerboseLoggingEnabled) {
1551            Log.v(TAG, "Update network last connect UID for " + networkId);
1552        }
1553        if (!doesUidBelongToCurrentUser(uid)) {
1554            Log.e(TAG, "UID " + uid + " not visible to the current user");
1555            return false;
1556        }
1557        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1558        if (config == null) {
1559            return false;
1560        }
1561        if (!canModifyNetwork(config, uid, ALLOW_LOCKDOWN_CHECK_BYPASS)) {
1562            Log.e(TAG, "UID " + uid + " does not have permission to update configuration "
1563                    + config.configKey());
1564            return false;
1565        }
1566        config.lastConnectUid = uid;
1567        return true;
1568    }
1569
1570    /**
1571     * Updates a network configuration after a successful connection to it.
1572     *
1573     * This method updates the following WifiConfiguration elements:
1574     * 1. Set the |lastConnected| timestamp.
1575     * 2. Increment |numAssociation| counter.
1576     * 3. Clear the disable reason counters in the associated |NetworkSelectionStatus|.
1577     * 4. Set the hasEverConnected| flag in the associated |NetworkSelectionStatus|.
1578     * 5. Sets the status of network as |CURRENT|.
1579     *
1580     * @param networkId network ID corresponding to the network.
1581     * @return true if the network was found, false otherwise.
1582     */
1583    public boolean updateNetworkAfterConnect(int networkId) {
1584        if (mVerboseLoggingEnabled) {
1585            Log.v(TAG, "Update network after connect for " + networkId);
1586        }
1587        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1588        if (config == null) {
1589            return false;
1590        }
1591        config.lastConnected = mClock.getWallClockMillis();
1592        config.numAssociation++;
1593        config.getNetworkSelectionStatus().clearDisableReasonCounter();
1594        config.getNetworkSelectionStatus().setHasEverConnected(true);
1595        setNetworkStatus(config, WifiConfiguration.Status.CURRENT);
1596        saveToStore(false);
1597        return true;
1598    }
1599
1600    /**
1601     * Updates a network configuration after disconnection from it.
1602     *
1603     * This method updates the following WifiConfiguration elements:
1604     * 1. Set the |lastDisConnected| timestamp.
1605     * 2. Sets the status of network back to |ENABLED|.
1606     *
1607     * @param networkId network ID corresponding to the network.
1608     * @return true if the network was found, false otherwise.
1609     */
1610    public boolean updateNetworkAfterDisconnect(int networkId) {
1611        if (mVerboseLoggingEnabled) {
1612            Log.v(TAG, "Update network after disconnect for " + networkId);
1613        }
1614        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1615        if (config == null) {
1616            return false;
1617        }
1618        config.lastDisconnected = mClock.getWallClockMillis();
1619        // If the network hasn't been disabled, mark it back as
1620        // enabled after disconnection.
1621        if (config.status == WifiConfiguration.Status.CURRENT) {
1622            setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
1623        }
1624        saveToStore(false);
1625        return true;
1626    }
1627
1628    /**
1629     * Set default GW MAC address for the provided network.
1630     *
1631     * @param networkId  network ID corresponding to the network.
1632     * @param macAddress MAC address of the gateway to be set.
1633     * @return true if the network was found, false otherwise.
1634     */
1635    public boolean setNetworkDefaultGwMacAddress(int networkId, String macAddress) {
1636        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1637        if (config == null) {
1638            return false;
1639        }
1640        config.defaultGwMacAddress = macAddress;
1641        return true;
1642    }
1643
1644    /**
1645     * Clear the {@link NetworkSelectionStatus#mCandidate},
1646     * {@link NetworkSelectionStatus#mCandidateScore} &
1647     * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
1648     *
1649     * This is invoked by Network Selector at the start of every selection procedure to clear all
1650     * configured networks' scan-result-candidates.
1651     *
1652     * @param networkId network ID corresponding to the network.
1653     * @return true if the network was found, false otherwise.
1654     */
1655    public boolean clearNetworkCandidateScanResult(int networkId) {
1656        if (mVerboseLoggingEnabled) {
1657            Log.v(TAG, "Clear network candidate scan result for " + networkId);
1658        }
1659        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1660        if (config == null) {
1661            return false;
1662        }
1663        config.getNetworkSelectionStatus().setCandidate(null);
1664        config.getNetworkSelectionStatus().setCandidateScore(Integer.MIN_VALUE);
1665        config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(false);
1666        return true;
1667    }
1668
1669    /**
1670     * Set the {@link NetworkSelectionStatus#mCandidate},
1671     * {@link NetworkSelectionStatus#mCandidateScore} &
1672     * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
1673     *
1674     * This is invoked by Network Selector when it sees a network during network selection procedure
1675     * to set the scan result candidate.
1676     *
1677     * @param networkId  network ID corresponding to the network.
1678     * @param scanResult Candidate ScanResult associated with this network.
1679     * @param score      Score assigned to the candidate.
1680     * @return true if the network was found, false otherwise.
1681     */
1682    public boolean setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score) {
1683        if (mVerboseLoggingEnabled) {
1684            Log.v(TAG, "Set network candidate scan result " + scanResult + " for " + networkId);
1685        }
1686        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1687        if (config == null) {
1688            return false;
1689        }
1690        config.getNetworkSelectionStatus().setCandidate(scanResult);
1691        config.getNetworkSelectionStatus().setCandidateScore(score);
1692        config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(true);
1693        return true;
1694    }
1695
1696    /**
1697     * Iterate through all the saved networks and remove the provided configuration from the
1698     * {@link NetworkSelectionStatus#mConnectChoice} from them.
1699     *
1700     * This is invoked when a network is removed from our records.
1701     *
1702     * @param connectChoiceConfigKey ConfigKey corresponding to the network that is being removed.
1703     */
1704    private void removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey) {
1705        if (mVerboseLoggingEnabled) {
1706            Log.v(TAG, "Removing connect choice from all networks " + connectChoiceConfigKey);
1707        }
1708        if (connectChoiceConfigKey == null) {
1709            return;
1710        }
1711        for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
1712            WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
1713            String connectChoice = status.getConnectChoice();
1714            if (TextUtils.equals(connectChoice, connectChoiceConfigKey)) {
1715                Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID
1716                        + " : " + config.networkId);
1717                clearNetworkConnectChoice(config.networkId);
1718            }
1719        }
1720    }
1721
1722    /**
1723     * Clear the {@link NetworkSelectionStatus#mConnectChoice} &
1724     * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network.
1725     *
1726     * @param networkId network ID corresponding to the network.
1727     * @return true if the network was found, false otherwise.
1728     */
1729    public boolean clearNetworkConnectChoice(int networkId) {
1730        if (mVerboseLoggingEnabled) {
1731            Log.v(TAG, "Clear network connect choice for " + networkId);
1732        }
1733        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1734        if (config == null) {
1735            return false;
1736        }
1737        config.getNetworkSelectionStatus().setConnectChoice(null);
1738        config.getNetworkSelectionStatus().setConnectChoiceTimestamp(
1739                NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
1740        saveToStore(false);
1741        return true;
1742    }
1743
1744    /**
1745     * Set the {@link NetworkSelectionStatus#mConnectChoice} &
1746     * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network.
1747     *
1748     * This is invoked by Network Selector when the user overrides the currently connected network
1749     * choice.
1750     *
1751     * @param networkId              network ID corresponding to the network.
1752     * @param connectChoiceConfigKey ConfigKey corresponding to the network which was chosen over
1753     *                               this network.
1754     * @param timestamp              timestamp at which the choice was made.
1755     * @return true if the network was found, false otherwise.
1756     */
1757    public boolean setNetworkConnectChoice(
1758            int networkId, String connectChoiceConfigKey, long timestamp) {
1759        if (mVerboseLoggingEnabled) {
1760            Log.v(TAG, "Set network connect choice " + connectChoiceConfigKey + " for " + networkId);
1761        }
1762        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1763        if (config == null) {
1764            return false;
1765        }
1766        config.getNetworkSelectionStatus().setConnectChoice(connectChoiceConfigKey);
1767        config.getNetworkSelectionStatus().setConnectChoiceTimestamp(timestamp);
1768        saveToStore(false);
1769        return true;
1770    }
1771
1772    /**
1773     * Increments the number of no internet access reports in the provided network.
1774     *
1775     * @param networkId network ID corresponding to the network.
1776     * @return true if the network was found, false otherwise.
1777     */
1778    public boolean incrementNetworkNoInternetAccessReports(int networkId) {
1779        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1780        if (config == null) {
1781            return false;
1782        }
1783        config.numNoInternetAccessReports++;
1784        return true;
1785    }
1786
1787    /**
1788     * Sets the internet access is validated or not in the provided network.
1789     *
1790     * @param networkId network ID corresponding to the network.
1791     * @param validated Whether access is validated or not.
1792     * @return true if the network was found, false otherwise.
1793     */
1794    public boolean setNetworkValidatedInternetAccess(int networkId, boolean validated) {
1795        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1796        if (config == null) {
1797            return false;
1798        }
1799        config.validatedInternetAccess = validated;
1800        config.numNoInternetAccessReports = 0;
1801        saveToStore(false);
1802        return true;
1803    }
1804
1805    /**
1806     * Sets whether the internet access is expected or not in the provided network.
1807     *
1808     * @param networkId network ID corresponding to the network.
1809     * @param expected  Whether access is expected or not.
1810     * @return true if the network was found, false otherwise.
1811     */
1812    public boolean setNetworkNoInternetAccessExpected(int networkId, boolean expected) {
1813        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
1814        if (config == null) {
1815            return false;
1816        }
1817        config.noInternetAccessExpected = expected;
1818        return true;
1819    }
1820
1821    /**
1822     * Helper method to clear out the {@link #mNextNetworkId} user/app network selection. This
1823     * is done when either the corresponding network is either removed or disabled.
1824     */
1825    private void clearLastSelectedNetwork() {
1826        if (mVerboseLoggingEnabled) {
1827            Log.v(TAG, "Clearing last selected network");
1828        }
1829        mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
1830        mLastSelectedTimeStamp = NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
1831    }
1832
1833    /**
1834     * Helper method to mark a network as the last selected one by an app/user. This is set
1835     * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
1836     * This is used by network selector to assign a special bonus during network selection.
1837     */
1838    private void setLastSelectedNetwork(int networkId) {
1839        if (mVerboseLoggingEnabled) {
1840            Log.v(TAG, "Setting last selected network to " + networkId);
1841        }
1842        mLastSelectedNetworkId = networkId;
1843        mLastSelectedTimeStamp = mClock.getElapsedSinceBootMillis();
1844    }
1845
1846    /**
1847     * Retrieve the network Id corresponding to the last network that was explicitly selected by
1848     * an app/user.
1849     *
1850     * @return network Id corresponding to the last selected network.
1851     */
1852    public int getLastSelectedNetwork() {
1853        return mLastSelectedNetworkId;
1854    }
1855
1856    /**
1857     * Retrieve the configKey corresponding to the last network that was explicitly selected by
1858     * an app/user.
1859     *
1860     * @return network Id corresponding to the last selected network.
1861     */
1862    public String getLastSelectedNetworkConfigKey() {
1863        if (mLastSelectedNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
1864            return "";
1865        }
1866        WifiConfiguration config = getInternalConfiguredNetwork(mLastSelectedNetworkId);
1867        if (config == null) {
1868            return "";
1869        }
1870        return config.configKey();
1871    }
1872
1873    /**
1874     * Retrieve the time stamp at which a network was explicitly selected by an app/user.
1875     *
1876     * @return timestamp in milliseconds from boot when this was set.
1877     */
1878    public long getLastSelectedTimeStamp() {
1879        return mLastSelectedTimeStamp;
1880    }
1881
1882    /**
1883     * Helper method to get the scan detail cache entry {@link #mScanDetailCaches} for the provided
1884     * network.
1885     *
1886     * @param networkId network ID corresponding to the network.
1887     * @return existing {@link ScanDetailCache} entry if one exists or null.
1888     */
1889    public ScanDetailCache getScanDetailCacheForNetwork(int networkId) {
1890        return mScanDetailCaches.get(networkId);
1891    }
1892
1893    /**
1894     * Helper method to get or create a scan detail cache entry {@link #mScanDetailCaches} for
1895     * the provided network.
1896     *
1897     * @param config configuration corresponding to the the network.
1898     * @return existing {@link ScanDetailCache} entry if one exists or a new instance created for
1899     * this network.
1900     */
1901    private ScanDetailCache getOrCreateScanDetailCacheForNetwork(WifiConfiguration config) {
1902        if (config == null) return null;
1903        ScanDetailCache cache = getScanDetailCacheForNetwork(config.networkId);
1904        if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
1905            cache = new ScanDetailCache(
1906                    config, SCAN_CACHE_ENTRIES_MAX_SIZE, SCAN_CACHE_ENTRIES_TRIM_SIZE);
1907            mScanDetailCaches.put(config.networkId, cache);
1908        }
1909        return cache;
1910    }
1911
1912    /**
1913     * Saves the provided ScanDetail into the corresponding scan detail cache entry
1914     * {@link #mScanDetailCaches} for the provided network.
1915     *
1916     * @param config     configuration corresponding to the the network.
1917     * @param scanDetail new scan detail instance to be saved into the cache.
1918     */
1919    private void saveToScanDetailCacheForNetwork(
1920            WifiConfiguration config, ScanDetail scanDetail) {
1921        ScanResult scanResult = scanDetail.getScanResult();
1922
1923        ScanDetailCache scanDetailCache = getOrCreateScanDetailCacheForNetwork(config);
1924        if (scanDetailCache == null) {
1925            Log.e(TAG, "Could not allocate scan cache for " + config.getPrintableSsid());
1926            return;
1927        }
1928
1929        // Adding a new BSSID
1930        ScanResult result = scanDetailCache.getScanResult(scanResult.BSSID);
1931        if (result != null) {
1932            // transfer the black list status
1933            scanResult.blackListTimestamp = result.blackListTimestamp;
1934            scanResult.numIpConfigFailures = result.numIpConfigFailures;
1935            scanResult.numConnection = result.numConnection;
1936        }
1937        if (config.ephemeral) {
1938            // For an ephemeral Wi-Fi config, the ScanResult should be considered
1939            // untrusted.
1940            scanResult.untrusted = true;
1941        }
1942
1943        // Add the scan detail to this network's scan detail cache.
1944        scanDetailCache.put(scanDetail);
1945
1946        // Since we added a scan result to this configuration, re-attempt linking.
1947        // TODO: Do we really need to do this after every scan result?
1948        attemptNetworkLinking(config);
1949    }
1950
1951    /**
1952     * Retrieves a configured network corresponding to the provided scan detail if one exists.
1953     *
1954     * @param scanDetail ScanDetail instance  to use for looking up the network.
1955     * @return WifiConfiguration object representing the network corresponding to the scanDetail,
1956     * null if none exists.
1957     */
1958    public WifiConfiguration getConfiguredNetworkForScanDetail(ScanDetail scanDetail) {
1959        ScanResult scanResult = scanDetail.getScanResult();
1960        if (scanResult == null) {
1961            Log.e(TAG, "No scan result found in scan detail");
1962            return null;
1963        }
1964        WifiConfiguration config = null;
1965        try {
1966            config = mConfiguredNetworks.getByScanResultForCurrentUser(scanResult);
1967        } catch (IllegalArgumentException e) {
1968            Log.e(TAG, "Failed to lookup network from config map", e);
1969        }
1970        if (config != null) {
1971            if (mVerboseLoggingEnabled) {
1972                Log.v(TAG, "getSavedNetworkFromScanDetail Found " + config.configKey()
1973                        + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]");
1974            }
1975        }
1976        return config;
1977    }
1978
1979    /**
1980     * Retrieves a configured network corresponding to the provided scan detail if one exists and
1981     * caches the provided |scanDetail| into the corresponding scan detail cache entry
1982     * {@link #mScanDetailCaches} for the retrieved network.
1983     *
1984     * @param scanDetail input a scanDetail from the scan result
1985     * @return WifiConfiguration object representing the network corresponding to the scanDetail,
1986     * null if none exists.
1987     */
1988    public WifiConfiguration getConfiguredNetworkForScanDetailAndCache(ScanDetail scanDetail) {
1989        WifiConfiguration network = getConfiguredNetworkForScanDetail(scanDetail);
1990        if (network == null) {
1991            return null;
1992        }
1993        saveToScanDetailCacheForNetwork(network, scanDetail);
1994        // Cache DTIM values parsed from the beacon frame Traffic Indication Map (TIM)
1995        // Information Element (IE), into the associated WifiConfigurations. Most of the
1996        // time there is no TIM IE in the scan result (Probe Response instead of Beacon
1997        // Frame), these scanResult DTIM's are negative and ignored.
1998        // Used for metrics collection.
1999        if (scanDetail.getNetworkDetail() != null
2000                && scanDetail.getNetworkDetail().getDtimInterval() > 0) {
2001            network.dtimInterval = scanDetail.getNetworkDetail().getDtimInterval();
2002        }
2003        return createExternalWifiConfiguration(network, true);
2004    }
2005
2006    /**
2007     * Update the scan detail cache associated with current connected network with latest
2008     * RSSI value in the provided WifiInfo.
2009     * This is invoked when we get an RSSI poll update after connection.
2010     *
2011     * @param info WifiInfo instance pointing to the current connected network.
2012     */
2013    public void updateScanDetailCacheFromWifiInfo(WifiInfo info) {
2014        WifiConfiguration config = getInternalConfiguredNetwork(info.getNetworkId());
2015        ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(info.getNetworkId());
2016        if (config != null && scanDetailCache != null) {
2017            ScanDetail scanDetail = scanDetailCache.getScanDetail(info.getBSSID());
2018            if (scanDetail != null) {
2019                ScanResult result = scanDetail.getScanResult();
2020                long previousSeen = result.seen;
2021                int previousRssi = result.level;
2022                // Update the scan result
2023                scanDetail.setSeen();
2024                result.level = info.getRssi();
2025                // Average the RSSI value
2026                result.averageRssi(previousRssi, previousSeen, SCAN_RESULT_MAXIMUM_AGE_MS);
2027                if (mVerboseLoggingEnabled) {
2028                    Log.v(TAG, "Updating scan detail cache freq=" + result.frequency
2029                            + " BSSID=" + result.BSSID
2030                            + " RSSI=" + result.level
2031                            + " for " + config.configKey());
2032                }
2033            }
2034        }
2035    }
2036
2037    /**
2038     * Save the ScanDetail to the ScanDetailCache of the given network.  This is used
2039     * by {@link com.android.server.wifi.hotspot2.PasspointNetworkEvaluator} for caching
2040     * ScanDetail for newly created {@link WifiConfiguration} for Passpoint network.
2041     *
2042     * @param networkId The ID of the network to save ScanDetail to
2043     * @param scanDetail The ScanDetail to cache
2044     */
2045    public void updateScanDetailForNetwork(int networkId, ScanDetail scanDetail) {
2046        WifiConfiguration network = getInternalConfiguredNetwork(networkId);
2047        if (network == null) {
2048            return;
2049        }
2050        saveToScanDetailCacheForNetwork(network, scanDetail);
2051    }
2052
2053    /**
2054     * Helper method to check if the 2 provided networks can be linked or not.
2055     * Networks are considered for linking if:
2056     * 1. Share the same GW MAC address.
2057     * 2. Scan results for the networks have AP's with MAC address which differ only in the last
2058     * nibble.
2059     *
2060     * @param network1         WifiConfiguration corresponding to network 1.
2061     * @param network2         WifiConfiguration corresponding to network 2.
2062     * @param scanDetailCache1 ScanDetailCache entry for network 1.
2063     * @param scanDetailCache1 ScanDetailCache entry for network 2.
2064     * @return true if the networks should be linked, false if the networks should be unlinked.
2065     */
2066    private boolean shouldNetworksBeLinked(
2067            WifiConfiguration network1, WifiConfiguration network2,
2068            ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2) {
2069        // TODO (b/30706406): Link networks only with same passwords if the
2070        // |mOnlyLinkSameCredentialConfigurations| flag is set.
2071        if (mOnlyLinkSameCredentialConfigurations) {
2072            if (!TextUtils.equals(network1.preSharedKey, network2.preSharedKey)) {
2073                if (mVerboseLoggingEnabled) {
2074                    Log.v(TAG, "shouldNetworksBeLinked unlink due to password mismatch");
2075                }
2076                return false;
2077            }
2078        }
2079        if (network1.defaultGwMacAddress != null && network2.defaultGwMacAddress != null) {
2080            // If both default GW are known, link only if they are equal
2081            if (network1.defaultGwMacAddress.equals(network2.defaultGwMacAddress)) {
2082                if (mVerboseLoggingEnabled) {
2083                    Log.v(TAG, "shouldNetworksBeLinked link due to same gw " + network2.SSID
2084                            + " and " + network1.SSID + " GW " + network1.defaultGwMacAddress);
2085                }
2086                return true;
2087            }
2088        } else {
2089            // We do not know BOTH default gateways hence we will try to link
2090            // hoping that WifiConfigurations are indeed behind the same gateway.
2091            // once both WifiConfiguration have been tried and thus once both default gateways
2092            // are known we will revisit the choice of linking them.
2093            if (scanDetailCache1 != null && scanDetailCache2 != null) {
2094                for (String abssid : scanDetailCache1.keySet()) {
2095                    for (String bbssid : scanDetailCache2.keySet()) {
2096                        if (abssid.regionMatches(
2097                                true, 0, bbssid, 0, LINK_CONFIGURATION_BSSID_MATCH_LENGTH)) {
2098                            // If first 16 ASCII characters of BSSID matches,
2099                            // we assume this is a DBDC.
2100                            if (mVerboseLoggingEnabled) {
2101                                Log.v(TAG, "shouldNetworksBeLinked link due to DBDC BSSID match "
2102                                        + network2.SSID + " and " + network1.SSID
2103                                        + " bssida " + abssid + " bssidb " + bbssid);
2104                            }
2105                            return true;
2106                        }
2107                    }
2108                }
2109            }
2110        }
2111        return false;
2112    }
2113
2114    /**
2115     * Helper methods to link 2 networks together.
2116     *
2117     * @param network1 WifiConfiguration corresponding to network 1.
2118     * @param network2 WifiConfiguration corresponding to network 2.
2119     */
2120    private void linkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
2121        if (mVerboseLoggingEnabled) {
2122            Log.v(TAG, "linkNetworks will link " + network2.configKey()
2123                    + " and " + network1.configKey());
2124        }
2125        if (network2.linkedConfigurations == null) {
2126            network2.linkedConfigurations = new HashMap<>();
2127        }
2128        if (network1.linkedConfigurations == null) {
2129            network1.linkedConfigurations = new HashMap<>();
2130        }
2131        // TODO (b/30638473): This needs to become a set instead of map, but it will need
2132        // public interface changes and need some migration of existing store data.
2133        network2.linkedConfigurations.put(network1.configKey(), 1);
2134        network1.linkedConfigurations.put(network2.configKey(), 1);
2135    }
2136
2137    /**
2138     * Helper methods to unlink 2 networks from each other.
2139     *
2140     * @param network1 WifiConfiguration corresponding to network 1.
2141     * @param network2 WifiConfiguration corresponding to network 2.
2142     */
2143    private void unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
2144        if (network2.linkedConfigurations != null
2145                && (network2.linkedConfigurations.get(network1.configKey()) != null)) {
2146            if (mVerboseLoggingEnabled) {
2147                Log.v(TAG, "unlinkNetworks un-link " + network1.configKey()
2148                        + " from " + network2.configKey());
2149            }
2150            network2.linkedConfigurations.remove(network1.configKey());
2151        }
2152        if (network1.linkedConfigurations != null
2153                && (network1.linkedConfigurations.get(network2.configKey()) != null)) {
2154            if (mVerboseLoggingEnabled) {
2155                Log.v(TAG, "unlinkNetworks un-link " + network2.configKey()
2156                        + " from " + network1.configKey());
2157            }
2158            network1.linkedConfigurations.remove(network2.configKey());
2159        }
2160    }
2161
2162    /**
2163     * This method runs through all the saved networks and checks if the provided network can be
2164     * linked with any of them.
2165     *
2166     * @param config WifiConfiguration object corresponding to the network that needs to be
2167     *               checked for potential links.
2168     */
2169    private void attemptNetworkLinking(WifiConfiguration config) {
2170        // Only link WPA_PSK config.
2171        if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
2172            return;
2173        }
2174        ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId);
2175        // Ignore configurations with large number of BSSIDs.
2176        if (scanDetailCache != null
2177                && scanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
2178            return;
2179        }
2180        for (WifiConfiguration linkConfig : getInternalConfiguredNetworks()) {
2181            if (linkConfig.configKey().equals(config.configKey())) {
2182                continue;
2183            }
2184            if (linkConfig.ephemeral) {
2185                continue;
2186            }
2187            // Network Selector will be allowed to dynamically jump from a linked configuration
2188            // to another, hence only link configurations that have WPA_PSK security type.
2189            if (!linkConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
2190                continue;
2191            }
2192            ScanDetailCache linkScanDetailCache =
2193                    getScanDetailCacheForNetwork(linkConfig.networkId);
2194            // Ignore configurations with large number of BSSIDs.
2195            if (linkScanDetailCache != null
2196                    && linkScanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
2197                continue;
2198            }
2199            // Check if the networks should be linked/unlinked.
2200            if (shouldNetworksBeLinked(
2201                    config, linkConfig, scanDetailCache, linkScanDetailCache)) {
2202                linkNetworks(config, linkConfig);
2203            } else {
2204                unlinkNetworks(config, linkConfig);
2205            }
2206        }
2207    }
2208
2209    /**
2210     * Helper method to fetch list of channels for a network from the associated ScanResult's cache
2211     * and add it to the provided channel as long as the size of the set is less than
2212     * |maxChannelSetSize|.
2213     *
2214     * @param channelSet        Channel set holding all the channels for the network.
2215     * @param scanDetailCache   ScanDetailCache entry associated with the network.
2216     * @param nowInMillis       current timestamp to be used for age comparison.
2217     * @param ageInMillis       only consider scan details whose timestamps are earlier than this
2218     *                          value.
2219     * @param maxChannelSetSize Maximum number of channels to be added to the set.
2220     * @return false if the list is full, true otherwise.
2221     */
2222    private boolean addToChannelSetForNetworkFromScanDetailCache(
2223            Set<Integer> channelSet, ScanDetailCache scanDetailCache,
2224            long nowInMillis, long ageInMillis, int maxChannelSetSize) {
2225        if (scanDetailCache != null && scanDetailCache.size() > 0) {
2226            for (ScanDetail scanDetail : scanDetailCache.values()) {
2227                ScanResult result = scanDetail.getScanResult();
2228                boolean valid = (nowInMillis - result.seen) < ageInMillis;
2229                if (mVerboseLoggingEnabled) {
2230                    Log.v(TAG, "fetchChannelSetForNetwork has " + result.BSSID + " freq "
2231                            + result.frequency + " age " + (nowInMillis - result.seen)
2232                            + " ?=" + valid);
2233                }
2234                if (valid) {
2235                    channelSet.add(result.frequency);
2236                }
2237                if (channelSet.size() >= maxChannelSetSize) {
2238                    return false;
2239                }
2240            }
2241        }
2242        return true;
2243    }
2244
2245    /**
2246     * Retrieve a set of channels on which AP's for the provided network was seen using the
2247     * internal ScanResult's cache {@link #mScanDetailCaches}. This is used for initiating partial
2248     * scans for the currently connected network.
2249     *
2250     * @param networkId       network ID corresponding to the network.
2251     * @param ageInMillis     only consider scan details whose timestamps are earlier than this value.
2252     * @param homeChannelFreq frequency of the currently connected network.
2253     * @return Set containing the frequencies on which this network was found, null if the network
2254     * was not found or there are no associated scan details in the cache.
2255     */
2256    public Set<Integer> fetchChannelSetForNetworkForPartialScan(int networkId, long ageInMillis,
2257                                                                int homeChannelFreq) {
2258        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
2259        if (config == null) {
2260            return null;
2261        }
2262        ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(networkId);
2263        if (scanDetailCache == null && config.linkedConfigurations == null) {
2264            Log.i(TAG, "No scan detail and linked configs associated with networkId " + networkId);
2265            return null;
2266        }
2267        if (mVerboseLoggingEnabled) {
2268            StringBuilder dbg = new StringBuilder();
2269            dbg.append("fetchChannelSetForNetworkForPartialScan ageInMillis ")
2270                    .append(ageInMillis)
2271                    .append(" for ")
2272                    .append(config.configKey())
2273                    .append(" max ")
2274                    .append(mMaxNumActiveChannelsForPartialScans);
2275            if (scanDetailCache != null) {
2276                dbg.append(" bssids " + scanDetailCache.size());
2277            }
2278            if (config.linkedConfigurations != null) {
2279                dbg.append(" linked " + config.linkedConfigurations.size());
2280            }
2281            Log.v(TAG, dbg.toString());
2282        }
2283        Set<Integer> channelSet = new HashSet<>();
2284
2285        // First add the currently connected network channel.
2286        if (homeChannelFreq > 0) {
2287            channelSet.add(homeChannelFreq);
2288            if (channelSet.size() >= mMaxNumActiveChannelsForPartialScans) {
2289                return channelSet;
2290            }
2291        }
2292
2293        long nowInMillis = mClock.getWallClockMillis();
2294
2295        // Then get channels for the network.
2296        if (!addToChannelSetForNetworkFromScanDetailCache(
2297                channelSet, scanDetailCache, nowInMillis, ageInMillis,
2298                mMaxNumActiveChannelsForPartialScans)) {
2299            return channelSet;
2300        }
2301
2302        // Lastly get channels for linked networks.
2303        if (config.linkedConfigurations != null) {
2304            for (String configKey : config.linkedConfigurations.keySet()) {
2305                WifiConfiguration linkedConfig = getInternalConfiguredNetwork(configKey);
2306                if (linkedConfig == null) {
2307                    continue;
2308                }
2309                ScanDetailCache linkedScanDetailCache =
2310                        getScanDetailCacheForNetwork(linkedConfig.networkId);
2311                if (!addToChannelSetForNetworkFromScanDetailCache(
2312                        channelSet, linkedScanDetailCache, nowInMillis, ageInMillis,
2313                        mMaxNumActiveChannelsForPartialScans)) {
2314                    break;
2315                }
2316            }
2317        }
2318        return channelSet;
2319    }
2320
2321    /**
2322     * Retrieves a list of all the saved networks before enabling disconnected/connected PNO.
2323     *
2324     * PNO network list sent to the firmware has limited size. If there are a lot of saved
2325     * networks, this list will be truncated and we might end up not sending the networks
2326     * with the highest chance of connecting to the firmware.
2327     * So, re-sort the network list based on the frequency of connection to those networks
2328     * and whether it was last seen in the scan results.
2329     *
2330     * TODO (b/30399964): Recalculate the list whenever network status changes.
2331     * @return list of networks with updated priorities.
2332     */
2333    public List<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList() {
2334        List<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>();
2335        List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
2336        // Remove any permanently disabled networks.
2337        Iterator<WifiConfiguration> iter = networks.iterator();
2338        while (iter.hasNext()) {
2339            WifiConfiguration config = iter.next();
2340            if (config.ephemeral || config.isPasspoint()
2341                    || config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
2342                iter.remove();
2343            }
2344        }
2345        Collections.sort(networks, sScanListComparator);
2346        // Let's use the network list size - 1 as the highest priority and then go down from there.
2347        // So, the most frequently connected network has the highest priority now.
2348        int priority = networks.size() - 1;
2349        for (WifiConfiguration config : networks) {
2350            pnoList.add(WifiConfigurationUtil.createPnoNetwork(config, priority));
2351            priority--;
2352        }
2353        return pnoList;
2354    }
2355
2356    /**
2357     * Retrieves a list of all the saved hidden networks for scans.
2358     *
2359     * Hidden network list sent to the firmware has limited size. If there are a lot of saved
2360     * networks, this list will be truncated and we might end up not sending the networks
2361     * with the highest chance of connecting to the firmware.
2362     * So, re-sort the network list based on the frequency of connection to those networks
2363     * and whether it was last seen in the scan results.
2364     *
2365     * @return list of networks with updated priorities.
2366     */
2367    public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList() {
2368        List<WifiScanner.ScanSettings.HiddenNetwork> hiddenList = new ArrayList<>();
2369        List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
2370        // Remove any permanently disabled networks or non hidden networks.
2371        Iterator<WifiConfiguration> iter = networks.iterator();
2372        while (iter.hasNext()) {
2373            WifiConfiguration config = iter.next();
2374            if (!config.hiddenSSID ||
2375                    config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
2376                iter.remove();
2377            }
2378        }
2379        Collections.sort(networks, sScanListComparator);
2380        // Let's use the network list size - 1 as the highest priority and then go down from there.
2381        // So, the most frequently connected network has the highest priority now.
2382        int priority = networks.size() - 1;
2383        for (WifiConfiguration config : networks) {
2384            hiddenList.add(
2385                    new WifiScanner.ScanSettings.HiddenNetwork(config.SSID));
2386            priority--;
2387        }
2388        return hiddenList;
2389    }
2390
2391    /**
2392     * Check if the provided ephemeral network was deleted by the user or not.
2393     *
2394     * @param ssid caller must ensure that the SSID passed thru this API match
2395     *             the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
2396     * @return true if network was deleted, false otherwise.
2397     */
2398    public boolean wasEphemeralNetworkDeleted(String ssid) {
2399        return mDeletedEphemeralSSIDs.contains(ssid);
2400    }
2401
2402    /**
2403     * Disable an ephemeral SSID for the purpose of network selection.
2404     *
2405     * The only way to "un-disable it" is if the user create a network for that SSID and then
2406     * forget it.
2407     *
2408     * @param ssid caller must ensure that the SSID passed thru this API match
2409     *             the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
2410     * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can
2411     * disconnect if this is the current network.
2412     */
2413    public WifiConfiguration disableEphemeralNetwork(String ssid) {
2414        if (ssid == null) {
2415            return null;
2416        }
2417        WifiConfiguration foundConfig = null;
2418        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2419            if (config.ephemeral && TextUtils.equals(config.SSID, ssid)) {
2420                foundConfig = config;
2421                break;
2422            }
2423        }
2424        mDeletedEphemeralSSIDs.add(ssid);
2425        Log.d(TAG, "Forget ephemeral SSID " + ssid + " num=" + mDeletedEphemeralSSIDs.size());
2426        if (foundConfig != null) {
2427            Log.d(TAG, "Found ephemeral config in disableEphemeralNetwork: "
2428                    + foundConfig.networkId);
2429        }
2430        return foundConfig;
2431    }
2432
2433    /**
2434     * Resets all sim networks state.
2435     */
2436    public void resetSimNetworks(boolean simPresent) {
2437        if (mVerboseLoggingEnabled) localLog("resetSimNetworks");
2438        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2439            if (TelephonyUtil.isSimConfig(config)) {
2440                String currentIdentity = null;
2441                if (simPresent) {
2442                    currentIdentity = TelephonyUtil.getSimIdentity(mTelephonyManager, config);
2443                }
2444                // Update the loaded config
2445                config.enterpriseConfig.setIdentity(currentIdentity);
2446                if (config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.PEAP) {
2447                    config.enterpriseConfig.setAnonymousIdentity("");
2448                }
2449            }
2450        }
2451        mSimPresent = simPresent;
2452    }
2453
2454    /**
2455     * Check if SIM is present.
2456     *
2457     * @return True if SIM is present, otherwise false.
2458     */
2459    public boolean isSimPresent() {
2460        return mSimPresent;
2461    }
2462
2463    /**
2464     * Any network using certificates to authenticate access requires unlocked key store; unless
2465     * the certificates can be stored with hardware encryption
2466     *
2467     * @return true if we need an unlocked keystore, false otherwise.
2468     */
2469    public boolean needsUnlockedKeyStore() {
2470        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2471            if (WifiConfigurationUtil.isConfigForEapNetwork(config)) {
2472                if (mWifiKeyStore.needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
2473                    return true;
2474                }
2475            }
2476        }
2477        return false;
2478    }
2479
2480    /**
2481     * Helper method to perform the following operations during user switch/unlock:
2482     * - Remove private networks of the old user.
2483     * - Load from the new user store file.
2484     * - Save the store files again to migrate any user specific networks from the shared store
2485     *   to user store.
2486     * This method assumes the user store is visible (i.e CE storage is unlocked). So, the caller
2487     * should ensure that the stores are accessible before invocation.
2488     *
2489     * @param userId The identifier of the new foreground user, after the unlock or switch.
2490     */
2491    private void handleUserUnlockOrSwitch(int userId) {
2492        if (mVerboseLoggingEnabled) {
2493            Log.v(TAG, "Loading from store after user switch/unlock for " + userId);
2494        }
2495        // Switch out the user store file.
2496        if (loadFromUserStoreAfterUnlockOrSwitch(userId)) {
2497            saveToStore(true);
2498            mPendingUnlockStoreRead = false;
2499        }
2500    }
2501
2502    /**
2503     * Handles the switch to a different foreground user:
2504     * - Flush the current state to the old user's store file.
2505     * - Switch the user specific store file.
2506     * - Reload the networks from the store files (shared & user).
2507     * - Write the store files to move any user specific private networks from shared store to user
2508     *   store.
2509     *
2510     * Need to be called when {@link com.android.server.SystemService#onSwitchUser(int)} is invoked.
2511     *
2512     * @param userId The identifier of the new foreground user, after the switch.
2513     * @return List of network ID's of all the private networks of the old user which will be
2514     * removed from memory.
2515     */
2516    public Set<Integer> handleUserSwitch(int userId) {
2517        if (mVerboseLoggingEnabled) {
2518            Log.v(TAG, "Handling user switch for " + userId);
2519        }
2520        if (userId == mCurrentUserId) {
2521            Log.w(TAG, "User already in foreground " + userId);
2522            return new HashSet<>();
2523        }
2524        if (mPendingStoreRead) {
2525            Log.wtf(TAG, "Unexpected user switch before store is read!");
2526            return new HashSet<>();
2527        }
2528        if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
2529            saveToStore(true);
2530        }
2531        // Remove any private networks of the old user before switching the userId.
2532        Set<Integer> removedNetworkIds = clearInternalUserData(mCurrentUserId);
2533        mConfiguredNetworks.setNewUser(userId);
2534        mCurrentUserId = userId;
2535
2536        if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
2537            handleUserUnlockOrSwitch(mCurrentUserId);
2538        } else {
2539            // Cannot read data from new user's CE store file before they log-in.
2540            mPendingUnlockStoreRead = true;
2541            Log.i(TAG, "Waiting for user unlock to load from store");
2542        }
2543        return removedNetworkIds;
2544    }
2545
2546    /**
2547     * Handles the unlock of foreground user. This maybe needed to read the store file if the user's
2548     * CE storage is not visible when {@link #handleUserSwitch(int)} is invoked.
2549     *
2550     * Need to be called when {@link com.android.server.SystemService#onUnlockUser(int)} is invoked.
2551     *
2552     * @param userId The identifier of the user that unlocked.
2553     */
2554    public void handleUserUnlock(int userId) {
2555        if (mVerboseLoggingEnabled) {
2556            Log.v(TAG, "Handling user unlock for " + userId);
2557        }
2558        if (mPendingStoreRead) {
2559            Log.w(TAG, "Ignore user unlock until store is read!");
2560            mDeferredUserUnlockRead = true;
2561            return;
2562        }
2563        if (userId == mCurrentUserId && mPendingUnlockStoreRead) {
2564            handleUserUnlockOrSwitch(mCurrentUserId);
2565        }
2566    }
2567
2568    /**
2569     * Handles the stop of foreground user. This is needed to write the store file to flush
2570     * out any pending data before the user's CE store storage is unavailable.
2571     *
2572     * Need to be called when {@link com.android.server.SystemService#onStopUser(int)} is invoked.
2573     *
2574     * @param userId The identifier of the user that stopped.
2575     */
2576    public void handleUserStop(int userId) {
2577        if (userId == mCurrentUserId && mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
2578            saveToStore(true);
2579            clearInternalData();
2580            mCurrentUserId = UserHandle.USER_SYSTEM;
2581        }
2582    }
2583
2584    /**
2585     * Helper method to clear internal databases.
2586     * This method clears the:
2587     *  - List of configured networks.
2588     *  - Map of scan detail caches.
2589     *  - List of deleted ephemeral networks.
2590     */
2591    private void clearInternalData() {
2592        mConfiguredNetworks.clear();
2593        mDeletedEphemeralSSIDs.clear();
2594        mScanDetailCaches.clear();
2595        clearLastSelectedNetwork();
2596    }
2597
2598    /**
2599     * Helper method to clear internal databases of the specified user.
2600     * This method clears the:
2601     *  - Private configured configured networks of the specified user.
2602     *  - Map of scan detail caches.
2603     *  - List of deleted ephemeral networks.
2604     *
2605     * @param userId The identifier of the current foreground user, before the switch.
2606     * @return List of network ID's of all the private networks of the old user which will be
2607     * removed from memory.
2608     */
2609    private Set<Integer> clearInternalUserData(int userId) {
2610        Set<Integer> removedNetworkIds = new HashSet<>();
2611        // Remove any private networks of the old user before switching the userId.
2612        for (WifiConfiguration config : getInternalConfiguredNetworks()) {
2613            if (!config.shared && WifiConfigurationUtil.doesUidBelongToAnyProfile(
2614                    config.creatorUid, mUserManager.getProfiles(userId))) {
2615                removedNetworkIds.add(config.networkId);
2616                mConfiguredNetworks.remove(config.networkId);
2617            }
2618        }
2619        mDeletedEphemeralSSIDs.clear();
2620        mScanDetailCaches.clear();
2621        clearLastSelectedNetwork();
2622        return removedNetworkIds;
2623    }
2624
2625    /**
2626     * Helper function to populate the internal (in-memory) data from the retrieved shared store
2627     * (file) data.
2628     *
2629     * @param configurations list of configurations retrieved from store.
2630     */
2631    private void loadInternalDataFromSharedStore(
2632            List<WifiConfiguration> configurations) {
2633        for (WifiConfiguration configuration : configurations) {
2634            configuration.networkId = mNextNetworkId++;
2635            if (mVerboseLoggingEnabled) {
2636                Log.v(TAG, "Adding network from shared store " + configuration.configKey());
2637            }
2638            try {
2639                mConfiguredNetworks.put(configuration);
2640            } catch (IllegalArgumentException e) {
2641                Log.e(TAG, "Failed to add network to config map", e);
2642            }
2643        }
2644    }
2645
2646    /**
2647     * Helper function to populate the internal (in-memory) data from the retrieved user store
2648     * (file) data.
2649     *
2650     * @param configurations        list of configurations retrieved from store.
2651     * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by
2652     *                              the user.
2653     */
2654    private void loadInternalDataFromUserStore(
2655            List<WifiConfiguration> configurations, Set<String> deletedEphemeralSSIDs) {
2656        for (WifiConfiguration configuration : configurations) {
2657            configuration.networkId = mNextNetworkId++;
2658            if (mVerboseLoggingEnabled) {
2659                Log.v(TAG, "Adding network from user store " + configuration.configKey());
2660            }
2661            try {
2662                mConfiguredNetworks.put(configuration);
2663            } catch (IllegalArgumentException e) {
2664                Log.e(TAG, "Failed to add network to config map", e);
2665            }
2666        }
2667        for (String ssid : deletedEphemeralSSIDs) {
2668            mDeletedEphemeralSSIDs.add(ssid);
2669        }
2670    }
2671
2672    /**
2673     * Helper function to populate the internal (in-memory) data from the retrieved stores (file)
2674     * data.
2675     * This method:
2676     * 1. Clears all existing internal data.
2677     * 2. Sends out the networks changed broadcast after loading all the data.
2678     *
2679     * @param sharedConfigurations  list of  network configurations retrieved from shared store.
2680     * @param userConfigurations    list of  network configurations retrieved from user store.
2681     * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by
2682     *                              the user.
2683     */
2684    private void loadInternalData(
2685            List<WifiConfiguration> sharedConfigurations,
2686            List<WifiConfiguration> userConfigurations, Set<String> deletedEphemeralSSIDs) {
2687        // Clear out all the existing in-memory lists and load the lists from what was retrieved
2688        // from the config store.
2689        clearInternalData();
2690        loadInternalDataFromSharedStore(sharedConfigurations);
2691        loadInternalDataFromUserStore(userConfigurations, deletedEphemeralSSIDs);
2692        if (mConfiguredNetworks.sizeForAllUsers() == 0) {
2693            Log.w(TAG, "No stored networks found.");
2694        }
2695        sendConfiguredNetworksChangedBroadcast();
2696        mPendingStoreRead = false;
2697    }
2698
2699    /**
2700     * Migrate data from legacy store files. The function performs the following operations:
2701     * 1. Check if the legacy store files are present.
2702     * 2. If yes, read all the data from the store files.
2703     * 3. Save it to the new store files.
2704     * 4. Delete the legacy store file.
2705     *
2706     * @return true if migration was successful or not needed (fresh install), false if it failed.
2707     */
2708    public boolean migrateFromLegacyStore() {
2709        if (!mWifiConfigStoreLegacy.areStoresPresent()) {
2710            Log.d(TAG, "Legacy store files not found. No migration needed!");
2711            return true;
2712        }
2713        WifiConfigStoreDataLegacy storeData = mWifiConfigStoreLegacy.read();
2714        Log.d(TAG, "Reading from legacy store completed");
2715        loadInternalData(storeData.getConfigurations(), new ArrayList<WifiConfiguration>(),
2716                storeData.getDeletedEphemeralSSIDs());
2717
2718        // Setup user store for the current user in case it have not setup yet, so that data
2719        // owned by the current user will be backed to the user store.
2720        if (mDeferredUserUnlockRead) {
2721            mWifiConfigStore.setUserStore(WifiConfigStore.createUserFile(mCurrentUserId));
2722            mDeferredUserUnlockRead = false;
2723        }
2724
2725        if (!saveToStore(true)) {
2726            return false;
2727        }
2728        mWifiConfigStoreLegacy.removeStores();
2729        Log.d(TAG, "Migration from legacy store completed");
2730        return true;
2731    }
2732
2733    /**
2734     * Read the config store and load the in-memory lists from the store data retrieved and sends
2735     * out the networks changed broadcast.
2736     *
2737     * This reads all the network configurations from:
2738     * 1. Shared WifiConfigStore.xml
2739     * 2. User WifiConfigStore.xml
2740     *
2741     * @return true on success or not needed (fresh install/pending legacy store migration),
2742     * false otherwise.
2743     */
2744    public boolean loadFromStore() {
2745        if (!mWifiConfigStore.areStoresPresent()) {
2746            Log.d(TAG, "New store files not found. No saved networks loaded!");
2747            if (!mWifiConfigStoreLegacy.areStoresPresent()) {
2748                // No legacy store files either, so reset the pending store read flag.
2749                mPendingStoreRead = false;
2750            }
2751            return true;
2752        }
2753        // If the user unlock comes in before we load from store, which means the user store have
2754        // not been setup yet for the current user.  Setup the user store before the read so that
2755        // configurations for the current user will also being loaded.
2756        if (mDeferredUserUnlockRead) {
2757            Log.i(TAG, "Handling user unlock before loading from store.");
2758            mWifiConfigStore.setUserStore(WifiConfigStore.createUserFile(mCurrentUserId));
2759            mDeferredUserUnlockRead = false;
2760        }
2761        try {
2762            mWifiConfigStore.read();
2763        } catch (IOException e) {
2764            Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e);
2765            return false;
2766        } catch (XmlPullParserException e) {
2767            Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e);
2768            return false;
2769        }
2770        loadInternalData(mNetworkListStoreData.getSharedConfigurations(),
2771                mNetworkListStoreData.getUserConfigurations(),
2772                mDeletedEphemeralSsidsStoreData.getSsidList());
2773        return true;
2774    }
2775
2776    /**
2777     * Read the user config store and load the in-memory lists from the store data retrieved and
2778     * sends out the networks changed broadcast.
2779     * This should be used for all user switches/unlocks to only load networks from the user
2780     * specific store and avoid reloading the shared networks.
2781     *
2782     * This reads all the network configurations from:
2783     * 1. User WifiConfigStore.xml
2784     *
2785     * @param userId The identifier of the foreground user.
2786     * @return true on success, false otherwise.
2787     */
2788    public boolean loadFromUserStoreAfterUnlockOrSwitch(int userId) {
2789        try {
2790            mWifiConfigStore.switchUserStoreAndRead(WifiConfigStore.createUserFile(userId));
2791        } catch (IOException e) {
2792            Log.wtf(TAG, "Reading from new store failed. All saved private networks are lost!", e);
2793            return false;
2794        } catch (XmlPullParserException e) {
2795            Log.wtf(TAG, "XML deserialization of store failed. All saved private networks are" +
2796                    "lost!", e);
2797            return false;
2798        }
2799        loadInternalDataFromUserStore(mNetworkListStoreData.getUserConfigurations(),
2800                mDeletedEphemeralSsidsStoreData.getSsidList());
2801        return true;
2802    }
2803
2804    /**
2805     * Save the current snapshot of the in-memory lists to the config store.
2806     *
2807     * @param forceWrite Whether the write needs to be forced or not.
2808     * @return Whether the write was successful or not, this is applicable only for force writes.
2809     */
2810    public boolean saveToStore(boolean forceWrite) {
2811        ArrayList<WifiConfiguration> sharedConfigurations = new ArrayList<>();
2812        ArrayList<WifiConfiguration> userConfigurations = new ArrayList<>();
2813        // List of network IDs for legacy Passpoint configuration to be removed.
2814        List<Integer> legacyPasspointNetId = new ArrayList<>();
2815        for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
2816            // Ignore ephemeral networks and non-legacy Passpoint configurations.
2817            if (config.ephemeral || (config.isPasspoint() && !config.isLegacyPasspointConfig)) {
2818                continue;
2819            }
2820
2821            // Migrate the legacy Passpoint configurations owned by the current user to
2822            // {@link PasspointManager}.
2823            if (config.isLegacyPasspointConfig && WifiConfigurationUtil.doesUidBelongToAnyProfile(
2824                        config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) {
2825                legacyPasspointNetId.add(config.networkId);
2826                // Migrate the legacy Passpoint configuration and add it to PasspointManager.
2827                if (!PasspointManager.addLegacyPasspointConfig(config)) {
2828                    Log.e(TAG, "Failed to migrate legacy Passpoint config: " + config.FQDN);
2829                }
2830                // This will prevent adding |config| to the |sharedConfigurations|.
2831                continue;
2832            }
2833
2834            // We push all shared networks & private networks not belonging to the current
2835            // user to the shared store. Ideally, private networks for other users should
2836            // not even be in memory,
2837            // But, this logic is in place to deal with store migration from N to O
2838            // because all networks were previously stored in a central file. We cannot
2839            // write these private networks to the user specific store until the corresponding
2840            // user logs in.
2841            if (config.shared || !WifiConfigurationUtil.doesUidBelongToAnyProfile(
2842                    config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) {
2843                sharedConfigurations.add(config);
2844            } else {
2845                userConfigurations.add(config);
2846            }
2847        }
2848
2849        // Remove the configurations for migrated Passpoint configurations.
2850        for (int networkId : legacyPasspointNetId) {
2851            mConfiguredNetworks.remove(networkId);
2852        }
2853
2854        // Setup store data for write.
2855        mNetworkListStoreData.setSharedConfigurations(sharedConfigurations);
2856        mNetworkListStoreData.setUserConfigurations(userConfigurations);
2857        mDeletedEphemeralSsidsStoreData.setSsidList(mDeletedEphemeralSSIDs);
2858
2859        try {
2860            mWifiConfigStore.write(forceWrite);
2861        } catch (IOException e) {
2862            Log.wtf(TAG, "Writing to store failed. Saved networks maybe lost!", e);
2863            return false;
2864        } catch (XmlPullParserException e) {
2865            Log.wtf(TAG, "XML serialization for store failed. Saved networks maybe lost!", e);
2866            return false;
2867        }
2868        return true;
2869    }
2870
2871    /**
2872     * Helper method for logging into local log buffer.
2873     */
2874    private void localLog(String s) {
2875        if (mLocalLog != null) {
2876            mLocalLog.log(s);
2877        }
2878    }
2879
2880    /**
2881     * Dump the local log buffer and other internal state of WifiConfigManager.
2882     */
2883    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2884        pw.println("Dump of WifiConfigManager");
2885        pw.println("WifiConfigManager - Log Begin ----");
2886        mLocalLog.dump(fd, pw, args);
2887        pw.println("WifiConfigManager - Log End ----");
2888        pw.println("WifiConfigManager - Configured networks Begin ----");
2889        for (WifiConfiguration network : getInternalConfiguredNetworks()) {
2890            pw.println(network);
2891        }
2892        pw.println("WifiConfigManager - Configured networks End ----");
2893        pw.println("WifiConfigManager - Next network ID to be allocated " + mNextNetworkId);
2894        pw.println("WifiConfigManager - Last selected network ID " + mLastSelectedNetworkId);
2895    }
2896
2897    /**
2898     * Returns true if the given uid has permission to add, update or remove proxy settings
2899     */
2900    private boolean canModifyProxySettings(int uid) {
2901        final DevicePolicyManagerInternal dpmi =
2902                mWifiPermissionsWrapper.getDevicePolicyManagerInternal();
2903        final boolean isUidProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
2904                DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
2905        final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
2906                DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
2907        final boolean hasNetworkSettingsPermission =
2908                mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
2909        // If |uid| corresponds to the device owner, allow all modifications.
2910        if (isUidDeviceOwner || isUidProfileOwner || hasNetworkSettingsPermission) {
2911            return true;
2912        }
2913        if (mVerboseLoggingEnabled) {
2914            Log.v(TAG, "UID: " + uid + " cannot modify WifiConfiguration proxy settings."
2915                    + " ConfigOverride=" + hasNetworkSettingsPermission
2916                    + " DeviceOwner=" + isUidDeviceOwner
2917                    + " ProfileOwner=" + isUidProfileOwner);
2918        }
2919        return false;
2920    }
2921
2922    /**
2923     * Set the saved network update event listener
2924     */
2925    public void setOnSavedNetworkUpdateListener(OnSavedNetworkUpdateListener listener) {
2926        mListener = listener;
2927    }
2928
2929    /**
2930     * Set extra failure reason for given config. Used to surface extra failure details to the UI
2931     * @param netId The network ID of the config to set the extra failure reason for
2932     * @param reason the WifiConfiguration.ExtraFailureReason failure code representing the most
2933     *               recent failure reason
2934     */
2935    public void setRecentFailureAssociationStatus(int netId, int reason) {
2936        WifiConfiguration config = getInternalConfiguredNetwork(netId);
2937        if (config == null) {
2938            return;
2939        }
2940        config.recentFailure.setAssociationStatus(reason);
2941    }
2942
2943    /**
2944     * @param netId The network ID of the config to clear the extra failure reason from
2945     */
2946    public void clearRecentFailureReason(int netId) {
2947        WifiConfiguration config = getInternalConfiguredNetwork(netId);
2948        if (config == null) {
2949            return;
2950        }
2951        config.recentFailure.clear();
2952    }
2953}
2954