1 /*
2  * Copyright (C) 2016 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 
17 package com.android.server.wifi.aware;
18 
19 import android.annotation.NonNull;
20 import android.hardware.wifi.V1_0.IWifiNanIface;
21 import android.hardware.wifi.V1_0.IfaceType;
22 import android.hardware.wifi.V1_0.WifiStatus;
23 import android.hardware.wifi.V1_0.WifiStatusCode;
24 import android.os.Handler;
25 import android.os.RemoteException;
26 import android.util.Log;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.server.wifi.HalDeviceManager;
30 
31 import java.io.FileDescriptor;
32 import java.io.PrintWriter;
33 
34 /**
35  * Manages the interface to Wi-Fi Aware HIDL (HAL).
36  */
37 public class WifiAwareNativeManager {
38     private static final String TAG = "WifiAwareNativeManager";
39     private static final boolean VDBG = false;
40     /* package */ boolean mDbg = false;
41 
42     // to be used for synchronizing access to any of the WifiAwareNative objects
43     private final Object mLock = new Object();
44 
45     private WifiAwareStateManager mWifiAwareStateManager;
46     private HalDeviceManager mHalDeviceManager;
47     private Handler mHandler;
48     private WifiAwareNativeCallback mWifiAwareNativeCallback;
49     private IWifiNanIface mWifiNanIface = null;
50     private InterfaceDestroyedListener mInterfaceDestroyedListener;
51     private InterfaceAvailableForRequestListener mInterfaceAvailableForRequestListener =
52             new InterfaceAvailableForRequestListener();
53     private int mReferenceCount = 0;
54 
WifiAwareNativeManager(WifiAwareStateManager awareStateManager, HalDeviceManager halDeviceManager, WifiAwareNativeCallback wifiAwareNativeCallback)55     WifiAwareNativeManager(WifiAwareStateManager awareStateManager,
56             HalDeviceManager halDeviceManager,
57             WifiAwareNativeCallback wifiAwareNativeCallback) {
58         mWifiAwareStateManager = awareStateManager;
59         mHalDeviceManager = halDeviceManager;
60         mWifiAwareNativeCallback = wifiAwareNativeCallback;
61     }
62 
63     /**
64      * (HIDL) Cast the input to a 1.2 NAN interface (possibly resulting in a null).
65      *
66      * Separate function so can be mocked in unit tests.
67      */
mockableCastTo_1_2(IWifiNanIface iface)68     public android.hardware.wifi.V1_2.IWifiNanIface mockableCastTo_1_2(IWifiNanIface iface) {
69         return android.hardware.wifi.V1_2.IWifiNanIface.castFrom(iface);
70     }
71 
72     /**
73      * Initialize the class - intended for late initialization.
74      *
75      * @param handler Handler on which to execute interface available callbacks.
76      */
start(Handler handler)77     public void start(Handler handler) {
78         mHandler = handler;
79         mHalDeviceManager.initialize();
80         mHalDeviceManager.registerStatusListener(
81                 new HalDeviceManager.ManagerStatusListener() {
82                     @Override
83                     public void onStatusChanged() {
84                         if (VDBG) Log.v(TAG, "onStatusChanged");
85                         // only care about isStarted (Wi-Fi started) not isReady - since if not
86                         // ready then Wi-Fi will also be down.
87                         if (mHalDeviceManager.isStarted()) {
88                             // 1. no problem registering duplicates - only one will be called
89                             // 2. will be called immediately if available
90                             mHalDeviceManager.registerInterfaceAvailableForRequestListener(
91                                     IfaceType.NAN, mInterfaceAvailableForRequestListener, mHandler);
92                         } else {
93                             awareIsDown();
94                         }
95                     }
96                 }, mHandler);
97         if (mHalDeviceManager.isStarted()) {
98             mHalDeviceManager.registerInterfaceAvailableForRequestListener(
99                     IfaceType.NAN, mInterfaceAvailableForRequestListener, mHandler);
100         }
101     }
102 
103     /**
104      * Returns the native HAL WifiNanIface through which commands to the NAN HAL are dispatched.
105      * Return may be null if not initialized/available.
106      */
107     @VisibleForTesting
getWifiNanIface()108     public IWifiNanIface getWifiNanIface() {
109         synchronized (mLock) {
110             return mWifiNanIface;
111         }
112     }
113 
114     /**
115      * Attempt to obtain the HAL NAN interface.
116      */
tryToGetAware()117     public void tryToGetAware() {
118         synchronized (mLock) {
119             if (mDbg) {
120                 Log.d(TAG, "tryToGetAware: mWifiNanIface=" + mWifiNanIface + ", mReferenceCount="
121                         + mReferenceCount);
122             }
123 
124             if (mWifiNanIface != null) {
125                 mReferenceCount++;
126                 return;
127             }
128             if (mHalDeviceManager == null) {
129                 Log.e(TAG, "tryToGetAware: mHalDeviceManager is null!?");
130                 awareIsDown();
131                 return;
132             }
133 
134             mInterfaceDestroyedListener = new InterfaceDestroyedListener();
135             IWifiNanIface iface = mHalDeviceManager.createNanIface(mInterfaceDestroyedListener,
136                     mHandler);
137             if (iface == null) {
138                 Log.e(TAG, "Was not able to obtain an IWifiNanIface (even though enabled!?)");
139                 awareIsDown();
140             } else {
141                 if (mDbg) Log.v(TAG, "Obtained an IWifiNanIface");
142 
143                 try {
144                     android.hardware.wifi.V1_2.IWifiNanIface iface12 = mockableCastTo_1_2(iface);
145                     WifiStatus status;
146                     if (iface12 == null) {
147                         mWifiAwareNativeCallback.mIsHal12OrLater = false;
148                         status = iface.registerEventCallback(mWifiAwareNativeCallback);
149                     } else {
150                         mWifiAwareNativeCallback.mIsHal12OrLater = true;
151                         status = iface12.registerEventCallback_1_2(mWifiAwareNativeCallback);
152                     }
153                     if (status.code != WifiStatusCode.SUCCESS) {
154                         Log.e(TAG, "IWifiNanIface.registerEventCallback error: " + statusString(
155                                 status));
156                         mHalDeviceManager.removeIface(iface);
157                         awareIsDown();
158                         return;
159                     }
160                 } catch (RemoteException e) {
161                     Log.e(TAG, "IWifiNanIface.registerEventCallback exception: " + e);
162                     awareIsDown();
163                     return;
164                 }
165                 mWifiNanIface = iface;
166                 mReferenceCount = 1;
167             }
168         }
169     }
170 
171     /**
172      * Release the HAL NAN interface.
173      */
releaseAware()174     public void releaseAware() {
175         if (mDbg) {
176             Log.d(TAG, "releaseAware: mWifiNanIface=" + mWifiNanIface + ", mReferenceCount="
177                     + mReferenceCount);
178         }
179 
180         if (mWifiNanIface == null) {
181             return;
182         }
183         if (mHalDeviceManager == null) {
184             Log.e(TAG, "releaseAware: mHalDeviceManager is null!?");
185             return;
186         }
187 
188         synchronized (mLock) {
189             mReferenceCount--;
190             if (mReferenceCount != 0) {
191                 return;
192             }
193             mInterfaceDestroyedListener.active = false;
194             mInterfaceDestroyedListener = null;
195             mHalDeviceManager.removeIface(mWifiNanIface);
196             mWifiNanIface = null;
197         }
198     }
199 
awareIsDown()200     private void awareIsDown() {
201         synchronized (mLock) {
202             if (mDbg) {
203                 Log.d(TAG, "awareIsDown: mWifiNanIface=" + mWifiNanIface + ", mReferenceCount ="
204                         + mReferenceCount);
205             }
206             mWifiNanIface = null;
207             mReferenceCount = 0;
208             mWifiAwareStateManager.disableUsage();
209         }
210     }
211 
212     private class InterfaceDestroyedListener implements
213             HalDeviceManager.InterfaceDestroyedListener {
214         public boolean active = true;
215 
216         @Override
onDestroyed(@onNull String ifaceName)217         public void onDestroyed(@NonNull String ifaceName) {
218             if (mDbg) {
219                 Log.d(TAG, "Interface was destroyed: mWifiNanIface=" + mWifiNanIface + ", active="
220                         + active);
221             }
222             if (active && mWifiNanIface != null) {
223                 awareIsDown();
224             } // else: we released it locally so no need to disable usage
225         }
226     }
227 
228     private class InterfaceAvailableForRequestListener implements
229             HalDeviceManager.InterfaceAvailableForRequestListener {
230         @Override
onAvailabilityChanged(boolean isAvailable)231         public void onAvailabilityChanged(boolean isAvailable) {
232             if (mDbg) {
233                 Log.d(TAG, "Interface availability = " + isAvailable + ", mWifiNanIface="
234                         + mWifiNanIface);
235             }
236             synchronized (mLock) {
237                 if (isAvailable) {
238                     mWifiAwareStateManager.enableUsage();
239                 } else if (mWifiNanIface == null) { // not available could mean already have NAN
240                     mWifiAwareStateManager.disableUsage();
241                 }
242             }
243         }
244     }
245 
statusString(WifiStatus status)246     private static String statusString(WifiStatus status) {
247         if (status == null) {
248             return "status=null";
249         }
250         StringBuilder sb = new StringBuilder();
251         sb.append(status.code).append(" (").append(status.description).append(")");
252         return sb.toString();
253     }
254 
255     /**
256      * Dump the internal state of the class.
257      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)258     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
259         pw.println("WifiAwareNativeManager:");
260         pw.println("  mWifiNanIface: " + mWifiNanIface);
261         pw.println("  mReferenceCount: " + mReferenceCount);
262         mWifiAwareNativeCallback.dump(fd, pw, args);
263         mHalDeviceManager.dump(fd, pw, args);
264     }
265 }
266