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
17package com.android.server.wifi.aware;
18
19import android.annotation.NonNull;
20import android.hardware.wifi.V1_0.IWifiNanIface;
21import android.hardware.wifi.V1_0.IfaceType;
22import android.hardware.wifi.V1_0.WifiStatus;
23import android.hardware.wifi.V1_0.WifiStatusCode;
24import android.os.Handler;
25import android.os.RemoteException;
26import android.util.Log;
27
28import com.android.internal.annotations.VisibleForTesting;
29import com.android.server.wifi.HalDeviceManager;
30
31import java.io.FileDescriptor;
32import java.io.PrintWriter;
33
34/**
35 * Manages the interface to Wi-Fi Aware HIDL (HAL).
36 */
37public 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
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     */
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     */
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
108    public IWifiNanIface getWifiNanIface() {
109        synchronized (mLock) {
110            return mWifiNanIface;
111        }
112    }
113
114    /**
115     * Attempt to obtain the HAL NAN interface.
116     */
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     */
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
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
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
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
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     */
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