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 */
16package com.android.server.wifi;
17
18import android.hardware.wifi.V1_0.IWifiApIface;
19import android.hardware.wifi.V1_0.IWifiChip;
20import android.hardware.wifi.V1_0.IWifiChipEventCallback;
21import android.hardware.wifi.V1_0.IWifiIface;
22import android.hardware.wifi.V1_0.IWifiRttController;
23import android.hardware.wifi.V1_0.IWifiRttControllerEventCallback;
24import android.hardware.wifi.V1_0.IWifiStaIface;
25import android.hardware.wifi.V1_0.IWifiStaIfaceEventCallback;
26import android.hardware.wifi.V1_0.IfaceType;
27import android.hardware.wifi.V1_0.RttBw;
28import android.hardware.wifi.V1_0.RttConfig;
29import android.hardware.wifi.V1_0.RttPeerType;
30import android.hardware.wifi.V1_0.RttPreamble;
31import android.hardware.wifi.V1_0.RttResponder;
32import android.hardware.wifi.V1_0.RttResult;
33import android.hardware.wifi.V1_0.RttType;
34import android.hardware.wifi.V1_0.StaBackgroundScanBucketEventReportSchemeMask;
35import android.hardware.wifi.V1_0.StaBackgroundScanBucketParameters;
36import android.hardware.wifi.V1_0.StaBackgroundScanParameters;
37import android.hardware.wifi.V1_0.StaLinkLayerRadioStats;
38import android.hardware.wifi.V1_0.StaLinkLayerStats;
39import android.hardware.wifi.V1_0.StaRoamingConfig;
40import android.hardware.wifi.V1_0.StaRoamingState;
41import android.hardware.wifi.V1_0.StaScanData;
42import android.hardware.wifi.V1_0.StaScanDataFlagMask;
43import android.hardware.wifi.V1_0.StaScanResult;
44import android.hardware.wifi.V1_0.WifiBand;
45import android.hardware.wifi.V1_0.WifiChannelWidthInMhz;
46import android.hardware.wifi.V1_0.WifiDebugHostWakeReasonStats;
47import android.hardware.wifi.V1_0.WifiDebugPacketFateFrameType;
48import android.hardware.wifi.V1_0.WifiDebugRingBufferFlags;
49import android.hardware.wifi.V1_0.WifiDebugRingBufferStatus;
50import android.hardware.wifi.V1_0.WifiDebugRxPacketFate;
51import android.hardware.wifi.V1_0.WifiDebugRxPacketFateReport;
52import android.hardware.wifi.V1_0.WifiDebugTxPacketFate;
53import android.hardware.wifi.V1_0.WifiDebugTxPacketFateReport;
54import android.hardware.wifi.V1_0.WifiInformationElement;
55import android.hardware.wifi.V1_0.WifiStatus;
56import android.hardware.wifi.V1_0.WifiStatusCode;
57import android.net.MacAddress;
58import android.net.apf.ApfCapabilities;
59import android.net.wifi.RttManager;
60import android.net.wifi.RttManager.ResponderConfig;
61import android.net.wifi.ScanResult;
62import android.net.wifi.WifiInfo;
63import android.net.wifi.WifiManager;
64import android.net.wifi.WifiScanner;
65import android.net.wifi.WifiSsid;
66import android.net.wifi.WifiWakeReasonAndCounts;
67import android.os.Handler;
68import android.os.Looper;
69import android.os.RemoteException;
70import android.text.TextUtils;
71import android.util.Log;
72import android.util.MutableBoolean;
73import android.util.MutableInt;
74
75import com.android.internal.annotations.VisibleForTesting;
76import com.android.internal.util.ArrayUtils;
77import com.android.internal.util.HexDump;
78import com.android.server.wifi.HalDeviceManager.InterfaceDestroyedListener;
79import com.android.server.wifi.util.BitMask;
80import com.android.server.wifi.util.NativeUtil;
81
82import com.google.errorprone.annotations.CompileTimeConstant;
83
84import libcore.util.NonNull;
85
86import java.util.ArrayList;
87import java.util.HashMap;
88import java.util.List;
89import java.util.Set;
90import java.util.stream.Collectors;
91
92/**
93 * Vendor HAL via HIDL
94 */
95public class WifiVendorHal {
96
97    private static final WifiLog sNoLog = new FakeWifiLog();
98
99    /**
100     * Chatty logging should use mVerboseLog
101     */
102    @VisibleForTesting
103    WifiLog mVerboseLog = sNoLog;
104
105    /**
106     * Errors should use mLog
107     */
108    @VisibleForTesting
109    WifiLog mLog = new LogcatLog("WifiVendorHal");
110
111    /**
112     * Enables or disables verbose logging
113     *
114     * @param verbose - with the obvious interpretation
115     */
116    public void enableVerboseLogging(boolean verbose) {
117        synchronized (sLock) {
118            if (verbose) {
119                mVerboseLog = mLog;
120                enter("verbose=true").flush();
121            } else {
122                enter("verbose=false").flush();
123                mVerboseLog = sNoLog;
124            }
125        }
126    }
127
128    /**
129     * Checks for a successful status result.
130     *
131     * Failures are logged to mLog.
132     *
133     * @param status is the WifiStatus generated by a hal call
134     * @return true for success, false for failure
135     */
136    private boolean ok(WifiStatus status) {
137        if (status.code == WifiStatusCode.SUCCESS) return true;
138
139        Thread cur = Thread.currentThread();
140        StackTraceElement[] trace = cur.getStackTrace();
141
142        mLog.err("% failed %")
143                .c(niceMethodName(trace, 3))
144                .c(status.toString())
145                .flush();
146
147        return false;
148    }
149
150    /**
151     * Logs the argument along with the method name.
152     *
153     * Always returns its argument.
154     */
155    private boolean boolResult(boolean result) {
156        if (mVerboseLog == sNoLog) return result;
157        // Currently only seen if verbose logging is on
158
159        Thread cur = Thread.currentThread();
160        StackTraceElement[] trace = cur.getStackTrace();
161
162        mVerboseLog.err("% returns %")
163                .c(niceMethodName(trace, 3))
164                .c(result)
165                .flush();
166
167        return result;
168    }
169
170    /**
171     * Logs the argument along with the method name.
172     *
173     * Always returns its argument.
174     */
175    private String stringResult(String result) {
176        if (mVerboseLog == sNoLog) return result;
177        // Currently only seen if verbose logging is on
178
179        Thread cur = Thread.currentThread();
180        StackTraceElement[] trace = cur.getStackTrace();
181
182        mVerboseLog.err("% returns %")
183                .c(niceMethodName(trace, 3))
184                .c(result)
185                .flush();
186
187        return result;
188    }
189
190    /**
191     * Logs the argument along with the method name.
192     *
193     * Always returns its argument.
194     */
195    private byte[] byteArrayResult(byte[] result) {
196        if (mVerboseLog == sNoLog) return result;
197        // Currently only seen if verbose logging is on
198
199        Thread cur = Thread.currentThread();
200        StackTraceElement[] trace = cur.getStackTrace();
201
202        mVerboseLog.err("% returns %")
203                .c(niceMethodName(trace, 3))
204                .c(HexDump.dumpHexString(result))
205                .flush();
206
207        return result;
208    }
209
210    /**
211     * Logs at method entry
212     *
213     * @param format string with % placeholders
214     * @return LogMessage formatter (remember to .flush())
215     */
216    private WifiLog.LogMessage enter(@CompileTimeConstant final String format) {
217        if (mVerboseLog == sNoLog) return sNoLog.info(format);
218        return mVerboseLog.trace(format, 1);
219    }
220
221    /**
222     * Gets the method name and line number from a stack trace.
223     *
224     * Attempts to skip frames created by lambdas to get a human-sensible name.
225     *
226     * @param trace, fo example obtained by Thread.currentThread().getStackTrace()
227     * @param start  frame number to log, typically 3
228     * @return string containing the method name and line number
229     */
230    private static String niceMethodName(StackTraceElement[] trace, int start) {
231        if (start >= trace.length) return "";
232        StackTraceElement s = trace[start];
233        String name = s.getMethodName();
234        if (name.contains("lambda$")) {
235            // Try to find a friendlier method name
236            String myFile = s.getFileName();
237            if (myFile != null) {
238                for (int i = start + 1; i < trace.length; i++) {
239                    if (myFile.equals(trace[i].getFileName())) {
240                        name = trace[i].getMethodName();
241                        break;
242                    }
243                }
244            }
245        }
246        return (name + "(l." + s.getLineNumber() + ")");
247    }
248
249    // Vendor HAL HIDL interface objects.
250    private IWifiChip mIWifiChip;
251    private IWifiRttController mIWifiRttController;
252    private HashMap<String, IWifiStaIface> mIWifiStaIfaces = new HashMap<>();
253    private HashMap<String, IWifiApIface> mIWifiApIfaces = new HashMap<>();
254    private final HalDeviceManager mHalDeviceManager;
255    private final HalDeviceManagerStatusListener mHalDeviceManagerStatusCallbacks;
256    private final IWifiStaIfaceEventCallback mIWifiStaIfaceEventCallback;
257    private final ChipEventCallback mIWifiChipEventCallback;
258    private final ChipEventCallbackV12 mIWifiChipEventCallbackV12;
259    private final RttEventCallback mRttEventCallback;
260
261    // Plumbing for event handling.
262    //
263    // Being final fields, they can be accessed without synchronization under
264    // some reasonable assumptions. See
265    // https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5
266    private final Looper mLooper;
267    private final Handler mHalEventHandler;
268
269    public WifiVendorHal(HalDeviceManager halDeviceManager,
270                         Looper looper) {
271        mHalDeviceManager = halDeviceManager;
272        mLooper = looper;
273        mHalEventHandler = new Handler(looper);
274        mHalDeviceManagerStatusCallbacks = new HalDeviceManagerStatusListener();
275        mIWifiStaIfaceEventCallback = new StaIfaceEventCallback();
276        mIWifiChipEventCallback = new ChipEventCallback();
277        mIWifiChipEventCallbackV12 = new ChipEventCallbackV12();
278        mRttEventCallback = new RttEventCallback();
279    }
280
281    public static final Object sLock = new Object();
282
283    private void handleRemoteException(RemoteException e) {
284        String methodName = niceMethodName(Thread.currentThread().getStackTrace(), 3);
285        mVerboseLog.err("% RemoteException in HIDL call %").c(methodName).c(e.toString()).flush();
286        clearState();
287    }
288
289    private WifiNative.VendorHalDeathEventHandler mDeathEventHandler;
290
291    /**
292     * Initialize the Hal device manager and register for status callbacks.
293     *
294     * @param handler Handler to notify if the vendor HAL dies.
295     * @return true on success, false otherwise.
296     */
297    public boolean initialize(WifiNative.VendorHalDeathEventHandler handler) {
298        synchronized (sLock) {
299            mHalDeviceManager.initialize();
300            mHalDeviceManager.registerStatusListener(mHalDeviceManagerStatusCallbacks, null);
301            mDeathEventHandler = handler;
302            return true;
303        }
304    }
305
306    private WifiNative.VendorHalRadioModeChangeEventHandler mRadioModeChangeEventHandler;
307
308    /**
309     * Register to listen for radio mode change events from the HAL.
310     *
311     * @param handler Handler to notify when the vendor HAL detects a radio mode change.
312     */
313    public void registerRadioModeChangeHandler(
314            WifiNative.VendorHalRadioModeChangeEventHandler handler) {
315        synchronized (sLock) {
316            mRadioModeChangeEventHandler = handler;
317        }
318    }
319
320    /**
321     * Returns whether the vendor HAL is supported on this device or not.
322     */
323    public boolean isVendorHalSupported() {
324        synchronized (sLock) {
325            return mHalDeviceManager.isSupported();
326        }
327    }
328
329    /**
330     * Bring up the HIDL Vendor HAL and configure for AP (Access Point) mode
331     *
332     * @return true for success
333     */
334    public boolean startVendorHalAp() {
335        synchronized (sLock) {
336            if (!startVendorHal()) {
337                return false;
338            }
339            if (TextUtils.isEmpty(createApIface(null))) {
340                stopVendorHal();
341                return false;
342            }
343            return true;
344        }
345    }
346
347    /**
348     * Bring up the HIDL Vendor HAL and configure for STA (Station) mode
349     *
350     * @return true for success
351     */
352    public boolean startVendorHalSta() {
353        synchronized (sLock) {
354            if (!startVendorHal()) {
355                return false;
356            }
357            if (TextUtils.isEmpty(createStaIface(false, null))) {
358                stopVendorHal();
359                return false;
360            }
361            return true;
362        }
363    }
364
365    /**
366     * Bring up the HIDL Vendor HAL.
367     * @return true on success, false otherwise.
368     */
369    public boolean startVendorHal() {
370        synchronized (sLock) {
371            if (!mHalDeviceManager.start()) {
372                mLog.err("Failed to start vendor HAL").flush();
373                return false;
374            }
375            mLog.info("Vendor Hal started successfully").flush();
376            return true;
377        }
378    }
379
380    /** Helper method to lookup the corresponding STA iface object using iface name. */
381    private IWifiStaIface getStaIface(@NonNull String ifaceName) {
382        synchronized (sLock) {
383            return mIWifiStaIfaces.get(ifaceName);
384        }
385    }
386
387    private class StaInterfaceDestroyedListenerInternal implements InterfaceDestroyedListener {
388        private final InterfaceDestroyedListener mExternalListener;
389
390        StaInterfaceDestroyedListenerInternal(InterfaceDestroyedListener externalListener) {
391            mExternalListener = externalListener;
392        }
393
394        @Override
395        public void onDestroyed(@NonNull String ifaceName) {
396            synchronized (sLock) {
397                mIWifiStaIfaces.remove(ifaceName);
398            }
399            if (mExternalListener != null) {
400                mExternalListener.onDestroyed(ifaceName);
401            }
402        }
403    }
404
405    /**
406     * Create a STA iface using {@link HalDeviceManager}.
407     *
408     * @param lowPrioritySta The requested STA has a low request priority (lower probability of
409     *                       getting created, higher probability of getting destroyed).
410     * @param destroyedListener Listener to be invoked when the interface is destroyed.
411     * @return iface name on success, null otherwise.
412     */
413    public String createStaIface(boolean lowPrioritySta,
414            InterfaceDestroyedListener destroyedListener) {
415        synchronized (sLock) {
416            IWifiStaIface iface = mHalDeviceManager.createStaIface(lowPrioritySta,
417                    new StaInterfaceDestroyedListenerInternal(destroyedListener), null);
418            if (iface == null) {
419                mLog.err("Failed to create STA iface").flush();
420                return stringResult(null);
421            }
422            String ifaceName = mHalDeviceManager.getName((IWifiIface) iface);
423            if (TextUtils.isEmpty(ifaceName)) {
424                mLog.err("Failed to get iface name").flush();
425                return stringResult(null);
426            }
427            if (!registerStaIfaceCallback(iface)) {
428                mLog.err("Failed to register STA iface callback").flush();
429                return stringResult(null);
430            }
431            mIWifiRttController = mHalDeviceManager.createRttController();
432            if (mIWifiRttController == null) {
433                mLog.err("Failed to create RTT controller").flush();
434                return stringResult(null);
435            }
436            if (!registerRttEventCallback()) {
437                mLog.err("Failed to register RTT controller callback").flush();
438                return stringResult(null);
439            }
440            if (!retrieveWifiChip((IWifiIface) iface)) {
441                mLog.err("Failed to get wifi chip").flush();
442                return stringResult(null);
443            }
444            enableLinkLayerStats(iface);
445            mIWifiStaIfaces.put(ifaceName, iface);
446            return ifaceName;
447        }
448    }
449
450    /**
451     * Remove a STA iface using {@link HalDeviceManager}.
452     *
453     * @param ifaceName Name of the interface being removed.
454     * @return true on success, false otherwise.
455     */
456    public boolean removeStaIface(@NonNull String ifaceName) {
457        synchronized (sLock) {
458            IWifiStaIface iface = getStaIface(ifaceName);
459            if (iface == null) return boolResult(false);
460
461            if (!mHalDeviceManager.removeIface((IWifiIface) iface)) {
462                mLog.err("Failed to remove STA iface").flush();
463                return boolResult(false);
464            }
465            mIWifiStaIfaces.remove(ifaceName);
466            return true;
467        }
468    }
469
470    /** Helper method to lookup the corresponding AP iface object using iface name. */
471    private IWifiApIface getApIface(@NonNull String ifaceName) {
472        synchronized (sLock) {
473            return mIWifiApIfaces.get(ifaceName);
474        }
475    }
476
477    private class ApInterfaceDestroyedListenerInternal implements InterfaceDestroyedListener {
478        private final InterfaceDestroyedListener mExternalListener;
479
480        ApInterfaceDestroyedListenerInternal(InterfaceDestroyedListener externalListener) {
481            mExternalListener = externalListener;
482        }
483
484        @Override
485        public void onDestroyed(@NonNull String ifaceName) {
486            synchronized (sLock) {
487                mIWifiApIfaces.remove(ifaceName);
488            }
489            if (mExternalListener != null) {
490                mExternalListener.onDestroyed(ifaceName);
491            }
492        }
493    }
494
495
496    /**
497     * Create a AP iface using {@link HalDeviceManager}.
498     *
499     * @param destroyedListener Listener to be invoked when the interface is destroyed.
500     * @return iface name on success, null otherwise.
501     */
502    public String createApIface(InterfaceDestroyedListener destroyedListener) {
503        synchronized (sLock) {
504            IWifiApIface iface = mHalDeviceManager.createApIface(
505                    new ApInterfaceDestroyedListenerInternal(destroyedListener), null);
506            if (iface == null) {
507                mLog.err("Failed to create AP iface").flush();
508                return stringResult(null);
509            }
510            String ifaceName = mHalDeviceManager.getName((IWifiIface) iface);
511            if (TextUtils.isEmpty(ifaceName)) {
512                mLog.err("Failed to get iface name").flush();
513                return stringResult(null);
514            }
515            if (!retrieveWifiChip((IWifiIface) iface)) {
516                mLog.err("Failed to get wifi chip").flush();
517                return stringResult(null);
518            }
519            mIWifiApIfaces.put(ifaceName, iface);
520            return ifaceName;
521        }
522    }
523
524    /**
525     * Remove an AP iface using {@link HalDeviceManager}.
526     *
527     * @param ifaceName Name of the interface being removed.
528     * @return true on success, false otherwise.
529     */
530    public boolean removeApIface(@NonNull String ifaceName) {
531        synchronized (sLock) {
532            IWifiApIface iface = getApIface(ifaceName);
533            if (iface == null) return boolResult(false);
534
535            if (!mHalDeviceManager.removeIface((IWifiIface) iface)) {
536                mLog.err("Failed to remove AP iface").flush();
537                return boolResult(false);
538            }
539            mIWifiApIfaces.remove(ifaceName);
540            return true;
541        }
542    }
543
544    private boolean retrieveWifiChip(IWifiIface iface) {
545        synchronized (sLock) {
546            boolean registrationNeeded = mIWifiChip == null;
547            mIWifiChip = mHalDeviceManager.getChip(iface);
548            if (mIWifiChip == null) {
549                mLog.err("Failed to get the chip created for the Iface").flush();
550                return false;
551            }
552            if (!registrationNeeded) {
553                return true;
554            }
555            if (!registerChipCallback()) {
556                mLog.err("Failed to register chip callback").flush();
557                return false;
558            }
559            return true;
560        }
561    }
562
563    /**
564     * Registers the sta iface callback.
565     */
566    private boolean registerStaIfaceCallback(IWifiStaIface iface) {
567        synchronized (sLock) {
568            if (iface == null) return boolResult(false);
569            if (mIWifiStaIfaceEventCallback == null) return boolResult(false);
570            try {
571                WifiStatus status =
572                        iface.registerEventCallback(mIWifiStaIfaceEventCallback);
573                return ok(status);
574            } catch (RemoteException e) {
575                handleRemoteException(e);
576                return false;
577            }
578        }
579    }
580
581    /**
582     * Registers the sta iface callback.
583     */
584    private boolean registerChipCallback() {
585        synchronized (sLock) {
586            if (mIWifiChip == null) return boolResult(false);
587            try {
588                WifiStatus status;
589                android.hardware.wifi.V1_2.IWifiChip iWifiChipV12 = getWifiChipForV1_2Mockable();
590                if (iWifiChipV12 != null) {
591                    status = iWifiChipV12.registerEventCallback_1_2(mIWifiChipEventCallbackV12);
592                } else {
593                    status = mIWifiChip.registerEventCallback(mIWifiChipEventCallback);
594                }
595                return ok(status);
596            } catch (RemoteException e) {
597                handleRemoteException(e);
598                return false;
599            }
600        }
601    }
602
603    /**
604     * Registers RTT event callback. Returns whether the registration is successful.
605     */
606    private boolean registerRttEventCallback() {
607        synchronized (sLock) {
608            if (mIWifiRttController == null) return boolResult(false);
609            if (mRttEventCallback == null) return boolResult(false);
610            try {
611                WifiStatus status = mIWifiRttController.registerEventCallback(mRttEventCallback);
612                return ok(status);
613            } catch (RemoteException e) {
614                handleRemoteException(e);
615                return false;
616            }
617        }
618    }
619
620    /**
621     * Stops the HAL
622     */
623    public void stopVendorHal() {
624        synchronized (sLock) {
625            mHalDeviceManager.stop();
626            clearState();
627            mLog.info("Vendor Hal stopped").flush();
628        }
629    }
630
631    /**
632     * Clears the state associated with a started Iface
633     *
634     * Caller should hold the lock.
635     */
636    private void clearState() {
637        mIWifiChip = null;
638        mIWifiStaIfaces.clear();
639        mIWifiApIfaces.clear();
640        mIWifiRttController = null;
641        mDriverDescription = null;
642        mFirmwareDescription = null;
643    }
644
645    /**
646     * Tests whether the HAL is started and atleast one iface is up.
647     */
648    public boolean isHalStarted() {
649        // For external use only. Methods in this class should test for null directly.
650        synchronized (sLock) {
651            return (!mIWifiStaIfaces.isEmpty() || !mIWifiApIfaces.isEmpty());
652        }
653    }
654
655    /**
656     * Gets the scan capabilities
657     *
658     * @param ifaceName Name of the interface.
659     * @param capabilities object to be filled in
660     * @return true for success, false for failure
661     */
662    public boolean getBgScanCapabilities(
663            @NonNull String ifaceName, WifiNative.ScanCapabilities capabilities) {
664        synchronized (sLock) {
665            IWifiStaIface iface = getStaIface(ifaceName);
666            if (iface == null) return boolResult(false);
667            try {
668                MutableBoolean ans = new MutableBoolean(false);
669                WifiNative.ScanCapabilities out = capabilities;
670                iface.getBackgroundScanCapabilities((status, cap) -> {
671                            if (!ok(status)) return;
672                            mVerboseLog.info("scan capabilities %").c(cap.toString()).flush();
673                            out.max_scan_cache_size = cap.maxCacheSize;
674                            out.max_ap_cache_per_scan = cap.maxApCachePerScan;
675                            out.max_scan_buckets = cap.maxBuckets;
676                            out.max_rssi_sample_size = 0;
677                            out.max_scan_reporting_threshold = cap.maxReportingThreshold;
678                            ans.value = true;
679                        }
680                );
681                return ans.value;
682            } catch (RemoteException e) {
683                handleRemoteException(e);
684                return false;
685            }
686        }
687    }
688
689    /**
690     * Holds the current background scan state, to implement pause and restart
691     */
692    @VisibleForTesting
693    class CurrentBackgroundScan {
694        public int cmdId;
695        public StaBackgroundScanParameters param;
696        public WifiNative.ScanEventHandler eventHandler = null;
697        public boolean paused = false;
698        public WifiScanner.ScanData[] latestScanResults = null;
699
700        CurrentBackgroundScan(int id, WifiNative.ScanSettings settings) {
701            cmdId = id;
702            param = new StaBackgroundScanParameters();
703            param.basePeriodInMs = settings.base_period_ms;
704            param.maxApPerScan = settings.max_ap_per_scan;
705            param.reportThresholdPercent = settings.report_threshold_percent;
706            param.reportThresholdNumScans = settings.report_threshold_num_scans;
707            if (settings.buckets != null) {
708                for (WifiNative.BucketSettings bs : settings.buckets) {
709                    param.buckets.add(makeStaBackgroundScanBucketParametersFromBucketSettings(bs));
710                }
711            }
712        }
713    }
714
715    /**
716     * Makes the Hal flavor of WifiNative.BucketSettings
717     *
718     * @param bs WifiNative.BucketSettings
719     * @return Hal flavor of bs
720     * @throws IllegalArgumentException if band value is not recognized
721     */
722    private StaBackgroundScanBucketParameters
723            makeStaBackgroundScanBucketParametersFromBucketSettings(WifiNative.BucketSettings bs) {
724        StaBackgroundScanBucketParameters pa = new StaBackgroundScanBucketParameters();
725        pa.bucketIdx = bs.bucket;
726        pa.band = makeWifiBandFromFrameworkBand(bs.band);
727        if (bs.channels != null) {
728            for (WifiNative.ChannelSettings cs : bs.channels) {
729                pa.frequencies.add(cs.frequency);
730            }
731        }
732        pa.periodInMs = bs.period_ms;
733        pa.eventReportScheme = makeReportSchemeFromBucketSettingsReportEvents(bs.report_events);
734        pa.exponentialMaxPeriodInMs = bs.max_period_ms;
735        // Although HAL API allows configurable base value for the truncated
736        // exponential back off scan. Native API and above support only
737        // truncated binary exponential back off scan.
738        // Hard code value of base to 2 here.
739        pa.exponentialBase = 2;
740        pa.exponentialStepCount = bs.step_count;
741        return pa;
742    }
743
744    /**
745     * Makes the Hal flavor of WifiScanner's band indication
746     *
747     * @param frameworkBand one of WifiScanner.WIFI_BAND_*
748     * @return A WifiBand value
749     * @throws IllegalArgumentException if frameworkBand is not recognized
750     */
751    private int makeWifiBandFromFrameworkBand(int frameworkBand) {
752        switch (frameworkBand) {
753            case WifiScanner.WIFI_BAND_UNSPECIFIED:
754                return WifiBand.BAND_UNSPECIFIED;
755            case WifiScanner.WIFI_BAND_24_GHZ:
756                return WifiBand.BAND_24GHZ;
757            case WifiScanner.WIFI_BAND_5_GHZ:
758                return WifiBand.BAND_5GHZ;
759            case WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY:
760                return WifiBand.BAND_5GHZ_DFS;
761            case WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS:
762                return WifiBand.BAND_5GHZ_WITH_DFS;
763            case WifiScanner.WIFI_BAND_BOTH:
764                return WifiBand.BAND_24GHZ_5GHZ;
765            case WifiScanner.WIFI_BAND_BOTH_WITH_DFS:
766                return WifiBand.BAND_24GHZ_5GHZ_WITH_DFS;
767            default:
768                throw new IllegalArgumentException("bad band " + frameworkBand);
769        }
770    }
771
772    /**
773     * Makes the Hal flavor of WifiScanner's report event mask
774     *
775     * @param reportUnderscoreEvents is logical OR of WifiScanner.REPORT_EVENT_* values
776     * @return Corresponding StaBackgroundScanBucketEventReportSchemeMask value
777     * @throws IllegalArgumentException if a mask bit is not recognized
778     */
779    private int makeReportSchemeFromBucketSettingsReportEvents(int reportUnderscoreEvents) {
780        int ans = 0;
781        BitMask in = new BitMask(reportUnderscoreEvents);
782        if (in.testAndClear(WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)) {
783            ans |= StaBackgroundScanBucketEventReportSchemeMask.EACH_SCAN;
784        }
785        if (in.testAndClear(WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)) {
786            ans |= StaBackgroundScanBucketEventReportSchemeMask.FULL_RESULTS;
787        }
788        if (in.testAndClear(WifiScanner.REPORT_EVENT_NO_BATCH)) {
789            ans |= StaBackgroundScanBucketEventReportSchemeMask.NO_BATCH;
790        }
791        if (in.value != 0) throw new IllegalArgumentException("bad " + reportUnderscoreEvents);
792        return ans;
793    }
794
795    private int mLastScanCmdId; // For assigning cmdIds to scans
796
797    @VisibleForTesting
798    CurrentBackgroundScan mScan = null;
799
800    /**
801     * Starts a background scan
802     *
803     * Any ongoing scan will be stopped first
804     *
805     * @param ifaceName    Name of the interface.
806     * @param settings     to control the scan
807     * @param eventHandler to call with the results
808     * @return true for success
809     */
810    public boolean startBgScan(@NonNull String ifaceName,
811                               WifiNative.ScanSettings settings,
812                               WifiNative.ScanEventHandler eventHandler) {
813        WifiStatus status;
814        if (eventHandler == null) return boolResult(false);
815        synchronized (sLock) {
816            IWifiStaIface iface = getStaIface(ifaceName);
817            if (iface == null) return boolResult(false);
818            try {
819                if (mScan != null && !mScan.paused) {
820                    ok(iface.stopBackgroundScan(mScan.cmdId));
821                    mScan = null;
822                }
823                mLastScanCmdId = (mLastScanCmdId % 9) + 1; // cycle through non-zero single digits
824                CurrentBackgroundScan scan = new CurrentBackgroundScan(mLastScanCmdId, settings);
825                status = iface.startBackgroundScan(scan.cmdId, scan.param);
826                if (!ok(status)) return false;
827                scan.eventHandler = eventHandler;
828                mScan = scan;
829                return true;
830            } catch (RemoteException e) {
831                handleRemoteException(e);
832                return false;
833            }
834        }
835    }
836
837
838    /**
839     * Stops any ongoing backgound scan
840     *
841     * @param ifaceName Name of the interface.
842     */
843    public void stopBgScan(@NonNull String ifaceName) {
844        WifiStatus status;
845        synchronized (sLock) {
846            IWifiStaIface iface = getStaIface(ifaceName);
847            if (iface == null) return;
848            try {
849                if (mScan != null) {
850                    ok(iface.stopBackgroundScan(mScan.cmdId));
851                    mScan = null;
852                }
853            } catch (RemoteException e) {
854                handleRemoteException(e);
855            }
856        }
857    }
858
859    /**
860     * Pauses an ongoing backgound scan
861     *
862     * @param ifaceName Name of the interface.
863     */
864    public void pauseBgScan(@NonNull String ifaceName) {
865        WifiStatus status;
866        synchronized (sLock) {
867            try {
868                IWifiStaIface iface = getStaIface(ifaceName);
869                if (iface == null) return;
870                if (mScan != null && !mScan.paused) {
871                    status = iface.stopBackgroundScan(mScan.cmdId);
872                    if (!ok(status)) return;
873                    mScan.paused = true;
874                }
875            } catch (RemoteException e) {
876                handleRemoteException(e);
877            }
878        }
879    }
880
881    /**
882     * Restarts a paused background scan
883     *
884     * @param ifaceName Name of the interface.
885     */
886    public void restartBgScan(@NonNull String ifaceName) {
887        WifiStatus status;
888        synchronized (sLock) {
889            IWifiStaIface iface = getStaIface(ifaceName);
890            if (iface == null) return;
891            try {
892                if (mScan != null && mScan.paused) {
893                    status = iface.startBackgroundScan(mScan.cmdId, mScan.param);
894                    if (!ok(status)) return;
895                    mScan.paused = false;
896                }
897            } catch (RemoteException e) {
898                handleRemoteException(e);
899            }
900        }
901    }
902
903    /**
904     * Gets the latest scan results received from the HIDL interface callback.
905     * TODO(b/35754840): This hop to fetch scan results after callback is unnecessary. Refactor
906     * WifiScanner to use the scan results from the callback.
907     *
908     * @param ifaceName Name of the interface.
909     */
910    public WifiScanner.ScanData[] getBgScanResults(@NonNull String ifaceName) {
911        synchronized (sLock) {
912            IWifiStaIface iface = getStaIface(ifaceName);
913            if (iface == null) return null;
914            if (mScan == null) return null;
915            return mScan.latestScanResults;
916        }
917    }
918
919    /**
920     * Get the link layer statistics
921     *
922     * Note - we always enable link layer stats on a STA interface.
923     *
924     * @param ifaceName Name of the interface.
925     * @return the statistics, or null if unable to do so
926     */
927    public WifiLinkLayerStats getWifiLinkLayerStats(@NonNull String ifaceName) {
928        class AnswerBox {
929            public StaLinkLayerStats value = null;
930        }
931        AnswerBox answer = new AnswerBox();
932        synchronized (sLock) {
933            try {
934                IWifiStaIface iface = getStaIface(ifaceName);
935                if (iface == null) return null;
936                iface.getLinkLayerStats((status, stats) -> {
937                    if (!ok(status)) return;
938                    answer.value = stats;
939                });
940            } catch (RemoteException e) {
941                handleRemoteException(e);
942                return null;
943            }
944        }
945        WifiLinkLayerStats stats = frameworkFromHalLinkLayerStats(answer.value);
946        return stats;
947    }
948
949    /**
950     * Makes the framework version of link layer stats from the hal version.
951     */
952    @VisibleForTesting
953    static WifiLinkLayerStats frameworkFromHalLinkLayerStats(StaLinkLayerStats stats) {
954        if (stats == null) return null;
955        WifiLinkLayerStats out = new WifiLinkLayerStats();
956        out.beacon_rx = stats.iface.beaconRx;
957        out.rssi_mgmt = stats.iface.avgRssiMgmt;
958        // Statistics are broken out by Wireless Multimedia Extensions categories
959        // WME Best Effort Access Category
960        out.rxmpdu_be = stats.iface.wmeBePktStats.rxMpdu;
961        out.txmpdu_be = stats.iface.wmeBePktStats.txMpdu;
962        out.lostmpdu_be = stats.iface.wmeBePktStats.lostMpdu;
963        out.retries_be = stats.iface.wmeBePktStats.retries;
964        // WME Background Access Category
965        out.rxmpdu_bk = stats.iface.wmeBkPktStats.rxMpdu;
966        out.txmpdu_bk = stats.iface.wmeBkPktStats.txMpdu;
967        out.lostmpdu_bk = stats.iface.wmeBkPktStats.lostMpdu;
968        out.retries_bk = stats.iface.wmeBkPktStats.retries;
969        // WME Video Access Category
970        out.rxmpdu_vi = stats.iface.wmeViPktStats.rxMpdu;
971        out.txmpdu_vi = stats.iface.wmeViPktStats.txMpdu;
972        out.lostmpdu_vi = stats.iface.wmeViPktStats.lostMpdu;
973        out.retries_vi = stats.iface.wmeViPktStats.retries;
974        // WME Voice Access Category
975        out.rxmpdu_vo = stats.iface.wmeVoPktStats.rxMpdu;
976        out.txmpdu_vo = stats.iface.wmeVoPktStats.txMpdu;
977        out.lostmpdu_vo = stats.iface.wmeVoPktStats.lostMpdu;
978        out.retries_vo = stats.iface.wmeVoPktStats.retries;
979        // TODO(b/36176141): Figure out how to coalesce this info for multi radio devices.
980        if (stats.radios.size() > 0) {
981            StaLinkLayerRadioStats radioStats = stats.radios.get(0);
982            out.on_time = radioStats.onTimeInMs;
983            out.tx_time = radioStats.txTimeInMs;
984            out.tx_time_per_level = new int[radioStats.txTimeInMsPerLevel.size()];
985            for (int i = 0; i < out.tx_time_per_level.length; i++) {
986                out.tx_time_per_level[i] = radioStats.txTimeInMsPerLevel.get(i);
987            }
988            out.rx_time = radioStats.rxTimeInMs;
989            out.on_time_scan = radioStats.onTimeInMsForScan;
990        }
991        out.timeStampInMs = stats.timeStampInMs;
992        return out;
993    }
994
995    @VisibleForTesting
996    boolean mLinkLayerStatsDebug = false;  // Passed to Hal
997
998    /**
999     * Enables the linkLayerStats in the Hal.
1000     *
1001     * This is called unconditionally whenever we create a STA interface.
1002     *
1003     * @param iface Iface object.
1004     */
1005    private void enableLinkLayerStats(IWifiStaIface iface) {
1006        synchronized (sLock) {
1007            try {
1008                WifiStatus status;
1009                status = iface.enableLinkLayerStatsCollection(mLinkLayerStatsDebug);
1010                if (!ok(status)) {
1011                    mLog.err("unable to enable link layer stats collection").flush();
1012                }
1013            } catch (RemoteException e) {
1014                handleRemoteException(e);
1015            }
1016        }
1017    }
1018
1019    /**
1020     * Translation table used by getSupportedFeatureSet for translating IWifiChip caps
1021     */
1022    private static final int[][] sChipFeatureCapabilityTranslation = {
1023            {WifiManager.WIFI_FEATURE_TX_POWER_LIMIT,
1024                    android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.SET_TX_POWER_LIMIT
1025            },
1026            {WifiManager.WIFI_FEATURE_D2D_RTT,
1027                    android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.D2D_RTT
1028            },
1029            {WifiManager.WIFI_FEATURE_D2AP_RTT,
1030                    android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.D2AP_RTT
1031            }
1032    };
1033
1034    /**
1035     * Feature bit mask translation for Chip
1036     *
1037     * @param capabilities bitmask defined IWifiChip.ChipCapabilityMask
1038     * @return bitmask defined by WifiManager.WIFI_FEATURE_*
1039     */
1040    @VisibleForTesting
1041    int wifiFeatureMaskFromChipCapabilities(int capabilities) {
1042        int features = 0;
1043        for (int i = 0; i < sChipFeatureCapabilityTranslation.length; i++) {
1044            if ((capabilities & sChipFeatureCapabilityTranslation[i][1]) != 0) {
1045                features |= sChipFeatureCapabilityTranslation[i][0];
1046            }
1047        }
1048        return features;
1049    }
1050
1051    /**
1052     * Translation table used by getSupportedFeatureSet for translating IWifiStaIface caps
1053     */
1054    private static final int[][] sStaFeatureCapabilityTranslation = {
1055            {WifiManager.WIFI_FEATURE_INFRA_5G,
1056                    IWifiStaIface.StaIfaceCapabilityMask.STA_5G
1057            },
1058            {WifiManager.WIFI_FEATURE_PASSPOINT,
1059                    IWifiStaIface.StaIfaceCapabilityMask.HOTSPOT
1060            },
1061            {WifiManager.WIFI_FEATURE_SCANNER,
1062                    IWifiStaIface.StaIfaceCapabilityMask.BACKGROUND_SCAN,
1063            },
1064            {WifiManager.WIFI_FEATURE_PNO,
1065                    IWifiStaIface.StaIfaceCapabilityMask.PNO
1066            },
1067            {WifiManager.WIFI_FEATURE_TDLS,
1068                    IWifiStaIface.StaIfaceCapabilityMask.TDLS
1069            },
1070            {WifiManager.WIFI_FEATURE_TDLS_OFFCHANNEL,
1071                    IWifiStaIface.StaIfaceCapabilityMask.TDLS_OFFCHANNEL
1072            },
1073            {WifiManager.WIFI_FEATURE_LINK_LAYER_STATS,
1074                    IWifiStaIface.StaIfaceCapabilityMask.LINK_LAYER_STATS
1075            },
1076            {WifiManager.WIFI_FEATURE_RSSI_MONITOR,
1077                    IWifiStaIface.StaIfaceCapabilityMask.RSSI_MONITOR
1078            },
1079            {WifiManager.WIFI_FEATURE_MKEEP_ALIVE,
1080                    IWifiStaIface.StaIfaceCapabilityMask.KEEP_ALIVE
1081            },
1082            {WifiManager.WIFI_FEATURE_CONFIG_NDO,
1083                    IWifiStaIface.StaIfaceCapabilityMask.ND_OFFLOAD
1084            },
1085            {WifiManager.WIFI_FEATURE_CONTROL_ROAMING,
1086                    IWifiStaIface.StaIfaceCapabilityMask.CONTROL_ROAMING
1087            },
1088            {WifiManager.WIFI_FEATURE_IE_WHITELIST,
1089                    IWifiStaIface.StaIfaceCapabilityMask.PROBE_IE_WHITELIST
1090            },
1091            {WifiManager.WIFI_FEATURE_SCAN_RAND,
1092                    IWifiStaIface.StaIfaceCapabilityMask.SCAN_RAND
1093            },
1094    };
1095
1096    /**
1097     * Feature bit mask translation for STAs
1098     *
1099     * @param capabilities bitmask defined IWifiStaIface.StaIfaceCapabilityMask
1100     * @return bitmask defined by WifiManager.WIFI_FEATURE_*
1101     */
1102    @VisibleForTesting
1103    int wifiFeatureMaskFromStaCapabilities(int capabilities) {
1104        int features = 0;
1105        for (int i = 0; i < sStaFeatureCapabilityTranslation.length; i++) {
1106            if ((capabilities & sStaFeatureCapabilityTranslation[i][1]) != 0) {
1107                features |= sStaFeatureCapabilityTranslation[i][0];
1108            }
1109        }
1110        return features;
1111    }
1112
1113    /**
1114     * Get the supported features
1115     *
1116     * The result may differ depending on the mode (STA or AP)
1117     *
1118     * @param ifaceName Name of the interface.
1119     * @return bitmask defined by WifiManager.WIFI_FEATURE_*
1120     */
1121    public int getSupportedFeatureSet(@NonNull String ifaceName) {
1122        int featureSet = 0;
1123        if (!mHalDeviceManager.isStarted()) {
1124            return featureSet; // TODO: can't get capabilities with Wi-Fi down
1125        }
1126        try {
1127            final MutableInt feat = new MutableInt(0);
1128            synchronized (sLock) {
1129                if (mIWifiChip != null) {
1130                    mIWifiChip.getCapabilities((status, capabilities) -> {
1131                        if (!ok(status)) return;
1132                        feat.value = wifiFeatureMaskFromChipCapabilities(capabilities);
1133                    });
1134                }
1135                IWifiStaIface iface = getStaIface(ifaceName);
1136                if (iface != null) {
1137                    iface.getCapabilities((status, capabilities) -> {
1138                        if (!ok(status)) return;
1139                        feat.value |= wifiFeatureMaskFromStaCapabilities(capabilities);
1140                    });
1141                }
1142            }
1143            featureSet = feat.value;
1144        } catch (RemoteException e) {
1145            handleRemoteException(e);
1146            return 0;
1147        }
1148
1149        Set<Integer> supportedIfaceTypes = mHalDeviceManager.getSupportedIfaceTypes();
1150        if (supportedIfaceTypes.contains(IfaceType.STA)) {
1151            featureSet |= WifiManager.WIFI_FEATURE_INFRA;
1152        }
1153        if (supportedIfaceTypes.contains(IfaceType.AP)) {
1154            featureSet |= WifiManager.WIFI_FEATURE_MOBILE_HOTSPOT;
1155        }
1156        if (supportedIfaceTypes.contains(IfaceType.P2P)) {
1157            featureSet |= WifiManager.WIFI_FEATURE_P2P;
1158        }
1159        if (supportedIfaceTypes.contains(IfaceType.NAN)) {
1160            featureSet |= WifiManager.WIFI_FEATURE_AWARE;
1161        }
1162
1163        return featureSet;
1164    }
1165
1166    /* RTT related commands/events */
1167
1168    /**
1169     * RTT (Round Trip Time) measurement capabilities of the device.
1170     */
1171    public RttManager.RttCapabilities getRttCapabilities() {
1172        class AnswerBox {
1173            public RttManager.RttCapabilities value = null;
1174        }
1175        synchronized (sLock) {
1176            if (mIWifiRttController == null) return null;
1177            try {
1178                AnswerBox box = new AnswerBox();
1179                mIWifiRttController.getCapabilities((status, capabilities) -> {
1180                    if (!ok(status)) return;
1181                    mVerboseLog.info("rtt capabilites %").c(capabilities.toString()).flush();
1182                    RttManager.RttCapabilities ans = new RttManager.RttCapabilities();
1183                    ans.oneSidedRttSupported = capabilities.rttOneSidedSupported;
1184                    ans.twoSided11McRttSupported = capabilities.rttFtmSupported;
1185                    ans.lciSupported = capabilities.lciSupported;
1186                    ans.lcrSupported = capabilities.lcrSupported;
1187                    ans.preambleSupported = frameworkPreambleFromHalPreamble(
1188                            capabilities.preambleSupport);
1189                    ans.bwSupported = frameworkBwFromHalBw(capabilities.bwSupport);
1190                    ans.responderSupported = capabilities.responderSupported;
1191                    ans.secureRttSupported = false;
1192                    ans.mcVersion = ((int) capabilities.mcVersion) & 0xff;
1193                    box.value = ans;
1194                });
1195                return box.value;
1196            } catch (RemoteException e) {
1197                handleRemoteException(e);
1198                return null;
1199            }
1200        }
1201    }
1202
1203    private int mRttCmdIdNext = 1;              // used to generate new command ids
1204    private int mRttCmdId;                      // id of currently active request
1205    // Event handler for current active RTT request.
1206    private WifiNative.RttEventHandler mRttEventHandler;
1207
1208    /**
1209     * Receives a callback from the Hal and passes it along to our client using RttEventHandler
1210     */
1211    private class RttEventCallback extends IWifiRttControllerEventCallback.Stub {
1212
1213        @Override
1214        public void onResults(int cmdId, java.util.ArrayList<RttResult> results) {
1215            WifiNative.RttEventHandler eventHandler;
1216            synchronized (sLock) {
1217                if (cmdId != mRttCmdId || mRttEventHandler == null) return;
1218                eventHandler = mRttEventHandler;
1219                // Reset the command id for RTT operations in WifiVendorHal.
1220                WifiVendorHal.this.mRttCmdId = 0;
1221            }
1222            RttManager.RttResult[] rtt = new RttManager.RttResult[results.size()];
1223            for (int i = 0; i < rtt.length; i++) {
1224                rtt[i] = frameworkRttResultFromHalRttResult(results.get(i));
1225            }
1226            eventHandler.onRttResults(rtt);
1227        }
1228    }
1229
1230    /**
1231     * Converts a Hal RttResult to a RttManager.RttResult
1232     */
1233    @VisibleForTesting
1234    static RttManager.RttResult frameworkRttResultFromHalRttResult(RttResult result) {
1235        RttManager.RttResult ans = new RttManager.RttResult();
1236        ans.bssid = NativeUtil.macAddressFromByteArray(result.addr);
1237        ans.burstNumber = result.burstNum;
1238        ans.measurementFrameNumber = result.measurementNumber;
1239        ans.successMeasurementFrameNumber = result.successNumber;
1240        ans.frameNumberPerBurstPeer = result.numberPerBurstPeer;
1241        ans.status = result.status; //TODO(b/35138520) - don't assume identity translation
1242        ans.retryAfterDuration = result.retryAfterDuration;
1243        ans.measurementType = result.type;
1244        ans.rssi = result.rssi;
1245        ans.rssiSpread = result.rssiSpread;
1246        //TODO(b/35138520) Fix HAL and framework to use the same units
1247        ans.txRate = result.txRate.bitRateInKbps;
1248        ans.rxRate = result.rxRate.bitRateInKbps;
1249        ans.rtt = result.rtt;
1250        ans.rttStandardDeviation = result.rttSd;
1251        ans.rttSpread = result.rttSpread;
1252        //TODO(b/35138520) These divide-by-10s were in the legacy Hal
1253        ans.distance = result.distanceInMm / 10; // Convert cm to mm
1254        ans.distanceStandardDeviation = result.distanceSdInMm / 10; // Convert cm to mm
1255        ans.distanceSpread = result.distanceSpreadInMm / 10;
1256
1257        ans.ts = result.timeStampInUs;
1258        ans.burstDuration = result.burstDurationInMs;
1259        ans.negotiatedBurstNum = result.negotiatedBurstNum;
1260        ans.LCI = ieFromHal(result.lci);
1261        ans.LCR = ieFromHal(result.lcr);
1262        ans.secure = false; // Not present in HIDL HAL
1263        return ans;
1264    }
1265
1266    /**
1267     * Convert a Hal WifiInformationElement to its RttManager equivalent
1268     */
1269    @VisibleForTesting
1270    static RttManager.WifiInformationElement ieFromHal(
1271            android.hardware.wifi.V1_0.WifiInformationElement ie) {
1272        if (ie == null) return null;
1273        RttManager.WifiInformationElement ans = new RttManager.WifiInformationElement();
1274        ans.id = ie.id;
1275        ans.data = NativeUtil.byteArrayFromArrayList(ie.data);
1276        return ans;
1277    }
1278
1279    @VisibleForTesting
1280    static RttConfig halRttConfigFromFrameworkRttParams(RttManager.RttParams params) {
1281        RttConfig rttConfig = new RttConfig();
1282        if (params.bssid != null) {
1283            byte[] addr = NativeUtil.macAddressToByteArray(params.bssid);
1284            for (int i = 0; i < rttConfig.addr.length; i++) {
1285                rttConfig.addr[i] = addr[i];
1286            }
1287        }
1288        rttConfig.type = halRttTypeFromFrameworkRttType(params.requestType);
1289        rttConfig.peer = halPeerFromFrameworkPeer(params.deviceType);
1290        rttConfig.channel.width = halChannelWidthFromFrameworkChannelWidth(params.channelWidth);
1291        rttConfig.channel.centerFreq = params.frequency;
1292        rttConfig.channel.centerFreq0 = params.centerFreq0;
1293        rttConfig.channel.centerFreq1 = params.centerFreq1;
1294        rttConfig.burstPeriod = params.interval; // In 100ms units, 0 means no specific
1295        rttConfig.numBurst = params.numberBurst;
1296        rttConfig.numFramesPerBurst = params.numSamplesPerBurst;
1297        rttConfig.numRetriesPerRttFrame = params.numRetriesPerMeasurementFrame;
1298        rttConfig.numRetriesPerFtmr = params.numRetriesPerFTMR;
1299        rttConfig.mustRequestLci = params.LCIRequest;
1300        rttConfig.mustRequestLcr = params.LCRRequest;
1301        rttConfig.burstDuration = params.burstTimeout;
1302        rttConfig.preamble = halPreambleFromFrameworkPreamble(params.preamble);
1303        rttConfig.bw = halBwFromFrameworkBw(params.bandwidth);
1304        return rttConfig;
1305    }
1306
1307    @VisibleForTesting
1308    static int halRttTypeFromFrameworkRttType(int frameworkRttType) {
1309        switch (frameworkRttType) {
1310            case RttManager.RTT_TYPE_ONE_SIDED:
1311                return RttType.ONE_SIDED;
1312            case RttManager.RTT_TYPE_TWO_SIDED:
1313                return RttType.TWO_SIDED;
1314            default:
1315                throw new IllegalArgumentException("bad " + frameworkRttType);
1316        }
1317    }
1318
1319    @VisibleForTesting
1320    static int frameworkRttTypeFromHalRttType(int halType) {
1321        switch (halType) {
1322            case RttType.ONE_SIDED:
1323                return RttManager.RTT_TYPE_ONE_SIDED;
1324            case RttType.TWO_SIDED:
1325                return RttManager.RTT_TYPE_TWO_SIDED;
1326            default:
1327                throw new IllegalArgumentException("bad " + halType);
1328        }
1329    }
1330
1331    @VisibleForTesting
1332    static int halPeerFromFrameworkPeer(int frameworkPeer) {
1333        switch (frameworkPeer) {
1334            case RttManager.RTT_PEER_TYPE_AP:
1335                return RttPeerType.AP;
1336            case RttManager.RTT_PEER_TYPE_STA:
1337                return RttPeerType.STA;
1338            case RttManager.RTT_PEER_P2P_GO:
1339                return RttPeerType.P2P_GO;
1340            case RttManager.RTT_PEER_P2P_CLIENT:
1341                return RttPeerType.P2P_CLIENT;
1342            case RttManager.RTT_PEER_NAN:
1343                return RttPeerType.NAN;
1344            default:
1345                throw new IllegalArgumentException("bad " + frameworkPeer);
1346        }
1347    }
1348
1349    @VisibleForTesting
1350    static int frameworkPeerFromHalPeer(int halPeer) {
1351        switch (halPeer) {
1352            case RttPeerType.AP:
1353                return RttManager.RTT_PEER_TYPE_AP;
1354            case RttPeerType.STA:
1355                return RttManager.RTT_PEER_TYPE_STA;
1356            case RttPeerType.P2P_GO:
1357                return RttManager.RTT_PEER_P2P_GO;
1358            case RttPeerType.P2P_CLIENT:
1359                return RttManager.RTT_PEER_P2P_CLIENT;
1360            case RttPeerType.NAN:
1361                return RttManager.RTT_PEER_NAN;
1362            default:
1363                throw new IllegalArgumentException("bad " + halPeer);
1364
1365        }
1366    }
1367
1368    @VisibleForTesting
1369    static int halChannelWidthFromFrameworkChannelWidth(int frameworkChannelWidth) {
1370        switch (frameworkChannelWidth) {
1371            case ScanResult.CHANNEL_WIDTH_20MHZ:
1372                return WifiChannelWidthInMhz.WIDTH_20;
1373            case ScanResult.CHANNEL_WIDTH_40MHZ:
1374                return WifiChannelWidthInMhz.WIDTH_40;
1375            case ScanResult.CHANNEL_WIDTH_80MHZ:
1376                return WifiChannelWidthInMhz.WIDTH_80;
1377            case ScanResult.CHANNEL_WIDTH_160MHZ:
1378                return WifiChannelWidthInMhz.WIDTH_160;
1379            case ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ:
1380                return WifiChannelWidthInMhz.WIDTH_80P80;
1381            default:
1382                throw new IllegalArgumentException("bad " + frameworkChannelWidth);
1383        }
1384    }
1385
1386    @VisibleForTesting
1387    static int frameworkChannelWidthFromHalChannelWidth(int halChannelWidth) {
1388        switch (halChannelWidth) {
1389            case WifiChannelWidthInMhz.WIDTH_20:
1390                return ScanResult.CHANNEL_WIDTH_20MHZ;
1391            case WifiChannelWidthInMhz.WIDTH_40:
1392                return ScanResult.CHANNEL_WIDTH_40MHZ;
1393            case WifiChannelWidthInMhz.WIDTH_80:
1394                return ScanResult.CHANNEL_WIDTH_80MHZ;
1395            case WifiChannelWidthInMhz.WIDTH_160:
1396                return ScanResult.CHANNEL_WIDTH_160MHZ;
1397            case WifiChannelWidthInMhz.WIDTH_80P80:
1398                return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ;
1399            default:
1400                throw new IllegalArgumentException("bad " + halChannelWidth);
1401        }
1402    }
1403
1404    @VisibleForTesting
1405    static int halPreambleFromFrameworkPreamble(int rttManagerPreamble) {
1406        BitMask checkoff = new BitMask(rttManagerPreamble);
1407        int flags = 0;
1408        if (checkoff.testAndClear(RttManager.PREAMBLE_LEGACY)) {
1409            flags |= RttPreamble.LEGACY;
1410        }
1411        if (checkoff.testAndClear(RttManager.PREAMBLE_HT)) {
1412            flags |= RttPreamble.HT;
1413        }
1414        if (checkoff.testAndClear(RttManager.PREAMBLE_VHT)) {
1415            flags |= RttPreamble.VHT;
1416        }
1417        if (checkoff.value != 0) {
1418            throw new IllegalArgumentException("bad " + rttManagerPreamble);
1419        }
1420        return flags;
1421    }
1422
1423    @VisibleForTesting
1424    static int frameworkPreambleFromHalPreamble(int halPreamble) {
1425        BitMask checkoff = new BitMask(halPreamble);
1426        int flags = 0;
1427        if (checkoff.testAndClear(RttPreamble.LEGACY)) {
1428            flags |= RttManager.PREAMBLE_LEGACY;
1429        }
1430        if (checkoff.testAndClear(RttPreamble.HT)) {
1431            flags |= RttManager.PREAMBLE_HT;
1432        }
1433        if (checkoff.testAndClear(RttPreamble.VHT)) {
1434            flags |= RttManager.PREAMBLE_VHT;
1435        }
1436        if (checkoff.value != 0) {
1437            throw new IllegalArgumentException("bad " + halPreamble);
1438        }
1439        return flags;
1440    }
1441
1442    @VisibleForTesting
1443    static int halBwFromFrameworkBw(int rttManagerBandwidth) {
1444        BitMask checkoff = new BitMask(rttManagerBandwidth);
1445        int flags = 0;
1446        if (checkoff.testAndClear(RttManager.RTT_BW_5_SUPPORT)) {
1447            flags |= RttBw.BW_5MHZ;
1448        }
1449        if (checkoff.testAndClear(RttManager.RTT_BW_10_SUPPORT)) {
1450            flags |= RttBw.BW_10MHZ;
1451        }
1452        if (checkoff.testAndClear(RttManager.RTT_BW_20_SUPPORT)) {
1453            flags |= RttBw.BW_20MHZ;
1454        }
1455        if (checkoff.testAndClear(RttManager.RTT_BW_40_SUPPORT)) {
1456            flags |= RttBw.BW_40MHZ;
1457        }
1458        if (checkoff.testAndClear(RttManager.RTT_BW_80_SUPPORT)) {
1459            flags |= RttBw.BW_80MHZ;
1460        }
1461        if (checkoff.testAndClear(RttManager.RTT_BW_160_SUPPORT)) {
1462            flags |= RttBw.BW_160MHZ;
1463        }
1464        if (checkoff.value != 0) {
1465            throw new IllegalArgumentException("bad " + rttManagerBandwidth);
1466        }
1467        return flags;
1468    }
1469
1470    @VisibleForTesting
1471    static int frameworkBwFromHalBw(int rttBw) {
1472        BitMask checkoff = new BitMask(rttBw);
1473        int flags = 0;
1474        if (checkoff.testAndClear(RttBw.BW_5MHZ)) {
1475            flags |= RttManager.RTT_BW_5_SUPPORT;
1476        }
1477        if (checkoff.testAndClear(RttBw.BW_10MHZ)) {
1478            flags |= RttManager.RTT_BW_10_SUPPORT;
1479        }
1480        if (checkoff.testAndClear(RttBw.BW_20MHZ)) {
1481            flags |= RttManager.RTT_BW_20_SUPPORT;
1482        }
1483        if (checkoff.testAndClear(RttBw.BW_40MHZ)) {
1484            flags |= RttManager.RTT_BW_40_SUPPORT;
1485        }
1486        if (checkoff.testAndClear(RttBw.BW_80MHZ)) {
1487            flags |= RttManager.RTT_BW_80_SUPPORT;
1488        }
1489        if (checkoff.testAndClear(RttBw.BW_160MHZ)) {
1490            flags |= RttManager.RTT_BW_160_SUPPORT;
1491        }
1492        if (checkoff.value != 0) {
1493            throw new IllegalArgumentException("bad " + rttBw);
1494        }
1495        return flags;
1496    }
1497
1498    @VisibleForTesting
1499    static ArrayList<RttConfig> halRttConfigArrayFromFrameworkRttParamsArray(
1500            RttManager.RttParams[] params) {
1501        final int length = params.length;
1502        ArrayList<RttConfig> configs = new ArrayList<RttConfig>(length);
1503        for (int i = 0; i < length; i++) {
1504            RttConfig config = halRttConfigFromFrameworkRttParams(params[i]);
1505            if (config != null) {
1506                configs.add(config);
1507            }
1508        }
1509        return configs;
1510    }
1511
1512    /**
1513     * Starts a new rtt request
1514     *
1515     * @param params
1516     * @param handler
1517     * @return success indication
1518     */
1519    public boolean requestRtt(RttManager.RttParams[] params, WifiNative.RttEventHandler handler) {
1520        ArrayList<RttConfig> rttConfigs;
1521        try {
1522            rttConfigs = halRttConfigArrayFromFrameworkRttParamsArray(params);
1523        } catch (IllegalArgumentException e) {
1524            mLog.err("Illegal argument for RTT request").c(e.toString()).flush();
1525            return false;
1526        }
1527        synchronized (sLock) {
1528            if (mIWifiRttController == null) return boolResult(false);
1529            if (mRttCmdId != 0) return boolResult(false);
1530            mRttCmdId = mRttCmdIdNext++;
1531            mRttEventHandler = handler;
1532            if (mRttCmdIdNext <= 0) mRttCmdIdNext = 1;
1533            try {
1534                WifiStatus status = mIWifiRttController.rangeRequest(mRttCmdId, rttConfigs);
1535                if (ok(status)) return true;
1536                mRttCmdId = 0;
1537                return false;
1538            } catch (RemoteException e) {
1539                handleRemoteException(e);
1540                return false;
1541            }
1542        }
1543    }
1544
1545    /**
1546     * Cancels an outstanding rtt request
1547     *
1548     * @param params
1549     * @return true if there was an outstanding request and it was successfully cancelled
1550     */
1551    public boolean cancelRtt(RttManager.RttParams[] params) {
1552        ArrayList<RttConfig> rttConfigs = halRttConfigArrayFromFrameworkRttParamsArray(params);
1553        synchronized (sLock) {
1554            if (mIWifiRttController == null) return boolResult(false);
1555            if (mRttCmdId == 0) return boolResult(false);
1556            ArrayList<byte[/* 6 */]> addrs = new ArrayList<byte[]>(rttConfigs.size());
1557            for (RttConfig x : rttConfigs) addrs.add(x.addr);
1558            try {
1559                WifiStatus status = mIWifiRttController.rangeCancel(mRttCmdId, addrs);
1560                mRttCmdId = 0;
1561                if (!ok(status)) return false;
1562                return true;
1563            } catch (RemoteException e) {
1564                handleRemoteException(e);
1565                return false;
1566            }
1567        }
1568    }
1569
1570    private int mRttResponderCmdId = 0;
1571
1572    /**
1573     * Get RTT responder information e.g. WiFi channel to enable responder on.
1574     *
1575     * @return info Instance of |RttResponder|, or null for error.
1576     */
1577    private RttResponder getRttResponder() {
1578        class AnswerBox {
1579            public RttResponder value = null;
1580        }
1581        synchronized (sLock) {
1582            if (mIWifiRttController == null) return null;
1583            AnswerBox answer = new AnswerBox();
1584            try {
1585                mIWifiRttController.getResponderInfo((status, info) -> {
1586                    if (!ok(status)) return;
1587                    answer.value = info;
1588                });
1589                return answer.value;
1590            } catch (RemoteException e) {
1591                handleRemoteException(e);
1592                return null;
1593            }
1594        }
1595    }
1596
1597    /**
1598     * Convert Hal RttResponder to a framework ResponderConfig
1599     *
1600     * @param info Instance of |RttResponder|
1601     * @return framework version of same
1602     */
1603    private ResponderConfig frameworkResponderConfigFromHalRttResponder(RttResponder info) {
1604        ResponderConfig config = new ResponderConfig();
1605        config.frequency = info.channel.centerFreq;
1606        config.centerFreq0 = info.channel.centerFreq0;
1607        config.centerFreq1 = info.channel.centerFreq1;
1608        config.channelWidth = frameworkChannelWidthFromHalChannelWidth(info.channel.width);
1609        config.preamble = frameworkPreambleFromHalPreamble(info.preamble);
1610        return config;
1611    }
1612
1613    /**
1614     * Enables RTT responder role on the device.
1615     *
1616     * @return {@link ResponderConfig} if the responder role is successfully enabled,
1617     * {@code null} otherwise.
1618     */
1619    public ResponderConfig enableRttResponder(int timeoutSeconds) {
1620        RttResponder info = getRttResponder();
1621        synchronized (sLock) {
1622            if (mIWifiRttController == null) return null;
1623            if (mRttResponderCmdId != 0) {
1624                mLog.err("responder mode already enabled - this shouldn't happen").flush();
1625                return null;
1626            }
1627            ResponderConfig config = null;
1628            int id = mRttCmdIdNext++;
1629            if (mRttCmdIdNext <= 0) mRttCmdIdNext = 1;
1630            try {
1631                WifiStatus status = mIWifiRttController.enableResponder(
1632                        /* cmdId */id,
1633                        /* WifiChannelInfo channelHint */null,
1634                        timeoutSeconds, info);
1635                if (ok(status)) {
1636                    mRttResponderCmdId = id;
1637                    config = frameworkResponderConfigFromHalRttResponder(info);
1638                    mVerboseLog.i("enabling rtt " + mRttResponderCmdId);
1639                }
1640                return config;
1641            } catch (RemoteException e) {
1642                handleRemoteException(e);
1643                return null;
1644            }
1645        }
1646    }
1647
1648    /**
1649     * Disables RTT responder role.
1650     *
1651     * @return {@code true} if responder role is successfully disabled,
1652     * {@code false} otherwise.
1653     */
1654    public boolean disableRttResponder() {
1655        synchronized (sLock) {
1656            if (mIWifiRttController == null) return boolResult(false);
1657            if (mRttResponderCmdId == 0) return boolResult(false);
1658            try {
1659                WifiStatus status = mIWifiRttController.disableResponder(mRttResponderCmdId);
1660                mRttResponderCmdId = 0;
1661                if (!ok(status)) return false;
1662                return true;
1663            } catch (RemoteException e) {
1664                handleRemoteException(e);
1665                return false;
1666            }
1667        }
1668    }
1669
1670    /**
1671     * Set the MAC OUI during scanning.
1672     * <p>
1673     * An OUI {Organizationally Unique Identifier} is a 24-bit number that
1674     * uniquely identifies a vendor or manufacturer.
1675     *
1676     * @param ifaceName Name of the interface.
1677     * @param oui
1678     * @return true for success
1679     */
1680    public boolean setScanningMacOui(@NonNull String ifaceName, byte[] oui) {
1681        if (oui == null) return boolResult(false);
1682        if (oui.length != 3) return boolResult(false);
1683        synchronized (sLock) {
1684            try {
1685                IWifiStaIface iface = getStaIface(ifaceName);
1686                if (iface == null) return boolResult(false);
1687                WifiStatus status = iface.setScanningMacOui(oui);
1688                if (!ok(status)) return false;
1689                return true;
1690            } catch (RemoteException e) {
1691                handleRemoteException(e);
1692                return false;
1693            }
1694        }
1695    }
1696
1697    /**
1698     * Set Mac address on the given interface
1699     *
1700     * @param ifaceName Name of the interface
1701     * @param mac MAC address to change into
1702     * @return true for success
1703     */
1704    public boolean setMacAddress(@NonNull String ifaceName, @NonNull MacAddress mac) {
1705        byte[] macByteArray = mac.toByteArray();
1706        synchronized (sLock) {
1707            try {
1708                android.hardware.wifi.V1_2.IWifiStaIface ifaceV12 =
1709                        getWifiStaIfaceForV1_2Mockable(ifaceName);
1710                if (ifaceV12 == null) return boolResult(false);
1711                WifiStatus status = ifaceV12.setMacAddress(macByteArray);
1712                if (!ok(status)) return false;
1713                return true;
1714            } catch (RemoteException e) {
1715                handleRemoteException(e);
1716                return false;
1717            }
1718        }
1719    }
1720
1721    /**
1722     * Get the APF (Android Packet Filter) capabilities of the device
1723     *
1724     * @param ifaceName Name of the interface.
1725     * @return APF capabilities object.
1726     */
1727    public ApfCapabilities getApfCapabilities(@NonNull String ifaceName) {
1728        class AnswerBox {
1729            public ApfCapabilities value = sNoApfCapabilities;
1730        }
1731        synchronized (sLock) {
1732            try {
1733                IWifiStaIface iface = getStaIface(ifaceName);
1734                if (iface == null) return sNoApfCapabilities;
1735                AnswerBox box = new AnswerBox();
1736                iface.getApfPacketFilterCapabilities((status, capabilities) -> {
1737                    if (!ok(status)) return;
1738                    box.value = new ApfCapabilities(
1739                        /* apfVersionSupported */   capabilities.version,
1740                        /* maximumApfProgramSize */ capabilities.maxLength,
1741                        /* apfPacketFormat */       android.system.OsConstants.ARPHRD_ETHER);
1742                });
1743                return box.value;
1744            } catch (RemoteException e) {
1745                handleRemoteException(e);
1746                return sNoApfCapabilities;
1747            }
1748        }
1749    }
1750
1751    private static final ApfCapabilities sNoApfCapabilities = new ApfCapabilities(0, 0, 0);
1752
1753    /**
1754     * Installs an APF program on this iface, replacing any existing program.
1755     *
1756     * @param ifaceName Name of the interface.
1757     * @param filter is the android packet filter program
1758     * @return true for success
1759     */
1760    public boolean installPacketFilter(@NonNull String ifaceName, byte[] filter) {
1761        int cmdId = 0; // We only aspire to support one program at a time
1762        if (filter == null) return boolResult(false);
1763        // Copy the program before taking the lock.
1764        ArrayList<Byte> program = NativeUtil.byteArrayToArrayList(filter);
1765        enter("filter length %").c(filter.length).flush();
1766        synchronized (sLock) {
1767            try {
1768                IWifiStaIface iface = getStaIface(ifaceName);
1769                if (iface == null) return boolResult(false);
1770                WifiStatus status = iface.installApfPacketFilter(cmdId, program);
1771                if (!ok(status)) return false;
1772                return true;
1773            } catch (RemoteException e) {
1774                handleRemoteException(e);
1775                return false;
1776            }
1777        }
1778    }
1779
1780    /**
1781     * Reads the APF program and data buffer on this iface.
1782     *
1783     * @param ifaceName Name of the interface
1784     * @return the buffer returned by the driver, or null in case of an error
1785     */
1786    public byte[] readPacketFilter(@NonNull String ifaceName) {
1787        class AnswerBox {
1788            public byte[] data = null;
1789        }
1790        AnswerBox answer = new AnswerBox();
1791        enter("").flush();
1792        // TODO: Must also take the wakelock here to prevent going to sleep with APF disabled.
1793        synchronized (sLock) {
1794            try {
1795                android.hardware.wifi.V1_2.IWifiStaIface ifaceV12 =
1796                        getWifiStaIfaceForV1_2Mockable(ifaceName);
1797                if (ifaceV12 == null) return byteArrayResult(null);
1798                ifaceV12.readApfPacketFilterData((status, dataByteArray) -> {
1799                    if (!ok(status)) return;
1800                    answer.data = NativeUtil.byteArrayFromArrayList(dataByteArray);
1801                });
1802                return byteArrayResult(answer.data);
1803            } catch (RemoteException e) {
1804                handleRemoteException(e);
1805                return byteArrayResult(null);
1806            }
1807        }
1808    }
1809
1810    /**
1811     * Set country code for this AP iface.
1812     *
1813     * @param ifaceName Name of the interface.
1814     * @param countryCode - two-letter country code (as ISO 3166)
1815     * @return true for success
1816     */
1817    public boolean setCountryCodeHal(@NonNull String ifaceName, String countryCode) {
1818        if (countryCode == null) return boolResult(false);
1819        if (countryCode.length() != 2) return boolResult(false);
1820        byte[] code;
1821        try {
1822            code = NativeUtil.stringToByteArray(countryCode);
1823        } catch (IllegalArgumentException e) {
1824            return boolResult(false);
1825        }
1826        synchronized (sLock) {
1827            try {
1828                IWifiApIface iface = getApIface(ifaceName);
1829                if (iface == null) return boolResult(false);
1830                WifiStatus status = iface.setCountryCode(code);
1831                if (!ok(status)) return false;
1832                return true;
1833            } catch (RemoteException e) {
1834                handleRemoteException(e);
1835                return false;
1836            }
1837        }
1838    }
1839
1840    private WifiNative.WifiLoggerEventHandler mLogEventHandler = null;
1841
1842    /**
1843     * Registers the logger callback and enables alerts.
1844     * Ring buffer data collection is only triggered when |startLoggingRingBuffer| is invoked.
1845     */
1846    public boolean setLoggingEventHandler(WifiNative.WifiLoggerEventHandler handler) {
1847        if (handler == null) return boolResult(false);
1848        synchronized (sLock) {
1849            if (mIWifiChip == null) return boolResult(false);
1850            if (mLogEventHandler != null) return boolResult(false);
1851            try {
1852                WifiStatus status = mIWifiChip.enableDebugErrorAlerts(true);
1853                if (!ok(status)) return false;
1854                mLogEventHandler = handler;
1855                return true;
1856            } catch (RemoteException e) {
1857                handleRemoteException(e);
1858                return false;
1859            }
1860        }
1861    }
1862
1863    /**
1864     * Stops all logging and resets the logger callback.
1865     * This stops both the alerts and ring buffer data collection.
1866     */
1867    public boolean resetLogHandler() {
1868        synchronized (sLock) {
1869            if (mIWifiChip == null) return boolResult(false);
1870            if (mLogEventHandler == null) return boolResult(false);
1871            try {
1872                WifiStatus status = mIWifiChip.enableDebugErrorAlerts(false);
1873                if (!ok(status)) return false;
1874                status = mIWifiChip.stopLoggingToDebugRingBuffer();
1875                if (!ok(status)) return false;
1876                mLogEventHandler = null;
1877                return true;
1878            } catch (RemoteException e) {
1879                handleRemoteException(e);
1880                return false;
1881            }
1882        }
1883    }
1884
1885    /**
1886     * Control debug data collection
1887     *
1888     * @param verboseLevel       0 to 3, inclusive. 0 stops logging.
1889     * @param flags              Ignored.
1890     * @param maxIntervalInSec   Maximum interval between reports; ignore if 0.
1891     * @param minDataSizeInBytes Minimum data size in buffer for report; ignore if 0.
1892     * @param ringName           Name of the ring for which data collection is to start.
1893     * @return true for success
1894     */
1895    public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxIntervalInSec,
1896                                          int minDataSizeInBytes, String ringName) {
1897        enter("verboseLevel=%, flags=%, maxIntervalInSec=%, minDataSizeInBytes=%, ringName=%")
1898                .c(verboseLevel).c(flags).c(maxIntervalInSec).c(minDataSizeInBytes).c(ringName)
1899                .flush();
1900        synchronized (sLock) {
1901            if (mIWifiChip == null) return boolResult(false);
1902            try {
1903                // note - flags are not used
1904                WifiStatus status = mIWifiChip.startLoggingToDebugRingBuffer(
1905                        ringName,
1906                        verboseLevel,
1907                        maxIntervalInSec,
1908                        minDataSizeInBytes
1909                );
1910                return ok(status);
1911            } catch (RemoteException e) {
1912                handleRemoteException(e);
1913                return false;
1914            }
1915        }
1916    }
1917
1918    /**
1919     * Pointlessly fail
1920     *
1921     * @return -1
1922     */
1923    public int getSupportedLoggerFeatureSet() {
1924        return -1;
1925    }
1926
1927    private String mDriverDescription; // Cached value filled by requestChipDebugInfo()
1928
1929    /**
1930     * Vendor-provided wifi driver version string
1931     */
1932    public String getDriverVersion() {
1933        synchronized (sLock) {
1934            if (mDriverDescription == null) requestChipDebugInfo();
1935            return mDriverDescription;
1936        }
1937    }
1938
1939    private String mFirmwareDescription; // Cached value filled by requestChipDebugInfo()
1940
1941    /**
1942     * Vendor-provided wifi firmware version string
1943     */
1944    public String getFirmwareVersion() {
1945        synchronized (sLock) {
1946            if (mFirmwareDescription == null) requestChipDebugInfo();
1947            return mFirmwareDescription;
1948        }
1949    }
1950
1951    /**
1952     * Refreshes our idea of the driver and firmware versions
1953     */
1954    private void requestChipDebugInfo() {
1955        mDriverDescription = null;
1956        mFirmwareDescription = null;
1957        try {
1958            if (mIWifiChip == null) return;
1959            mIWifiChip.requestChipDebugInfo((status, chipDebugInfo) -> {
1960                if (!ok(status)) return;
1961                mDriverDescription = chipDebugInfo.driverDescription;
1962                mFirmwareDescription = chipDebugInfo.firmwareDescription;
1963            });
1964        } catch (RemoteException e) {
1965            handleRemoteException(e);
1966            return;
1967        }
1968        mLog.info("Driver: % Firmware: %")
1969                .c(mDriverDescription)
1970                .c(mFirmwareDescription)
1971                .flush();
1972    }
1973
1974    /**
1975     * Creates RingBufferStatus from the Hal version
1976     */
1977    private static WifiNative.RingBufferStatus ringBufferStatus(WifiDebugRingBufferStatus h) {
1978        WifiNative.RingBufferStatus ans = new WifiNative.RingBufferStatus();
1979        ans.name = h.ringName;
1980        ans.flag = frameworkRingBufferFlagsFromHal(h.flags);
1981        ans.ringBufferId = h.ringId;
1982        ans.ringBufferByteSize = h.sizeInBytes;
1983        ans.verboseLevel = h.verboseLevel;
1984        // Remaining fields are unavailable
1985        //  writtenBytes;
1986        //  readBytes;
1987        //  writtenRecords;
1988        return ans;
1989    }
1990
1991    /**
1992     * Translates a hal wifiDebugRingBufferFlag to the WifiNative version
1993     */
1994    private static int frameworkRingBufferFlagsFromHal(int wifiDebugRingBufferFlag) {
1995        BitMask checkoff = new BitMask(wifiDebugRingBufferFlag);
1996        int flags = 0;
1997        if (checkoff.testAndClear(WifiDebugRingBufferFlags.HAS_BINARY_ENTRIES)) {
1998            flags |= WifiNative.RingBufferStatus.HAS_BINARY_ENTRIES;
1999        }
2000        if (checkoff.testAndClear(WifiDebugRingBufferFlags.HAS_ASCII_ENTRIES)) {
2001            flags |= WifiNative.RingBufferStatus.HAS_ASCII_ENTRIES;
2002        }
2003        if (checkoff.testAndClear(WifiDebugRingBufferFlags.HAS_PER_PACKET_ENTRIES)) {
2004            flags |= WifiNative.RingBufferStatus.HAS_PER_PACKET_ENTRIES;
2005        }
2006        if (checkoff.value != 0) {
2007            throw new IllegalArgumentException("Unknown WifiDebugRingBufferFlag " + checkoff.value);
2008        }
2009        return flags;
2010    }
2011
2012    /**
2013     * Creates array of RingBufferStatus from the Hal version
2014     */
2015    private static WifiNative.RingBufferStatus[] makeRingBufferStatusArray(
2016            ArrayList<WifiDebugRingBufferStatus> ringBuffers) {
2017        WifiNative.RingBufferStatus[] ans = new WifiNative.RingBufferStatus[ringBuffers.size()];
2018        int i = 0;
2019        for (WifiDebugRingBufferStatus b : ringBuffers) {
2020            ans[i++] = ringBufferStatus(b);
2021        }
2022        return ans;
2023    }
2024
2025    /**
2026     * API to get the status of all ring buffers supported by driver
2027     */
2028    public WifiNative.RingBufferStatus[] getRingBufferStatus() {
2029        class AnswerBox {
2030            public WifiNative.RingBufferStatus[] value = null;
2031        }
2032        AnswerBox ans = new AnswerBox();
2033        synchronized (sLock) {
2034            if (mIWifiChip == null) return null;
2035            try {
2036                mIWifiChip.getDebugRingBuffersStatus((status, ringBuffers) -> {
2037                    if (!ok(status)) return;
2038                    ans.value = makeRingBufferStatusArray(ringBuffers);
2039                });
2040            } catch (RemoteException e) {
2041                handleRemoteException(e);
2042                return null;
2043            }
2044        }
2045        return ans.value;
2046    }
2047
2048    /**
2049     * Indicates to driver that all the data has to be uploaded urgently
2050     */
2051    public boolean getRingBufferData(String ringName) {
2052        enter("ringName %").c(ringName).flush();
2053        synchronized (sLock) {
2054            if (mIWifiChip == null) return boolResult(false);
2055            try {
2056                WifiStatus status = mIWifiChip.forceDumpToDebugRingBuffer(ringName);
2057                return ok(status);
2058            } catch (RemoteException e) {
2059                handleRemoteException(e);
2060                return false;
2061            }
2062        }
2063    }
2064
2065    /**
2066     * Request vendor debug info from the firmware
2067     */
2068    public byte[] getFwMemoryDump() {
2069        class AnswerBox {
2070            public byte[] value;
2071        }
2072        AnswerBox ans = new AnswerBox();
2073        synchronized (sLock) {
2074            if (mIWifiChip == null) return (null);
2075            try {
2076                mIWifiChip.requestFirmwareDebugDump((status, blob) -> {
2077                    if (!ok(status)) return;
2078                    ans.value = NativeUtil.byteArrayFromArrayList(blob);
2079                });
2080            } catch (RemoteException e) {
2081                handleRemoteException(e);
2082                return null;
2083            }
2084        }
2085        return ans.value;
2086    }
2087
2088    /**
2089     * Request vendor debug info from the driver
2090     */
2091    public byte[] getDriverStateDump() {
2092        class AnswerBox {
2093            public byte[] value;
2094        }
2095        AnswerBox ans = new AnswerBox();
2096        synchronized (sLock) {
2097            if (mIWifiChip == null) return (null);
2098            try {
2099                mIWifiChip.requestDriverDebugDump((status, blob) -> {
2100                    if (!ok(status)) return;
2101                    ans.value = NativeUtil.byteArrayFromArrayList(blob);
2102                });
2103            } catch (RemoteException e) {
2104                handleRemoteException(e);
2105                return null;
2106            }
2107        }
2108        return ans.value;
2109    }
2110
2111    /**
2112     * Start packet fate monitoring
2113     * <p>
2114     * Once started, monitoring remains active until HAL is unloaded.
2115     *
2116     * @param ifaceName Name of the interface.
2117     * @return true for success
2118     */
2119    public boolean startPktFateMonitoring(@NonNull String ifaceName) {
2120        synchronized (sLock) {
2121            IWifiStaIface iface = getStaIface(ifaceName);
2122            if (iface == null) return boolResult(false);
2123            try {
2124                WifiStatus status = iface.startDebugPacketFateMonitoring();
2125                return ok(status);
2126            } catch (RemoteException e) {
2127                handleRemoteException(e);
2128                return false;
2129            }
2130        }
2131    }
2132
2133    private byte halToFrameworkPktFateFrameType(int type) {
2134        switch (type) {
2135            case WifiDebugPacketFateFrameType.UNKNOWN:
2136                return WifiLoggerHal.FRAME_TYPE_UNKNOWN;
2137            case WifiDebugPacketFateFrameType.ETHERNET_II:
2138                return WifiLoggerHal.FRAME_TYPE_ETHERNET_II;
2139            case WifiDebugPacketFateFrameType.MGMT_80211:
2140                return WifiLoggerHal.FRAME_TYPE_80211_MGMT;
2141            default:
2142                throw new IllegalArgumentException("bad " + type);
2143        }
2144    }
2145
2146    private byte halToFrameworkRxPktFate(int type) {
2147        switch (type) {
2148            case WifiDebugRxPacketFate.SUCCESS:
2149                return WifiLoggerHal.RX_PKT_FATE_SUCCESS;
2150            case WifiDebugRxPacketFate.FW_QUEUED:
2151                return WifiLoggerHal.RX_PKT_FATE_FW_QUEUED;
2152            case WifiDebugRxPacketFate.FW_DROP_FILTER:
2153                return WifiLoggerHal.RX_PKT_FATE_FW_DROP_FILTER;
2154            case WifiDebugRxPacketFate.FW_DROP_INVALID:
2155                return WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID;
2156            case WifiDebugRxPacketFate.FW_DROP_NOBUFS:
2157                return WifiLoggerHal.RX_PKT_FATE_FW_DROP_NOBUFS;
2158            case WifiDebugRxPacketFate.FW_DROP_OTHER:
2159                return WifiLoggerHal.RX_PKT_FATE_FW_DROP_OTHER;
2160            case WifiDebugRxPacketFate.DRV_QUEUED:
2161                return WifiLoggerHal.RX_PKT_FATE_DRV_QUEUED;
2162            case WifiDebugRxPacketFate.DRV_DROP_FILTER:
2163                return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_FILTER;
2164            case WifiDebugRxPacketFate.DRV_DROP_INVALID:
2165                return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_INVALID;
2166            case WifiDebugRxPacketFate.DRV_DROP_NOBUFS:
2167                return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_NOBUFS;
2168            case WifiDebugRxPacketFate.DRV_DROP_OTHER:
2169                return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_OTHER;
2170            default:
2171                throw new IllegalArgumentException("bad " + type);
2172        }
2173    }
2174
2175    private byte halToFrameworkTxPktFate(int type) {
2176        switch (type) {
2177            case WifiDebugTxPacketFate.ACKED:
2178                return WifiLoggerHal.TX_PKT_FATE_ACKED;
2179            case WifiDebugTxPacketFate.SENT:
2180                return WifiLoggerHal.TX_PKT_FATE_SENT;
2181            case WifiDebugTxPacketFate.FW_QUEUED:
2182                return WifiLoggerHal.TX_PKT_FATE_FW_QUEUED;
2183            case WifiDebugTxPacketFate.FW_DROP_INVALID:
2184                return WifiLoggerHal.TX_PKT_FATE_FW_DROP_INVALID;
2185            case WifiDebugTxPacketFate.FW_DROP_NOBUFS:
2186                return WifiLoggerHal.TX_PKT_FATE_FW_DROP_NOBUFS;
2187            case WifiDebugTxPacketFate.FW_DROP_OTHER:
2188                return WifiLoggerHal.TX_PKT_FATE_FW_DROP_OTHER;
2189            case WifiDebugTxPacketFate.DRV_QUEUED:
2190                return WifiLoggerHal.TX_PKT_FATE_DRV_QUEUED;
2191            case WifiDebugTxPacketFate.DRV_DROP_INVALID:
2192                return WifiLoggerHal.TX_PKT_FATE_DRV_DROP_INVALID;
2193            case WifiDebugTxPacketFate.DRV_DROP_NOBUFS:
2194                return WifiLoggerHal.TX_PKT_FATE_DRV_DROP_NOBUFS;
2195            case WifiDebugTxPacketFate.DRV_DROP_OTHER:
2196                return WifiLoggerHal.TX_PKT_FATE_DRV_DROP_OTHER;
2197            default:
2198                throw new IllegalArgumentException("bad " + type);
2199        }
2200    }
2201
2202    /**
2203     * Retrieve fates of outbound packets
2204     * <p>
2205     * Reports the outbound frames for the most recent association (space allowing).
2206     *
2207     * @param ifaceName Name of the interface.
2208     * @param reportBufs
2209     * @return true for success
2210     */
2211    public boolean getTxPktFates(@NonNull String ifaceName, WifiNative.TxFateReport[] reportBufs) {
2212        if (ArrayUtils.isEmpty(reportBufs)) return boolResult(false);
2213        synchronized (sLock) {
2214            IWifiStaIface iface = getStaIface(ifaceName);
2215            if (iface == null) return boolResult(false);
2216            try {
2217                MutableBoolean ok = new MutableBoolean(false);
2218                iface.getDebugTxPacketFates((status, fates) -> {
2219                            if (!ok(status)) return;
2220                            int i = 0;
2221                            for (WifiDebugTxPacketFateReport fate : fates) {
2222                                if (i >= reportBufs.length) break;
2223                                byte code = halToFrameworkTxPktFate(fate.fate);
2224                                long us = fate.frameInfo.driverTimestampUsec;
2225                                byte type =
2226                                        halToFrameworkPktFateFrameType(fate.frameInfo.frameType);
2227                                byte[] frame =
2228                                        NativeUtil.byteArrayFromArrayList(
2229                                                fate.frameInfo.frameContent);
2230                                reportBufs[i++] =
2231                                        new WifiNative.TxFateReport(code, us, type, frame);
2232                            }
2233                            ok.value = true;
2234                        }
2235                );
2236                return ok.value;
2237            } catch (RemoteException e) {
2238                handleRemoteException(e);
2239                return false;
2240            }
2241        }
2242    }
2243
2244    /**
2245     * Retrieve fates of inbound packets
2246     * <p>
2247     * Reports the inbound frames for the most recent association (space allowing).
2248     *
2249     * @param ifaceName Name of the interface.
2250     * @param reportBufs
2251     * @return true for success
2252     */
2253    public boolean getRxPktFates(@NonNull String ifaceName, WifiNative.RxFateReport[] reportBufs) {
2254        if (ArrayUtils.isEmpty(reportBufs)) return boolResult(false);
2255        synchronized (sLock) {
2256            IWifiStaIface iface = getStaIface(ifaceName);
2257            if (iface == null) return boolResult(false);
2258            try {
2259                MutableBoolean ok = new MutableBoolean(false);
2260                iface.getDebugRxPacketFates((status, fates) -> {
2261                            if (!ok(status)) return;
2262                            int i = 0;
2263                            for (WifiDebugRxPacketFateReport fate : fates) {
2264                                if (i >= reportBufs.length) break;
2265                                byte code = halToFrameworkRxPktFate(fate.fate);
2266                                long us = fate.frameInfo.driverTimestampUsec;
2267                                byte type =
2268                                        halToFrameworkPktFateFrameType(fate.frameInfo.frameType);
2269                                byte[] frame =
2270                                        NativeUtil.byteArrayFromArrayList(
2271                                                fate.frameInfo.frameContent);
2272                                reportBufs[i++] =
2273                                        new WifiNative.RxFateReport(code, us, type, frame);
2274                            }
2275                            ok.value = true;
2276                        }
2277                );
2278                return ok.value;
2279            } catch (RemoteException e) {
2280                handleRemoteException(e);
2281                return false;
2282            }
2283        }
2284    }
2285
2286    /**
2287     * Start sending the specified keep alive packets periodically.
2288     *
2289     * @param ifaceName Name of the interface.
2290     * @param slot
2291     * @param srcMac
2292     * @param dstMac
2293     * @param keepAlivePacket
2294     * @param protocol
2295     * @param periodInMs
2296     * @return 0 for success, -1 for error
2297     */
2298    public int startSendingOffloadedPacket(
2299            @NonNull String ifaceName, int slot, byte[] srcMac, byte[] dstMac,
2300            byte[] packet, int protocol, int periodInMs) {
2301        enter("slot=% periodInMs=%").c(slot).c(periodInMs).flush();
2302
2303        ArrayList<Byte> data = NativeUtil.byteArrayToArrayList(packet);
2304
2305        synchronized (sLock) {
2306            IWifiStaIface iface = getStaIface(ifaceName);
2307            if (iface == null) return -1;
2308            try {
2309                WifiStatus status = iface.startSendingKeepAlivePackets(
2310                        slot,
2311                        data,
2312                        (short) protocol,
2313                        srcMac,
2314                        dstMac,
2315                        periodInMs);
2316                if (!ok(status)) return -1;
2317                return 0;
2318            } catch (RemoteException e) {
2319                handleRemoteException(e);
2320                return -1;
2321            }
2322        }
2323    }
2324
2325    /**
2326     * Stop sending the specified keep alive packets.
2327     *
2328     * @param ifaceName Name of the interface.
2329     * @param slot id - same as startSendingOffloadedPacket call.
2330     * @return 0 for success, -1 for error
2331     */
2332    public int stopSendingOffloadedPacket(@NonNull String ifaceName, int slot) {
2333        enter("slot=%").c(slot).flush();
2334
2335        synchronized (sLock) {
2336            IWifiStaIface iface = getStaIface(ifaceName);
2337            if (iface == null) return -1;
2338            try {
2339                WifiStatus status = iface.stopSendingKeepAlivePackets(slot);
2340                if (!ok(status)) return -1;
2341                return 0;
2342            } catch (RemoteException e) {
2343                handleRemoteException(e);
2344                return -1;
2345            }
2346        }
2347    }
2348
2349    /**
2350     * A fixed cmdId for our RssiMonitoring (we only do one at a time)
2351     */
2352    @VisibleForTesting
2353    static final int sRssiMonCmdId = 7551;
2354
2355    /**
2356     * Our client's handler
2357     */
2358    private WifiNative.WifiRssiEventHandler mWifiRssiEventHandler;
2359
2360    /**
2361     * Start RSSI monitoring on the currently connected access point.
2362     *
2363     * @param ifaceName        Name of the interface.
2364     * @param maxRssi          Maximum RSSI threshold.
2365     * @param minRssi          Minimum RSSI threshold.
2366     * @param rssiEventHandler Called when RSSI goes above maxRssi or below minRssi
2367     * @return 0 for success, -1 for failure
2368     */
2369    public int startRssiMonitoring(@NonNull String ifaceName, byte maxRssi, byte minRssi,
2370                                   WifiNative.WifiRssiEventHandler rssiEventHandler) {
2371        enter("maxRssi=% minRssi=%").c(maxRssi).c(minRssi).flush();
2372        if (maxRssi <= minRssi) return -1;
2373        if (rssiEventHandler == null) return -1;
2374        synchronized (sLock) {
2375            IWifiStaIface iface = getStaIface(ifaceName);
2376            if (iface == null) return -1;
2377            try {
2378                iface.stopRssiMonitoring(sRssiMonCmdId);
2379                WifiStatus status;
2380                status = iface.startRssiMonitoring(sRssiMonCmdId, maxRssi, minRssi);
2381                if (!ok(status)) return -1;
2382                mWifiRssiEventHandler = rssiEventHandler;
2383                return 0;
2384            } catch (RemoteException e) {
2385                handleRemoteException(e);
2386                return -1;
2387            }
2388        }
2389    }
2390
2391    /**
2392     * Stop RSSI monitoring
2393     *
2394     * @param ifaceName Name of the interface.
2395     * @return 0 for success, -1 for failure
2396     */
2397    public int stopRssiMonitoring(@NonNull String ifaceName) {
2398        synchronized (sLock) {
2399            mWifiRssiEventHandler = null;
2400            IWifiStaIface iface = getStaIface(ifaceName);
2401            if (iface == null) return -1;
2402            try {
2403                WifiStatus status = iface.stopRssiMonitoring(sRssiMonCmdId);
2404                if (!ok(status)) return -1;
2405                return 0;
2406            } catch (RemoteException e) {
2407                handleRemoteException(e);
2408                return -1;
2409            }
2410        }
2411    }
2412
2413    //TODO - belongs in NativeUtil
2414    private static int[] intsFromArrayList(ArrayList<Integer> a) {
2415        if (a == null) return null;
2416        int[] b = new int[a.size()];
2417        int i = 0;
2418        for (Integer e : a) b[i++] = e;
2419        return b;
2420    }
2421
2422    /**
2423     * Translates from Hal version of wake reason stats to the framework version of same
2424     *
2425     * @param h - Hal version of wake reason stats
2426     * @return framework version of same
2427     */
2428    private static WifiWakeReasonAndCounts halToFrameworkWakeReasons(
2429            WifiDebugHostWakeReasonStats h) {
2430        if (h == null) return null;
2431        WifiWakeReasonAndCounts ans = new WifiWakeReasonAndCounts();
2432        ans.totalCmdEventWake = h.totalCmdEventWakeCnt;
2433        ans.totalDriverFwLocalWake = h.totalDriverFwLocalWakeCnt;
2434        ans.totalRxDataWake = h.totalRxPacketWakeCnt;
2435        ans.rxUnicast = h.rxPktWakeDetails.rxUnicastCnt;
2436        ans.rxMulticast = h.rxPktWakeDetails.rxMulticastCnt;
2437        ans.rxBroadcast = h.rxPktWakeDetails.rxBroadcastCnt;
2438        ans.icmp = h.rxIcmpPkWakeDetails.icmpPkt;
2439        ans.icmp6 = h.rxIcmpPkWakeDetails.icmp6Pkt;
2440        ans.icmp6Ra = h.rxIcmpPkWakeDetails.icmp6Ra;
2441        ans.icmp6Na = h.rxIcmpPkWakeDetails.icmp6Na;
2442        ans.icmp6Ns = h.rxIcmpPkWakeDetails.icmp6Ns;
2443        ans.ipv4RxMulticast = h.rxMulticastPkWakeDetails.ipv4RxMulticastAddrCnt;
2444        ans.ipv6Multicast = h.rxMulticastPkWakeDetails.ipv6RxMulticastAddrCnt;
2445        ans.otherRxMulticast = h.rxMulticastPkWakeDetails.otherRxMulticastAddrCnt;
2446        ans.cmdEventWakeCntArray = intsFromArrayList(h.cmdEventWakeCntPerType);
2447        ans.driverFWLocalWakeCntArray = intsFromArrayList(h.driverFwLocalWakeCntPerType);
2448        return ans;
2449    }
2450
2451    /**
2452     * Fetch the host wakeup reasons stats from wlan driver.
2453     *
2454     * @return the |WifiWakeReasonAndCounts| from the wlan driver, or null on failure.
2455     */
2456    public WifiWakeReasonAndCounts getWlanWakeReasonCount() {
2457        class AnswerBox {
2458            public WifiDebugHostWakeReasonStats value = null;
2459        }
2460        AnswerBox ans = new AnswerBox();
2461        synchronized (sLock) {
2462            if (mIWifiChip == null) return null;
2463            try {
2464                mIWifiChip.getDebugHostWakeReasonStats((status, stats) -> {
2465                    if (ok(status)) {
2466                        ans.value = stats;
2467                    }
2468                });
2469                return halToFrameworkWakeReasons(ans.value);
2470            } catch (RemoteException e) {
2471                handleRemoteException(e);
2472                return null;
2473            }
2474        }
2475    }
2476
2477    /**
2478     * Enable/Disable Neighbour discovery offload functionality in the firmware.
2479     *
2480     * @param ifaceName Name of the interface.
2481     * @param enabled true to enable, false to disable.
2482     * @return true for success, false for failure
2483     */
2484    public boolean configureNeighborDiscoveryOffload(@NonNull String ifaceName, boolean enabled) {
2485        enter("enabled=%").c(enabled).flush();
2486        synchronized (sLock) {
2487            IWifiStaIface iface = getStaIface(ifaceName);
2488            if (iface == null) return boolResult(false);
2489            try {
2490                WifiStatus status = iface.enableNdOffload(enabled);
2491                if (!ok(status)) return false;
2492            } catch (RemoteException e) {
2493                handleRemoteException(e);
2494                return false;
2495            }
2496        }
2497        return true;
2498    }
2499
2500    // Firmware roaming control.
2501
2502    /**
2503     * Query the firmware roaming capabilities.
2504     *
2505     * @param ifaceName Name of the interface.
2506     * @param capabilities object to be filled in
2507     * @return true for success; false for failure
2508     */
2509    public boolean getRoamingCapabilities(@NonNull String ifaceName,
2510                                          WifiNative.RoamingCapabilities capabilities) {
2511        synchronized (sLock) {
2512            IWifiStaIface iface = getStaIface(ifaceName);
2513            if (iface == null) return boolResult(false);
2514            try {
2515                MutableBoolean ok = new MutableBoolean(false);
2516                WifiNative.RoamingCapabilities out = capabilities;
2517                iface.getRoamingCapabilities((status, cap) -> {
2518                    if (!ok(status)) return;
2519                    out.maxBlacklistSize = cap.maxBlacklistSize;
2520                    out.maxWhitelistSize = cap.maxWhitelistSize;
2521                    ok.value = true;
2522                });
2523                return ok.value;
2524            } catch (RemoteException e) {
2525                handleRemoteException(e);
2526                return false;
2527            }
2528        }
2529    }
2530
2531    /**
2532     * Enable/disable firmware roaming.
2533     *
2534     * @param ifaceName Name of the interface.
2535     * @param state the intended roaming state
2536     * @return SUCCESS, FAILURE, or BUSY
2537     */
2538    public int enableFirmwareRoaming(@NonNull String ifaceName, int state) {
2539        synchronized (sLock) {
2540            IWifiStaIface iface = getStaIface(ifaceName);
2541            if (iface == null) return WifiStatusCode.ERROR_NOT_STARTED;
2542            try {
2543                byte val;
2544                switch (state) {
2545                    case WifiNative.DISABLE_FIRMWARE_ROAMING:
2546                        val = StaRoamingState.DISABLED;
2547                        break;
2548                    case WifiNative.ENABLE_FIRMWARE_ROAMING:
2549                        val = StaRoamingState.ENABLED;
2550                        break;
2551                    default:
2552                        mLog.err("enableFirmwareRoaming invalid argument %").c(state).flush();
2553                        return WifiStatusCode.ERROR_INVALID_ARGS;
2554                }
2555
2556                WifiStatus status = iface.setRoamingState(val);
2557                mVerboseLog.d("setRoamingState returned " + status.code);
2558                return status.code;
2559            } catch (RemoteException e) {
2560                handleRemoteException(e);
2561                return WifiStatusCode.ERROR_UNKNOWN;
2562            }
2563        }
2564    }
2565
2566    /**
2567     * Set firmware roaming configurations.
2568     *
2569     * @param ifaceName Name of the interface.
2570     * @param config new roaming configuration object
2571     * @return true for success; false for failure
2572     */
2573    public boolean configureRoaming(@NonNull String ifaceName, WifiNative.RoamingConfig config) {
2574        synchronized (sLock) {
2575            IWifiStaIface iface = getStaIface(ifaceName);
2576            if (iface == null) return boolResult(false);
2577            try {
2578                StaRoamingConfig roamingConfig = new StaRoamingConfig();
2579
2580                // parse the blacklist BSSIDs if any
2581                if (config.blacklistBssids != null) {
2582                    for (String bssid : config.blacklistBssids) {
2583                        byte[] mac = NativeUtil.macAddressToByteArray(bssid);
2584                        roamingConfig.bssidBlacklist.add(mac);
2585                    }
2586                }
2587
2588                // parse the whitelist SSIDs if any
2589                if (config.whitelistSsids != null) {
2590                    for (String ssidStr : config.whitelistSsids) {
2591                        String unquotedSsidStr = WifiInfo.removeDoubleQuotes(ssidStr);
2592
2593                        int len = unquotedSsidStr.length();
2594                        if (len > 32) {
2595                            mLog.err("configureRoaming: skip invalid SSID %")
2596                                    .r(unquotedSsidStr).flush();
2597                            continue;
2598                        }
2599                        byte[] ssid = new byte[len];
2600                        for (int i = 0; i < len; i++) {
2601                            ssid[i] = (byte) unquotedSsidStr.charAt(i);
2602                        }
2603                        roamingConfig.ssidWhitelist.add(ssid);
2604                    }
2605                }
2606
2607                WifiStatus status = iface.configureRoaming(roamingConfig);
2608                if (!ok(status)) return false;
2609            } catch (RemoteException e) {
2610                handleRemoteException(e);
2611                return false;
2612            } catch (IllegalArgumentException e) {
2613                mLog.err("Illegal argument for roaming configuration").c(e.toString()).flush();
2614                return false;
2615            }
2616            return true;
2617        }
2618    }
2619
2620    /**
2621     * Method to mock out the V1_1 IWifiChip retrieval in unit tests.
2622     *
2623     * @return 1.1 IWifiChip object if the device is running the 1.1 wifi hal service, null
2624     * otherwise.
2625     */
2626    protected android.hardware.wifi.V1_1.IWifiChip getWifiChipForV1_1Mockable() {
2627        if (mIWifiChip == null) return null;
2628        return android.hardware.wifi.V1_1.IWifiChip.castFrom(mIWifiChip);
2629    }
2630
2631    /**
2632     * Method to mock out the V1_2 IWifiChip retrieval in unit tests.
2633     *
2634     * @return 1.2 IWifiChip object if the device is running the 1.2 wifi hal service, null
2635     * otherwise.
2636     */
2637    protected android.hardware.wifi.V1_2.IWifiChip getWifiChipForV1_2Mockable() {
2638        if (mIWifiChip == null) return null;
2639        return android.hardware.wifi.V1_2.IWifiChip.castFrom(mIWifiChip);
2640    }
2641
2642    /**
2643     * Method to mock out the V1_2 IWifiStaIface retrieval in unit tests.
2644     *
2645     * @param ifaceName Name of the interface
2646     * @return 1.2 IWifiStaIface object if the device is running the 1.2 wifi hal service, null
2647     * otherwise.
2648     */
2649    protected android.hardware.wifi.V1_2.IWifiStaIface getWifiStaIfaceForV1_2Mockable(
2650            @NonNull String ifaceName) {
2651        IWifiStaIface iface = getStaIface(ifaceName);
2652        if (iface == null) return null;
2653        return android.hardware.wifi.V1_2.IWifiStaIface.castFrom(iface);
2654    }
2655
2656
2657    private int frameworkToHalTxPowerScenario(int scenario) {
2658        switch (scenario) {
2659            case WifiNative.TX_POWER_SCENARIO_VOICE_CALL:
2660                return android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL;
2661            default:
2662                throw new IllegalArgumentException("bad scenario: " + scenario);
2663        }
2664    }
2665
2666    /**
2667     * Select one of the pre-configured TX power level scenarios or reset it back to normal.
2668     * Primarily used for meeting SAR requirements during voice calls.
2669     *
2670     * @param scenario Should be one {@link WifiNative#TX_POWER_SCENARIO_NORMAL} or
2671     *        {@link WifiNative#TX_POWER_SCENARIO_VOICE_CALL}.
2672     * @return true for success; false for failure or if the HAL version does not support this API.
2673     */
2674    public boolean selectTxPowerScenario(int scenario) {
2675        synchronized (sLock) {
2676            try {
2677                android.hardware.wifi.V1_1.IWifiChip iWifiChipV11 = getWifiChipForV1_1Mockable();
2678                if (iWifiChipV11 == null) return boolResult(false);
2679                WifiStatus status;
2680                if (scenario != WifiNative.TX_POWER_SCENARIO_NORMAL) {
2681                    int halScenario;
2682                    try {
2683                        halScenario = frameworkToHalTxPowerScenario(scenario);
2684                    } catch (IllegalArgumentException e) {
2685                        mLog.err("Illegal argument for select tx power scenario")
2686                                .c(e.toString()).flush();
2687                        return false;
2688                    }
2689                    status = iWifiChipV11.selectTxPowerScenario(halScenario);
2690                } else {
2691                    status = iWifiChipV11.resetTxPowerScenario();
2692                }
2693                if (!ok(status)) return false;
2694            } catch (RemoteException e) {
2695                handleRemoteException(e);
2696                return false;
2697            }
2698            return true;
2699        }
2700    }
2701
2702    // This creates a blob of IE elements from the array received.
2703    // TODO: This ugly conversion can be removed if we put IE elements in ScanResult.
2704    private static byte[] hidlIeArrayToFrameworkIeBlob(ArrayList<WifiInformationElement> ies) {
2705        if (ies == null || ies.isEmpty()) return new byte[0];
2706        ArrayList<Byte> ieBlob = new ArrayList<>();
2707        for (WifiInformationElement ie : ies) {
2708            ieBlob.add(ie.id);
2709            ieBlob.addAll(ie.data);
2710        }
2711        return NativeUtil.byteArrayFromArrayList(ieBlob);
2712    }
2713
2714    // This is only filling up the fields of Scan Result used by Gscan clients.
2715    private static ScanResult hidlToFrameworkScanResult(StaScanResult scanResult) {
2716        if (scanResult == null) return null;
2717        ScanResult frameworkScanResult = new ScanResult();
2718        frameworkScanResult.SSID = NativeUtil.encodeSsid(scanResult.ssid);
2719        frameworkScanResult.wifiSsid =
2720                WifiSsid.createFromByteArray(NativeUtil.byteArrayFromArrayList(scanResult.ssid));
2721        frameworkScanResult.BSSID = NativeUtil.macAddressFromByteArray(scanResult.bssid);
2722        frameworkScanResult.level = scanResult.rssi;
2723        frameworkScanResult.frequency = scanResult.frequency;
2724        frameworkScanResult.timestamp = scanResult.timeStampInUs;
2725        return frameworkScanResult;
2726    }
2727
2728    private static ScanResult[] hidlToFrameworkScanResults(ArrayList<StaScanResult> scanResults) {
2729        if (scanResults == null || scanResults.isEmpty()) return new ScanResult[0];
2730        ScanResult[] frameworkScanResults = new ScanResult[scanResults.size()];
2731        int i = 0;
2732        for (StaScanResult scanResult : scanResults) {
2733            frameworkScanResults[i++] = hidlToFrameworkScanResult(scanResult);
2734        }
2735        return frameworkScanResults;
2736    }
2737
2738    /**
2739     * This just returns whether the scan was interrupted or not.
2740     */
2741    private static int hidlToFrameworkScanDataFlags(int flag) {
2742        if (flag == StaScanDataFlagMask.INTERRUPTED) {
2743            return 1;
2744        } else {
2745            return 0;
2746        }
2747    }
2748
2749    private static WifiScanner.ScanData[] hidlToFrameworkScanDatas(
2750            int cmdId, ArrayList<StaScanData> scanDatas) {
2751        if (scanDatas == null || scanDatas.isEmpty()) return new WifiScanner.ScanData[0];
2752        WifiScanner.ScanData[] frameworkScanDatas = new WifiScanner.ScanData[scanDatas.size()];
2753        int i = 0;
2754        for (StaScanData scanData : scanDatas) {
2755            int flags = hidlToFrameworkScanDataFlags(scanData.flags);
2756            ScanResult[] frameworkScanResults = hidlToFrameworkScanResults(scanData.results);
2757            frameworkScanDatas[i++] =
2758                    new WifiScanner.ScanData(cmdId, flags, scanData.bucketsScanned, false,
2759                            frameworkScanResults);
2760        }
2761        return frameworkScanDatas;
2762    }
2763
2764    /**
2765     * Callback for events on the STA interface.
2766     */
2767    private class StaIfaceEventCallback extends IWifiStaIfaceEventCallback.Stub {
2768        @Override
2769        public void onBackgroundScanFailure(int cmdId) {
2770            mVerboseLog.d("onBackgroundScanFailure " + cmdId);
2771            WifiNative.ScanEventHandler eventHandler;
2772            synchronized (sLock) {
2773                if (mScan == null || cmdId != mScan.cmdId) return;
2774                eventHandler = mScan.eventHandler;
2775            }
2776            eventHandler.onScanStatus(WifiNative.WIFI_SCAN_FAILED);
2777        }
2778
2779        @Override
2780        public void onBackgroundFullScanResult(
2781                int cmdId, int bucketsScanned, StaScanResult result) {
2782            mVerboseLog.d("onBackgroundFullScanResult " + cmdId);
2783            WifiNative.ScanEventHandler eventHandler;
2784            synchronized (sLock) {
2785                if (mScan == null || cmdId != mScan.cmdId) return;
2786                eventHandler = mScan.eventHandler;
2787            }
2788            eventHandler.onFullScanResult(hidlToFrameworkScanResult(result), bucketsScanned);
2789        }
2790
2791        @Override
2792        public void onBackgroundScanResults(int cmdId, ArrayList<StaScanData> scanDatas) {
2793            mVerboseLog.d("onBackgroundScanResults " + cmdId);
2794            WifiNative.ScanEventHandler eventHandler;
2795            // WifiScanner currently uses the results callback to fetch the scan results.
2796            // So, simulate that by sending out the notification and then caching the results
2797            // locally. This will then be returned to WifiScanner via getScanResults.
2798            synchronized (sLock) {
2799                if (mScan == null || cmdId != mScan.cmdId) return;
2800                eventHandler = mScan.eventHandler;
2801                mScan.latestScanResults = hidlToFrameworkScanDatas(cmdId, scanDatas);
2802            }
2803            eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
2804        }
2805
2806        @Override
2807        public void onRssiThresholdBreached(int cmdId, byte[/* 6 */] currBssid, int currRssi) {
2808            mVerboseLog.d("onRssiThresholdBreached " + cmdId + "currRssi " + currRssi);
2809            WifiNative.WifiRssiEventHandler eventHandler;
2810            synchronized (sLock) {
2811                if (mWifiRssiEventHandler == null || cmdId != sRssiMonCmdId) return;
2812                eventHandler = mWifiRssiEventHandler;
2813            }
2814            eventHandler.onRssiThresholdBreached((byte) currRssi);
2815        }
2816    }
2817
2818    /**
2819     * Callback for events on the chip.
2820     */
2821    private class ChipEventCallback extends IWifiChipEventCallback.Stub {
2822        @Override
2823        public void onChipReconfigured(int modeId) {
2824            mVerboseLog.d("onChipReconfigured " + modeId);
2825        }
2826
2827        @Override
2828        public void onChipReconfigureFailure(WifiStatus status) {
2829            mVerboseLog.d("onChipReconfigureFailure " + status);
2830        }
2831
2832        public void onIfaceAdded(int type, String name) {
2833            mVerboseLog.d("onIfaceAdded " + type + ", name: " + name);
2834        }
2835
2836        @Override
2837        public void onIfaceRemoved(int type, String name) {
2838            mVerboseLog.d("onIfaceRemoved " + type + ", name: " + name);
2839        }
2840
2841        @Override
2842        public void onDebugRingBufferDataAvailable(
2843                WifiDebugRingBufferStatus status, java.util.ArrayList<Byte> data) {
2844            //TODO(b/35875078) Reinstate logging when execessive callbacks are fixed
2845            // mVerboseLog.d("onDebugRingBufferDataAvailable " + status);
2846            mHalEventHandler.post(() -> {
2847                WifiNative.WifiLoggerEventHandler eventHandler;
2848                synchronized (sLock) {
2849                    if (mLogEventHandler == null || status == null || data == null) return;
2850                    eventHandler = mLogEventHandler;
2851                }
2852                // Because |sLock| has been released, there is a chance that we'll execute
2853                // a spurious callback (after someone has called resetLogHandler()).
2854                //
2855                // However, the alternative risks deadlock. Consider:
2856                // [T1.1] WifiDiagnostics.captureBugReport()
2857                // [T1.2] -- acquire WifiDiagnostics object's intrinsic lock
2858                // [T1.3]    -> WifiVendorHal.getRingBufferData()
2859                // [T1.4]       -- acquire WifiVendorHal.sLock
2860                // [T2.1] <lambda>()
2861                // [T2.2] -- acquire WifiVendorHal.sLock
2862                // [T2.3]    -> WifiDiagnostics.onRingBufferData()
2863                // [T2.4]       -- acquire WifiDiagnostics object's intrinsic lock
2864                //
2865                // The problem here is that the two threads acquire the locks in opposite order.
2866                // If, for example, T2.2 executes between T1.2 and 1.4, then T1 and T2
2867                // will be deadlocked.
2868                int sizeBefore = data.size();
2869                boolean conversionFailure = false;
2870                try {
2871                    eventHandler.onRingBufferData(
2872                            ringBufferStatus(status), NativeUtil.byteArrayFromArrayList(data));
2873                    int sizeAfter = data.size();
2874                    if (sizeAfter != sizeBefore) {
2875                        conversionFailure = true;
2876                    }
2877                } catch (ArrayIndexOutOfBoundsException e) {
2878                    conversionFailure = true;
2879                }
2880                if (conversionFailure) {
2881                    Log.wtf("WifiVendorHal", "Conversion failure detected in "
2882                            + "onDebugRingBufferDataAvailable. "
2883                            + "The input ArrayList |data| is potentially corrupted. "
2884                            + "Starting size=" + sizeBefore + ", "
2885                            + "final size=" + data.size());
2886                }
2887            });
2888        }
2889
2890        @Override
2891        public void onDebugErrorAlert(int errorCode, java.util.ArrayList<Byte> debugData) {
2892            mLog.w("onDebugErrorAlert " + errorCode);
2893            mHalEventHandler.post(() -> {
2894                WifiNative.WifiLoggerEventHandler eventHandler;
2895                synchronized (sLock) {
2896                    if (mLogEventHandler == null || debugData == null) return;
2897                    eventHandler = mLogEventHandler;
2898                }
2899                // See comment in onDebugRingBufferDataAvailable(), for an explanation
2900                // of why this callback is invoked without |sLock| held.
2901                eventHandler.onWifiAlert(
2902                        errorCode, NativeUtil.byteArrayFromArrayList(debugData));
2903            });
2904        }
2905    }
2906
2907    /**
2908     * Callback for events on the 1.2 chip.
2909     */
2910    private class ChipEventCallbackV12 extends
2911            android.hardware.wifi.V1_2.IWifiChipEventCallback.Stub {
2912        @Override
2913        public void onChipReconfigured(int modeId) {
2914            mIWifiChipEventCallback.onChipReconfigured(modeId);
2915        }
2916
2917        @Override
2918        public void onChipReconfigureFailure(WifiStatus status) {
2919            mIWifiChipEventCallback.onChipReconfigureFailure(status);
2920        }
2921
2922        public void onIfaceAdded(int type, String name) {
2923            mIWifiChipEventCallback.onIfaceAdded(type, name);
2924        }
2925
2926        @Override
2927        public void onIfaceRemoved(int type, String name) {
2928            mIWifiChipEventCallback.onIfaceRemoved(type, name);
2929        }
2930
2931        @Override
2932        public void onDebugRingBufferDataAvailable(
2933                WifiDebugRingBufferStatus status, java.util.ArrayList<Byte> data) {
2934            mIWifiChipEventCallback.onDebugRingBufferDataAvailable(status, data);
2935        }
2936
2937        @Override
2938        public void onDebugErrorAlert(int errorCode, java.util.ArrayList<Byte> debugData) {
2939            mIWifiChipEventCallback.onDebugErrorAlert(errorCode, debugData);
2940        }
2941
2942        private boolean areSameIfaceNames(List<IfaceInfo> ifaceList1, List<IfaceInfo> ifaceList2) {
2943            List<String> ifaceNamesList1 = ifaceList1
2944                    .stream()
2945                    .map(i -> i.name)
2946                    .collect(Collectors.toList());
2947            List<String> ifaceNamesList2 = ifaceList2
2948                    .stream()
2949                    .map(i -> i.name)
2950                    .collect(Collectors.toList());
2951            return ifaceNamesList1.containsAll(ifaceNamesList2);
2952        }
2953
2954        private boolean areSameIfaces(List<IfaceInfo> ifaceList1, List<IfaceInfo> ifaceList2) {
2955            return ifaceList1.containsAll(ifaceList2);
2956        }
2957
2958        @Override
2959        public void onRadioModeChange(ArrayList<RadioModeInfo> radioModeInfoList) {
2960            mVerboseLog.d("onRadioModeChange " + radioModeInfoList);
2961            WifiNative.VendorHalRadioModeChangeEventHandler handler;
2962            synchronized (sLock) {
2963                if (mRadioModeChangeEventHandler == null || radioModeInfoList == null) return;
2964                handler = mRadioModeChangeEventHandler;
2965            }
2966            // Should only contain 1 or 2 radio infos.
2967            if (radioModeInfoList.size() == 0 || radioModeInfoList.size() > 2) {
2968                mLog.e("Unexpected number of radio info in list " + radioModeInfoList.size());
2969                return;
2970            }
2971            RadioModeInfo radioModeInfo0 = radioModeInfoList.get(0);
2972            RadioModeInfo radioModeInfo1 =
2973                    radioModeInfoList.size() == 2 ? radioModeInfoList.get(1) : null;
2974            // Number of ifaces on each radio should be equal.
2975            if (radioModeInfo1 != null
2976                    && radioModeInfo0.ifaceInfos.size() != radioModeInfo1.ifaceInfos.size()) {
2977                mLog.e("Unexpected number of iface info in list "
2978                        + radioModeInfo0.ifaceInfos.size() + ", "
2979                        + radioModeInfo1.ifaceInfos.size());
2980                return;
2981            }
2982            int numIfacesOnEachRadio = radioModeInfo0.ifaceInfos.size();
2983            // Only 1 or 2 ifaces should be present on each radio.
2984            if (numIfacesOnEachRadio == 0 || numIfacesOnEachRadio > 2) {
2985                mLog.e("Unexpected number of iface info in list " + numIfacesOnEachRadio);
2986                return;
2987            }
2988            // 2 ifaces simultaneous on 2 radios.
2989            if (radioModeInfoList.size() == 2 && numIfacesOnEachRadio == 1) {
2990                // Iface on radio0 should be different from the iface on radio1 for DBS & SBS.
2991                if (areSameIfaceNames(radioModeInfo0.ifaceInfos, radioModeInfo1.ifaceInfos)) {
2992                    mLog.e("Unexpected for both radio infos to have same iface");
2993                    return;
2994                }
2995                if (radioModeInfo0.bandInfo != radioModeInfo1.bandInfo) {
2996                    handler.onDbs();
2997                } else {
2998                    handler.onSbs(radioModeInfo0.bandInfo);
2999                }
3000            // 2 ifaces time sharing on 1 radio.
3001            } else if (radioModeInfoList.size() == 1 && numIfacesOnEachRadio == 2) {
3002                IfaceInfo ifaceInfo0 = radioModeInfo0.ifaceInfos.get(0);
3003                IfaceInfo ifaceInfo1 = radioModeInfo0.ifaceInfos.get(1);
3004                if (ifaceInfo0.channel != ifaceInfo1.channel) {
3005                    handler.onMcc(radioModeInfo0.bandInfo);
3006                } else {
3007                    handler.onScc(radioModeInfo0.bandInfo);
3008                }
3009            } else {
3010                // Not concurrency scenario, uninteresting...
3011            }
3012        }
3013    }
3014
3015    /**
3016     * Hal Device Manager callbacks.
3017     */
3018    public class HalDeviceManagerStatusListener implements HalDeviceManager.ManagerStatusListener {
3019        @Override
3020        public void onStatusChanged() {
3021            boolean isReady = mHalDeviceManager.isReady();
3022            boolean isStarted = mHalDeviceManager.isStarted();
3023
3024            mVerboseLog.i("Device Manager onStatusChanged. isReady(): " + isReady
3025                    + ", isStarted(): " + isStarted);
3026            if (!isReady) {
3027                // Probably something unpleasant, e.g. the server died
3028                WifiNative.VendorHalDeathEventHandler handler;
3029                synchronized (sLock) {
3030                    clearState();
3031                    handler = mDeathEventHandler;
3032                }
3033                if (handler != null) {
3034                    handler.onDeath();
3035                }
3036            }
3037        }
3038    }
3039}
3040