• 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.net.MacAddress;
21 import android.net.wifi.SoftApConfiguration;
22 import android.net.wifi.WifiManager;
23 import android.net.wifi.util.Environment;
24 import android.os.Build;
25 import android.os.Handler;
26 import android.util.Log;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.server.wifi.WifiNative.HostapdDeathEventHandler;
30 import com.android.server.wifi.WifiNative.SoftApHalCallback;
31 
32 import java.io.PrintWriter;
33 
34 import javax.annotation.concurrent.ThreadSafe;
35 
36 /**
37  * To maintain thread-safety, the locking protocol is that every non-static method (regardless of
38  * access level) acquires mLock.
39  */
40 @ThreadSafe
41 public class HostapdHal {
42     private static final String TAG = "HostapdHal";
43 
44     private final Object mLock = new Object();
45     private boolean mVerboseLoggingEnabled = false;
46     private boolean mVerboseHalLoggingEnabled = false;
47     private final Context mContext;
48     private final Handler mEventHandler;
49 
50     // Hostapd HAL interface object - might be implemented by HIDL or AIDL
51     private IHostapdHal mIHostapd;
52 
HostapdHal(Context context, Handler handler)53     public HostapdHal(Context context, Handler handler) {
54         mContext = context;
55         mEventHandler = handler;
56     }
57 
58     /**
59      * Enable/Disable verbose logging.
60      *
61      * @param enable true to enable, false to disable.
62      */
enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled)63     public void enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled) {
64         synchronized (mLock) {
65             mVerboseLoggingEnabled = verboseEnabled;
66             mVerboseHalLoggingEnabled = halVerboseEnabled;
67             if (mIHostapd != null) {
68                 mIHostapd.enableVerboseLogging(verboseEnabled, halVerboseEnabled);
69             }
70         }
71     }
72 
73     /**
74      * Initialize the HostapdHal. Creates the internal IHostapdHal object
75      * and calls its initialize method.
76      *
77      * @return true if the initialization succeeded
78      */
initialize()79     public boolean initialize() {
80         synchronized (mLock) {
81             if (mVerboseLoggingEnabled) {
82                 Log.i(TAG, "Initializing Hostapd Service.");
83             }
84             if (mIHostapd != null) {
85                 Log.wtf(TAG, "Hostapd HAL has already been initialized.");
86                 return false;
87             }
88             mIHostapd = createIHostapdHalMockable();
89             if (mIHostapd == null) {
90                 Log.e(TAG, "Failed to get Hostapd HAL instance");
91                 return false;
92             }
93             mIHostapd.enableVerboseLogging(mVerboseLoggingEnabled, mVerboseHalLoggingEnabled);
94             if (!mIHostapd.initialize()) {
95                 Log.e(TAG, "Fail to init hostapd, Stopping hostapd startup");
96                 mIHostapd = null;
97                 return false;
98             }
99             return true;
100         }
101     }
102 
103     /**
104      * Wrapper function to create the IHostapdHal object. Created to be mockable in unit tests.
105      */
106     @VisibleForTesting
createIHostapdHalMockable()107     protected IHostapdHal createIHostapdHalMockable() {
108         synchronized (mLock) {
109             // Prefer AIDL implementation if service is declared.
110             if (HostapdHalAidlImp.serviceDeclared()) {
111                 Log.i(TAG, "Initializing hostapd using AIDL implementation.");
112                 return new HostapdHalAidlImp(mContext, mEventHandler);
113 
114             } else if (HostapdHalHidlImp.serviceDeclared()) {
115                 Log.i(TAG, "Initializing hostapd using HIDL implementation.");
116                 return new HostapdHalHidlImp(mContext, mEventHandler);
117             }
118             Log.e(TAG, "No HIDL or AIDL service available for hostapd.");
119             return null;
120         }
121     }
122 
123     /**
124      * Returns whether or not the hostapd supports getting the AP info from the callback.
125      */
isApInfoCallbackSupported()126     public boolean isApInfoCallbackSupported() {
127         synchronized (mLock) {
128             String methodStr = "isApInfoCallbackSupported";
129             if (mIHostapd == null) {
130                 return handleNullIHostapd(methodStr);
131             }
132             return mIHostapd.isApInfoCallbackSupported();
133         }
134     }
135 
136     /**
137      * Register the provided callback handler for SoftAp events.
138      * <p>
139      * Note that only one callback can be registered at a time - any registration overrides previous
140      * registrations.
141      *
142      * @param ifaceName Name of the interface.
143      * @param listener Callback listener for AP events.
144      * @return true on success, false on failure.
145      */
registerApCallback(@onNull String ifaceName, @NonNull SoftApHalCallback callback)146     public boolean registerApCallback(@NonNull String ifaceName,
147             @NonNull SoftApHalCallback callback) {
148         synchronized (mLock) {
149             String methodStr = "registerApCallback";
150             if (mIHostapd == null) {
151                 return handleNullIHostapd(methodStr);
152             }
153             return mIHostapd.registerApCallback(ifaceName, callback);
154         }
155     }
156 
157     /**
158      * Add and start a new access point.
159      *
160      * @param ifaceName Name of the interface.
161      * @param config Configuration to use for the AP.
162      * @param isMetered Indicates the network is metered or not.
163      * @param onFailureListener A runnable to be triggered on failure.
164      * @return true on success, false otherwise.
165      */
addAccessPoint(@onNull String ifaceName, @NonNull SoftApConfiguration config, boolean isMetered, @NonNull Runnable onFailureListener)166     public boolean addAccessPoint(@NonNull String ifaceName, @NonNull SoftApConfiguration config,
167                                   boolean isMetered, @NonNull Runnable onFailureListener) {
168         synchronized (mLock) {
169             String methodStr = "addAccessPoint";
170             if (mIHostapd == null) {
171                 return handleNullIHostapd(methodStr);
172             }
173             return mIHostapd.addAccessPoint(ifaceName, config, isMetered, 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 
handleNullIHostapd(String methodStr)295     private boolean handleNullIHostapd(String methodStr) {
296         Log.e(TAG, "Cannot call " + methodStr + " because mIHostapd is null.");
297         return false;
298     }
299 
dump(PrintWriter pw)300     protected void dump(PrintWriter pw) {
301         synchronized (mLock) {
302             pw.println("Dump of HostapdHal");
303             pw.println("AIDL service declared: " + HostapdHalAidlImp.serviceDeclared());
304             pw.println("HIDL service declared: " + HostapdHalHidlImp.serviceDeclared());
305             boolean initialized = mIHostapd != null;
306             pw.println("Initialized: " + initialized);
307             if (initialized) {
308                 pw.println("Implementation: " + mIHostapd.getClass().getSimpleName());
309                 mIHostapd.dump(pw);
310             }
311         }
312     }
313 
314     /**
315      * Returns whether or not the hostapd HAL supports reporting single instance died event.
316      */
isSoftApInstanceDiedHandlerSupported()317     public boolean isSoftApInstanceDiedHandlerSupported() {
318         return Environment.isVndkApiLevelNewerThan(Build.VERSION_CODES.S);
319     }
320 }
321