/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wifi.aware; import android.annotation.NonNull; import android.os.Handler; import android.os.WorkSource; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.server.wifi.HalDeviceManager; import com.android.server.wifi.WifiNative; import com.android.server.wifi.hal.WifiNanIface; import com.android.wifi.flags.FeatureFlags; import java.io.FileDescriptor; import java.io.PrintWriter; /** * Manages the interface to the Wi-Fi Aware HAL. */ public class WifiAwareNativeManager { private static final String TAG = "WifiAwareNativeManager"; private boolean mVerboseLoggingEnabled = false; // to be used for synchronizing access to any of the WifiAwareNative objects private final Object mLock = new Object(); private WifiAwareStateManager mWifiAwareStateManager; private HalDeviceManager mHalDeviceManager; private WifiNative mWifiNative; private Handler mHandler; private WifiAwareNativeCallback mWifiAwareNativeCallback; private final FeatureFlags mFeatureFlags; private WifiNanIface mWifiNanIface = null; private WifiNative.Iface mNanIface; private InterfaceDestroyedListener mInterfaceDestroyedListener; private int mReferenceCount = 0; WifiAwareNativeManager(WifiAwareStateManager awareStateManager, HalDeviceManager halDeviceManager, WifiAwareNativeCallback wifiAwareNativeCallback, WifiNative wifiNative, FeatureFlags featureFlags) { mWifiAwareStateManager = awareStateManager; mHalDeviceManager = halDeviceManager; mWifiNative = wifiNative; mFeatureFlags = featureFlags; mWifiAwareNativeCallback = wifiAwareNativeCallback; } /** * Enable/Disable verbose logging. */ public void enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled) { mVerboseLoggingEnabled = verboseEnabled; if (mWifiNanIface != null) { mWifiNanIface.enableVerboseLogging(halVerboseEnabled); } } /** * Initialize the class - intended for late initialization. * * @param handler Handler on which to execute interface available callbacks. */ public void start(Handler handler) { mHandler = handler; mHalDeviceManager.initialize(); mHalDeviceManager.registerStatusListener( new HalDeviceManager.ManagerStatusListener() { @Override public void onStatusChanged() { if (mVerboseLoggingEnabled) Log.v(TAG, "onStatusChanged"); // only care about isStarted (Wi-Fi started) not isReady - since if not // ready then Wi-Fi will also be down. if (mHalDeviceManager.isStarted()) { mWifiAwareStateManager.tryToGetAwareCapability(); } else { awareIsDown(mWifiAwareStateManager.isD2dAllowedWhenStaDisabled()); } } }, mHandler); if (mHalDeviceManager.isStarted()) { mWifiAwareStateManager.tryToGetAwareCapability(); } } /** * Returns the WifiNanIface through which commands to the NAN HAL are dispatched. * Return may be null if not initialized/available. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public WifiNanIface getWifiNanIface() { synchronized (mLock) { return mWifiNanIface; } } /** * Attempt to obtain the HAL NAN interface. */ public void tryToGetAware(@NonNull WorkSource requestorWs) { synchronized (mLock) { if (mVerboseLoggingEnabled) { Log.d(TAG, "tryToGetAware: mWifiNanIface=" + mWifiNanIface + ", mReferenceCount=" + mReferenceCount + ", requestorWs=" + requestorWs); } if (mWifiNanIface != null) { mReferenceCount++; return; } if (mHalDeviceManager == null) { Log.e(TAG, "tryToGetAware: mHalDeviceManager is null!?"); awareIsDown(mWifiAwareStateManager.isD2dAllowedWhenStaDisabled()); return; } mInterfaceDestroyedListener = new InterfaceDestroyedListener(); mNanIface = mWifiNative.createNanIface(mInterfaceDestroyedListener, mHandler, requestorWs); if (mNanIface != null) { mWifiNanIface = (WifiNanIface) mNanIface.iface; } if (mWifiNanIface == null) { Log.e(TAG, "Was not able to obtain a WifiNanIface (even though enabled!?)"); awareIsDown(true); } else { if (mVerboseLoggingEnabled) Log.v(TAG, "Obtained a WifiNanIface"); if (!mWifiNanIface.registerFrameworkCallback(mWifiAwareNativeCallback)) { Log.e(TAG, "Unable to register callback with WifiNanIface"); mHalDeviceManager.removeIface(mWifiNanIface); awareIsDown(mWifiAwareStateManager.isD2dAllowedWhenStaDisabled()); return; } mReferenceCount = 1; mWifiNanIface.enableVerboseLogging(mVerboseLoggingEnabled); } } } /** * Release the HAL NAN interface. */ public void releaseAware() { if (mVerboseLoggingEnabled) { Log.d(TAG, "releaseAware: mWifiNanIface=" + mWifiNanIface + ", mReferenceCount=" + mReferenceCount); } if (mWifiNanIface == null) { return; } if (mHalDeviceManager == null) { Log.e(TAG, "releaseAware: mHalDeviceManager is null!?"); return; } synchronized (mLock) { mReferenceCount--; if (mReferenceCount != 0) { return; } mInterfaceDestroyedListener.active = false; mInterfaceDestroyedListener = null; mHalDeviceManager.removeIface(mWifiNanIface); if (mNanIface != null) { final int nanIfaceId = mNanIface.id; // HAL may be stop when Nan is toredown, // clean mNanIface first to avoid infinite loop in clean up mNanIface = null; mWifiNative.teardownNanIface(nanIfaceId); } mWifiNanIface = null; mWifiAwareNativeCallback.resetChannelInfo(); } } /** * Replace requestorWs in-place when iface is already enabled. */ public boolean replaceRequestorWs(@NonNull WorkSource requestorWs) { synchronized (mLock) { if (mVerboseLoggingEnabled) { Log.d(TAG, "replaceRequestorWs: mWifiNanIface=" + mWifiNanIface + ", mReferenceCount=" + mReferenceCount + ", requestorWs=" + requestorWs); } if (mWifiNanIface == null) { return false; } if (mHalDeviceManager == null) { Log.e(TAG, "tryToGetAware: mHalDeviceManager is null!?"); awareIsDown(mWifiAwareStateManager.isD2dAllowedWhenStaDisabled()); return false; } return mHalDeviceManager.replaceRequestorWsForNanIface(mWifiNanIface, requestorWs); } } private void awareIsDown(boolean markAsAvailable) { synchronized (mLock) { if (mVerboseLoggingEnabled) { Log.d(TAG, "awareIsDown: mWifiNanIface=" + mWifiNanIface + ", mReferenceCount =" + mReferenceCount); } if (mNanIface != null) { final int nanIfaceId = mNanIface.id; // HAL may be stop when Nan is toredown, // clean mNanIface first to avoid infinite loop in clean up mNanIface = null; mWifiNative.teardownNanIface(nanIfaceId); } mWifiNanIface = null; mReferenceCount = 0; mWifiAwareStateManager.disableUsage(markAsAvailable); } } private class InterfaceDestroyedListener implements HalDeviceManager.InterfaceDestroyedListener { public boolean active = true; @Override public void onDestroyed(@NonNull String ifaceName) { if (mVerboseLoggingEnabled) { Log.d(TAG, "Interface was destroyed: mWifiNanIface=" + mWifiNanIface + ", active=" + active); } if (active && mWifiNanIface != null) { awareIsDown(true); } // else: we released it locally so no need to disable usage } } /** * Dump the internal state of the class. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("WifiAwareNativeManager:"); pw.println(" mWifiNanIface: " + mWifiNanIface); pw.println(" mReferenceCount: " + mReferenceCount); mWifiAwareNativeCallback.dump(fd, pw, args); } }