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