• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.server.wifi;
17 
18 import android.annotation.NonNull;
19 import android.content.Context;
20 import android.hardware.wifi.hostapd.V1_0.HostapdStatus;
21 import android.hardware.wifi.hostapd.V1_0.HostapdStatusCode;
22 import android.hardware.wifi.hostapd.V1_0.IHostapd;
23 import android.hardware.wifi.hostapd.V1_2.DebugLevel;
24 import android.hardware.wifi.hostapd.V1_2.Ieee80211ReasonCode;
25 import android.hardware.wifi.hostapd.V1_3.Bandwidth;
26 import android.hardware.wifi.hostapd.V1_3.Generation;
27 import android.hidl.manager.V1_0.IServiceManager;
28 import android.hidl.manager.V1_0.IServiceNotification;
29 import android.net.MacAddress;
30 import android.net.wifi.ScanResult;
31 import android.net.wifi.SoftApConfiguration;
32 import android.net.wifi.SoftApConfiguration.BandType;
33 import android.net.wifi.SoftApInfo;
34 import android.net.wifi.WifiManager;
35 import android.os.Handler;
36 import android.os.IHwBinder.DeathRecipient;
37 import android.os.RemoteException;
38 import android.text.TextUtils;
39 import android.util.Log;
40 
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.modules.utils.build.SdkLevel;
43 import com.android.server.wifi.WifiNative.HostapdDeathEventHandler;
44 import com.android.server.wifi.WifiNative.SoftApListener;
45 import com.android.server.wifi.util.ApConfigUtil;
46 import com.android.server.wifi.util.NativeUtil;
47 import com.android.wifi.resources.R;
48 
49 import java.util.ArrayList;
50 import java.util.HashMap;
51 import java.util.List;
52 import java.util.NoSuchElementException;
53 import java.util.Random;
54 import java.util.concurrent.CountDownLatch;
55 import java.util.concurrent.TimeUnit;
56 
57 import javax.annotation.concurrent.ThreadSafe;
58 
59 /**
60  * To maintain thread-safety, the locking protocol is that every non-static method (regardless of
61  * access level) acquires mLock.
62  */
63 @ThreadSafe
64 public class HostapdHal {
65     private static final String TAG = "HostapdHal";
66     @VisibleForTesting
67     public static final String HAL_INSTANCE_NAME = "default";
68     @VisibleForTesting
69     public static final long WAIT_FOR_DEATH_TIMEOUT_MS = 50L;
70 
71     private final Object mLock = new Object();
72     private boolean mVerboseLoggingEnabled = false;
73     private final Context mContext;
74     private final Handler mEventHandler;
75 
76     // Hostapd HAL interface objects
77     private IServiceManager mIServiceManager = null;
78     private IHostapd mIHostapd;
79     private HashMap<String, Runnable> mSoftApFailureListeners = new HashMap<>();
80     private SoftApListener mSoftApEventListener;
81     private HostapdDeathEventHandler mDeathEventHandler;
82     private ServiceManagerDeathRecipient mServiceManagerDeathRecipient;
83     private HostapdDeathRecipient mHostapdDeathRecipient;
84     // Death recipient cookie registered for current supplicant instance.
85     private long mDeathRecipientCookie = 0;
86 
87     private final IServiceNotification mServiceNotificationCallback =
88             new IServiceNotification.Stub() {
89         public void onRegistration(String fqName, String name, boolean preexisting) {
90             synchronized (mLock) {
91                 if (mVerboseLoggingEnabled) {
92                     Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName
93                             + ", " + name + " preexisting=" + preexisting);
94                 }
95                 if (!initHostapdService()) {
96                     Log.e(TAG, "initalizing IHostapd failed.");
97                     hostapdServiceDiedHandler(mDeathRecipientCookie);
98                 } else {
99                     Log.i(TAG, "Completed initialization of IHostapd.");
100                 }
101             }
102         }
103     };
104     private class ServiceManagerDeathRecipient implements DeathRecipient {
105         @Override
serviceDied(long cookie)106         public void serviceDied(long cookie) {
107             mEventHandler.post(() -> {
108                 synchronized (mLock) {
109                     Log.w(TAG, "IServiceManager died: cookie=" + cookie);
110                     hostapdServiceDiedHandler(mDeathRecipientCookie);
111                     mIServiceManager = null; // Will need to register a new ServiceNotification
112                 }
113             });
114         }
115     }
116     private class HostapdDeathRecipient implements DeathRecipient {
117         @Override
serviceDied(long cookie)118         public void serviceDied(long cookie) {
119             mEventHandler.post(() -> {
120                 synchronized (mLock) {
121                     Log.w(TAG, "IHostapd/IHostapd died: cookie=" + cookie);
122                     hostapdServiceDiedHandler(cookie);
123                 }
124             });
125         }
126     }
127 
HostapdHal(Context context, Handler handler)128     public HostapdHal(Context context, Handler handler) {
129         mContext = context;
130         mEventHandler = handler;
131         mServiceManagerDeathRecipient = new ServiceManagerDeathRecipient();
132         mHostapdDeathRecipient = new HostapdDeathRecipient();
133     }
134 
135     /**
136      * Enable/Disable verbose logging.
137      *
138      * @param enable true to enable, false to disable.
139      */
enableVerboseLogging(boolean enable)140     void enableVerboseLogging(boolean enable) {
141         synchronized (mLock) {
142             mVerboseLoggingEnabled = enable;
143             setLogLevel();
144         }
145     }
146 
147     /**
148      * Uses the IServiceManager to check if the device is running V1_1 of the HAL from the VINTF for
149      * the device.
150      * @return true if supported, false otherwise.
151      */
isV1_1()152     private boolean isV1_1() {
153         return checkHalVersionByInterfaceName(
154                 android.hardware.wifi.hostapd.V1_1.IHostapd.kInterfaceName);
155     }
156 
157     /**
158      * Uses the IServiceManager to check if the device is running V1_2 of the HAL from the VINTF for
159      * the device.
160      * @return true if supported, false otherwise.
161      */
isV1_2()162     private boolean isV1_2() {
163         return checkHalVersionByInterfaceName(
164                 android.hardware.wifi.hostapd.V1_2.IHostapd.kInterfaceName);
165     }
166 
167     /**
168      * Uses the IServiceManager to check if the device is running V1_3 of the HAL from the VINTF for
169      * the device.
170      * @return true if supported, false otherwise.
171      */
isV1_3()172     private boolean isV1_3() {
173         return checkHalVersionByInterfaceName(
174                 android.hardware.wifi.hostapd.V1_3.IHostapd.kInterfaceName);
175     }
176 
checkHalVersionByInterfaceName(String interfaceName)177     private boolean checkHalVersionByInterfaceName(String interfaceName) {
178         if (interfaceName == null) {
179             return false;
180         }
181         synchronized (mLock) {
182             if (mIServiceManager == null) {
183                 Log.e(TAG, "checkHalVersionByInterfaceName called but mServiceManager is null!?");
184                 return false;
185             }
186             try {
187                 return (mIServiceManager.getTransport(
188                         interfaceName,
189                         HAL_INSTANCE_NAME)
190                         != IServiceManager.Transport.EMPTY);
191             } catch (RemoteException e) {
192                 Log.e(TAG, "Exception while operating on IServiceManager: " + e);
193                 handleRemoteException(e, "getTransport");
194                 return false;
195             }
196         }
197     }
198 
199     /**
200      * Link to death for IServiceManager object.
201      * @return true on success, false otherwise.
202      */
linkToServiceManagerDeath()203     private boolean linkToServiceManagerDeath() {
204         synchronized (mLock) {
205             if (mIServiceManager == null) return false;
206             try {
207                 if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) {
208                     Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
209                     hostapdServiceDiedHandler(mDeathRecipientCookie);
210                     mIServiceManager = null; // Will need to register a new ServiceNotification
211                     return false;
212                 }
213             } catch (RemoteException e) {
214                 Log.e(TAG, "IServiceManager.linkToDeath exception", e);
215                 mIServiceManager = null; // Will need to register a new ServiceNotification
216                 return false;
217             }
218             return true;
219         }
220     }
221 
222     /**
223      * Returns whether or not the hostapd supported to get the AP info from the callback.
224      */
isApInfoCallbackSupported()225     public boolean isApInfoCallbackSupported() {
226         return isV1_3();
227     }
228 
229     /**
230      * Registers a service notification for the IHostapd service, which triggers intialization of
231      * the IHostapd
232      * @return true if the service notification was successfully registered
233      */
initialize()234     public boolean initialize() {
235         synchronized (mLock) {
236             if (mVerboseLoggingEnabled) {
237                 Log.i(TAG, "Registering IHostapd service ready callback.");
238             }
239             mIHostapd = null;
240             if (mIServiceManager != null) {
241                 // Already have an IServiceManager and serviceNotification registered, don't
242                 // don't register another.
243                 return true;
244             }
245             try {
246                 mIServiceManager = getServiceManagerMockable();
247                 if (mIServiceManager == null) {
248                     Log.e(TAG, "Failed to get HIDL Service Manager");
249                     return false;
250                 }
251                 if (!linkToServiceManagerDeath()) {
252                     return false;
253                 }
254                 /* TODO(b/33639391) : Use the new IHostapd.registerForNotifications() once it
255                    exists */
256                 if (!mIServiceManager.registerForNotifications(
257                         IHostapd.kInterfaceName, "", mServiceNotificationCallback)) {
258                     Log.e(TAG, "Failed to register for notifications to "
259                             + IHostapd.kInterfaceName);
260                     mIServiceManager = null; // Will need to register a new ServiceNotification
261                     return false;
262                 }
263             } catch (RemoteException e) {
264                 Log.e(TAG, "Exception while trying to register a listener for IHostapd service: "
265                         + e);
266                 hostapdServiceDiedHandler(mDeathRecipientCookie);
267                 mIServiceManager = null; // Will need to register a new ServiceNotification
268                 return false;
269             }
270             return true;
271         }
272     }
273 
274     /**
275      * Link to death for IHostapd object.
276      * @return true on success, false otherwise.
277      */
linkToHostapdDeath(DeathRecipient deathRecipient, long cookie)278     private boolean linkToHostapdDeath(DeathRecipient deathRecipient, long cookie) {
279         synchronized (mLock) {
280             if (mIHostapd == null) return false;
281             try {
282                 if (!mIHostapd.linkToDeath(deathRecipient, cookie)) {
283                     Log.wtf(TAG, "Error on linkToDeath on IHostapd");
284                     hostapdServiceDiedHandler(mDeathRecipientCookie);
285                     return false;
286                 }
287             } catch (RemoteException e) {
288                 Log.e(TAG, "IHostapd.linkToDeath exception", e);
289                 return false;
290             }
291             return true;
292         }
293     }
294 
registerCallback( android.hardware.wifi.hostapd.V1_1.IHostapdCallback callback)295     private boolean registerCallback(
296             android.hardware.wifi.hostapd.V1_1.IHostapdCallback callback) {
297         synchronized (mLock) {
298             String methodStr = "registerCallback_1_1";
299             try {
300                 android.hardware.wifi.hostapd.V1_1.IHostapd iHostapdV1_1 = getHostapdMockableV1_1();
301                 if (iHostapdV1_1 == null) return false;
302                 HostapdStatus status =  iHostapdV1_1.registerCallback(callback);
303                 return checkStatusAndLogFailure(status, methodStr);
304             } catch (RemoteException e) {
305                 handleRemoteException(e, methodStr);
306                 return false;
307             }
308         }
309     }
310 
registerCallback_1_3( android.hardware.wifi.hostapd.V1_3.IHostapdCallback callback)311     private boolean registerCallback_1_3(
312             android.hardware.wifi.hostapd.V1_3.IHostapdCallback callback) {
313         synchronized (mLock) {
314             String methodStr = "registerCallback_1_3";
315             try {
316                 android.hardware.wifi.hostapd.V1_3.IHostapd iHostapdV1_3 = getHostapdMockableV1_3();
317                 if (iHostapdV1_3 == null) return false;
318                 android.hardware.wifi.hostapd.V1_2.HostapdStatus status =
319                         iHostapdV1_3.registerCallback_1_3(callback);
320                 return checkStatusAndLogFailure12(status, methodStr);
321             } catch (RemoteException e) {
322                 handleRemoteException(e, methodStr);
323                 return false;
324             }
325         }
326     }
327 
328     /**
329      * Initialize the IHostapd object.
330      * @return true on success, false otherwise.
331      */
initHostapdService()332     private boolean initHostapdService() {
333         synchronized (mLock) {
334             try {
335                 mIHostapd = getHostapdMockable();
336             } catch (RemoteException e) {
337                 Log.e(TAG, "IHostapd.getService exception: " + e);
338                 return false;
339             } catch (NoSuchElementException e) {
340                 Log.e(TAG, "IHostapd.getService exception: " + e);
341                 return false;
342             }
343             if (mIHostapd == null) {
344                 Log.e(TAG, "Got null IHostapd service. Stopping hostapd HIDL startup");
345                 return false;
346             }
347             if (!linkToHostapdDeath(mHostapdDeathRecipient, ++mDeathRecipientCookie)) {
348                 Log.e(TAG, "Fail to link to Hostapd Death, Stopping hostapd HIDL startup");
349                 mIHostapd = null;
350                 return false;
351             }
352             // Register for callbacks for 1.1 hostapd.
353             if (isV1_3()) {
354                 if (!registerCallback_1_3(new HostapdCallback_1_3())) {
355                     Log.e(TAG, "Fail to regiester Callback 1_3, Stopping hostapd HIDL startup");
356                     mIHostapd = null;
357                     return false;
358                 }
359             } else if (isV1_1() && !registerCallback(new HostapdCallback())) {
360                 Log.e(TAG, "Fail to regiester Callback, Stopping hostapd HIDL startup");
361                 mIHostapd = null;
362                 return false;
363             }
364 
365             // Setup log level
366             setLogLevel();
367         }
368         return true;
369     }
370 
371     /**
372      * Register the provided callback handler for SoftAp events.
373      * <p>
374      * Note that only one callback can be registered at a time - any registration overrides previous
375      * registrations.
376      *
377      * @param ifaceName Name of the interface.
378      * @param listener Callback listener for AP events.
379      * @return true on success, false on failure.
380      */
registerApCallback(@onNull String ifaceName, @NonNull SoftApListener listener)381     public boolean registerApCallback(@NonNull String ifaceName,
382             @NonNull SoftApListener listener) {
383         if (listener == null) {
384             Log.e(TAG, "registerApCallback called with a null callback");
385             return false;
386         }
387 
388         if (!isV1_3()) {
389             Log.d(TAG, "The current HAL doesn't support event callback.");
390             return false;
391         }
392         mSoftApEventListener = listener;
393         Log.i(TAG, "registerApCallback Successful in " + ifaceName);
394         return true;
395     }
396 
397     /**
398      * Add and start a new access point.
399      *
400      * @param ifaceName Name of the interface.
401      * @param config Configuration to use for the AP.
402      * @param isMetered Indicates the network is metered or not.
403      * @param onFailureListener A runnable to be triggered on failure.
404      * @return true on success, false otherwise.
405      */
addAccessPoint(@onNull String ifaceName, @NonNull SoftApConfiguration config, boolean isMetered, @NonNull Runnable onFailureListener)406     public boolean addAccessPoint(@NonNull String ifaceName, @NonNull SoftApConfiguration config,
407                                   boolean isMetered, @NonNull Runnable onFailureListener) {
408         synchronized (mLock) {
409             final String methodStr = "addAccessPoint";
410             IHostapd.IfaceParams ifaceParamsV1_0 = prepareIfaceParamsV1_0(ifaceName, config);
411             android.hardware.wifi.hostapd.V1_2.IHostapd.NetworkParams nwParamsV1_2 =
412                     prepareNetworkParamsV1_2(config);
413             if (nwParamsV1_2 == null) return false;
414             if (!checkHostapdAndLogFailure(methodStr)) return false;
415             try {
416                 HostapdStatus status;
417                 if (!isV1_1()) {
418                     // V1_0 case
419                     status = mIHostapd.addAccessPoint(ifaceParamsV1_0, nwParamsV1_2.V1_0);
420                     if (!checkStatusAndLogFailure(status, methodStr)) {
421                         return false;
422                     }
423                 } else {
424                     android.hardware.wifi.hostapd.V1_1.IHostapd.IfaceParams ifaceParamsV1_1 =
425                             prepareIfaceParamsV1_1(ifaceParamsV1_0, config);
426                     if (!isV1_2()) {
427                         // V1_1 case
428                         android.hardware.wifi.hostapd.V1_1.IHostapd iHostapdV1_1 =
429                                 getHostapdMockableV1_1();
430                         if (iHostapdV1_1 == null) return false;
431                         status = iHostapdV1_1.addAccessPoint_1_1(ifaceParamsV1_1,
432                                 nwParamsV1_2.V1_0);
433                         if (!checkStatusAndLogFailure(status, methodStr)) {
434                             return false;
435                         }
436                     } else {
437                         // V1_2 & V1_3 case
438                         android.hardware.wifi.hostapd.V1_2.HostapdStatus status12;
439                         android.hardware.wifi.hostapd.V1_2.IHostapd.IfaceParams ifaceParamsV1_2 =
440                                 prepareIfaceParamsV1_2(ifaceParamsV1_1, config);
441                         if (!isV1_3()) {
442                             // V1_2 case
443                             android.hardware.wifi.hostapd.V1_2.IHostapd iHostapdV1_2 =
444                                     getHostapdMockableV1_2();
445                             if (iHostapdV1_2 == null) return false;
446                             status12 = iHostapdV1_2.addAccessPoint_1_2(ifaceParamsV1_2,
447                                     nwParamsV1_2);
448                         } else {
449                             // V1_3 case
450                             android.hardware.wifi.hostapd.V1_3
451                                     .IHostapd.NetworkParams nwParamsV1_3 =
452                                     new android.hardware.wifi.hostapd.V1_3
453                                     .IHostapd.NetworkParams();
454                             nwParamsV1_3.V1_2 = nwParamsV1_2;
455                             nwParamsV1_3.isMetered = isMetered;
456                             android.hardware.wifi.hostapd.V1_3.IHostapd.IfaceParams ifaceParams1_3 =
457                                     prepareIfaceParamsV1_3(ifaceParamsV1_2, config);
458                             android.hardware.wifi.hostapd.V1_3.IHostapd iHostapdV1_3 =
459                                     getHostapdMockableV1_3();
460                             if (iHostapdV1_3 == null) return false;
461                             status12 = iHostapdV1_3.addAccessPoint_1_3(ifaceParams1_3,
462                                     nwParamsV1_3);
463                         }
464                         if (!checkStatusAndLogFailure12(status12, methodStr)) {
465                             return false;
466                         }
467                     }
468                 }
469 
470                 mSoftApFailureListeners.put(ifaceName, onFailureListener);
471                 return true;
472             } catch (IllegalArgumentException e) {
473                 Log.e(TAG, "Unrecognized apBand: " + config.getBand());
474                 return false;
475             } catch (RemoteException e) {
476                 handleRemoteException(e, methodStr);
477                 return false;
478             }
479         }
480     }
481 
482     /**
483      * Remove a previously started access point.
484      *
485      * @param ifaceName Name of the interface.
486      * @return true on success, false otherwise.
487      */
removeAccessPoint(@onNull String ifaceName)488     public boolean removeAccessPoint(@NonNull String ifaceName) {
489         synchronized (mLock) {
490             final String methodStr = "removeAccessPoint";
491             if (!checkHostapdAndLogFailure(methodStr)) return false;
492             try {
493                 HostapdStatus status = mIHostapd.removeAccessPoint(ifaceName);
494                 if (!checkStatusAndLogFailure(status, methodStr)) {
495                     return false;
496                 }
497                 mSoftApFailureListeners.remove(ifaceName);
498                 mSoftApEventListener = null;
499                 return true;
500             } catch (RemoteException e) {
501                 handleRemoteException(e, methodStr);
502                 return false;
503             }
504         }
505     }
506 
507     /**
508      * Remove a previously connected client.
509      *
510      * @param ifaceName Name of the interface.
511      * @param client Mac Address of the client.
512      * @param reasonCode One of disconnect reason code which defined in {@link WifiManager}.
513      * @return true on success, false otherwise.
514      */
forceClientDisconnect(@onNull String ifaceName, @NonNull MacAddress client, int reasonCode)515     public boolean forceClientDisconnect(@NonNull String ifaceName,
516             @NonNull MacAddress client, int reasonCode) {
517         final String methodStr = "forceClientDisconnect";
518         if (isV1_2()) {
519             try {
520                 android.hardware.wifi.hostapd.V1_2.IHostapd iHostapdV1_2 =
521                         getHostapdMockableV1_2();
522                 if (iHostapdV1_2 == null) return false;
523                 byte[] clientMacByteArray = client.toByteArray();
524                 short disconnectReason;
525                 switch (reasonCode) {
526                     case WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER:
527                         disconnectReason = Ieee80211ReasonCode.WLAN_REASON_PREV_AUTH_NOT_VALID;
528                         break;
529                     case WifiManager.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS:
530                         disconnectReason = Ieee80211ReasonCode.WLAN_REASON_DISASSOC_AP_BUSY;
531                         break;
532                     case WifiManager.SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED:
533                         disconnectReason = Ieee80211ReasonCode.WLAN_REASON_UNSPECIFIED;
534                         break;
535                     default:
536                         throw new IllegalArgumentException(
537                                 "Unknown disconnect reason code:" + reasonCode);
538                 }
539                 android.hardware.wifi.hostapd.V1_2.HostapdStatus status =
540                         iHostapdV1_2.forceClientDisconnect(ifaceName,
541                         clientMacByteArray, disconnectReason);
542                 if (status.code == HostapdStatusCode.SUCCESS) {
543                     return true;
544                 }
545                 Log.d(TAG, "Error when call forceClientDisconnect, status.code = " + status.code);
546             } catch (RemoteException e) {
547                 handleRemoteException(e, methodStr);
548             }
549         } else {
550             Log.d(TAG, "HIDL doesn't support forceClientDisconnect");
551         }
552         return false;
553     }
554 
555     /**
556      * Registers a death notification for hostapd.
557      * @return Returns true on success.
558      */
registerDeathHandler(@onNull HostapdDeathEventHandler handler)559     public boolean registerDeathHandler(@NonNull HostapdDeathEventHandler handler) {
560         if (mDeathEventHandler != null) {
561             Log.e(TAG, "Death handler already present");
562         }
563         mDeathEventHandler = handler;
564         return true;
565     }
566 
567     /**
568      * Deregisters a death notification for hostapd.
569      * @return Returns true on success.
570      */
deregisterDeathHandler()571     public boolean deregisterDeathHandler() {
572         if (mDeathEventHandler == null) {
573             Log.e(TAG, "No Death handler present");
574         }
575         mDeathEventHandler = null;
576         return true;
577     }
578 
579     /**
580      * Clear internal state.
581      */
clearState()582     private void clearState() {
583         synchronized (mLock) {
584             mIHostapd = null;
585         }
586     }
587 
588     /**
589      * Handle hostapd death.
590      */
hostapdServiceDiedHandler(long cookie)591     private void hostapdServiceDiedHandler(long cookie) {
592         synchronized (mLock) {
593             if (mDeathRecipientCookie != cookie) {
594                 Log.i(TAG, "Ignoring stale death recipient notification");
595                 return;
596             }
597             clearState();
598             if (mDeathEventHandler != null) {
599                 mDeathEventHandler.onDeath();
600             }
601         }
602     }
603 
604     /**
605      * Signals whether Initialization completed successfully.
606      */
isInitializationStarted()607     public boolean isInitializationStarted() {
608         synchronized (mLock) {
609             return mIServiceManager != null;
610         }
611     }
612 
613     /**
614      * Signals whether Initialization completed successfully.
615      */
isInitializationComplete()616     public boolean isInitializationComplete() {
617         synchronized (mLock) {
618             return mIHostapd != null;
619         }
620     }
621 
622     /**
623      * Start the hostapd daemon.
624      *
625      * @return true on success, false otherwise.
626      */
startDaemon()627     public boolean startDaemon() {
628         synchronized (mLock) {
629             try {
630                 // This should startup hostapd daemon using the lazy start HAL mechanism.
631                 getHostapdMockable();
632             } catch (RemoteException e) {
633                 Log.e(TAG, "Exception while trying to start hostapd: "
634                         + e);
635                 hostapdServiceDiedHandler(mDeathRecipientCookie);
636                 return false;
637             } catch (NoSuchElementException e) {
638                 // We're starting the daemon, so expect |NoSuchElementException|.
639                 Log.d(TAG, "Successfully triggered start of hostapd using HIDL");
640             }
641             return true;
642         }
643     }
644 
645     /**
646      * Terminate the hostapd daemon & wait for it's death.
647      */
terminate()648     public void terminate() {
649         synchronized (mLock) {
650             // Register for a new death listener to block until hostapd is dead.
651             final long waitForDeathCookie = new Random().nextLong();
652             final CountDownLatch waitForDeathLatch = new CountDownLatch(1);
653             linkToHostapdDeath((cookie) -> {
654                 Log.d(TAG, "IHostapd died: cookie=" + cookie);
655                 if (cookie != waitForDeathCookie) return;
656                 waitForDeathLatch.countDown();
657             }, waitForDeathCookie);
658 
659             final String methodStr = "terminate";
660             if (!checkHostapdAndLogFailure(methodStr)) return;
661             try {
662                 mIHostapd.terminate();
663             } catch (RemoteException e) {
664                 handleRemoteException(e, methodStr);
665             }
666 
667             // Now wait for death listener callback to confirm that it's dead.
668             try {
669                 if (!waitForDeathLatch.await(WAIT_FOR_DEATH_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
670                     Log.w(TAG, "Timed out waiting for confirmation of hostapd death");
671                 }
672             } catch (InterruptedException e) {
673                 Log.w(TAG, "Failed to wait for hostapd death");
674             }
675         }
676     }
677 
678     /**
679      * Wrapper functions to access static HAL methods, created to be mockable in unit tests
680      */
681     @VisibleForTesting
getServiceManagerMockable()682     protected IServiceManager getServiceManagerMockable() throws RemoteException {
683         synchronized (mLock) {
684             return IServiceManager.getService();
685         }
686     }
687 
688     @VisibleForTesting
getHostapdMockable()689     protected IHostapd getHostapdMockable() throws RemoteException {
690         synchronized (mLock) {
691             return IHostapd.getService();
692         }
693     }
694 
695     @VisibleForTesting
getHostapdMockableV1_1()696     protected android.hardware.wifi.hostapd.V1_1.IHostapd getHostapdMockableV1_1()
697             throws RemoteException {
698         synchronized (mLock) {
699             try {
700                 return android.hardware.wifi.hostapd.V1_1.IHostapd.castFrom(mIHostapd);
701             } catch (NoSuchElementException e) {
702                 Log.e(TAG, "Failed to get IHostapd", e);
703                 return null;
704             }
705         }
706     }
707 
708     @VisibleForTesting
getHostapdMockableV1_2()709     protected android.hardware.wifi.hostapd.V1_2.IHostapd getHostapdMockableV1_2()
710             throws RemoteException {
711         synchronized (mLock) {
712             try {
713                 return android.hardware.wifi.hostapd.V1_2.IHostapd.castFrom(mIHostapd);
714             } catch (NoSuchElementException e) {
715                 Log.e(TAG, "Failed to get IHostapd", e);
716                 return null;
717             }
718         }
719     }
720 
721     @VisibleForTesting
getHostapdMockableV1_3()722     protected android.hardware.wifi.hostapd.V1_3.IHostapd getHostapdMockableV1_3()
723             throws RemoteException {
724         synchronized (mLock) {
725             try {
726                 return android.hardware.wifi.hostapd.V1_3.IHostapd.castFrom(mIHostapd);
727             } catch (NoSuchElementException e) {
728                 Log.e(TAG, "Failed to get IHostapd", e);
729                 return null;
730             }
731         }
732     }
733 
updateIfaceParams_1_2FromResource( android.hardware.wifi.hostapd.V1_2.IHostapd.IfaceParams ifaceParams12)734     private void updateIfaceParams_1_2FromResource(
735             android.hardware.wifi.hostapd.V1_2.IHostapd.IfaceParams ifaceParams12) {
736         ifaceParams12.hwModeParams.enable80211AX =
737                 mContext.getResources().getBoolean(
738                 R.bool.config_wifiSoftapIeee80211axSupported);
739         ifaceParams12.hwModeParams.enable6GhzBand =
740                 ApConfigUtil.isBandSupported(SoftApConfiguration.BAND_6GHZ, mContext);
741         ifaceParams12.hwModeParams.enableHeSingleUserBeamformer =
742                 mContext.getResources().getBoolean(
743                 R.bool.config_wifiSoftapHeSuBeamformerSupported);
744         ifaceParams12.hwModeParams.enableHeSingleUserBeamformee =
745                 mContext.getResources().getBoolean(
746                 R.bool.config_wifiSoftapHeSuBeamformeeSupported);
747         ifaceParams12.hwModeParams.enableHeMultiUserBeamformer =
748                 mContext.getResources().getBoolean(
749                 R.bool.config_wifiSoftapHeMuBeamformerSupported);
750         ifaceParams12.hwModeParams.enableHeTargetWakeTime =
751                 mContext.getResources().getBoolean(R.bool.config_wifiSoftapHeTwtSupported);
752     }
753 
754     private android.hardware.wifi.hostapd.V1_0.IHostapd.IfaceParams
prepareIfaceParamsV1_0(String ifaceName, SoftApConfiguration config)755             prepareIfaceParamsV1_0(String ifaceName, SoftApConfiguration config) {
756         IHostapd.IfaceParams ifaceParamsV1_0 = new IHostapd.IfaceParams();
757         ifaceParamsV1_0.ifaceName = ifaceName;
758         ifaceParamsV1_0.hwModeParams.enable80211N = true;
759         ifaceParamsV1_0.hwModeParams.enable80211AC = mContext.getResources().getBoolean(
760                 R.bool.config_wifi_softap_ieee80211ac_supported);
761         boolean enableAcs = ApConfigUtil.isAcsSupported(mContext) && config.getChannel() == 0;
762         if (enableAcs) {
763             ifaceParamsV1_0.channelParams.enableAcs = true;
764             ifaceParamsV1_0.channelParams.acsShouldExcludeDfs = !mContext.getResources()
765                     .getBoolean(R.bool.config_wifiSoftapAcsIncludeDfs);
766         }
767         ifaceParamsV1_0.channelParams.channel = config.getChannel();
768         ifaceParamsV1_0.channelParams.band = getHalBand(config.getBand());
769         return ifaceParamsV1_0;
770     }
771 
772     private android.hardware.wifi.hostapd.V1_1.IHostapd.IfaceParams
prepareIfaceParamsV1_1( android.hardware.wifi.hostapd.V1_0.IHostapd.IfaceParams ifaceParamsV10, SoftApConfiguration config)773             prepareIfaceParamsV1_1(
774             android.hardware.wifi.hostapd.V1_0.IHostapd.IfaceParams ifaceParamsV10,
775             SoftApConfiguration config) {
776         android.hardware.wifi.hostapd.V1_1.IHostapd.IfaceParams ifaceParamsV1_1 =
777                 new android.hardware.wifi.hostapd.V1_1.IHostapd.IfaceParams();
778         ifaceParamsV1_1.V1_0 = ifaceParamsV10;
779         ifaceParamsV10.channelParams.band = getHalBand(config.getBand());
780 
781         if (ifaceParamsV10.channelParams.enableAcs) {
782             if ((config.getBand() & SoftApConfiguration.BAND_2GHZ) != 0) {
783                 ifaceParamsV1_1.channelParams.acsChannelRanges.addAll(
784                         toAcsChannelRanges(mContext.getResources().getString(
785                         R.string.config_wifiSoftap2gChannelList)));
786             }
787             if ((config.getBand() & SoftApConfiguration.BAND_5GHZ) != 0) {
788                 ifaceParamsV1_1.channelParams.acsChannelRanges.addAll(
789                         toAcsChannelRanges(mContext.getResources().getString(
790                         R.string.config_wifiSoftap5gChannelList)));
791             }
792         }
793         return ifaceParamsV1_1;
794     }
795 
796     private android.hardware.wifi.hostapd.V1_2.IHostapd.IfaceParams
prepareIfaceParamsV1_2( android.hardware.wifi.hostapd.V1_1.IHostapd.IfaceParams ifaceParamsV11, SoftApConfiguration config)797             prepareIfaceParamsV1_2(
798             android.hardware.wifi.hostapd.V1_1.IHostapd.IfaceParams ifaceParamsV11,
799             SoftApConfiguration config) {
800         android.hardware.wifi.hostapd.V1_2.IHostapd.IfaceParams ifaceParamsV1_2 =
801                 new android.hardware.wifi.hostapd.V1_2.IHostapd.IfaceParams();
802         ifaceParamsV1_2.V1_1 = ifaceParamsV11;
803         updateIfaceParams_1_2FromResource(ifaceParamsV1_2);
804         //Update 80211ax support with the configuration.
805         ifaceParamsV1_2.hwModeParams.enable80211AX &= config.isIeee80211axEnabledInternal();
806 
807         ifaceParamsV1_2.channelParams.bandMask = getHalBandMask(config.getBand());
808 
809         // Prepare freq ranges/lists if needed
810         if (ifaceParamsV11.V1_0.channelParams.enableAcs && ApConfigUtil.isSendFreqRangesNeeded(
811                 config.getBand(), mContext)) {
812             prepareAcsChannelFreqRangesMhz(ifaceParamsV1_2.channelParams, config.getBand());
813         }
814         return ifaceParamsV1_2;
815     }
816 
817     private android.hardware.wifi.hostapd.V1_3.IHostapd.IfaceParams
prepareIfaceParamsV1_3( android.hardware.wifi.hostapd.V1_2.IHostapd.IfaceParams ifaceParamsV12, SoftApConfiguration config)818             prepareIfaceParamsV1_3(
819             android.hardware.wifi.hostapd.V1_2.IHostapd.IfaceParams ifaceParamsV12,
820             SoftApConfiguration config) {
821         android.hardware.wifi.hostapd.V1_3.IHostapd.IfaceParams ifaceParamsV1_3 =
822                 new android.hardware.wifi.hostapd.V1_3.IHostapd.IfaceParams();
823         ifaceParamsV1_3.V1_2 = ifaceParamsV12;
824         ArrayList<android.hardware.wifi.hostapd.V1_3.IHostapd.ChannelParams>
825                 channelParams1_3List = new ArrayList<>();
826         if (!SdkLevel.isAtLeastS()) {
827             return ifaceParamsV1_3;
828         }
829         for (int i = 0; i < config.getChannels().size(); i++) {
830             android.hardware.wifi.hostapd.V1_3.IHostapd.ChannelParams channelParam13 =
831                     new android.hardware.wifi.hostapd.V1_3.IHostapd.ChannelParams();
832             // Prepare channel
833             channelParam13.channel = config.getChannels().valueAt(i);
834             // Prepare enableAcs
835             channelParam13.enableAcs = ApConfigUtil.isAcsSupported(mContext)
836                     && channelParam13.channel == 0;
837             // Prepare the bandMask
838             channelParam13.V1_2.bandMask = getHalBandMask(config.getChannels().keyAt(i));
839             channelParam13.bandMask = channelParam13.V1_2.bandMask;
840             // Prepare  AcsChannelFreqRangesMhz
841             if (channelParam13.enableAcs && ApConfigUtil.isSendFreqRangesNeeded(
842                     config.getChannels().keyAt(i), mContext)) {
843                 prepareAcsChannelFreqRangesMhz(
844                         channelParam13.V1_2, config.getChannels().keyAt(i));
845             }
846             channelParams1_3List.add(channelParam13);
847         }
848         ifaceParamsV1_3.channelParamsList = channelParams1_3List;
849         return ifaceParamsV1_3;
850     }
851 
852     private android.hardware.wifi.hostapd.V1_2.IHostapd.NetworkParams
prepareNetworkParamsV1_2(SoftApConfiguration config)853             prepareNetworkParamsV1_2(SoftApConfiguration config) {
854         android.hardware.wifi.hostapd.V1_2.IHostapd.NetworkParams nwParamsV1_2 =
855                 new android.hardware.wifi.hostapd.V1_2.IHostapd.NetworkParams();
856         nwParamsV1_2.V1_0.ssid.addAll(NativeUtil.stringToByteArrayList(config.getSsid()));
857         nwParamsV1_2.V1_0.isHidden = config.isHiddenSsid();
858         int encryptionType = getEncryptionType(config);
859         nwParamsV1_2.encryptionType = encryptionType;
860         nwParamsV1_2.passphrase = (config.getPassphrase() != null)
861                     ? config.getPassphrase() : "";
862         if (encryptionType
863                 == android.hardware.wifi.hostapd.V1_2.IHostapd.EncryptionType.WPA3_SAE
864                 || encryptionType == android.hardware.wifi.hostapd.V1_2.IHostapd
865                 .EncryptionType.WPA3_SAE_TRANSITION) {
866             if (!isV1_2()) {
867                 // It should not happen since we should reject configuration in SoftApManager
868                 Log.e(TAG, "Unsupported Configuration found: " + config);
869                 return null;
870             }
871         } else {
872             // Fill old parameter for old hidl.
873             nwParamsV1_2.V1_0.encryptionType = encryptionType;
874             nwParamsV1_2.V1_0.pskPassphrase = (config.getPassphrase() != null)
875                     ? config.getPassphrase() : "";
876         }
877         return nwParamsV1_2;
878     }
879 
getEncryptionType(SoftApConfiguration localConfig)880     private static int getEncryptionType(SoftApConfiguration localConfig) {
881         int encryptionType;
882         switch (localConfig.getSecurityType()) {
883             case SoftApConfiguration.SECURITY_TYPE_OPEN:
884                 encryptionType = IHostapd.EncryptionType.NONE;
885                 break;
886             case SoftApConfiguration.SECURITY_TYPE_WPA2_PSK:
887                 encryptionType = IHostapd.EncryptionType.WPA2;
888                 break;
889             case SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION:
890                 encryptionType = android.hardware.wifi.hostapd.V1_2
891                         .IHostapd.EncryptionType.WPA3_SAE_TRANSITION;
892                 break;
893             case SoftApConfiguration.SECURITY_TYPE_WPA3_SAE:
894                 encryptionType = android.hardware.wifi.hostapd.V1_2
895                         .IHostapd.EncryptionType.WPA3_SAE;
896                 break;
897             default:
898                 // We really shouldn't default to None, but this was how NetworkManagementService
899                 // used to do this.
900                 encryptionType = IHostapd.EncryptionType.NONE;
901                 break;
902         }
903         return encryptionType;
904     }
905 
getHalBandMask(int apBand)906     private static int getHalBandMask(int apBand) {
907         int bandMask = 0;
908 
909         if (!ApConfigUtil.isBandValid(apBand)) {
910             throw new IllegalArgumentException();
911         }
912 
913         if (ApConfigUtil.containsBand(apBand, SoftApConfiguration.BAND_2GHZ)) {
914             bandMask |= android.hardware.wifi.hostapd.V1_2.IHostapd.BandMask.BAND_2_GHZ;
915         }
916         if (ApConfigUtil.containsBand(apBand, SoftApConfiguration.BAND_5GHZ)) {
917             bandMask |= android.hardware.wifi.hostapd.V1_2.IHostapd.BandMask.BAND_5_GHZ;
918         }
919         if (ApConfigUtil.containsBand(apBand, SoftApConfiguration.BAND_6GHZ)) {
920             bandMask |= android.hardware.wifi.hostapd.V1_2.IHostapd.BandMask.BAND_6_GHZ;
921         }
922         if (ApConfigUtil.containsBand(apBand, SoftApConfiguration.BAND_60GHZ)) {
923             bandMask |= android.hardware.wifi.hostapd.V1_3.IHostapd.BandMask.BAND_60_GHZ;
924         }
925 
926         return bandMask;
927     }
928 
getHalBand(int apBand)929     private static int getHalBand(int apBand) {
930         if (!ApConfigUtil.isBandValid(apBand)) {
931             throw new IllegalArgumentException();
932         }
933 
934         switch (apBand) {
935             case SoftApConfiguration.BAND_2GHZ:
936                 return IHostapd.Band.BAND_2_4_GHZ;
937             case SoftApConfiguration.BAND_5GHZ:
938                 return IHostapd.Band.BAND_5_GHZ;
939             default:
940                 return IHostapd.Band.BAND_ANY;
941         }
942     }
943 
944     /**
945      * Convert channel list string like '1-6,11' to list of AcsChannelRanges
946      */
947     private List<android.hardware.wifi.hostapd.V1_1.IHostapd.AcsChannelRange>
toAcsChannelRanges(String channelListStr)948             toAcsChannelRanges(String channelListStr) {
949         ArrayList<android.hardware.wifi.hostapd.V1_1.IHostapd.AcsChannelRange> acsChannelRanges =
950                 new ArrayList<>();
951 
952         for (String channelRange : channelListStr.split(",")) {
953             android.hardware.wifi.hostapd.V1_1.IHostapd.AcsChannelRange acsChannelRange =
954                     new android.hardware.wifi.hostapd.V1_1.IHostapd.AcsChannelRange();
955             try {
956                 if (channelRange.contains("-")) {
957                     String[] channels  = channelRange.split("-");
958                     if (channels.length != 2) {
959                         Log.e(TAG, "Unrecognized channel range, length is " + channels.length);
960                         continue;
961                     }
962                     int start = Integer.parseInt(channels[0].trim());
963                     int end = Integer.parseInt(channels[1].trim());
964                     if (start > end) {
965                         Log.e(TAG, "Invalid channel range, from " + start + " to " + end);
966                         continue;
967                     }
968                     acsChannelRange.start = start;
969                     acsChannelRange.end = end;
970                 } else if (!TextUtils.isEmpty(channelRange)) {
971                     acsChannelRange.start = Integer.parseInt(channelRange.trim());
972                     acsChannelRange.end = acsChannelRange.start;
973                 }
974             } catch (NumberFormatException e) {
975                 // Ignore malformed value
976                 Log.e(TAG, "Malformed channel value detected: " + e);
977                 continue;
978             }
979             acsChannelRanges.add(acsChannelRange);
980         }
981         return acsChannelRanges;
982     }
983 
984 
985     /**
986      * Prepare the acsChannelFreqRangesMhz in V1_2.IHostapd.ChannelParams.
987      */
prepareAcsChannelFreqRangesMhz( android.hardware.wifi.hostapd.V1_2.IHostapd.ChannelParams channelParams12, @BandType int band)988     private void prepareAcsChannelFreqRangesMhz(
989             android.hardware.wifi.hostapd.V1_2.IHostapd.ChannelParams channelParams12,
990             @BandType int band) {
991         if ((band & SoftApConfiguration.BAND_2GHZ) != 0) {
992             channelParams12.acsChannelFreqRangesMhz.addAll(
993                     toAcsFreqRanges(SoftApConfiguration.BAND_2GHZ));
994         }
995         if ((band & SoftApConfiguration.BAND_5GHZ) != 0) {
996             channelParams12.acsChannelFreqRangesMhz.addAll(
997                     toAcsFreqRanges(SoftApConfiguration.BAND_5GHZ));
998         }
999         if ((band & SoftApConfiguration.BAND_6GHZ) != 0) {
1000             channelParams12.acsChannelFreqRangesMhz.addAll(
1001                     toAcsFreqRanges(SoftApConfiguration.BAND_6GHZ));
1002         }
1003     }
1004 
1005     /**
1006      * Convert channel list string like '1-6,11' to list of AcsFreqRange
1007      */
1008     private List<android.hardware.wifi.hostapd.V1_2.IHostapd.AcsFrequencyRange>
toAcsFreqRanges(@andType int band)1009             toAcsFreqRanges(@BandType int band) {
1010         List<android.hardware.wifi.hostapd.V1_2.IHostapd.AcsFrequencyRange>
1011                 acsFrequencyRanges = new ArrayList<>();
1012 
1013         if (!ApConfigUtil.isBandValid(band) || ApConfigUtil.isMultiband(band)) {
1014             Log.e(TAG, "Invalid band : " + band);
1015             return acsFrequencyRanges;
1016         }
1017 
1018         String channelListStr;
1019         switch (band) {
1020             case SoftApConfiguration.BAND_2GHZ:
1021                 channelListStr = mContext.getResources().getString(
1022                         R.string.config_wifiSoftap2gChannelList);
1023                 if (TextUtils.isEmpty(channelListStr)) {
1024                     channelListStr = ScanResult.BAND_24_GHZ_FIRST_CH_NUM + "-"
1025                             + ScanResult.BAND_24_GHZ_LAST_CH_NUM;
1026                 }
1027                 break;
1028             case SoftApConfiguration.BAND_5GHZ:
1029                 channelListStr = mContext.getResources().getString(
1030                         R.string.config_wifiSoftap5gChannelList);
1031                 if (TextUtils.isEmpty(channelListStr)) {
1032                     channelListStr = ScanResult.BAND_5_GHZ_FIRST_CH_NUM + "-"
1033                             + ScanResult.BAND_5_GHZ_LAST_CH_NUM;
1034                 }
1035                 break;
1036             case SoftApConfiguration.BAND_6GHZ:
1037                 channelListStr = mContext.getResources().getString(
1038                         R.string.config_wifiSoftap6gChannelList);
1039                 if (TextUtils.isEmpty(channelListStr)) {
1040                     channelListStr = ScanResult.BAND_6_GHZ_FIRST_CH_NUM + "-"
1041                             + ScanResult.BAND_6_GHZ_LAST_CH_NUM;
1042                 }
1043                 break;
1044             default:
1045                 return acsFrequencyRanges;
1046         }
1047 
1048         for (String channelRange : channelListStr.split(",")) {
1049             android.hardware.wifi.hostapd.V1_2.IHostapd.AcsFrequencyRange acsFrequencyRange =
1050                     new android.hardware.wifi.hostapd.V1_2.IHostapd.AcsFrequencyRange();
1051             try {
1052                 if (channelRange.contains("-")) {
1053                     String[] channels  = channelRange.split("-");
1054                     if (channels.length != 2) {
1055                         Log.e(TAG, "Unrecognized channel range, length is " + channels.length);
1056                         continue;
1057                     }
1058                     int start = Integer.parseInt(channels[0].trim());
1059                     int end = Integer.parseInt(channels[1].trim());
1060                     if (start > end) {
1061                         Log.e(TAG, "Invalid channel range, from " + start + " to " + end);
1062                         continue;
1063                     }
1064                     acsFrequencyRange.start = ApConfigUtil.convertChannelToFrequency(start, band);
1065                     acsFrequencyRange.end = ApConfigUtil.convertChannelToFrequency(end, band);
1066                 } else if (!TextUtils.isEmpty(channelRange)) {
1067                     int channel = Integer.parseInt(channelRange.trim());
1068                     acsFrequencyRange.start = ApConfigUtil.convertChannelToFrequency(channel, band);
1069                     acsFrequencyRange.end = acsFrequencyRange.start;
1070                 }
1071             } catch (NumberFormatException e) {
1072                 // Ignore malformed value
1073                 Log.e(TAG, "Malformed channel value detected: " + e);
1074                 continue;
1075             }
1076             acsFrequencyRanges.add(acsFrequencyRange);
1077         }
1078         return acsFrequencyRanges;
1079     }
1080 
1081     /**
1082      * Returns false if Hostapd is null, and logs failure to call methodStr
1083      */
checkHostapdAndLogFailure(String methodStr)1084     private boolean checkHostapdAndLogFailure(String methodStr) {
1085         synchronized (mLock) {
1086             if (mIHostapd == null) {
1087                 Log.e(TAG, "Can't call " + methodStr + ", IHostapd is null");
1088                 return false;
1089             }
1090             return true;
1091         }
1092     }
1093 
1094     /**
1095      * Returns true if provided status code is SUCCESS, logs debug message and returns false
1096      * otherwise
1097      */
checkStatusAndLogFailure(HostapdStatus status, String methodStr)1098     private boolean checkStatusAndLogFailure(HostapdStatus status,
1099             String methodStr) {
1100         synchronized (mLock) {
1101             if (status.code != HostapdStatusCode.SUCCESS) {
1102                 Log.e(TAG, "IHostapd." + methodStr + " failed: " + status.code
1103                         + ", " + status.debugMessage);
1104                 return false;
1105             } else {
1106                 if (mVerboseLoggingEnabled) {
1107                     Log.d(TAG, "IHostapd." + methodStr + " succeeded");
1108                 }
1109                 return true;
1110             }
1111         }
1112     }
1113 
1114     /**
1115      * Returns true if provided status code is SUCCESS, logs debug message and returns false
1116      * otherwise
1117      */
checkStatusAndLogFailure12( android.hardware.wifi.hostapd.V1_2.HostapdStatus status, String methodStr)1118     private boolean checkStatusAndLogFailure12(
1119             android.hardware.wifi.hostapd.V1_2.HostapdStatus status, String methodStr) {
1120         synchronized (mLock) {
1121             if (status.code != HostapdStatusCode.SUCCESS) {
1122                 Log.e(TAG, "IHostapd." + methodStr + " failed: " + status.code
1123                         + ", " + status.debugMessage);
1124                 return false;
1125             } else {
1126                 if (mVerboseLoggingEnabled) {
1127                     Log.d(TAG, "IHostapd." + methodStr + " succeeded");
1128                 }
1129                 return true;
1130             }
1131         }
1132     }
1133 
handleRemoteException(RemoteException e, String methodStr)1134     private void handleRemoteException(RemoteException e, String methodStr) {
1135         synchronized (mLock) {
1136             hostapdServiceDiedHandler(mDeathRecipientCookie);
1137             Log.e(TAG, "IHostapd." + methodStr + " failed with exception", e);
1138         }
1139     }
1140 
1141     private class HostapdCallback extends
1142             android.hardware.wifi.hostapd.V1_1.IHostapdCallback.Stub {
1143         @Override
onFailure(String ifaceName)1144         public void onFailure(String ifaceName) {
1145             Log.w(TAG, "Failure on iface " + ifaceName);
1146             Runnable onFailureListener = mSoftApFailureListeners.get(ifaceName);
1147             if (onFailureListener != null) {
1148                 onFailureListener.run();
1149             }
1150         }
1151     }
1152 
1153     /**
1154      * Map hal bandwidth to SoftApInfo.
1155      *
1156      * @param bandwidth The channel bandwidth of the AP which is defined in the HAL.
1157      * @return The channel bandwidth in the SoftApinfo.
1158      */
1159     @VisibleForTesting
mapHalBandwidthToSoftApInfo(int bandwidth)1160     public int mapHalBandwidthToSoftApInfo(int bandwidth) {
1161         switch (bandwidth) {
1162             case Bandwidth.WIFI_BANDWIDTH_20_NOHT:
1163                 return SoftApInfo.CHANNEL_WIDTH_20MHZ_NOHT;
1164             case Bandwidth.WIFI_BANDWIDTH_20:
1165                 return SoftApInfo.CHANNEL_WIDTH_20MHZ;
1166             case Bandwidth.WIFI_BANDWIDTH_40:
1167                 return SoftApInfo.CHANNEL_WIDTH_40MHZ;
1168             case Bandwidth.WIFI_BANDWIDTH_80:
1169                 return SoftApInfo.CHANNEL_WIDTH_80MHZ;
1170             case Bandwidth.WIFI_BANDWIDTH_80P80:
1171                 return SoftApInfo.CHANNEL_WIDTH_80MHZ_PLUS_MHZ;
1172             case Bandwidth.WIFI_BANDWIDTH_160:
1173                 return SoftApInfo.CHANNEL_WIDTH_160MHZ;
1174             case Bandwidth.WIFI_BANDWIDTH_2160:
1175                 return SoftApInfo.CHANNEL_WIDTH_2160MHZ;
1176             case Bandwidth.WIFI_BANDWIDTH_4320:
1177                 return SoftApInfo.CHANNEL_WIDTH_4320MHZ;
1178             case Bandwidth.WIFI_BANDWIDTH_6480:
1179                 return SoftApInfo.CHANNEL_WIDTH_6480MHZ;
1180             case Bandwidth.WIFI_BANDWIDTH_8640:
1181                 return SoftApInfo.CHANNEL_WIDTH_8640MHZ;
1182             default:
1183                 return SoftApInfo.CHANNEL_WIDTH_INVALID;
1184         }
1185     }
1186 
1187     /**
1188      * Map hal generation to wifi standard.
1189      *
1190      * @param generation The operation mode of the AP which is defined in HAL.
1191      * @return The wifi standard in the ScanResult.
1192      */
1193     @VisibleForTesting
mapHalGenerationToWifiStandard(int generation)1194     public int mapHalGenerationToWifiStandard(int generation) {
1195         switch (generation) {
1196             case Generation.WIFI_STANDARD_LEGACY:
1197                 return ScanResult.WIFI_STANDARD_LEGACY;
1198             case Generation.WIFI_STANDARD_11N:
1199                 return ScanResult.WIFI_STANDARD_11N;
1200             case Generation.WIFI_STANDARD_11AC:
1201                 return ScanResult.WIFI_STANDARD_11AC;
1202             case Generation.WIFI_STANDARD_11AX:
1203                 return ScanResult.WIFI_STANDARD_11AX;
1204             case Generation.WIFI_STANDARD_11AD:
1205                 return ScanResult.WIFI_STANDARD_11AD;
1206             default:
1207                 return ScanResult.WIFI_STANDARD_UNKNOWN;
1208         }
1209     }
1210 
1211     private class HostapdCallback_1_3 extends
1212             android.hardware.wifi.hostapd.V1_3.IHostapdCallback.Stub {
1213         @Override
onFailure(String ifaceName)1214         public void onFailure(String ifaceName) {
1215             Log.w(TAG, "Failure on iface " + ifaceName);
1216             Runnable onFailureListener = mSoftApFailureListeners.get(ifaceName);
1217             if (onFailureListener != null) {
1218                 onFailureListener.run();
1219             }
1220         }
1221 
1222         @Override
onApInstanceInfoChanged(String ifaceName, String apIfaceInstance, int frequency, int bandwidth, int generation, byte[] apIfaceInstanceMacAddress)1223         public void onApInstanceInfoChanged(String ifaceName, String apIfaceInstance,
1224                 int frequency, int bandwidth, int generation, byte[] apIfaceInstanceMacAddress) {
1225             Log.d(TAG, "onApInstanceInfoChanged on " + ifaceName + " / " + apIfaceInstance);
1226             try {
1227                 if (mSoftApEventListener != null) {
1228                     mSoftApEventListener.onInfoChanged(apIfaceInstance, frequency,
1229                             mapHalBandwidthToSoftApInfo(bandwidth),
1230                             mapHalGenerationToWifiStandard(generation),
1231                             MacAddress.fromBytes(apIfaceInstanceMacAddress));
1232                 }
1233             } catch (IllegalArgumentException iae) {
1234                 Log.e(TAG, " Invalid apIfaceInstanceMacAddress, " + iae);
1235             }
1236         }
1237 
1238         @Override
onConnectedClientsChanged(String ifaceName, String apIfaceInstance, byte[] clientAddress, boolean isConnected)1239         public void onConnectedClientsChanged(String ifaceName, String apIfaceInstance,
1240                     byte[] clientAddress, boolean isConnected) {
1241             try {
1242                 Log.d(TAG, "onConnectedClientsChanged on " + ifaceName + " / " + apIfaceInstance
1243                         + " and Mac is " + MacAddress.fromBytes(clientAddress).toString()
1244                         + " isConnected: " + isConnected);
1245                 if (mSoftApEventListener != null) {
1246                     mSoftApEventListener.onConnectedClientsChanged(apIfaceInstance,
1247                             MacAddress.fromBytes(clientAddress), isConnected);
1248                 }
1249             } catch (IllegalArgumentException iae) {
1250                 Log.e(TAG, " Invalid clientAddress, " + iae);
1251             }
1252         }
1253     }
1254 
1255     /**
1256      * Set the debug log level for hostapd.
1257      *
1258      * @return true if request is sent successfully, false otherwise.
1259      */
setLogLevel()1260     public boolean setLogLevel() {
1261         synchronized (mLock) {
1262             final String methodStr = "setDebugParams";
1263             if (!checkHostapdAndLogFailure(methodStr)) return false;
1264             if (isV1_2()) {
1265                 try {
1266                     android.hardware.wifi.hostapd.V1_2.IHostapd iHostapdV1_2 =
1267                             getHostapdMockableV1_2();
1268                     if (iHostapdV1_2 == null) return false;
1269                     android.hardware.wifi.hostapd.V1_2.HostapdStatus status =
1270                             iHostapdV1_2.setDebugParams(mVerboseLoggingEnabled
1271                                     ? DebugLevel.DEBUG
1272                                     : DebugLevel.INFO);
1273                     return checkStatusAndLogFailure12(status, methodStr);
1274                 } catch (RemoteException e) {
1275                     handleRemoteException(e, methodStr);
1276                 }
1277             } else {
1278                 Log.d(TAG, "HIDL doesn't support setDebugParams");
1279             }
1280             return false;
1281         }
1282     }
1283 }
1284