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