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