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