• 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.net.MacAddress;
20 import android.net.wifi.SoftApConfiguration;
21 import android.net.wifi.WifiContext;
22 import android.net.wifi.WifiManager;
23 import android.os.Handler;
24 import android.util.Log;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 import com.android.server.wifi.WifiNative.HostapdDeathEventHandler;
28 import com.android.server.wifi.WifiNative.SoftApHalCallback;
29 
30 import java.io.PrintWriter;
31 import java.util.List;
32 
33 import javax.annotation.concurrent.ThreadSafe;
34 
35 /**
36  * To maintain thread-safety, the locking protocol is that every non-static method (regardless of
37  * access level) acquires mLock.
38  */
39 @ThreadSafe
40 public class HostapdHal {
41     private static final String TAG = "HostapdHal";
42 
43     private final Object mLock = new Object();
44     private boolean mVerboseLoggingEnabled = false;
45     private boolean mVerboseHalLoggingEnabled = false;
46     private final WifiContext mContext;
47     private final Handler mEventHandler;
48 
49     // Hostapd HAL interface object - might be implemented by HIDL or AIDL
50     private IHostapdHal mIHostapd;
51 
HostapdHal(WifiContext context, Handler handler)52     public HostapdHal(WifiContext context, Handler handler) {
53         mContext = context;
54         mEventHandler = handler;
55     }
56 
57     /**
58      * Enable/Disable verbose logging.
59      */
enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled)60     public void enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled) {
61         synchronized (mLock) {
62             mVerboseLoggingEnabled = verboseEnabled;
63             mVerboseHalLoggingEnabled = halVerboseEnabled;
64             if (mIHostapd != null) {
65                 mIHostapd.enableVerboseLogging(verboseEnabled, halVerboseEnabled);
66             }
67         }
68     }
69 
70     /**
71      * Initialize the HostapdHal. Creates the internal IHostapdHal object
72      * and calls its initialize method.
73      *
74      * @return true if the initialization succeeded
75      */
initialize()76     public boolean initialize() {
77         synchronized (mLock) {
78             if (mVerboseLoggingEnabled) {
79                 Log.i(TAG, "Initializing Hostapd Service.");
80             }
81             if (mIHostapd != null) {
82                 Log.wtf(TAG, "Hostapd HAL has already been initialized.");
83                 return false;
84             }
85             mIHostapd = createIHostapdHalMockable();
86             if (mIHostapd == null) {
87                 Log.e(TAG, "Failed to get Hostapd HAL instance");
88                 return false;
89             }
90             mIHostapd.enableVerboseLogging(mVerboseLoggingEnabled, mVerboseHalLoggingEnabled);
91             if (!mIHostapd.initialize()) {
92                 Log.e(TAG, "Fail to init hostapd, Stopping hostapd startup");
93                 mIHostapd = null;
94                 return false;
95             }
96             return true;
97         }
98     }
99 
100     /**
101      * Wrapper function to create the IHostapdHal object. Created to be mockable in unit tests.
102      */
103     @VisibleForTesting
createIHostapdHalMockable()104     protected IHostapdHal createIHostapdHalMockable() {
105         synchronized (mLock) {
106             // Prefer AIDL implementation if service is declared.
107             if (HostapdHalAidlImp.serviceDeclared()) {
108                 Log.i(TAG, "Initializing hostapd using AIDL implementation.");
109                 return new HostapdHalAidlImp(mContext, mEventHandler);
110 
111             } else if (HostapdHalHidlImp.serviceDeclared()) {
112                 Log.i(TAG, "Initializing hostapd using HIDL implementation.");
113                 return new HostapdHalHidlImp(mContext, mEventHandler);
114             }
115             Log.e(TAG, "No HIDL or AIDL service available for hostapd.");
116             return null;
117         }
118     }
119 
120     /**
121      * Returns whether or not the hostapd supports getting the AP info from the callback.
122      */
isApInfoCallbackSupported()123     public boolean isApInfoCallbackSupported() {
124         synchronized (mLock) {
125             String methodStr = "isApInfoCallbackSupported";
126             if (mIHostapd == null) {
127                 return handleNullIHostapd(methodStr);
128             }
129             return mIHostapd.isApInfoCallbackSupported();
130         }
131     }
132 
133     /**
134      * Register the provided callback handler for SoftAp events.
135      * <p>
136      * Note that only one callback can be registered at a time - any registration overrides previous
137      * registrations.
138      *
139      * @param ifaceName Name of the interface.
140      * @param listener Callback listener for AP events.
141      * @return true on success, false on failure.
142      */
registerApCallback(@onNull String ifaceName, @NonNull SoftApHalCallback callback)143     public boolean registerApCallback(@NonNull String ifaceName,
144             @NonNull SoftApHalCallback callback) {
145         synchronized (mLock) {
146             String methodStr = "registerApCallback";
147             if (mIHostapd == null) {
148                 return handleNullIHostapd(methodStr);
149             }
150             return mIHostapd.registerApCallback(ifaceName, callback);
151         }
152     }
153 
154     /**
155      * Add and start a new access point.
156      *
157      * @param ifaceName Name of the interface.
158      * @param config Configuration to use for the AP.
159      * @param isMetered Indicates the network is metered or not.
160      * @param onFailureListener A runnable to be triggered on failure.
161      * @return true on success, false otherwise.
162      */
addAccessPoint(@onNull String ifaceName, @NonNull SoftApConfiguration config, boolean isMetered, boolean isUsingMultiLinkOperation, @NonNull List<String> instanceIdentities, @NonNull Runnable onFailureListener)163     public boolean addAccessPoint(@NonNull String ifaceName, @NonNull SoftApConfiguration config,
164                                   boolean isMetered, boolean isUsingMultiLinkOperation,
165                                   @NonNull List<String> instanceIdentities,
166                                   @NonNull Runnable onFailureListener) {
167         synchronized (mLock) {
168             String methodStr = "addAccessPoint";
169             if (mIHostapd == null) {
170                 return handleNullIHostapd(methodStr);
171             }
172             return mIHostapd.addAccessPoint(ifaceName, config, isMetered, isUsingMultiLinkOperation,
173                     instanceIdentities, onFailureListener);
174         }
175     }
176 
177     /**
178      * Remove a previously started access point.
179      *
180      * @param ifaceName Name of the interface.
181      * @return true on success, false otherwise.
182      */
removeAccessPoint(@onNull String ifaceName)183     public boolean removeAccessPoint(@NonNull String ifaceName) {
184         synchronized (mLock) {
185             String methodStr = "removeAccessPoint";
186             if (mIHostapd == null) {
187                 return handleNullIHostapd(methodStr);
188             }
189             return mIHostapd.removeAccessPoint(ifaceName);
190         }
191     }
192 
193     /**
194      * Remove a previously connected client.
195      *
196      * @param ifaceName Name of the interface.
197      * @param client Mac Address of the client.
198      * @param reasonCode One of disconnect reason code which defined in {@link WifiManager}.
199      * @return true on success, false otherwise.
200      */
forceClientDisconnect(@onNull String ifaceName, @NonNull MacAddress client, int reasonCode)201     public boolean forceClientDisconnect(@NonNull String ifaceName,
202             @NonNull MacAddress client, int reasonCode) {
203         synchronized (mLock) {
204             String methodStr = "forceClientDisconnect";
205             if (mIHostapd == null) {
206                 return handleNullIHostapd(methodStr);
207             }
208             return mIHostapd.forceClientDisconnect(ifaceName, client, reasonCode);
209         }
210     }
211 
212     /**
213      * Registers a death notification for hostapd.
214      * @return Returns true on success.
215      */
registerDeathHandler(@onNull HostapdDeathEventHandler handler)216     public boolean registerDeathHandler(@NonNull HostapdDeathEventHandler handler) {
217         synchronized (mLock) {
218             String methodStr = "registerDeathHandler";
219             if (mIHostapd == null) {
220                 return handleNullIHostapd(methodStr);
221             }
222             return mIHostapd.registerDeathHandler(handler);
223         }
224     }
225 
226     /**
227      * Deregisters a death notification for hostapd.
228      * @return Returns true on success.
229      */
deregisterDeathHandler()230     public boolean deregisterDeathHandler() {
231         synchronized (mLock) {
232             String methodStr = "deregisterDeathHandler";
233             if (mIHostapd == null) {
234                 return handleNullIHostapd(methodStr);
235             }
236             return mIHostapd.deregisterDeathHandler();
237         }
238     }
239 
240     /**
241      * Signals whether Initialization completed successfully.
242      */
isInitializationStarted()243     public boolean isInitializationStarted() {
244         synchronized (mLock) {
245             String methodStr = "isInitializationStarted";
246             if (mIHostapd == null) {
247                 return handleNullIHostapd(methodStr);
248             }
249             return mIHostapd.isInitializationStarted();
250         }
251     }
252 
253     /**
254      * Signals whether Initialization completed successfully.
255      */
isInitializationComplete()256     public boolean isInitializationComplete() {
257         synchronized (mLock) {
258             String methodStr = "isInitializationComplete";
259             if (mIHostapd == null) {
260                 return handleNullIHostapd(methodStr);
261             }
262             return mIHostapd.isInitializationComplete();
263         }
264     }
265 
266     /**
267      * Start the hostapd daemon.
268      *
269      * @return true on success, false otherwise.
270      */
startDaemon()271     public boolean startDaemon() {
272         synchronized (mLock) {
273             String methodStr = "startDaemon";
274             if (mIHostapd == null) {
275                 return handleNullIHostapd(methodStr);
276             }
277             return mIHostapd.startDaemon();
278         }
279     }
280 
281     /**
282      * Terminate the hostapd daemon & wait for it's death.
283      */
terminate()284     public void terminate() {
285         synchronized (mLock) {
286             String methodStr = "terminate";
287             if (mIHostapd == null) {
288                 handleNullIHostapd(methodStr);
289                 return;
290             }
291             mIHostapd.terminate();
292         }
293     }
294 
295     /**
296      * See comments for
297      * {@link IHostapdHal#removeLinkFromMultipleLinkBridgedApIface(String, String)}.
298      */
removeLinkFromMultipleLinkBridgedApIface(@onNull String ifaceName, @NonNull String apIfaceInstance)299     public void removeLinkFromMultipleLinkBridgedApIface(@NonNull String ifaceName,
300             @NonNull String apIfaceInstance) {
301         synchronized (mLock) {
302             String methodStr = "removeLinkFromMultipleLinkBridgedApIface";
303             if (mIHostapd == null) {
304                 handleNullIHostapd(methodStr);
305                 return;
306             }
307             mIHostapd.removeLinkFromMultipleLinkBridgedApIface(
308                     ifaceName, apIfaceInstance);
309         }
310     }
311 
handleNullIHostapd(String methodStr)312     private boolean handleNullIHostapd(String methodStr) {
313         Log.e(TAG, "Cannot call " + methodStr + " because mIHostapd is null.");
314         return false;
315     }
316 
dump(PrintWriter pw)317     protected void dump(PrintWriter pw) {
318         synchronized (mLock) {
319             pw.println("Dump of HostapdHal");
320             pw.println("AIDL service declared: " + HostapdHalAidlImp.serviceDeclared());
321             pw.println("HIDL service declared: " + HostapdHalHidlImp.serviceDeclared());
322             boolean initialized = mIHostapd != null;
323             pw.println("Initialized: " + initialized);
324             if (initialized) {
325                 pw.println("Implementation: " + mIHostapd.getClass().getSimpleName());
326                 mIHostapd.dump(pw);
327             }
328         }
329     }
330 
331     /**
332      * Returns whether the hostapd HAL supports reporting the single instance died event.
333      */
isSoftApInstanceDiedHandlerSupported()334     public boolean isSoftApInstanceDiedHandlerSupported() {
335         return (mIHostapd != null) && (mIHostapd instanceof HostapdHalAidlImp);
336     }
337 }
338