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
17package com.android.server.wifi.rtt;
18
19import android.hardware.wifi.V1_0.IWifiRttController;
20import android.hardware.wifi.V1_0.IWifiRttControllerEventCallback;
21import android.hardware.wifi.V1_0.RttBw;
22import android.hardware.wifi.V1_0.RttCapabilities;
23import android.hardware.wifi.V1_0.RttConfig;
24import android.hardware.wifi.V1_0.RttPeerType;
25import android.hardware.wifi.V1_0.RttPreamble;
26import android.hardware.wifi.V1_0.RttResult;
27import android.hardware.wifi.V1_0.RttType;
28import android.hardware.wifi.V1_0.WifiChannelWidthInMhz;
29import android.hardware.wifi.V1_0.WifiStatus;
30import android.hardware.wifi.V1_0.WifiStatusCode;
31import android.net.wifi.rtt.RangingRequest;
32import android.net.wifi.rtt.ResponderConfig;
33import android.os.Handler;
34import android.os.RemoteException;
35import android.util.Log;
36
37import com.android.server.wifi.HalDeviceManager;
38
39import java.io.FileDescriptor;
40import java.io.PrintWriter;
41import java.util.ArrayList;
42import java.util.ListIterator;
43
44/**
45 * TBD
46 */
47public class RttNative extends IWifiRttControllerEventCallback.Stub {
48    private static final String TAG = "RttNative";
49    private static final boolean VDBG = false; // STOPSHIP if true
50    /* package */ boolean mDbg = false;
51
52    private final RttServiceImpl mRttService;
53    private final HalDeviceManager mHalDeviceManager;
54
55    private Object mLock = new Object();
56
57    private volatile IWifiRttController mIWifiRttController;
58    private volatile RttCapabilities mRttCapabilities;
59
60    public RttNative(RttServiceImpl rttService, HalDeviceManager halDeviceManager) {
61        mRttService = rttService;
62        mHalDeviceManager = halDeviceManager;
63    }
64
65    /**
66     * Initialize the object - registering with the HAL device manager.
67     */
68    public void start(Handler handler) {
69        synchronized (mLock) {
70            mHalDeviceManager.initialize();
71            mHalDeviceManager.registerStatusListener(() -> {
72                if (VDBG) Log.d(TAG, "hdm.onStatusChanged");
73                updateController();
74            }, handler);
75            updateController();
76        }
77    }
78
79    /**
80     * Returns true if Wi-Fi is ready for RTT requests, false otherwise.
81     */
82    public boolean isReady() {
83        return mIWifiRttController != null;
84    }
85
86    private void updateController() {
87        if (mDbg) Log.v(TAG, "updateController: mIWifiRttController=" + mIWifiRttController);
88
89        // only care about isStarted (Wi-Fi started) not isReady - since if not
90        // ready then Wi-Fi will also be down.
91        synchronized (mLock) {
92            IWifiRttController localWifiRttController = mIWifiRttController;
93            if (mHalDeviceManager.isStarted()) {
94                if (localWifiRttController == null) {
95                    localWifiRttController = mHalDeviceManager.createRttController();
96                    if (localWifiRttController == null) {
97                        Log.e(TAG, "updateController: Failed creating RTT controller - but Wifi is "
98                                + "started!");
99                    } else {
100                        try {
101                            localWifiRttController.registerEventCallback(this);
102                        } catch (RemoteException e) {
103                            Log.e(TAG, "updateController: exception registering callback: " + e);
104                            localWifiRttController = null;
105                        }
106                    }
107                }
108            } else {
109                localWifiRttController = null;
110            }
111            mIWifiRttController = localWifiRttController;
112
113            if (mIWifiRttController == null) {
114                mRttService.disable();
115            } else {
116                mRttService.enableIfPossible();
117                updateRttCapabilities();
118            }
119        }
120    }
121
122    /**
123     * Updates the RTT capabilities.
124     */
125    void updateRttCapabilities() {
126        if (mRttCapabilities != null) {
127            return;
128        }
129
130        synchronized (mLock) {
131            try {
132                mIWifiRttController.getCapabilities(
133                        (status, capabilities) -> {
134                            if (status.code != WifiStatusCode.SUCCESS) {
135                                Log.e(TAG,
136                                        "updateController: error requesting capabilities -- code="
137                                                + status.code);
138                                return;
139                            }
140                            if (mDbg) {
141                                Log.v(TAG, "updateController: RTT capabilities=" + capabilities);
142                            }
143                            mRttCapabilities = capabilities;
144                        });
145            } catch (RemoteException e) {
146                Log.e(TAG, "updateController: exception requesting capabilities: " + e);
147            }
148
149            if (mRttCapabilities != null && !mRttCapabilities.rttFtmSupported) {
150                Log.wtf(TAG, "Firmware indicates RTT is not supported - but device supports RTT - "
151                        + "ignored!?");
152            }
153        }
154    }
155
156    /**
157     * Issue a range request to the HAL.
158     *
159     * @param cmdId Command ID for the request. Will be used in the corresponding
160     * {@link #onResults(int, ArrayList)}.
161     * @param request Range request.
162     * @param isCalledFromPrivilegedContext Indicates whether privileged APIs are permitted,
163     *                                      initially: support for one-sided RTT.
164     *
165     * @return Success status: true for success, false for failure.
166     */
167    public boolean rangeRequest(int cmdId, RangingRequest request,
168            boolean isCalledFromPrivilegedContext) {
169        if (mDbg) {
170            Log.v(TAG,
171                    "rangeRequest: cmdId=" + cmdId + ", # of requests=" + request.mRttPeers.size());
172        }
173        if (VDBG) Log.v(TAG, "rangeRequest: request=" + request);
174        updateRttCapabilities();
175        synchronized (mLock) {
176            if (!isReady()) {
177                Log.e(TAG, "rangeRequest: RttController is null");
178                return false;
179            }
180
181            ArrayList<RttConfig> rttConfig = convertRangingRequestToRttConfigs(request,
182                    isCalledFromPrivilegedContext, mRttCapabilities);
183            if (rttConfig == null) {
184                Log.e(TAG, "rangeRequest: invalid request parameters");
185                return false;
186            }
187            if (rttConfig.size() == 0) {
188                Log.e(TAG, "rangeRequest: all requests invalidated");
189                mRttService.onRangingResults(cmdId, new ArrayList<>());
190                return true;
191            }
192
193            try {
194                WifiStatus status = mIWifiRttController.rangeRequest(cmdId, rttConfig);
195                if (status.code != WifiStatusCode.SUCCESS) {
196                    Log.e(TAG, "rangeRequest: cannot issue range request -- code=" + status.code);
197                    return false;
198                }
199            } catch (RemoteException e) {
200                Log.e(TAG, "rangeRequest: exception issuing range request: " + e);
201                return false;
202            }
203
204            return true;
205        }
206    }
207
208    /**
209     * Cancel an outstanding ranging request: no guarantees of execution - we will ignore any
210     * results which are returned for the canceled request.
211     *
212     * @param cmdId The cmdId issued with the original rangeRequest command.
213     * @param macAddresses A list of MAC addresses for which to cancel the operation.
214     * @return Success status: true for success, false for failure.
215     */
216    public boolean rangeCancel(int cmdId, ArrayList<byte[]> macAddresses) {
217        if (mDbg) Log.v(TAG, "rangeCancel: cmdId=" + cmdId);
218        synchronized (mLock) {
219            if (!isReady()) {
220                Log.e(TAG, "rangeCancel: RttController is null");
221                return false;
222            }
223
224            try {
225                WifiStatus status = mIWifiRttController.rangeCancel(cmdId, macAddresses);
226                if (status.code != WifiStatusCode.SUCCESS) {
227                    Log.e(TAG, "rangeCancel: cannot issue range cancel -- code=" + status.code);
228                    return false;
229                }
230            } catch (RemoteException e) {
231                Log.e(TAG, "rangeCancel: exception issuing range cancel: " + e);
232                return false;
233            }
234
235            return true;
236        }
237    }
238
239    /**
240     * Callback from HAL with range results.
241     *
242     * @param cmdId Command ID specified in the original request
243     * {@link #rangeRequest(int, RangingRequest, boolean)}.
244     * @param halResults A list of range results.
245     */
246    @Override
247    public void onResults(int cmdId, ArrayList<RttResult> halResults) {
248        if (mDbg) Log.v(TAG, "onResults: cmdId=" + cmdId + ", # of results=" + halResults.size());
249
250        // sanitize HAL results
251        if (halResults == null) {
252            halResults = new ArrayList<>();
253        }
254        ListIterator<RttResult> lit = halResults.listIterator();
255        while (lit.hasNext()) {
256            if (lit.next() == null) {
257                lit.remove();
258            }
259        }
260        mRttService.onRangingResults(cmdId, halResults);
261    }
262
263    private static ArrayList<RttConfig> convertRangingRequestToRttConfigs(RangingRequest request,
264            boolean isCalledFromPrivilegedContext, RttCapabilities cap) {
265        ArrayList<RttConfig> rttConfigs = new ArrayList<>(request.mRttPeers.size());
266
267        // Skipping any configurations which have an error (printing out a message).
268        // The caller will only get results for valid configurations.
269        for (ResponderConfig responder: request.mRttPeers) {
270            if (!isCalledFromPrivilegedContext) {
271                if (!responder.supports80211mc) {
272                    Log.e(TAG, "Invalid responder: does not support 802.11mc");
273                    continue;
274                }
275            }
276
277            RttConfig config = new RttConfig();
278
279            System.arraycopy(responder.macAddress.toByteArray(), 0, config.addr, 0,
280                    config.addr.length);
281
282            try {
283                config.type = responder.supports80211mc ? RttType.TWO_SIDED : RttType.ONE_SIDED;
284                if (config.type == RttType.ONE_SIDED && cap != null && !cap.rttOneSidedSupported) {
285                    Log.w(TAG, "Device does not support one-sided RTT");
286                    continue;
287                }
288
289                config.peer = halRttPeerTypeFromResponderType(responder.responderType);
290                config.channel.width = halChannelWidthFromResponderChannelWidth(
291                        responder.channelWidth);
292                config.channel.centerFreq = responder.frequency;
293                config.channel.centerFreq0 = responder.centerFreq0;
294                config.channel.centerFreq1 = responder.centerFreq1;
295                config.bw = halRttChannelBandwidthFromResponderChannelWidth(responder.channelWidth);
296                config.preamble = halRttPreambleFromResponderPreamble(responder.preamble);
297
298                if (config.peer == RttPeerType.NAN) {
299                    config.mustRequestLci = false;
300                    config.mustRequestLcr = false;
301                    config.burstPeriod = 0;
302                    config.numBurst = 0;
303                    config.numFramesPerBurst = 5;
304                    config.numRetriesPerRttFrame = 0; // irrelevant for 2-sided RTT
305                    config.numRetriesPerFtmr = 3;
306                    config.burstDuration = 9;
307                } else { // AP + all non-NAN requests
308                    config.mustRequestLci = isCalledFromPrivilegedContext;
309                    config.mustRequestLcr = isCalledFromPrivilegedContext;
310                    config.burstPeriod = 0;
311                    config.numBurst = 0;
312                    config.numFramesPerBurst = 8;
313                    config.numRetriesPerRttFrame = (config.type == RttType.TWO_SIDED ? 0 : 3);
314                    config.numRetriesPerFtmr = 3;
315                    config.burstDuration = 9;
316
317                    if (cap != null) { // constrain parameters per device capabilities
318                        config.mustRequestLci = config.mustRequestLci && cap.lciSupported;
319                        config.mustRequestLcr = config.mustRequestLcr && cap.lcrSupported;
320                        config.bw = halRttChannelBandwidthCapabilityLimiter(config.bw, cap);
321                        config.preamble = halRttPreambleCapabilityLimiter(config.preamble, cap);
322                    }
323                }
324            } catch (IllegalArgumentException e) {
325                Log.e(TAG, "Invalid configuration: " + e.getMessage());
326                continue;
327            }
328
329            rttConfigs.add(config);
330        }
331
332        return rttConfigs;
333    }
334
335    private static int halRttPeerTypeFromResponderType(int responderType) {
336        switch (responderType) {
337            case ResponderConfig.RESPONDER_AP:
338                return RttPeerType.AP;
339            case ResponderConfig.RESPONDER_STA:
340                return RttPeerType.STA;
341            case ResponderConfig.RESPONDER_P2P_GO:
342                return RttPeerType.P2P_GO;
343            case ResponderConfig.RESPONDER_P2P_CLIENT:
344                return RttPeerType.P2P_CLIENT;
345            case ResponderConfig.RESPONDER_AWARE:
346                return RttPeerType.NAN;
347            default:
348                throw new IllegalArgumentException(
349                        "halRttPeerTypeFromResponderType: bad " + responderType);
350        }
351    }
352
353    private static int halChannelWidthFromResponderChannelWidth(int responderChannelWidth) {
354        switch (responderChannelWidth) {
355            case ResponderConfig.CHANNEL_WIDTH_20MHZ:
356                return WifiChannelWidthInMhz.WIDTH_20;
357            case ResponderConfig.CHANNEL_WIDTH_40MHZ:
358                return WifiChannelWidthInMhz.WIDTH_40;
359            case ResponderConfig.CHANNEL_WIDTH_80MHZ:
360                return WifiChannelWidthInMhz.WIDTH_80;
361            case ResponderConfig.CHANNEL_WIDTH_160MHZ:
362                return WifiChannelWidthInMhz.WIDTH_160;
363            case ResponderConfig.CHANNEL_WIDTH_80MHZ_PLUS_MHZ:
364                return WifiChannelWidthInMhz.WIDTH_80P80;
365            default:
366                throw new IllegalArgumentException(
367                        "halChannelWidthFromResponderChannelWidth: bad " + responderChannelWidth);
368        }
369    }
370
371    private static int halRttChannelBandwidthFromResponderChannelWidth(int responderChannelWidth) {
372        switch (responderChannelWidth) {
373            case ResponderConfig.CHANNEL_WIDTH_20MHZ:
374                return RttBw.BW_20MHZ;
375            case ResponderConfig.CHANNEL_WIDTH_40MHZ:
376                return RttBw.BW_40MHZ;
377            case ResponderConfig.CHANNEL_WIDTH_80MHZ:
378                return RttBw.BW_80MHZ;
379            case ResponderConfig.CHANNEL_WIDTH_160MHZ:
380                return RttBw.BW_160MHZ;
381            case ResponderConfig.CHANNEL_WIDTH_80MHZ_PLUS_MHZ:
382                return RttBw.BW_160MHZ;
383            default:
384                throw new IllegalArgumentException(
385                        "halRttChannelBandwidthFromHalBandwidth: bad " + responderChannelWidth);
386        }
387    }
388
389    private static int halRttPreambleFromResponderPreamble(int responderPreamble) {
390        switch (responderPreamble) {
391            case ResponderConfig.PREAMBLE_LEGACY:
392                return RttPreamble.LEGACY;
393            case ResponderConfig.PREAMBLE_HT:
394                return RttPreamble.HT;
395            case ResponderConfig.PREAMBLE_VHT:
396                return RttPreamble.VHT;
397            default:
398                throw new IllegalArgumentException(
399                        "halRttPreambleFromResponderPreamble: bad " + responderPreamble);
400        }
401    }
402
403    /**
404     * Check to see whether the selected RTT channel bandwidth is supported by the device.
405     * If not supported: return the next lower bandwidth which is supported
406     * If none: throw an IllegalArgumentException.
407     *
408     * Note: the halRttChannelBandwidth is a single bit flag of the ones used in cap.bwSupport (HAL
409     * specifications).
410     */
411    private static int halRttChannelBandwidthCapabilityLimiter(int halRttChannelBandwidth,
412            RttCapabilities cap) {
413        while ((halRttChannelBandwidth != 0) && ((halRttChannelBandwidth & cap.bwSupport) == 0)) {
414            halRttChannelBandwidth >>= 1;
415        }
416
417        if (halRttChannelBandwidth != 0) {
418            return halRttChannelBandwidth;
419        }
420
421        throw new IllegalArgumentException(
422                "RTT BW=" + halRttChannelBandwidth + ", not supported by device capabilities=" + cap
423                        + " - and no supported alternative");
424    }
425
426    /**
427     * Check to see whether the selected RTT preamble is supported by the device.
428     * If not supported: return the next "lower" preamble which is supported
429     * If none: throw an IllegalArgumentException.
430     *
431     * Note: the halRttPreamble is a single bit flag of the ones used in cap.preambleSupport (HAL
432     * specifications).
433     */
434    private static int halRttPreambleCapabilityLimiter(int halRttPreamble, RttCapabilities cap) {
435        while ((halRttPreamble != 0) && ((halRttPreamble & cap.preambleSupport) == 0)) {
436            halRttPreamble >>= 1;
437        }
438
439        if (halRttPreamble != 0) {
440            return halRttPreamble;
441        }
442
443        throw new IllegalArgumentException(
444                "RTT Preamble=" + halRttPreamble + ", not supported by device capabilities=" + cap
445                        + " - and no supported alternative");
446    }
447
448
449    /**
450     * Dump the internal state of the class.
451     */
452    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
453        pw.println("RttNative:");
454        pw.println("  mHalDeviceManager: " + mHalDeviceManager);
455        pw.println("  mIWifiRttController: " + mIWifiRttController);
456        pw.println("  mRttCapabilities: " + mRttCapabilities);
457    }
458}
459