• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi;
18 
19 import static android.net.wifi.WifiManager.EASY_CONNECT_NETWORK_ROLE_AP;
20 
21 import android.annotation.Nullable;
22 import android.content.Context;
23 import android.net.wifi.EasyConnectStatusCallback;
24 import android.net.wifi.IDppCallback;
25 import android.net.wifi.ScanResult;
26 import android.net.wifi.WifiConfiguration;
27 import android.net.wifi.WifiManager;
28 import android.os.Build;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.RemoteException;
32 import android.text.TextUtils;
33 import android.util.Log;
34 import android.util.SparseArray;
35 
36 import androidx.annotation.RequiresApi;
37 
38 import com.android.internal.annotations.VisibleForTesting;
39 import com.android.internal.util.WakeupMessage;
40 import com.android.modules.utils.build.SdkLevel;
41 import com.android.server.wifi.SupplicantStaIfaceHal.DppAkm;
42 import com.android.server.wifi.SupplicantStaIfaceHal.DppCurve;
43 import com.android.server.wifi.SupplicantStaIfaceHal.DppEventType;
44 import com.android.server.wifi.SupplicantStaIfaceHal.DppFailureCode;
45 import com.android.server.wifi.SupplicantStaIfaceHal.DppNetRole;
46 import com.android.server.wifi.SupplicantStaIfaceHal.DppProgressCode;
47 import com.android.server.wifi.WifiNative.DppEventCallback;
48 import com.android.server.wifi.util.ApConfigUtil;
49 import com.android.server.wifi.util.WifiPermissionsUtil;
50 
51 import java.util.ArrayList;
52 import java.util.List;
53 
54 /**
55  * DPP Manager class
56  * Implements the DPP Initiator APIs and callbacks
57  */
58 public class DppManager {
59     private static final String TAG = "DppManager";
60     private final Handler mHandler;
61 
62     private DppRequestInfo mDppRequestInfo = null;
63     private final WifiNative mWifiNative;
64     private String mClientIfaceName;
65     private boolean mVerboseLoggingEnabled;
66     WifiConfigManager mWifiConfigManager;
67     private final Context mContext;
68     @VisibleForTesting
69     public WakeupMessage mDppTimeoutMessage = null;
70     private final Clock mClock;
71     private static final String DPP_TIMEOUT_TAG = TAG + " Request Timeout";
72     private static final int DPP_TIMEOUT_MS = 40_000; // 40 seconds
73     private static final int DPP_RESPONDER_TIMEOUT_MS = 300_000; // 5 minutes
74     public static final int DPP_AUTH_ROLE_INACTIVE = -1;
75     public static final int DPP_AUTH_ROLE_INITIATOR = 0;
76     public static final int DPP_AUTH_ROLE_RESPONDER = 1;
77     private final DppMetrics mDppMetrics;
78     private final ScanRequestProxy mScanRequestProxy;
79     private final WifiPermissionsUtil mWifiPermissionsUtil;
80 
81     private final DppEventCallback mDppEventCallback = new DppEventCallback() {
82         @Override
83         public void onSuccessConfigReceived(WifiConfiguration newWifiConfiguration) {
84             mHandler.post(() -> {
85                 DppManager.this.onSuccessConfigReceived(newWifiConfiguration);
86             });
87         }
88 
89         @Override
90         public void onSuccess(int dppStatusCode) {
91             mHandler.post(() -> {
92                 DppManager.this.onSuccess(dppStatusCode);
93             });
94         }
95 
96         @Override
97         public void onProgress(int dppStatusCode) {
98             mHandler.post(() -> {
99                 DppManager.this.onProgress(dppStatusCode);
100             });
101         }
102 
103         @Override
104         public void onFailure(int dppStatusCode, String ssid, String channelList, int[] bandList) {
105             mHandler.post(() -> {
106                 DppManager.this.onFailure(dppStatusCode, ssid, channelList, bandList);
107             });
108         }
109 
110         @Override
111         public void onDppConfiguratorKeyUpdate(byte[] key) {
112             mHandler.post(() -> {
113                 DppManager.this.onDppConfiguratorKeyUpdate(key);
114             });
115         }
116     };
117 
DppManager(Handler handler, WifiNative wifiNative, WifiConfigManager wifiConfigManager, Context context, DppMetrics dppMetrics, ScanRequestProxy scanRequestProxy, WifiPermissionsUtil wifiPermissionsUtil)118     DppManager(Handler handler, WifiNative wifiNative, WifiConfigManager wifiConfigManager,
119             Context context, DppMetrics dppMetrics, ScanRequestProxy scanRequestProxy,
120             WifiPermissionsUtil wifiPermissionsUtil) {
121         mHandler = handler;
122         mWifiNative = wifiNative;
123         mWifiConfigManager = wifiConfigManager;
124         mWifiNative.registerDppEventCallback(mDppEventCallback);
125         mContext = context;
126         mClock = new Clock();
127         mDppMetrics = dppMetrics;
128         mScanRequestProxy = scanRequestProxy;
129         mWifiPermissionsUtil = wifiPermissionsUtil;
130 
131         // Setup timer
132         mDppTimeoutMessage = new WakeupMessage(mContext, mHandler,
133                 DPP_TIMEOUT_TAG, () -> {
134             timeoutDppRequest();
135         });
136     }
137 
encodeStringToHex(String str)138     private static String encodeStringToHex(String str) {
139         if ((str.length() > 1) && (str.charAt(0) == '"') && (str.charAt(str.length() - 1) == '"')) {
140             // Remove the surrounding quotes
141             str = str.substring(1, str.length() - 1);
142 
143             // Convert to Hex
144             char[] charsArray = str.toCharArray();
145             StringBuffer hexBuffer = new StringBuffer();
146             for (int i = 0; i < charsArray.length; i++) {
147                 hexBuffer.append(Integer.toHexString((int) charsArray[i]));
148             }
149             return hexBuffer.toString();
150         }
151         return str;
152     }
153 
timeoutDppRequest()154     private void timeoutDppRequest() {
155         logd("DPP timeout");
156 
157         if (mDppRequestInfo == null) {
158             Log.e(TAG, "DPP timeout with no request info");
159             return;
160         }
161 
162         // Clean up supplicant resources
163         if (!mWifiNative.stopDppInitiator(mClientIfaceName)) {
164             Log.e(TAG, "Failed to stop DPP Initiator");
165         }
166 
167         // Clean up resources and let the caller know about the timeout
168         onFailure(DppFailureCode.TIMEOUT);
169     }
170 
171     /**
172      * Generate DPP connection keys for Configurator. This is used to connect to
173      * other devices (APs) which this configurator has enrolled using DPP-AKM.
174      * DPP connection keys are received via DppEventCallback.onSuccessConfigReceived
175      *
176      * @param networkId Network ID of DPP-AKM wifi configuration.
177      */
generateSelfDppConfiguration(int networkId)178     private void generateSelfDppConfiguration(int networkId) {
179         WifiConfiguration config = mWifiConfigManager
180                 .getConfiguredNetworkWithoutMasking(networkId);
181 
182         if (config == null || !config.isSecurityType(WifiConfiguration.SECURITY_TYPE_DPP)
183                 || !config.isDppConfigurator() || (config.getDppConnector().length > 0)) {
184             Log.e(TAG, "Not eligible for generateSelfDppConfiguration yet.");
185             return;
186         }
187 
188         mDppRequestInfo.isGeneratingSelfConfiguration = true;
189 
190         if (!mWifiNative.generateSelfDppConfiguration(mClientIfaceName,
191                     config.SSID, config.getDppPrivateEcKey())) {
192             Log.e(TAG, "generateSelfDppConfiguration failed!!");
193             mDppRequestInfo.isGeneratingSelfConfiguration = false;
194         }
195 
196         return;
197     }
198 
199     /**
200      * Start DPP request in Configurator-Initiator mode. The goal of this call is to send the
201      * selected Wi-Fi configuration to a remote peer so it could join that network.
202      *
203      * @param uid                 UID
204      * @param packageName         Package name of the calling app
205      * @param clientIfaceName     Client interface to use for this operation.
206      * @param binder              Binder object
207      * @param enrolleeUri         The Enrollee URI, scanned externally (e.g. via QR code)
208      * @param selectedNetworkId   The selected Wi-Fi network ID to be sent
209      * @param enrolleeNetworkRole Network role of remote enrollee: STA or AP
210      * @param callback            DPP Callback object
211      */
startDppAsConfiguratorInitiator(int uid, @Nullable String packageName, @Nullable String clientIfaceName, IBinder binder, String enrolleeUri, int selectedNetworkId, @WifiManager.EasyConnectNetworkRole int enrolleeNetworkRole, IDppCallback callback)212     public void startDppAsConfiguratorInitiator(int uid, @Nullable String packageName,
213             @Nullable String clientIfaceName, IBinder binder, String enrolleeUri,
214             int selectedNetworkId, @WifiManager.EasyConnectNetworkRole int enrolleeNetworkRole,
215             IDppCallback callback) {
216         mDppMetrics.updateDppConfiguratorInitiatorRequests();
217         if (isSessionInProgress()) {
218             try {
219                 Log.e(TAG, "DPP request already in progress");
220                 Log.e(TAG, "Ongoing request - UID: " + mDppRequestInfo.uid
221                         + " Package: " + mDppRequestInfo.packageName
222                         + ", New request - UID: " + uid + " Package: " + packageName);
223 
224                 mDppMetrics.updateDppFailure(EasyConnectStatusCallback
225                         .EASY_CONNECT_EVENT_FAILURE_BUSY);
226                 // On going DPP. Call the failure callback directly
227                 callback.onFailure(EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY, null,
228                         null, new int[0]);
229             } catch (RemoteException e) {
230                 // Empty
231             }
232             return;
233         }
234 
235         mClientIfaceName = clientIfaceName;
236         if (mClientIfaceName == null) {
237             try {
238                 Log.e(TAG, "Wi-Fi client interface does not exist");
239                 // On going DPP. Call the failure callback directly
240                 mDppMetrics.updateDppFailure(EasyConnectStatusCallback
241                         .EASY_CONNECT_EVENT_FAILURE_GENERIC);
242                 callback.onFailure(EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC,
243                         null, null, new int[0]);
244             } catch (RemoteException e) {
245                 // Empty
246             }
247             return;
248         }
249 
250         WifiConfiguration selectedNetwork = mWifiConfigManager
251                 .getConfiguredNetworkWithoutMasking(selectedNetworkId);
252 
253         if (selectedNetwork == null) {
254             try {
255                 Log.e(TAG, "Selected network is null");
256                 // On going DPP. Call the failure callback directly
257                 mDppMetrics.updateDppFailure(EasyConnectStatusCallback
258                         .EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK);
259                 callback.onFailure(EasyConnectStatusCallback
260                         .EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK, null, null, new int[0]);
261             } catch (RemoteException e) {
262                 // Empty
263             }
264             return;
265         }
266 
267         String password = null;
268         String psk = null;
269         int securityAkm;
270         byte[] privEcKey = null;
271 
272         // Currently support either SAE mode or PSK mode or DPP mode
273         // Check PSK first because PSK config always has a SAE type as a upgrading type.
274         if (selectedNetwork.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)) {
275             if (selectedNetwork.preSharedKey.matches("[0-9A-Fa-f]{64}")) {
276                 // PSK
277                 psk = selectedNetwork.preSharedKey;
278             } else {
279                 // Passphrase
280                 password = selectedNetwork.preSharedKey;
281             }
282             securityAkm = DppAkm.PSK;
283         } else if (selectedNetwork.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)) {
284             // SAE
285             password = selectedNetwork.preSharedKey;
286             securityAkm = DppAkm.SAE;
287         } else if (selectedNetwork.isSecurityType(WifiConfiguration.SECURITY_TYPE_DPP)) {
288             // DPP
289             if (selectedNetwork.isDppConfigurator()) {
290                 privEcKey = selectedNetwork.getDppPrivateEcKey();
291             } else {
292                 if (enrolleeNetworkRole != EASY_CONNECT_NETWORK_ROLE_AP) {
293                     try {
294                         Log.e(TAG, "Device is not configured previously to configure"
295                                 + "the peer enrollee devices to this network");
296                         callback.onFailure(
297                                 EasyConnectStatusCallback
298                                 .EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK,
299                                 null, null, new int[0]);
300                     } catch (RemoteException e) {
301                         // Empty
302                     }
303                     return;
304                 }
305             }
306             securityAkm = DppAkm.DPP;
307         } else {
308             try {
309                 // Key management must be either PSK or SAE or DPP
310                 Log.e(TAG, "Key management must be either PSK or SAE");
311                 mDppMetrics.updateDppFailure(EasyConnectStatusCallback
312                         .EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK);
313                 callback.onFailure(
314                         EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK, null,
315                         null, new int[0]);
316             } catch (RemoteException e) {
317                 // Empty
318             }
319             return;
320         }
321 
322         mDppRequestInfo = new DppRequestInfo();
323         mDppRequestInfo.uid = uid;
324         mDppRequestInfo.packageName = packageName;
325         mDppRequestInfo.binder = binder;
326         mDppRequestInfo.callback = callback;
327         mDppRequestInfo.authRole = DPP_AUTH_ROLE_INITIATOR;
328         mDppRequestInfo.networkId = selectedNetworkId;
329 
330         if (!linkToDeath(mDppRequestInfo)) {
331             // Notify failure and clean up
332             onFailure(EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC);
333             return;
334         }
335 
336         logd("Interface " + mClientIfaceName + ": Initializing URI: " + enrolleeUri);
337 
338         mDppRequestInfo.startTime = mClock.getElapsedSinceBootMillis();
339         mDppTimeoutMessage.schedule(mDppRequestInfo.startTime + DPP_TIMEOUT_MS);
340 
341         // Send Enrollee URI and get a peer ID
342         int peerId = mWifiNative.addDppPeerUri(mClientIfaceName, enrolleeUri);
343 
344         if (peerId < 0) {
345             Log.e(TAG, "DPP add URI failure");
346 
347             // Notify failure and clean up
348             onFailure(DppFailureCode.INVALID_URI);
349             return;
350         }
351         mDppRequestInfo.peerId = peerId;
352 
353         // Auth init
354         logd("Authenticating");
355 
356         String ssidEncoded = encodeStringToHex(selectedNetwork.SSID);
357         String passwordEncoded = null;
358 
359         if (password != null) {
360             passwordEncoded = encodeStringToHex(selectedNetwork.preSharedKey);
361         }
362 
363         if (!mWifiNative.startDppConfiguratorInitiator(mClientIfaceName,
364                 mDppRequestInfo.peerId, 0, ssidEncoded, passwordEncoded, psk,
365                 enrolleeNetworkRole == EASY_CONNECT_NETWORK_ROLE_AP ? DppNetRole.AP
366                         : DppNetRole.STA,
367                 securityAkm, privEcKey)) {
368             Log.e(TAG, "DPP Start Configurator Initiator failure");
369 
370             // Notify failure and clean up
371             onFailure(DppFailureCode.FAILURE);
372             return;
373         }
374 
375         logd("Success: Started DPP Initiator with peer ID "
376                 + mDppRequestInfo.peerId);
377     }
378 
379     /**
380      * Start DPP request in Enrollee-Initiator mode. The goal of this call is to receive a
381      * Wi-Fi configuration object from the peer configurator in order to join a network.
382      *
383      * @param uid             UID
384      * @param clientIfaceName     Client interface to use for this operation.
385      * @param binder          Binder object
386      * @param configuratorUri The Configurator URI, scanned externally (e.g. via QR code)
387      * @param callback        DPP Callback object
388      */
startDppAsEnrolleeInitiator(int uid, @Nullable String clientIfaceName, IBinder binder, String configuratorUri, IDppCallback callback)389     public void startDppAsEnrolleeInitiator(int uid, @Nullable String clientIfaceName,
390             IBinder binder, String configuratorUri, IDppCallback callback) {
391         mDppMetrics.updateDppEnrolleeInitiatorRequests();
392         if (isSessionInProgress()) {
393             try {
394                 Log.e(TAG, "DPP request already in progress");
395                 Log.e(TAG, "Ongoing request UID: " + mDppRequestInfo.uid + ", new UID: "
396                         + uid);
397 
398                 mDppMetrics.updateDppFailure(EasyConnectStatusCallback
399                         .EASY_CONNECT_EVENT_FAILURE_BUSY);
400                 // On going DPP. Call the failure callback directly
401                 callback.onFailure(EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY, null,
402                         null, new int[0]);
403             } catch (RemoteException e) {
404                 // Empty
405             }
406             return;
407         }
408 
409         mDppRequestInfo = new DppRequestInfo();
410         mDppRequestInfo.uid = uid;
411         mDppRequestInfo.binder = binder;
412         mDppRequestInfo.callback = callback;
413         mDppRequestInfo.authRole = DPP_AUTH_ROLE_INITIATOR;
414 
415         if (!linkToDeath(mDppRequestInfo)) {
416             // Notify failure and clean up
417             onFailure(DppFailureCode.FAILURE);
418             return;
419         }
420 
421         mDppRequestInfo.startTime = mClock.getElapsedSinceBootMillis();
422         mDppTimeoutMessage.schedule(mDppRequestInfo.startTime + DPP_TIMEOUT_MS);
423 
424         mClientIfaceName = clientIfaceName;
425         logd("Interface " + mClientIfaceName + ": Initializing URI: " + configuratorUri);
426 
427         // Send Configurator URI and get a peer ID
428         int peerId = mWifiNative.addDppPeerUri(mClientIfaceName, configuratorUri);
429 
430         if (peerId < 0) {
431             Log.e(TAG, "DPP add URI failure");
432             onFailure(DppFailureCode.INVALID_URI);
433             return;
434         }
435         mDppRequestInfo.peerId = peerId;
436 
437         // Auth init
438         logd("Authenticating");
439 
440         if (!mWifiNative.startDppEnrolleeInitiator(mClientIfaceName, mDppRequestInfo.peerId,
441                 0)) {
442             Log.e(TAG, "DPP Start Enrollee Initiator failure");
443 
444             // Notify failure and clean up
445             onFailure(DppFailureCode.FAILURE);
446             return;
447         }
448 
449         logd("Success: Started DPP Initiator with peer ID "
450                 + mDppRequestInfo.peerId);
451     }
452 
453     /**
454      * Start DPP request in Enrollee-Responder mode. The goal of this call is to receive a
455      * Wi-Fi configuration object from the peer configurator by showing a QR code and being scanned
456      * by the peer configurator.
457      *
458      * @param uid             UID
459      * @param clientIfaceName Client interface to use for this operation.
460      * @param binder          Binder object
461      * @param deviceInfo      The Device specific info to attach to the generated URI
462      * @param curve           Elliptic curve cryptography type used to generate DPP
463      *                        public/private key pair.
464      * @param callback        DPP Callback object
465      */
startDppAsEnrolleeResponder(int uid, @Nullable String clientIfaceName, IBinder binder, @Nullable String deviceInfo, @WifiManager.EasyConnectCryptographyCurve int curve, IDppCallback callback)466     public void startDppAsEnrolleeResponder(int uid, @Nullable String clientIfaceName,
467             IBinder binder, @Nullable String deviceInfo,
468             @WifiManager.EasyConnectCryptographyCurve int curve, IDppCallback callback) {
469         mDppMetrics.updateDppEnrolleeResponderRequests();
470         if (isSessionInProgress()) {
471             try {
472                 Log.e(TAG, "DPP request already in progress");
473                 Log.e(TAG, "Ongoing request UID: " + mDppRequestInfo.uid + ", new UID: "
474                         + uid);
475 
476                 mDppMetrics.updateDppFailure(EasyConnectStatusCallback
477                         .EASY_CONNECT_EVENT_FAILURE_BUSY);
478                 // On going DPP. Call the failure callback directly
479                 callback.onFailure(EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY,
480                         null, null, new int[0]);
481             } catch (RemoteException e) {
482                 // Empty
483             }
484             return;
485         }
486 
487         mDppRequestInfo = new DppRequestInfo();
488         mDppRequestInfo.uid = uid;
489         mDppRequestInfo.binder = binder;
490         mDppRequestInfo.callback = callback;
491         mDppRequestInfo.authRole = DPP_AUTH_ROLE_RESPONDER;
492 
493         if (!linkToDeath(mDppRequestInfo)) {
494             // Notify failure and clean up
495             onFailure(DppFailureCode.FAILURE);
496             return;
497         }
498 
499         mDppRequestInfo.startTime = mClock.getElapsedSinceBootMillis();
500         mDppTimeoutMessage.schedule(mDppRequestInfo.startTime + DPP_RESPONDER_TIMEOUT_MS);
501 
502         mClientIfaceName = clientIfaceName;
503         logd("Interface " + mClientIfaceName + " Product Info: " + deviceInfo
504                 + " Curve: " + curve);
505 
506         String info = deviceInfo == null ? "" : deviceInfo;
507         // Generate a QR code based bootstrap info
508         WifiNative.DppBootstrapQrCodeInfo bootstrapInfo = null;
509         if (SdkLevel.isAtLeastS()) {
510             bootstrapInfo =
511                     mWifiNative.generateDppBootstrapInfoForResponder(mClientIfaceName, deviceInfo,
512                             convertEasyConnectCryptographyCurveToHidlDppCurve(curve));
513         }
514 
515         if (bootstrapInfo == null || bootstrapInfo.bootstrapId < 0
516                 || TextUtils.isEmpty(bootstrapInfo.uri)) {
517             Log.e(TAG, "DPP request to generate URI failed");
518             onFailure(DppFailureCode.URI_GENERATION);
519             return;
520         }
521 
522         mDppRequestInfo.bootstrapId = bootstrapInfo.bootstrapId;
523         logd("BootstrapId:" + mDppRequestInfo.bootstrapId + " URI: " + bootstrapInfo.uri);
524 
525         if (!mWifiNative.startDppEnrolleeResponder(mClientIfaceName, bootstrapInfo.listenChannel)) {
526             Log.e(TAG, "DPP Start Enrollee Responder failure");
527             // Notify failure and clean up
528             onFailure(DppFailureCode.FAILURE);
529             return;
530         }
531 
532         logd("Success: Started DPP Enrollee Responder on listen channel "
533                 + bootstrapInfo.listenChannel);
534 
535         try {
536             mDppRequestInfo.callback.onBootstrapUriGenerated(bootstrapInfo.uri);
537         } catch (RemoteException e) {
538             Log.e(TAG, " onBootstrapUriGenerated Callback failure");
539             onFailure(DppFailureCode.FAILURE);
540             return;
541         }
542     }
543 
544     /**
545      * Stop a current DPP session
546      *
547      * @param uid User ID
548      */
stopDppSession(int uid)549     public void stopDppSession(int uid) {
550         if (!isSessionInProgress()) {
551             logd("UID " + uid + " called stop DPP session with no active DPP session");
552             return;
553         }
554 
555         if (mDppRequestInfo.uid != uid) {
556             Log.e(TAG, "UID " + uid + " called stop DPP session but UID " + mDppRequestInfo.uid
557                     + " has started it");
558             return;
559         }
560 
561         // Clean up supplicant resources
562         if (mDppRequestInfo.authRole == DPP_AUTH_ROLE_INITIATOR) {
563             if (!mWifiNative.stopDppInitiator(mClientIfaceName)) {
564                 Log.e(TAG, "Failed to stop DPP Initiator");
565             }
566         }
567 
568         mDppRequestInfo.isGeneratingSelfConfiguration = false;
569         cleanupDppResources();
570 
571         logd("Success: Stopped DPP Session");
572     }
573 
cleanupDppResources()574     private void cleanupDppResources() {
575         logd("DPP clean up resources");
576         if (!isSessionInProgress()) {
577             return;
578         }
579 
580         if (mDppRequestInfo.isGeneratingSelfConfiguration) {
581             logd("Generate Self Configuration in progress. Skip cleanup");
582             return;
583         }
584 
585         // Cancel pending timeout
586         mDppTimeoutMessage.cancel();
587 
588         // Remove the URI from the supplicant list
589         if (mDppRequestInfo.authRole == DPP_AUTH_ROLE_INITIATOR) {
590             if (!mWifiNative.removeDppUri(mClientIfaceName, mDppRequestInfo.peerId)) {
591                 Log.e(TAG, "Failed to remove DPP URI ID " + mDppRequestInfo.peerId);
592             }
593         } else if (mDppRequestInfo.authRole == DPP_AUTH_ROLE_RESPONDER) {
594             if (!mWifiNative.stopDppResponder(mClientIfaceName, mDppRequestInfo.bootstrapId)) {
595                 Log.e(TAG, "Failed to stop DPP Responder");
596             }
597         }
598 
599         mDppRequestInfo.binder.unlinkToDeath(mDppRequestInfo.dr, 0);
600 
601         mDppRequestInfo = null;
602     }
603 
604     /**
605      * Indicates whether there is a dpp session in progress or not.
606      */
isSessionInProgress()607     public boolean isSessionInProgress() {
608         return mDppRequestInfo != null;
609     }
610 
611     private static class DppRequestInfo {
612         public int uid;
613         public String packageName;
614         public IBinder binder;
615         public IBinder.DeathRecipient dr;
616         public int peerId;
617         public IDppCallback callback;
618         public long startTime;
619         public int authRole = DPP_AUTH_ROLE_INACTIVE;
620         public int bootstrapId;
621         public int networkId;
622         public boolean isGeneratingSelfConfiguration = false;
623 
624         @Override
toString()625         public String toString() {
626             return new StringBuilder("DppRequestInfo: uid=").append(uid).append(", binder=").append(
627                     binder).append(", dr=").append(dr)
628                     .append(", callback=").append(callback)
629                     .append(", peerId=").append(peerId)
630                     .append(", authRole=").append(authRole)
631                     .append(", bootstrapId=").append(bootstrapId)
632                     .append(", nId=").append(networkId).toString();
633         }
634     }
635 
636     /**
637      * Enable vervose logging from DppManager
638      *
639      * @param verbose 0 to disable verbose logging, or any other value to enable.
640      */
enableVerboseLogging(boolean verboseEnabled)641     public void enableVerboseLogging(boolean verboseEnabled) {
642         mVerboseLoggingEnabled = verboseEnabled;
643     }
644 
onSuccessConfigReceived(WifiConfiguration newWifiConfiguration)645     private void onSuccessConfigReceived(WifiConfiguration newWifiConfiguration) {
646         try {
647             logd("onSuccessConfigReceived");
648 
649             if (mDppRequestInfo != null && mDppRequestInfo.isGeneratingSelfConfiguration) {
650                 WifiConfiguration existingWifiConfig = mWifiConfigManager
651                         .getConfiguredNetworkWithoutMasking(mDppRequestInfo.networkId);
652 
653                 if (newWifiConfiguration.isSecurityType(WifiConfiguration.SECURITY_TYPE_DPP)
654                         && existingWifiConfig != null && existingWifiConfig.isDppConfigurator()
655                         && TextUtils.equals(existingWifiConfig.SSID, newWifiConfiguration.SSID)
656                         && existingWifiConfig.isSecurityType(WifiConfiguration.SECURITY_TYPE_DPP)) {
657                     if (newWifiConfiguration.getDppConnector().length > 0
658                             && newWifiConfiguration.getDppCSignKey().length > 0
659                             && newWifiConfiguration.getDppNetAccessKey().length > 0) {
660                         Log.d(TAG, "Updating DPP Connection keys for self");
661                         existingWifiConfig.setDppConnectionKeys(
662                                 newWifiConfiguration.getDppConnector(),
663                                 newWifiConfiguration.getDppCSignKey(),
664                                 newWifiConfiguration.getDppNetAccessKey());
665 
666                         NetworkUpdateResult networkUpdateResult = mWifiConfigManager
667                                 .addOrUpdateNetwork(existingWifiConfig, mDppRequestInfo.uid);
668 
669                         if (!networkUpdateResult.isSuccess()) {
670                             Log.e(TAG, "DPP configuration generated, but failed to update network");
671                             mDppRequestInfo.callback.onFailure(EasyConnectStatusCallback
672                                     .EASY_CONNECT_EVENT_FAILURE_CONFIGURATION, null,
673                                     null, new int[0]);
674                         }
675                     }
676                 }
677                 // Done with self configuration. reset flag.
678                 mDppRequestInfo.isGeneratingSelfConfiguration = false;
679             } else if (mDppRequestInfo != null) {
680                 long now = mClock.getElapsedSinceBootMillis();
681                 mDppMetrics.updateDppOperationTime((int) (now - mDppRequestInfo.startTime));
682 
683                 NetworkUpdateResult networkUpdateResult = mWifiConfigManager
684                         .addOrUpdateNetwork(newWifiConfiguration, mDppRequestInfo.uid);
685 
686                 if (networkUpdateResult.isSuccess()) {
687                     mDppMetrics.updateDppEnrolleeSuccess();
688                     if (mDppRequestInfo.authRole == DPP_AUTH_ROLE_RESPONDER) {
689                         mDppMetrics.updateDppEnrolleeResponderSuccess();
690                     }
691                     mDppRequestInfo.callback.onSuccessConfigReceived(
692                             networkUpdateResult.getNetworkId());
693                 } else {
694                     Log.e(TAG, "DPP configuration received, but failed to update network");
695                     mDppMetrics.updateDppFailure(EasyConnectStatusCallback
696                             .EASY_CONNECT_EVENT_FAILURE_CONFIGURATION);
697                     mDppRequestInfo.callback.onFailure(EasyConnectStatusCallback
698                             .EASY_CONNECT_EVENT_FAILURE_CONFIGURATION, null, null, new int[0]);
699                 }
700             } else {
701                 Log.e(TAG, "Unexpected null Wi-Fi configuration object");
702             }
703         } catch (RemoteException e) {
704             Log.e(TAG, "Callback failure");
705         }
706 
707         // Success, DPP is complete. Clear the DPP session automatically
708         cleanupDppResources();
709     }
710 
onSuccess(int dppStatusCode)711     private void onSuccess(int dppStatusCode) {
712         try {
713             if (mDppRequestInfo == null) {
714                 Log.e(TAG, "onSuccess event without a request information object");
715                 return;
716             }
717 
718             logd("onSuccess: " + dppStatusCode);
719             long now = mClock.getElapsedSinceBootMillis();
720             mDppMetrics.updateDppOperationTime((int) (now - mDppRequestInfo.startTime));
721 
722             int dppSuccessCode;
723 
724             // Convert from HAL codes to WifiManager/user codes
725             switch (dppStatusCode) {
726                 case DppEventType.CONFIGURATION_SENT:
727                     mDppMetrics.updateDppR1CapableEnrolleeResponderDevices();
728                     dppSuccessCode = EasyConnectStatusCallback
729                             .EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT;
730                     // For Configurator STA, generate self signed keys for network access.
731                     generateSelfDppConfiguration(mDppRequestInfo.networkId);
732                     break;
733 
734                 case DppEventType.CONFIGURATION_APPLIED:
735                     dppSuccessCode = EasyConnectStatusCallback
736                             .EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED;
737                     break;
738 
739                 default:
740                     Log.e(TAG, "onSuccess: unknown code " + dppStatusCode);
741                     // Success, DPP is complete. Clear the DPP session automatically
742                     mDppRequestInfo.isGeneratingSelfConfiguration = false;
743                     cleanupDppResources();
744                     return;
745             }
746 
747             mDppMetrics.updateDppConfiguratorSuccess(dppSuccessCode);
748             mDppRequestInfo.callback.onSuccess(dppSuccessCode);
749 
750         } catch (RemoteException e) {
751             Log.e(TAG, "Callback failure");
752         }
753 
754         // Success, DPP is complete. Clear the DPP session automatically
755         cleanupDppResources();
756     }
757 
onProgress(int dppStatusCode)758     private void onProgress(int dppStatusCode) {
759         try {
760             if (mDppRequestInfo == null) {
761                 Log.e(TAG, "onProgress event without a request information object");
762                 return;
763             }
764 
765             logd("onProgress: " + dppStatusCode);
766 
767             int dppProgressCode;
768 
769             // Convert from HAL codes to WifiManager/user codes
770             switch (dppStatusCode) {
771                 case DppProgressCode.AUTHENTICATION_SUCCESS:
772                     dppProgressCode = EasyConnectStatusCallback
773                             .EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS;
774                     break;
775 
776                 case DppProgressCode.RESPONSE_PENDING:
777                     dppProgressCode = EasyConnectStatusCallback
778                             .EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING;
779                     break;
780 
781                 case DppProgressCode.CONFIGURATION_SENT_WAITING_RESPONSE:
782                     mDppMetrics.updateDppR2CapableEnrolleeResponderDevices();
783                     dppProgressCode = EasyConnectStatusCallback
784                             .EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE;
785                     break;
786 
787                 case DppProgressCode.CONFIGURATION_ACCEPTED:
788                     dppProgressCode = EasyConnectStatusCallback
789                             .EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_ACCEPTED;
790                     break;
791 
792                 default:
793                     Log.e(TAG, "onProgress: unknown code " + dppStatusCode);
794                     return;
795             }
796 
797             mDppRequestInfo.callback.onProgress(dppProgressCode);
798 
799         } catch (RemoteException e) {
800             Log.e(TAG, "Callback failure");
801         }
802     }
803 
onFailure(int dppStatusCode)804     private void onFailure(int dppStatusCode) {
805         onFailure(dppStatusCode, null, null, null);
806     }
807 
onDppConfiguratorKeyUpdate(byte[] privEcKey)808     private void onDppConfiguratorKeyUpdate(byte[] privEcKey) {
809         if (mDppRequestInfo == null) {
810             Log.e(TAG,
811                     "onDppConfiguratorKeyUpdate event without a request information object");
812             return;
813         }
814 
815         WifiConfiguration selectedNetwork = mWifiConfigManager
816                 .getConfiguredNetworkWithoutMasking(mDppRequestInfo.networkId);
817 
818         if (selectedNetwork != null && privEcKey != null && privEcKey.length > 0
819                 && selectedNetwork.isSecurityType(WifiConfiguration.SECURITY_TYPE_DPP)) {
820             Log.d(TAG, "Updating network access keys for DPP networkId="
821                     + mDppRequestInfo.networkId);
822             selectedNetwork.setDppConfigurator(privEcKey);
823 
824             NetworkUpdateResult networkUpdateResult = mWifiConfigManager
825                     .addOrUpdateNetwork(selectedNetwork, mDppRequestInfo.uid);
826 
827             if (!networkUpdateResult.isSuccess()) {
828                 Log.e(TAG, "Failed to update DPP configurator key.");
829             }
830         }
831     }
832 
833     /**
834      *
835      * This function performs the Enrollee compatibility check with the network.
836      * Compatibilty check is done based on the channel match.
837      * The logic looks into the scan cache and checks if network's
838      * operating channel match with one of the channel in enrollee's scanned channel list.
839      *
840      * @param ssid Network name.
841      * @param channelList contains the list of operating class/channels enrollee used to search for
842      *                    the network.
843      *                    Reference: DPP spec section: DPP Connection Status Object section.
844      *                    (eg for channelList: "81/1,2,3,4,5,6,7,8,9,10,11,117/40,115/48")
845      * @return True On compatibility check failures due to error conditions or
846      *              when AP is not seen in scan cache or when AP is seen in scan cache and
847      *              operating channel is included in enrollee's scanned channel list.
848      *         False when network's operating channel is not included in Enrollee's
849      *              scanned channel list.
850      *
851      */
isEnrolleeCompatibleWithNetwork(String ssid, String channelList)852     private boolean isEnrolleeCompatibleWithNetwork(String ssid, String channelList) {
853         if (ssid == null || channelList == null) {
854             return true;
855         }
856         SparseArray<int[]> dppChannelList = WifiManager.parseDppChannelList(channelList);
857 
858         if (dppChannelList.size() == 0) {
859             Log.d(TAG, "No channels found after parsing channel list string");
860             return true;
861         }
862 
863         List<Integer> freqList = new ArrayList<Integer>();
864 
865         /* Convert the received operatingClass/channels to list of frequencies */
866         for (int i = 0; i < dppChannelList.size(); i++) {
867             /* Derive the band corresponding to operating class */
868             int operatingClass = dppChannelList.keyAt(i);
869             int[] channels = dppChannelList.get(operatingClass);
870             int band = ApConfigUtil.getBandFromOperatingClass(operatingClass);
871             if (band < 0) {
872                 Log.e(TAG, "Band corresponding to the operating class: " + operatingClass
873                         + " not found in the table");
874                 continue;
875             }
876             /* Derive frequency list from channel and band */
877             for (int j = 0; j < channels.length; j++) {
878                 int freq = ApConfigUtil.convertChannelToFrequency(channels[j], band);
879                 if (freq < 0) {
880                     Log.e(TAG, "Invalid frequency after converting channel: " + channels[j]
881                             + " band: " + band);
882                     continue;
883                 }
884                 freqList.add(freq);
885             }
886         }
887 
888         if (freqList.size() == 0) {
889             Log.d(TAG, "frequency list is empty");
890             return true;
891         }
892 
893         /* Check the scan cache for the network enrollee tried to find */
894         boolean isNetworkInScanCache = false;
895         boolean channelMatch = false;
896         for (ScanResult scanResult : mScanRequestProxy.getScanResults()) {
897             if (!TextUtils.equals(ssid, scanResult.SSID)) {
898                 continue;
899             }
900             isNetworkInScanCache = true;
901             if (freqList.contains(scanResult.frequency)) {
902                 channelMatch = true;
903                 break;
904             }
905         }
906 
907         if (isNetworkInScanCache & !channelMatch) {
908             Log.d(TAG, "Optionally update the error code to "
909                     + "ENROLLEE_FAILED_TO_SCAN_NETWORK_CHANNEL as enrollee didn't scan"
910                     + "network's operating channel");
911             mDppMetrics.updateDppR2EnrolleeResponderIncompatibleConfiguration();
912             return false;
913         }
914         return true;
915     }
916 
917     private @EasyConnectStatusCallback.EasyConnectFailureStatusCode int
getFailureStatusCodeOnEnrolleeInCompatibleWithNetwork()918             getFailureStatusCodeOnEnrolleeInCompatibleWithNetwork() {
919         if (!SdkLevel.isAtLeastS() || mDppRequestInfo.packageName != null
920                 && mWifiPermissionsUtil.isTargetSdkLessThan(
921                 mDppRequestInfo.packageName, Build.VERSION_CODES.S,
922                 mDppRequestInfo.uid)) {
923             return EasyConnectStatusCallback
924                     .EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE;
925         } else {
926             return EasyConnectStatusCallback
927                     .EASY_CONNECT_EVENT_FAILURE_ENROLLEE_FAILED_TO_SCAN_NETWORK_CHANNEL;
928         }
929     }
930 
onFailure(int dppStatusCode, String ssid, String channelList, int[] bandList)931     private void onFailure(int dppStatusCode, String ssid, String channelList, int[] bandList) {
932         try {
933             if (mDppRequestInfo == null) {
934                 Log.e(TAG, "onFailure event without a request information object");
935                 return;
936             }
937 
938             logd("OnFailure: " + dppStatusCode);
939 
940             long now = mClock.getElapsedSinceBootMillis();
941             mDppMetrics.updateDppOperationTime((int) (now - mDppRequestInfo.startTime));
942 
943             int dppFailureCode;
944 
945             // Convert from HAL codes to WifiManager/user codes
946             switch (dppStatusCode) {
947                 case DppFailureCode.INVALID_URI:
948                     dppFailureCode =
949                             EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_URI;
950                     break;
951 
952                 case DppFailureCode.AUTHENTICATION:
953                     dppFailureCode =
954                             EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION;
955                     break;
956 
957                 case DppFailureCode.NOT_COMPATIBLE:
958                     dppFailureCode =
959                             EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE;
960                     break;
961 
962                 case DppFailureCode.CONFIGURATION:
963                     dppFailureCode =
964                             EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CONFIGURATION;
965                     break;
966 
967                 case DppFailureCode.BUSY:
968                     dppFailureCode = EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY;
969                     break;
970 
971                 case DppFailureCode.TIMEOUT:
972                     dppFailureCode = EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_TIMEOUT;
973                     break;
974 
975                 case DppFailureCode.NOT_SUPPORTED:
976                     dppFailureCode =
977                             EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED;
978                     break;
979 
980                 case DppFailureCode.CANNOT_FIND_NETWORK:
981                     // This is the only case where channel list is populated, according to the
982                     // DPP spec section 6.3.5.2 DPP Connection Status Object
983                     if (isEnrolleeCompatibleWithNetwork(ssid, channelList)) {
984                         dppFailureCode =
985                                 EasyConnectStatusCallback
986                                 .EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK;
987                     } else {
988                         dppFailureCode = getFailureStatusCodeOnEnrolleeInCompatibleWithNetwork();
989                     }
990                     break;
991 
992                 case DppFailureCode.ENROLLEE_AUTHENTICATION:
993                     dppFailureCode = EasyConnectStatusCallback
994                             .EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION;
995                     break;
996 
997                 case DppFailureCode.CONFIGURATION_REJECTED:
998                     dppFailureCode = EasyConnectStatusCallback
999                             .EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION;
1000                     break;
1001 
1002                 case DppFailureCode.URI_GENERATION:
1003                     if (SdkLevel.isAtLeastS()) {
1004                         dppFailureCode = EasyConnectStatusCallback
1005                                 .EASY_CONNECT_EVENT_FAILURE_URI_GENERATION;
1006                     } else {
1007                         dppFailureCode = EasyConnectStatusCallback
1008                                 .EASY_CONNECT_EVENT_FAILURE_GENERIC;
1009                     }
1010                     break;
1011 
1012                 case DppFailureCode.FAILURE:
1013                 default:
1014                     dppFailureCode = EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC;
1015                     break;
1016             }
1017 
1018             mDppMetrics.updateDppFailure(dppFailureCode);
1019             if (bandList == null) {
1020                 bandList = new int[0];
1021             }
1022             mDppRequestInfo.callback.onFailure(dppFailureCode, ssid, channelList, bandList);
1023 
1024         } catch (RemoteException e) {
1025             Log.e(TAG, "Callback failure");
1026         }
1027 
1028         // All failures are fatal, clear the DPP session
1029         mDppRequestInfo.isGeneratingSelfConfiguration = false;
1030         cleanupDppResources();
1031     }
1032 
logd(String message)1033     private void logd(String message) {
1034         if (mVerboseLoggingEnabled) {
1035             Log.d(TAG, message);
1036         }
1037     }
1038 
linkToDeath(DppRequestInfo dppRequestInfo)1039     private boolean linkToDeath(DppRequestInfo dppRequestInfo) {
1040         // register for binder death
1041         dppRequestInfo.dr = new IBinder.DeathRecipient() {
1042             @Override
1043             public void binderDied() {
1044                 if (dppRequestInfo == null) {
1045                     return;
1046                 }
1047 
1048                 logd("binderDied: uid=" + dppRequestInfo.uid);
1049 
1050                 mHandler.post(() -> {
1051                     dppRequestInfo.isGeneratingSelfConfiguration = false;
1052                     cleanupDppResources();
1053                 });
1054             }
1055         };
1056 
1057         try {
1058             dppRequestInfo.binder.linkToDeath(dppRequestInfo.dr, 0);
1059         } catch (RemoteException e) {
1060             Log.e(TAG, "Error on linkToDeath - " + e);
1061             dppRequestInfo.dr = null;
1062             return false;
1063         }
1064 
1065         return true;
1066     }
1067 
1068     @RequiresApi(Build.VERSION_CODES.S)
convertEasyConnectCryptographyCurveToHidlDppCurve( @ifiManager.EasyConnectCryptographyCurve int curve)1069     private int convertEasyConnectCryptographyCurveToHidlDppCurve(
1070             @WifiManager.EasyConnectCryptographyCurve int curve) {
1071         switch (curve) {
1072             case WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1:
1073                 return DppCurve.SECP384R1;
1074             case WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1:
1075                 return DppCurve.SECP521R1;
1076             case WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1:
1077                 return DppCurve.BRAINPOOLP256R1;
1078             case WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1:
1079                 return DppCurve.BRAINPOOLP384R1;
1080             case WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1:
1081                 return DppCurve.BRAINPOOLP512R1;
1082             case WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1:
1083             default:
1084                 return DppCurve.PRIME256V1;
1085         }
1086     }
1087 }
1088