1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.hardware.wifi.V1_0.IWifi;
22import android.hardware.wifi.V1_0.IWifiApIface;
23import android.hardware.wifi.V1_0.IWifiChip;
24import android.hardware.wifi.V1_0.IWifiChipEventCallback;
25import android.hardware.wifi.V1_0.IWifiEventCallback;
26import android.hardware.wifi.V1_0.IWifiIface;
27import android.hardware.wifi.V1_0.IWifiNanIface;
28import android.hardware.wifi.V1_0.IWifiP2pIface;
29import android.hardware.wifi.V1_0.IWifiRttController;
30import android.hardware.wifi.V1_0.IWifiStaIface;
31import android.hardware.wifi.V1_0.IfaceType;
32import android.hardware.wifi.V1_0.WifiDebugRingBufferStatus;
33import android.hardware.wifi.V1_0.WifiStatus;
34import android.hardware.wifi.V1_0.WifiStatusCode;
35import android.hidl.manager.V1_0.IServiceManager;
36import android.hidl.manager.V1_0.IServiceNotification;
37import android.os.Handler;
38import android.os.HidlSupport.Mutable;
39import android.os.HwRemoteBinder;
40import android.os.RemoteException;
41import android.util.Log;
42import android.util.LongSparseArray;
43import android.util.MutableBoolean;
44import android.util.MutableInt;
45import android.util.Pair;
46import android.util.SparseArray;
47
48import com.android.internal.annotations.VisibleForTesting;
49
50import java.io.FileDescriptor;
51import java.io.PrintWriter;
52import java.util.ArrayList;
53import java.util.Arrays;
54import java.util.HashMap;
55import java.util.HashSet;
56import java.util.Iterator;
57import java.util.List;
58import java.util.Map;
59import java.util.Set;
60
61/**
62 * Handles device management through the HAL (HIDL) interface.
63 */
64public class HalDeviceManager {
65    private static final String TAG = "HalDevMgr";
66    private static final boolean VDBG = false;
67    private boolean mDbg = false;
68
69    private static final int START_HAL_RETRY_INTERVAL_MS = 20;
70    // Number of attempts a start() is re-tried. A value of 0 means no retries after a single
71    // attempt.
72    @VisibleForTesting
73    public static final int START_HAL_RETRY_TIMES = 3;
74    @VisibleForTesting
75    public static final String HAL_INSTANCE_NAME = "default";
76
77    private final Clock mClock;
78
79    // public API
80    public HalDeviceManager(Clock clock) {
81        mClock = clock;
82
83        mInterfaceAvailableForRequestListeners.put(IfaceType.STA, new HashMap<>());
84        mInterfaceAvailableForRequestListeners.put(IfaceType.AP, new HashMap<>());
85        mInterfaceAvailableForRequestListeners.put(IfaceType.P2P, new HashMap<>());
86        mInterfaceAvailableForRequestListeners.put(IfaceType.NAN, new HashMap<>());
87    }
88
89    /* package */ void enableVerboseLogging(int verbose) {
90        if (verbose > 0) {
91            mDbg = true;
92        } else {
93            mDbg = false;
94        }
95        if (VDBG) {
96            mDbg = true; // just override
97        }
98    }
99
100    /**
101     * Actually starts the HalDeviceManager: separate from constructor since may want to phase
102     * at a later time.
103     *
104     * TODO: if decide that no need for separating construction from initialization (e.g. both are
105     * done at injector) then move to constructor.
106     */
107    public void initialize() {
108        initializeInternal();
109    }
110
111    /**
112     * Register a ManagerStatusListener to get information about the status of the manager. Use the
113     * isReady() and isStarted() methods to check status immediately after registration and when
114     * triggered.
115     *
116     * It is safe to re-register the same callback object - duplicates are detected and only a
117     * single copy kept.
118     *
119     * @param listener ManagerStatusListener listener object.
120     * @param handler Handler on which to dispatch listener. Null implies the listener will be
121     *                invoked synchronously from the context of the client which triggered the
122     *                state change.
123     */
124    public void registerStatusListener(@NonNull ManagerStatusListener listener,
125            @Nullable Handler handler) {
126        synchronized (mLock) {
127            if (!mManagerStatusListeners.add(new ManagerStatusListenerProxy(listener, handler))) {
128                Log.w(TAG, "registerStatusListener: duplicate registration ignored");
129            }
130        }
131    }
132
133    /**
134     * Returns whether the vendor HAL is supported on this device or not.
135     */
136    public boolean isSupported() {
137        return isSupportedInternal();
138    }
139
140    /**
141     * Returns the current status of the HalDeviceManager: whether or not it is ready to execute
142     * commands. A return of 'false' indicates that the HAL service (IWifi) is not available. Use
143     * the registerStatusListener() to listener for status changes.
144     */
145    public boolean isReady() {
146        return mWifi != null;
147    }
148
149    /**
150     * Returns the current status of Wi-Fi: started (true) or stopped (false).
151     *
152     * Note: direct call to HIDL.
153     */
154    public boolean isStarted() {
155        return isWifiStarted();
156    }
157
158    /**
159     * Attempts to start Wi-Fi (using HIDL). Returns the success (true) or failure (false) or
160     * the start operation. Will also dispatch any registered ManagerStatusCallback.onStart() on
161     * success.
162     *
163     * Note: direct call to HIDL.
164     */
165    public boolean start() {
166        return startWifi();
167    }
168
169    /**
170     * Stops Wi-Fi. Will also dispatch any registeredManagerStatusCallback.onStop().
171     *
172     * Note: direct call to HIDL - failure is not-expected.
173     */
174    public void stop() {
175        stopWifi();
176    }
177
178    /**
179     * HAL device manager status change listener.
180     */
181    public interface ManagerStatusListener {
182        /**
183         * Indicates that the status of the HalDeviceManager has changed. Use isReady() and
184         * isStarted() to obtain status information.
185         */
186        void onStatusChanged();
187    }
188
189    /**
190     * Return the set of supported interface types across all Wi-Fi chips on the device.
191     *
192     * @return A set of IfaceTypes constants (possibly empty, e.g. on error).
193     */
194    public Set<Integer> getSupportedIfaceTypes() {
195        return getSupportedIfaceTypesInternal(null);
196    }
197
198    /**
199     * Return the set of supported interface types for the specified Wi-Fi chip.
200     *
201     * @return A set of IfaceTypes constants  (possibly empty, e.g. on error).
202     */
203    public Set<Integer> getSupportedIfaceTypes(IWifiChip chip) {
204        return getSupportedIfaceTypesInternal(chip);
205    }
206
207    // interface-specific behavior
208
209    /**
210     * Create a STA interface if possible. Changes chip mode and removes conflicting interfaces if
211     * needed and permitted by priority.
212     *
213     * @param lowPrioritySta Indicates whether the requested STA is a low priority STA. The priority
214     *                       and preemption rules for low priority STA are:
215     *                       - Do not destroy any interface for it (even another low priority STA)
216     *                       - Destroy it for any other request
217     * @param destroyedListener Optional (nullable) listener to call when the allocated interface
218     *                          is removed. Will only be registered and used if an interface is
219     *                          created successfully.
220     * @param handler Handler on which to dispatch listener. Null implies the listener will be
221     *                invoked synchronously from the context of the client which triggered the
222     *                iface destruction.
223     * @return A newly created interface - or null if the interface could not be created.
224     */
225    public IWifiStaIface createStaIface(boolean lowPrioritySta,
226            @Nullable InterfaceDestroyedListener destroyedListener, @Nullable Handler handler) {
227        return (IWifiStaIface) createIface(IfaceType.STA, lowPrioritySta, destroyedListener,
228                handler);
229    }
230
231    /**
232     * Create AP interface if possible (see createStaIface doc).
233     */
234    public IWifiApIface createApIface(@Nullable InterfaceDestroyedListener destroyedListener,
235            @Nullable Handler handler) {
236        return (IWifiApIface) createIface(IfaceType.AP, false, destroyedListener, handler);
237    }
238
239    /**
240     * Create P2P interface if possible (see createStaIface doc).
241     */
242    public IWifiP2pIface createP2pIface(@Nullable InterfaceDestroyedListener destroyedListener,
243            @Nullable Handler handler) {
244        return (IWifiP2pIface) createIface(IfaceType.P2P, false, destroyedListener, handler);
245    }
246
247    /**
248     * Create NAN interface if possible (see createStaIface doc).
249     */
250    public IWifiNanIface createNanIface(@Nullable InterfaceDestroyedListener destroyedListener,
251            @Nullable Handler handler) {
252        return (IWifiNanIface) createIface(IfaceType.NAN, false, destroyedListener, handler);
253    }
254
255    /**
256     * Removes (releases/destroys) the given interface. Will trigger any registered
257     * InterfaceDestroyedListeners and possibly some InterfaceAvailableForRequestListeners if we
258     * can potentially create some other interfaces as a result of removing this interface.
259     */
260    public boolean removeIface(IWifiIface iface) {
261        boolean success = removeIfaceInternal(iface);
262        dispatchAvailableForRequestListeners();
263        return success;
264    }
265
266    /**
267     * Returns the IWifiChip corresponding to the specified interface (or null on error).
268     *
269     * Note: clients must not perform chip mode changes or interface management (create/delete)
270     * operations on IWifiChip directly. However, they can use the IWifiChip interface to perform
271     * other functions - e.g. calling the debug/trace methods.
272     */
273    public IWifiChip getChip(IWifiIface iface) {
274        String name = getName(iface);
275        int type = getType(iface);
276        if (VDBG) Log.d(TAG, "getChip: iface(name)=" + name);
277
278        synchronized (mLock) {
279            InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(Pair.create(name, type));
280            if (cacheEntry == null) {
281                Log.e(TAG, "getChip: no entry for iface(name)=" + name);
282                return null;
283            }
284
285            return cacheEntry.chip;
286        }
287    }
288
289    /**
290     * Register an InterfaceDestroyedListener to the specified iface - returns true on success
291     * and false on failure. This listener is in addition to the one registered when the interface
292     * was created - allowing non-creators to monitor interface status.
293     *
294     * @param destroyedListener Listener to call when the allocated interface is removed.
295     *                          Will only be registered and used if an interface is created
296     *                          successfully.
297     * @param handler Handler on which to dispatch listener. Null implies the listener will be
298     *                invoked synchronously from the context of the client which triggered the
299     *                iface destruction.
300     */
301    public boolean registerDestroyedListener(IWifiIface iface,
302            @NonNull InterfaceDestroyedListener destroyedListener,
303            @Nullable Handler handler) {
304        String name = getName(iface);
305        int type = getType(iface);
306        if (VDBG) Log.d(TAG, "registerDestroyedListener: iface(name)=" + name);
307
308        synchronized (mLock) {
309            InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(Pair.create(name, type));
310            if (cacheEntry == null) {
311                Log.e(TAG, "registerDestroyedListener: no entry for iface(name)=" + name);
312                return false;
313            }
314
315            return cacheEntry.destroyedListeners.add(
316                    new InterfaceDestroyedListenerProxy(name, destroyedListener, handler));
317        }
318    }
319
320    /**
321     * Register a listener to be called when an interface of the specified type could be requested.
322     * No guarantees are provided (some other entity could request it first). The listener is
323     * active from registration until unregistration - using
324     * unregisterInterfaceAvailableForRequestListener().
325     *
326     * Only a single instance of a listener will be registered (even if the specified looper is
327     * different).
328     *
329     * Note that if it is possible to create the specified interface type at registration time
330     * then the callback will be triggered immediately.
331     *
332     * @param ifaceType The interface type (IfaceType) to be monitored.
333     * @param listener Listener to call when an interface of the requested
334     *                 type could be created
335     * @param handler Handler on which to dispatch listener. Null implies the listener will be
336     *                invoked synchronously from the context of the client which triggered the
337     *                mode change.
338     */
339    public void registerInterfaceAvailableForRequestListener(int ifaceType,
340            @NonNull InterfaceAvailableForRequestListener listener, @Nullable Handler handler) {
341        if (VDBG) {
342            Log.d(TAG, "registerInterfaceAvailableForRequestListener: ifaceType=" + ifaceType
343                    + ", listener=" + listener + ", handler=" + handler);
344        }
345
346        synchronized (mLock) {
347            InterfaceAvailableForRequestListenerProxy proxy =
348                    new InterfaceAvailableForRequestListenerProxy(listener, handler);
349            if (mInterfaceAvailableForRequestListeners.get(ifaceType).containsKey(proxy)) {
350                if (VDBG) {
351                    Log.d(TAG,
352                            "registerInterfaceAvailableForRequestListener: dup listener skipped: "
353                                    + listener);
354                }
355                return;
356            }
357            mInterfaceAvailableForRequestListeners.get(ifaceType).put(proxy, null);
358        }
359
360        WifiChipInfo[] chipInfos = getAllChipInfo();
361        if (chipInfos == null) {
362            Log.e(TAG,
363                    "registerInterfaceAvailableForRequestListener: no chip info found - but "
364                            + "possibly registered pre-started - ignoring");
365            return;
366        }
367        dispatchAvailableForRequestListenersForType(ifaceType, chipInfos);
368    }
369
370    /**
371     * Unregisters a listener registered with registerInterfaceAvailableForRequestListener().
372     */
373    public void unregisterInterfaceAvailableForRequestListener(
374            int ifaceType,
375            InterfaceAvailableForRequestListener listener) {
376        if (VDBG) {
377            Log.d(TAG, "unregisterInterfaceAvailableForRequestListener: ifaceType=" + ifaceType);
378        }
379
380        synchronized (mLock) {
381            mInterfaceAvailableForRequestListeners.get(ifaceType).remove(
382                    new InterfaceAvailableForRequestListenerProxy(listener, null));
383        }
384    }
385
386    /**
387     * Return the name of the input interface or null on error.
388     */
389    public static String getName(IWifiIface iface) {
390        if (iface == null) {
391            return "<null>";
392        }
393
394        Mutable<String> nameResp = new Mutable<>();
395        try {
396            iface.getName((WifiStatus status, String name) -> {
397                if (status.code == WifiStatusCode.SUCCESS) {
398                    nameResp.value = name;
399                } else {
400                    Log.e(TAG, "Error on getName: " + statusString(status));
401                }
402            });
403        } catch (RemoteException e) {
404            Log.e(TAG, "Exception on getName: " + e);
405        }
406
407        return nameResp.value;
408    }
409
410    /**
411     * Called when interface is destroyed.
412     */
413    public interface InterfaceDestroyedListener {
414        /**
415         * Called for every interface on which registered when destroyed - whether
416         * destroyed by releaseIface() or through chip mode change or through Wi-Fi
417         * going down.
418         *
419         * Can be registered when the interface is requested with createXxxIface() - will
420         * only be valid if the interface creation was successful - i.e. a non-null was returned.
421         *
422         * @param ifaceName Name of the interface that was destroyed.
423         */
424        void onDestroyed(@NonNull String ifaceName);
425    }
426
427    /**
428     * Called when an interface type availability for creation is changed.
429     */
430    public interface InterfaceAvailableForRequestListener {
431        /**
432         * Called when an interface type availability for creation is updated. Registered with
433         * registerInterfaceAvailableForRequestListener() and unregistered with
434         * unregisterInterfaceAvailableForRequestListener().
435         */
436        void onAvailabilityChanged(boolean isAvailable);
437    }
438
439    /**
440     * Creates a IWifiRttController. A direct match to the IWifiChip.createRttController() method.
441     *
442     * Returns the created IWifiRttController or a null on error.
443     */
444    public IWifiRttController createRttController() {
445        if (VDBG) Log.d(TAG, "createRttController");
446        synchronized (mLock) {
447            if (mWifi == null) {
448                Log.e(TAG, "createRttController: null IWifi");
449                return null;
450            }
451
452            WifiChipInfo[] chipInfos = getAllChipInfo();
453            if (chipInfos == null) {
454                Log.e(TAG, "createRttController: no chip info found");
455                stopWifi(); // major error: shutting down
456                return null;
457            }
458
459            for (WifiChipInfo chipInfo : chipInfos) {
460                Mutable<IWifiRttController> rttResp = new Mutable<>();
461                try {
462                    chipInfo.chip.createRttController(null,
463                            (WifiStatus status, IWifiRttController rtt) -> {
464                                if (status.code == WifiStatusCode.SUCCESS) {
465                                    rttResp.value = rtt;
466                                } else {
467                                    Log.e(TAG,
468                                            "IWifiChip.createRttController failed: " + statusString(
469                                                    status));
470                                }
471                            });
472                } catch (RemoteException e) {
473                    Log.e(TAG, "IWifiChip.createRttController exception: " + e);
474                }
475                if (rttResp.value != null) {
476                    return rttResp.value;
477                }
478            }
479
480            Log.e(TAG, "createRttController: not available from any of the chips");
481            return null;
482        }
483    }
484
485    // internal state
486
487    /* This "PRIORITY" is not for deciding interface elimination (that is controlled by
488     * allowedToDeleteIfaceTypeForRequestedType. This priority is used for:
489     * - Comparing 2 configuration options
490     * - Order of dispatch of available for request listeners
491     */
492    private static final int[] IFACE_TYPES_BY_PRIORITY =
493            {IfaceType.AP, IfaceType.STA, IfaceType.P2P, IfaceType.NAN};
494
495    private final Object mLock = new Object();
496
497    private IServiceManager mServiceManager;
498    private IWifi mWifi;
499    private final WifiEventCallback mWifiEventCallback = new WifiEventCallback();
500    private final Set<ManagerStatusListenerProxy> mManagerStatusListeners = new HashSet<>();
501    private final SparseArray<Map<InterfaceAvailableForRequestListenerProxy, Boolean>>
502            mInterfaceAvailableForRequestListeners = new SparseArray<>();
503    private final SparseArray<IWifiChipEventCallback.Stub> mDebugCallbacks = new SparseArray<>();
504
505    /*
506     * This is the only place where we cache HIDL information in this manager. Necessary since
507     * we need to keep a list of registered destroyed listeners. Will be validated regularly
508     * in getAllChipInfoAndValidateCache().
509     */
510    private final Map<Pair<String, Integer>, InterfaceCacheEntry> mInterfaceInfoCache =
511            new HashMap<>();
512
513    private class InterfaceCacheEntry {
514        public IWifiChip chip;
515        public int chipId;
516        public String name;
517        public int type;
518        public Set<InterfaceDestroyedListenerProxy> destroyedListeners = new HashSet<>();
519        public long creationTime;
520        public boolean isLowPriority;
521
522        @Override
523        public String toString() {
524            StringBuilder sb = new StringBuilder();
525            sb.append("{name=").append(name).append(", type=").append(type)
526                    .append(", destroyedListeners.size()=").append(destroyedListeners.size())
527                    .append(", creationTime=").append(creationTime).append(
528                    ", isLowPriority=").append(isLowPriority).append("}");
529            return sb.toString();
530        }
531    }
532
533    private class WifiIfaceInfo {
534        public String name;
535        public IWifiIface iface;
536    }
537
538    private class WifiChipInfo {
539        public IWifiChip chip;
540        public int chipId;
541        public ArrayList<IWifiChip.ChipMode> availableModes;
542        public boolean currentModeIdValid;
543        public int currentModeId;
544        public WifiIfaceInfo[][] ifaces = new WifiIfaceInfo[IFACE_TYPES_BY_PRIORITY.length][];
545
546        @Override
547        public String toString() {
548            StringBuilder sb = new StringBuilder();
549            sb.append("{chipId=").append(chipId).append(", availableModes=").append(availableModes)
550                    .append(", currentModeIdValid=").append(currentModeIdValid)
551                    .append(", currentModeId=").append(currentModeId);
552            for (int type: IFACE_TYPES_BY_PRIORITY) {
553                sb.append(", ifaces[" + type + "].length=").append(ifaces[type].length);
554            }
555            sb.append(")");
556            return sb.toString();
557        }
558    }
559
560    /**
561     * Wrapper function to access the HIDL services. Created to be mockable in unit-tests.
562     */
563    protected IWifi getWifiServiceMockable() {
564        try {
565            return IWifi.getService();
566        } catch (RemoteException e) {
567            Log.e(TAG, "Exception getting IWifi service: " + e);
568            return null;
569        }
570    }
571
572    protected IServiceManager getServiceManagerMockable() {
573        try {
574            return IServiceManager.getService();
575        } catch (RemoteException e) {
576            Log.e(TAG, "Exception getting IServiceManager: " + e);
577            return null;
578        }
579    }
580
581    // internal implementation
582
583    private void initializeInternal() {
584        initIServiceManagerIfNecessary();
585        if (isSupportedInternal()) {
586            initIWifiIfNecessary();
587        }
588    }
589
590    private void teardownInternal() {
591        managerStatusListenerDispatch();
592        dispatchAllDestroyedListeners();
593        mInterfaceAvailableForRequestListeners.get(IfaceType.STA).clear();
594        mInterfaceAvailableForRequestListeners.get(IfaceType.AP).clear();
595        mInterfaceAvailableForRequestListeners.get(IfaceType.P2P).clear();
596        mInterfaceAvailableForRequestListeners.get(IfaceType.NAN).clear();
597    }
598
599    private final HwRemoteBinder.DeathRecipient mServiceManagerDeathRecipient =
600            cookie -> {
601                Log.wtf(TAG, "IServiceManager died: cookie=" + cookie);
602                synchronized (mLock) {
603                    mServiceManager = null;
604                    // theoretically can call initServiceManager again here - but
605                    // there's no point since most likely system is going to reboot
606                }
607            };
608
609    private final IServiceNotification mServiceNotificationCallback =
610            new IServiceNotification.Stub() {
611                @Override
612                public void onRegistration(String fqName, String name,
613                                           boolean preexisting) {
614                    Log.d(TAG, "IWifi registration notification: fqName=" + fqName
615                            + ", name=" + name + ", preexisting=" + preexisting);
616                    synchronized (mLock) {
617                        initIWifiIfNecessary();
618                    }
619                }
620            };
621
622    /**
623     * Failures of IServiceManager are most likely system breaking in any case. Behavior here
624     * will be to WTF and continue.
625     */
626    private void initIServiceManagerIfNecessary() {
627        if (mDbg) Log.d(TAG, "initIServiceManagerIfNecessary");
628
629        synchronized (mLock) {
630            if (mServiceManager != null) {
631                return;
632            }
633
634            mServiceManager = getServiceManagerMockable();
635            if (mServiceManager == null) {
636                Log.wtf(TAG, "Failed to get IServiceManager instance");
637            } else {
638                try {
639                    if (!mServiceManager.linkToDeath(
640                            mServiceManagerDeathRecipient, /* don't care */ 0)) {
641                        Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
642                        mServiceManager = null;
643                        return;
644                    }
645
646                    if (!mServiceManager.registerForNotifications(IWifi.kInterfaceName, "",
647                            mServiceNotificationCallback)) {
648                        Log.wtf(TAG, "Failed to register a listener for IWifi service");
649                        mServiceManager = null;
650                    }
651                } catch (RemoteException e) {
652                    Log.wtf(TAG, "Exception while operating on IServiceManager: " + e);
653                    mServiceManager = null;
654                }
655            }
656        }
657    }
658
659    /**
660     * Uses the IServiceManager to query if the vendor HAL is present in the VINTF for the device
661     * or not.
662     * @return true if supported, false otherwise.
663     */
664    private boolean isSupportedInternal() {
665        if (VDBG) Log.d(TAG, "isSupportedInternal");
666
667        synchronized (mLock) {
668            if (mServiceManager == null) {
669                Log.e(TAG, "isSupported: called but mServiceManager is null!?");
670                return false;
671            }
672            try {
673                return (mServiceManager.getTransport(IWifi.kInterfaceName, HAL_INSTANCE_NAME)
674                        != IServiceManager.Transport.EMPTY);
675            } catch (RemoteException e) {
676                Log.wtf(TAG, "Exception while operating on IServiceManager: " + e);
677                return false;
678            }
679        }
680    }
681
682    private final HwRemoteBinder.DeathRecipient mIWifiDeathRecipient =
683            cookie -> {
684                Log.e(TAG, "IWifi HAL service died! Have a listener for it ... cookie=" + cookie);
685                synchronized (mLock) { // prevents race condition with surrounding method
686                    mWifi = null;
687                    teardownInternal();
688                    // don't restart: wait for registration notification
689                }
690            };
691
692    /**
693     * Initialize IWifi and register death listener and event callback.
694     *
695     * - It is possible that IWifi is not ready - we have a listener on IServiceManager for it.
696     * - It is not expected that any of the registrations will fail. Possible indication that
697     *   service died after we obtained a handle to it.
698     *
699     * Here and elsewhere we assume that death listener will do the right thing!
700    */
701    private void initIWifiIfNecessary() {
702        if (mDbg) Log.d(TAG, "initIWifiIfNecessary");
703
704        synchronized (mLock) {
705            if (mWifi != null) {
706                return;
707            }
708
709            try {
710                mWifi = getWifiServiceMockable();
711                if (mWifi == null) {
712                    Log.e(TAG, "IWifi not (yet) available - but have a listener for it ...");
713                    return;
714                }
715
716                if (!mWifi.linkToDeath(mIWifiDeathRecipient, /* don't care */ 0)) {
717                    Log.e(TAG, "Error on linkToDeath on IWifi - will retry later");
718                    return;
719                }
720
721                WifiStatus status = mWifi.registerEventCallback(mWifiEventCallback);
722                if (status.code != WifiStatusCode.SUCCESS) {
723                    Log.e(TAG, "IWifi.registerEventCallback failed: " + statusString(status));
724                    mWifi = null;
725                    return;
726                }
727                // Stopping wifi just in case. This would also trigger the status callback.
728                stopWifi();
729            } catch (RemoteException e) {
730                Log.e(TAG, "Exception while operating on IWifi: " + e);
731            }
732        }
733    }
734
735    /**
736     * Registers event listeners on all IWifiChips after a successful start: DEBUG only!
737     *
738     * We don't need the listeners since any callbacks are just confirmation of status codes we
739     * obtain directly from mode changes or interface creation/deletion.
740     *
741     * Relies (to the degree we care) on the service removing all listeners when Wi-Fi is stopped.
742     */
743    private void initIWifiChipDebugListeners() {
744        if (VDBG) Log.d(TAG, "initIWifiChipDebugListeners");
745
746        if (!VDBG) {
747            return;
748        }
749
750        synchronized (mLock) {
751            try {
752                MutableBoolean statusOk = new MutableBoolean(false);
753                Mutable<ArrayList<Integer>> chipIdsResp = new Mutable<>();
754
755                // get all chip IDs
756                mWifi.getChipIds((WifiStatus status, ArrayList<Integer> chipIds) -> {
757                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
758                    if (statusOk.value) {
759                        chipIdsResp.value = chipIds;
760                    } else {
761                        Log.e(TAG, "getChipIds failed: " + statusString(status));
762                    }
763                });
764                if (!statusOk.value) {
765                    return;
766                }
767
768                Log.d(TAG, "getChipIds=" + chipIdsResp.value);
769                if (chipIdsResp.value.size() == 0) {
770                    Log.e(TAG, "Should have at least 1 chip!");
771                    return;
772                }
773
774                // register a callback for each chip
775                Mutable<IWifiChip> chipResp = new Mutable<>();
776                for (Integer chipId: chipIdsResp.value) {
777                    mWifi.getChip(chipId, (WifiStatus status, IWifiChip chip) -> {
778                        statusOk.value = status.code == WifiStatusCode.SUCCESS;
779                        if (statusOk.value) {
780                            chipResp.value = chip;
781                        } else {
782                            Log.e(TAG, "getChip failed: " + statusString(status));
783                        }
784                    });
785                    if (!statusOk.value) {
786                        continue; // still try next one?
787                    }
788
789                    IWifiChipEventCallback.Stub callback =
790                            new IWifiChipEventCallback.Stub() {
791                                @Override
792                                public void onChipReconfigured(int modeId) throws RemoteException {
793                                    Log.d(TAG, "onChipReconfigured: modeId=" + modeId);
794                                }
795
796                                @Override
797                                public void onChipReconfigureFailure(WifiStatus status)
798                                        throws RemoteException {
799                                    Log.d(TAG, "onChipReconfigureFailure: status=" + statusString(
800                                            status));
801                                }
802
803                                @Override
804                                public void onIfaceAdded(int type, String name)
805                                        throws RemoteException {
806                                    Log.d(TAG, "onIfaceAdded: type=" + type + ", name=" + name);
807                                }
808
809                                @Override
810                                public void onIfaceRemoved(int type, String name)
811                                        throws RemoteException {
812                                    Log.d(TAG, "onIfaceRemoved: type=" + type + ", name=" + name);
813                                }
814
815                                @Override
816                                public void onDebugRingBufferDataAvailable(
817                                        WifiDebugRingBufferStatus status,
818                                        ArrayList<Byte> data) throws RemoteException {
819                                    Log.d(TAG, "onDebugRingBufferDataAvailable");
820                                }
821
822                                @Override
823                                public void onDebugErrorAlert(int errorCode,
824                                        ArrayList<Byte> debugData)
825                                        throws RemoteException {
826                                    Log.d(TAG, "onDebugErrorAlert");
827                                }
828                            };
829                    mDebugCallbacks.put(chipId, callback); // store to prevent GC: needed by HIDL
830                    WifiStatus status = chipResp.value.registerEventCallback(callback);
831                    if (status.code != WifiStatusCode.SUCCESS) {
832                        Log.e(TAG, "registerEventCallback failed: " + statusString(status));
833                        continue; // still try next one?
834                    }
835                }
836            } catch (RemoteException e) {
837                Log.e(TAG, "initIWifiChipDebugListeners: exception: " + e);
838                return;
839            }
840        }
841    }
842
843    /**
844     * Get current information about all the chips in the system: modes, current mode (if any), and
845     * any existing interfaces.
846     *
847     * Intended to be called whenever we need to configure the chips - information is NOT cached (to
848     * reduce the likelihood that we get out-of-sync).
849     */
850    private WifiChipInfo[] getAllChipInfo() {
851        if (VDBG) Log.d(TAG, "getAllChipInfo");
852
853        synchronized (mLock) {
854            if (mWifi == null) {
855                Log.e(TAG, "getAllChipInfo: called but mWifi is null!?");
856                return null;
857            }
858
859            try {
860                MutableBoolean statusOk = new MutableBoolean(false);
861                Mutable<ArrayList<Integer>> chipIdsResp = new Mutable<>();
862
863                // get all chip IDs
864                mWifi.getChipIds((WifiStatus status, ArrayList<Integer> chipIds) -> {
865                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
866                    if (statusOk.value) {
867                        chipIdsResp.value = chipIds;
868                    } else {
869                        Log.e(TAG, "getChipIds failed: " + statusString(status));
870                    }
871                });
872                if (!statusOk.value) {
873                    return null;
874                }
875
876                if (VDBG) Log.d(TAG, "getChipIds=" + chipIdsResp.value);
877                if (chipIdsResp.value.size() == 0) {
878                    Log.e(TAG, "Should have at least 1 chip!");
879                    return null;
880                }
881
882                int chipInfoIndex = 0;
883                WifiChipInfo[] chipsInfo = new WifiChipInfo[chipIdsResp.value.size()];
884
885                Mutable<IWifiChip> chipResp = new Mutable<>();
886                for (Integer chipId: chipIdsResp.value) {
887                    mWifi.getChip(chipId, (WifiStatus status, IWifiChip chip) -> {
888                        statusOk.value = status.code == WifiStatusCode.SUCCESS;
889                        if (statusOk.value) {
890                            chipResp.value = chip;
891                        } else {
892                            Log.e(TAG, "getChip failed: " + statusString(status));
893                        }
894                    });
895                    if (!statusOk.value) {
896                        return null;
897                    }
898
899                    Mutable<ArrayList<IWifiChip.ChipMode>> availableModesResp = new Mutable<>();
900                    chipResp.value.getAvailableModes(
901                            (WifiStatus status, ArrayList<IWifiChip.ChipMode> modes) -> {
902                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
903                                if (statusOk.value) {
904                                    availableModesResp.value = modes;
905                                } else {
906                                    Log.e(TAG, "getAvailableModes failed: " + statusString(status));
907                                }
908                            });
909                    if (!statusOk.value) {
910                        return null;
911                    }
912
913                    MutableBoolean currentModeValidResp = new MutableBoolean(false);
914                    MutableInt currentModeResp = new MutableInt(0);
915                    chipResp.value.getMode((WifiStatus status, int modeId) -> {
916                        statusOk.value = status.code == WifiStatusCode.SUCCESS;
917                        if (statusOk.value) {
918                            currentModeValidResp.value = true;
919                            currentModeResp.value = modeId;
920                        } else if (status.code == WifiStatusCode.ERROR_NOT_AVAILABLE) {
921                            statusOk.value = true; // valid response
922                        } else {
923                            Log.e(TAG, "getMode failed: " + statusString(status));
924                        }
925                    });
926                    if (!statusOk.value) {
927                        return null;
928                    }
929
930                    Mutable<ArrayList<String>> ifaceNamesResp = new Mutable<>();
931                    MutableInt ifaceIndex = new MutableInt(0);
932
933                    chipResp.value.getStaIfaceNames(
934                            (WifiStatus status, ArrayList<String> ifnames) -> {
935                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
936                                if (statusOk.value) {
937                                    ifaceNamesResp.value = ifnames;
938                                } else {
939                                    Log.e(TAG, "getStaIfaceNames failed: " + statusString(status));
940                                }
941                            });
942                    if (!statusOk.value) {
943                        return null;
944                    }
945
946                    WifiIfaceInfo[] staIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
947                    for (String ifaceName: ifaceNamesResp.value) {
948                        chipResp.value.getStaIface(ifaceName,
949                                (WifiStatus status, IWifiStaIface iface) -> {
950                                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
951                                    if (statusOk.value) {
952                                        WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
953                                        ifaceInfo.name = ifaceName;
954                                        ifaceInfo.iface = iface;
955                                        staIfaces[ifaceIndex.value++] = ifaceInfo;
956                                    } else {
957                                        Log.e(TAG, "getStaIface failed: " + statusString(status));
958                                    }
959                                });
960                        if (!statusOk.value) {
961                            return null;
962                        }
963                    }
964
965                    ifaceIndex.value = 0;
966                    chipResp.value.getApIfaceNames(
967                            (WifiStatus status, ArrayList<String> ifnames) -> {
968                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
969                                if (statusOk.value) {
970                                    ifaceNamesResp.value = ifnames;
971                                } else {
972                                    Log.e(TAG, "getApIfaceNames failed: " + statusString(status));
973                                }
974                            });
975                    if (!statusOk.value) {
976                        return null;
977                    }
978
979                    WifiIfaceInfo[] apIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
980                    for (String ifaceName: ifaceNamesResp.value) {
981                        chipResp.value.getApIface(ifaceName,
982                                (WifiStatus status, IWifiApIface iface) -> {
983                                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
984                                    if (statusOk.value) {
985                                        WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
986                                        ifaceInfo.name = ifaceName;
987                                        ifaceInfo.iface = iface;
988                                        apIfaces[ifaceIndex.value++] = ifaceInfo;
989                                    } else {
990                                        Log.e(TAG, "getApIface failed: " + statusString(status));
991                                    }
992                                });
993                        if (!statusOk.value) {
994                            return null;
995                        }
996                    }
997
998                    ifaceIndex.value = 0;
999                    chipResp.value.getP2pIfaceNames(
1000                            (WifiStatus status, ArrayList<String> ifnames) -> {
1001                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
1002                                if (statusOk.value) {
1003                                    ifaceNamesResp.value = ifnames;
1004                                } else {
1005                                    Log.e(TAG, "getP2pIfaceNames failed: " + statusString(status));
1006                                }
1007                            });
1008                    if (!statusOk.value) {
1009                        return null;
1010                    }
1011
1012                    WifiIfaceInfo[] p2pIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
1013                    for (String ifaceName: ifaceNamesResp.value) {
1014                        chipResp.value.getP2pIface(ifaceName,
1015                                (WifiStatus status, IWifiP2pIface iface) -> {
1016                                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
1017                                    if (statusOk.value) {
1018                                        WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
1019                                        ifaceInfo.name = ifaceName;
1020                                        ifaceInfo.iface = iface;
1021                                        p2pIfaces[ifaceIndex.value++] = ifaceInfo;
1022                                    } else {
1023                                        Log.e(TAG, "getP2pIface failed: " + statusString(status));
1024                                    }
1025                                });
1026                        if (!statusOk.value) {
1027                            return null;
1028                        }
1029                    }
1030
1031                    ifaceIndex.value = 0;
1032                    chipResp.value.getNanIfaceNames(
1033                            (WifiStatus status, ArrayList<String> ifnames) -> {
1034                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
1035                                if (statusOk.value) {
1036                                    ifaceNamesResp.value = ifnames;
1037                                } else {
1038                                    Log.e(TAG, "getNanIfaceNames failed: " + statusString(status));
1039                                }
1040                            });
1041                    if (!statusOk.value) {
1042                        return null;
1043                    }
1044
1045                    WifiIfaceInfo[] nanIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
1046                    for (String ifaceName: ifaceNamesResp.value) {
1047                        chipResp.value.getNanIface(ifaceName,
1048                                (WifiStatus status, IWifiNanIface iface) -> {
1049                                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
1050                                    if (statusOk.value) {
1051                                        WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
1052                                        ifaceInfo.name = ifaceName;
1053                                        ifaceInfo.iface = iface;
1054                                        nanIfaces[ifaceIndex.value++] = ifaceInfo;
1055                                    } else {
1056                                        Log.e(TAG, "getNanIface failed: " + statusString(status));
1057                                    }
1058                                });
1059                        if (!statusOk.value) {
1060                            return null;
1061                        }
1062                    }
1063
1064                    WifiChipInfo chipInfo = new WifiChipInfo();
1065                    chipsInfo[chipInfoIndex++] = chipInfo;
1066
1067                    chipInfo.chip = chipResp.value;
1068                    chipInfo.chipId = chipId;
1069                    chipInfo.availableModes = availableModesResp.value;
1070                    chipInfo.currentModeIdValid = currentModeValidResp.value;
1071                    chipInfo.currentModeId = currentModeResp.value;
1072                    chipInfo.ifaces[IfaceType.STA] = staIfaces;
1073                    chipInfo.ifaces[IfaceType.AP] = apIfaces;
1074                    chipInfo.ifaces[IfaceType.P2P] = p2pIfaces;
1075                    chipInfo.ifaces[IfaceType.NAN] = nanIfaces;
1076                }
1077
1078                return chipsInfo;
1079            } catch (RemoteException e) {
1080                Log.e(TAG, "getAllChipInfoAndValidateCache exception: " + e);
1081            }
1082        }
1083
1084        return null;
1085    }
1086
1087    /**
1088     * Checks the local state of this object (the cached state) against the input 'chipInfos'
1089     * state (which is a live representation of the Wi-Fi firmware status - read through the HAL).
1090     * Returns 'true' if there are no discrepancies - 'false' otherwise.
1091     *
1092     * A discrepancy is if any local state contains references to a chip or interface which are not
1093     * found on the information read from the chip.
1094     */
1095    private boolean validateInterfaceCache(WifiChipInfo[] chipInfos) {
1096        if (VDBG) Log.d(TAG, "validateInterfaceCache");
1097
1098        synchronized (mLock) {
1099            for (InterfaceCacheEntry entry: mInterfaceInfoCache.values()) {
1100                // search for chip
1101                WifiChipInfo matchingChipInfo = null;
1102                for (WifiChipInfo ci: chipInfos) {
1103                    if (ci.chipId == entry.chipId) {
1104                        matchingChipInfo = ci;
1105                        break;
1106                    }
1107                }
1108                if (matchingChipInfo == null) {
1109                    Log.e(TAG, "validateInterfaceCache: no chip found for " + entry);
1110                    return false;
1111                }
1112
1113                // search for interface
1114                WifiIfaceInfo[] ifaceInfoList = matchingChipInfo.ifaces[entry.type];
1115                if (ifaceInfoList == null) {
1116                    Log.e(TAG, "validateInterfaceCache: invalid type on entry " + entry);
1117                    return false;
1118                }
1119
1120                boolean matchFound = false;
1121                for (WifiIfaceInfo ifaceInfo: ifaceInfoList) {
1122                    if (ifaceInfo.name.equals(entry.name)) {
1123                        matchFound = true;
1124                        break;
1125                    }
1126                }
1127                if (!matchFound) {
1128                    Log.e(TAG, "validateInterfaceCache: no interface found for " + entry);
1129                    return false;
1130                }
1131            }
1132        }
1133
1134        return true;
1135    }
1136
1137    private boolean isWifiStarted() {
1138        if (VDBG) Log.d(TAG, "isWifiStart");
1139
1140        synchronized (mLock) {
1141            try {
1142                if (mWifi == null) {
1143                    Log.w(TAG, "isWifiStarted called but mWifi is null!?");
1144                    return false;
1145                } else {
1146                    return mWifi.isStarted();
1147                }
1148            } catch (RemoteException e) {
1149                Log.e(TAG, "isWifiStarted exception: " + e);
1150                return false;
1151            }
1152        }
1153    }
1154
1155    private boolean startWifi() {
1156        if (VDBG) Log.d(TAG, "startWifi");
1157
1158        synchronized (mLock) {
1159            try {
1160                if (mWifi == null) {
1161                    Log.w(TAG, "startWifi called but mWifi is null!?");
1162                    return false;
1163                } else {
1164                    int triedCount = 0;
1165                    while (triedCount <= START_HAL_RETRY_TIMES) {
1166                        WifiStatus status = mWifi.start();
1167                        if (status.code == WifiStatusCode.SUCCESS) {
1168                            initIWifiChipDebugListeners();
1169                            managerStatusListenerDispatch();
1170                            if (triedCount != 0) {
1171                                Log.d(TAG, "start IWifi succeeded after trying "
1172                                         + triedCount + " times");
1173                            }
1174                            return true;
1175                        } else if (status.code == WifiStatusCode.ERROR_NOT_AVAILABLE) {
1176                            // Should retry. Hal might still be stopping.
1177                            Log.e(TAG, "Cannot start IWifi: " + statusString(status)
1178                                    + ", Retrying...");
1179                            try {
1180                                Thread.sleep(START_HAL_RETRY_INTERVAL_MS);
1181                            } catch (InterruptedException ignore) {
1182                                // no-op
1183                            }
1184                            triedCount++;
1185                        } else {
1186                            // Should not retry on other failures.
1187                            Log.e(TAG, "Cannot start IWifi: " + statusString(status));
1188                            return false;
1189                        }
1190                    }
1191                    Log.e(TAG, "Cannot start IWifi after trying " + triedCount + " times");
1192                    return false;
1193                }
1194            } catch (RemoteException e) {
1195                Log.e(TAG, "startWifi exception: " + e);
1196                return false;
1197            }
1198        }
1199    }
1200
1201    private void stopWifi() {
1202        if (VDBG) Log.d(TAG, "stopWifi");
1203
1204        synchronized (mLock) {
1205            try {
1206                if (mWifi == null) {
1207                    Log.w(TAG, "stopWifi called but mWifi is null!?");
1208                } else {
1209                    WifiStatus status = mWifi.stop();
1210                    if (status.code != WifiStatusCode.SUCCESS) {
1211                        Log.e(TAG, "Cannot stop IWifi: " + statusString(status));
1212                    }
1213
1214                    // even on failure since WTF??
1215                    teardownInternal();
1216                }
1217            } catch (RemoteException e) {
1218                Log.e(TAG, "stopWifi exception: " + e);
1219            }
1220        }
1221    }
1222
1223    private class WifiEventCallback extends IWifiEventCallback.Stub {
1224        @Override
1225        public void onStart() throws RemoteException {
1226            if (VDBG) Log.d(TAG, "IWifiEventCallback.onStart");
1227            // NOP: only happens in reaction to my calls - will handle directly
1228        }
1229
1230        @Override
1231        public void onStop() throws RemoteException {
1232            if (VDBG) Log.d(TAG, "IWifiEventCallback.onStop");
1233            // NOP: only happens in reaction to my calls - will handle directly
1234        }
1235
1236        @Override
1237        public void onFailure(WifiStatus status) throws RemoteException {
1238            Log.e(TAG, "IWifiEventCallback.onFailure: " + statusString(status));
1239            teardownInternal();
1240
1241            // No need to do anything else: listeners may (will) re-start Wi-Fi
1242        }
1243    }
1244
1245    private void managerStatusListenerDispatch() {
1246        synchronized (mLock) {
1247            for (ManagerStatusListenerProxy cb : mManagerStatusListeners) {
1248                cb.trigger();
1249            }
1250        }
1251    }
1252
1253    private class ManagerStatusListenerProxy  extends
1254            ListenerProxy<ManagerStatusListener> {
1255        ManagerStatusListenerProxy(ManagerStatusListener statusListener, Handler handler) {
1256            super(statusListener, handler, "ManagerStatusListenerProxy");
1257        }
1258
1259        @Override
1260        protected void action() {
1261            mListener.onStatusChanged();
1262        }
1263    }
1264
1265    Set<Integer> getSupportedIfaceTypesInternal(IWifiChip chip) {
1266        Set<Integer> results = new HashSet<>();
1267
1268        WifiChipInfo[] chipInfos = getAllChipInfo();
1269        if (chipInfos == null) {
1270            Log.e(TAG, "getSupportedIfaceTypesInternal: no chip info found");
1271            return results;
1272        }
1273
1274        MutableInt chipIdIfProvided = new MutableInt(0); // NOT using 0 as a magic value
1275        if (chip != null) {
1276            MutableBoolean statusOk = new MutableBoolean(false);
1277            try {
1278                chip.getId((WifiStatus status, int id) -> {
1279                    if (status.code == WifiStatusCode.SUCCESS) {
1280                        chipIdIfProvided.value = id;
1281                        statusOk.value = true;
1282                    } else {
1283                        Log.e(TAG, "getSupportedIfaceTypesInternal: IWifiChip.getId() error: "
1284                                + statusString(status));
1285                        statusOk.value = false;
1286                    }
1287                });
1288            } catch (RemoteException e) {
1289                Log.e(TAG, "getSupportedIfaceTypesInternal IWifiChip.getId() exception: " + e);
1290                return results;
1291            }
1292            if (!statusOk.value) {
1293                return results;
1294            }
1295        }
1296
1297        for (WifiChipInfo wci: chipInfos) {
1298            if (chip != null && wci.chipId != chipIdIfProvided.value) {
1299                continue;
1300            }
1301
1302            for (IWifiChip.ChipMode cm: wci.availableModes) {
1303                for (IWifiChip.ChipIfaceCombination cic: cm.availableCombinations) {
1304                    for (IWifiChip.ChipIfaceCombinationLimit cicl: cic.limits) {
1305                        for (int type: cicl.types) {
1306                            results.add(type);
1307                        }
1308                    }
1309                }
1310            }
1311        }
1312
1313        return results;
1314    }
1315
1316    private IWifiIface createIface(int ifaceType, boolean lowPriority,
1317            InterfaceDestroyedListener destroyedListener, Handler handler) {
1318        if (mDbg) {
1319            Log.d(TAG, "createIface: ifaceType=" + ifaceType + ", lowPriority=" + lowPriority);
1320        }
1321
1322        synchronized (mLock) {
1323            WifiChipInfo[] chipInfos = getAllChipInfo();
1324            if (chipInfos == null) {
1325                Log.e(TAG, "createIface: no chip info found");
1326                stopWifi(); // major error: shutting down
1327                return null;
1328            }
1329
1330            if (!validateInterfaceCache(chipInfos)) {
1331                Log.e(TAG, "createIface: local cache is invalid!");
1332                stopWifi(); // major error: shutting down
1333                return null;
1334            }
1335
1336            IWifiIface iface = createIfaceIfPossible(chipInfos, ifaceType, lowPriority,
1337                    destroyedListener, handler);
1338            if (iface != null) { // means that some configuration has changed
1339                if (!dispatchAvailableForRequestListeners()) {
1340                    return null; // catastrophic failure - shut down
1341                }
1342            }
1343
1344            return iface;
1345        }
1346    }
1347
1348    private IWifiIface createIfaceIfPossible(WifiChipInfo[] chipInfos, int ifaceType,
1349            boolean lowPriority, InterfaceDestroyedListener destroyedListener, Handler handler) {
1350        if (VDBG) {
1351            Log.d(TAG, "createIfaceIfPossible: chipInfos=" + Arrays.deepToString(chipInfos)
1352                    + ", ifaceType=" + ifaceType + ", lowPriority=" + lowPriority);
1353        }
1354        synchronized (mLock) {
1355            IfaceCreationData bestIfaceCreationProposal = null;
1356            for (WifiChipInfo chipInfo: chipInfos) {
1357                for (IWifiChip.ChipMode chipMode: chipInfo.availableModes) {
1358                    for (IWifiChip.ChipIfaceCombination chipIfaceCombo : chipMode
1359                            .availableCombinations) {
1360                        int[][] expandedIfaceCombos = expandIfaceCombos(chipIfaceCombo);
1361                        if (VDBG) {
1362                            Log.d(TAG, chipIfaceCombo + " expands to "
1363                                    + Arrays.deepToString(expandedIfaceCombos));
1364                        }
1365
1366                        for (int[] expandedIfaceCombo: expandedIfaceCombos) {
1367                            IfaceCreationData currentProposal = canIfaceComboSupportRequest(
1368                                    chipInfo, chipMode, expandedIfaceCombo, ifaceType, lowPriority);
1369                            if (compareIfaceCreationData(currentProposal,
1370                                    bestIfaceCreationProposal)) {
1371                                if (VDBG) Log.d(TAG, "new proposal accepted");
1372                                bestIfaceCreationProposal = currentProposal;
1373                            }
1374                        }
1375                    }
1376                }
1377            }
1378
1379            if (bestIfaceCreationProposal != null) {
1380                IWifiIface iface = executeChipReconfiguration(bestIfaceCreationProposal, ifaceType);
1381                if (iface != null) {
1382                    InterfaceCacheEntry cacheEntry = new InterfaceCacheEntry();
1383
1384                    cacheEntry.chip = bestIfaceCreationProposal.chipInfo.chip;
1385                    cacheEntry.chipId = bestIfaceCreationProposal.chipInfo.chipId;
1386                    cacheEntry.name = getName(iface);
1387                    cacheEntry.type = ifaceType;
1388                    if (destroyedListener != null) {
1389                        cacheEntry.destroyedListeners.add(
1390                                new InterfaceDestroyedListenerProxy(
1391                                        cacheEntry.name, destroyedListener, handler));
1392                    }
1393                    cacheEntry.creationTime = mClock.getUptimeSinceBootMillis();
1394                    cacheEntry.isLowPriority = lowPriority;
1395
1396                    if (mDbg) Log.d(TAG, "createIfaceIfPossible: added cacheEntry=" + cacheEntry);
1397                    mInterfaceInfoCache.put(
1398                            Pair.create(cacheEntry.name, cacheEntry.type), cacheEntry);
1399                    return iface;
1400                }
1401            }
1402        }
1403
1404        return null;
1405    }
1406
1407    // similar to createIfaceIfPossible - but simpler code: not looking for best option just
1408    // for any option (so terminates on first one).
1409    private boolean isItPossibleToCreateIface(WifiChipInfo[] chipInfos, int ifaceType) {
1410        if (VDBG) {
1411            Log.d(TAG, "isItPossibleToCreateIface: chipInfos=" + Arrays.deepToString(chipInfos)
1412                    + ", ifaceType=" + ifaceType);
1413        }
1414
1415        for (WifiChipInfo chipInfo: chipInfos) {
1416            for (IWifiChip.ChipMode chipMode: chipInfo.availableModes) {
1417                for (IWifiChip.ChipIfaceCombination chipIfaceCombo : chipMode
1418                        .availableCombinations) {
1419                    int[][] expandedIfaceCombos = expandIfaceCombos(chipIfaceCombo);
1420                    if (VDBG) {
1421                        Log.d(TAG, chipIfaceCombo + " expands to "
1422                                + Arrays.deepToString(expandedIfaceCombos));
1423                    }
1424
1425                    for (int[] expandedIfaceCombo: expandedIfaceCombos) {
1426                        if (canIfaceComboSupportRequest(chipInfo, chipMode, expandedIfaceCombo,
1427                                ifaceType, false) != null) {
1428                            return true;
1429                        }
1430                    }
1431                }
1432            }
1433        }
1434
1435        return false;
1436    }
1437
1438    /**
1439     * Expands (or provides an alternative representation) of the ChipIfaceCombination as all
1440     * possible combinations of interface.
1441     *
1442     * Returns [# of combinations][4 (IfaceType)]
1443     *
1444     * Note: there could be duplicates - allow (inefficient but ...).
1445     * TODO: optimize by using a Set as opposed to a []: will remove duplicates. Will need to
1446     * provide correct hashes.
1447     */
1448    private int[][] expandIfaceCombos(IWifiChip.ChipIfaceCombination chipIfaceCombo) {
1449        int numOfCombos = 1;
1450        for (IWifiChip.ChipIfaceCombinationLimit limit: chipIfaceCombo.limits) {
1451            for (int i = 0; i < limit.maxIfaces; ++i) {
1452                numOfCombos *= limit.types.size();
1453            }
1454        }
1455
1456        int[][] expandedIfaceCombos = new int[numOfCombos][IFACE_TYPES_BY_PRIORITY.length];
1457
1458        int span = numOfCombos; // span of an individual type (or sub-tree size)
1459        for (IWifiChip.ChipIfaceCombinationLimit limit: chipIfaceCombo.limits) {
1460            for (int i = 0; i < limit.maxIfaces; ++i) {
1461                span /= limit.types.size();
1462                for (int k = 0; k < numOfCombos; ++k) {
1463                    expandedIfaceCombos[k][limit.types.get((k / span) % limit.types.size())]++;
1464                }
1465            }
1466        }
1467
1468        return expandedIfaceCombos;
1469    }
1470
1471    private class IfaceCreationData {
1472        public WifiChipInfo chipInfo;
1473        public int chipModeId;
1474        public List<WifiIfaceInfo> interfacesToBeRemovedFirst;
1475
1476        @Override
1477        public String toString() {
1478            StringBuilder sb = new StringBuilder();
1479            sb.append("{chipInfo=").append(chipInfo).append(", chipModeId=").append(chipModeId)
1480                    .append(", interfacesToBeRemovedFirst=").append(interfacesToBeRemovedFirst)
1481                    .append(")");
1482            return sb.toString();
1483        }
1484    }
1485
1486    /**
1487     * Checks whether the input chip-iface-combo can support the requested interface type: if not
1488     * then returns null, if yes then returns information containing the list of interfaces which
1489     * would have to be removed first before the requested interface can be created.
1490     *
1491     * Note: the list of interfaces to be removed is EMPTY if a chip mode change is required - in
1492     * that case ALL the interfaces on the current chip have to be removed first.
1493     *
1494     * Response determined based on:
1495     * - Mode configuration: i.e. could the mode support the interface type in principle
1496     * - Priority information: i.e. are we 'allowed' to remove interfaces in order to create the
1497     *   requested interface
1498     */
1499    private IfaceCreationData canIfaceComboSupportRequest(WifiChipInfo chipInfo,
1500            IWifiChip.ChipMode chipMode, int[] chipIfaceCombo, int ifaceType, boolean lowPriority) {
1501        if (VDBG) {
1502            Log.d(TAG, "canIfaceComboSupportRequest: chipInfo=" + chipInfo + ", chipMode="
1503                    + chipMode + ", chipIfaceCombo=" + chipIfaceCombo + ", ifaceType=" + ifaceType
1504                    + ", lowPriority=" + lowPriority);
1505        }
1506
1507        // short-circuit: does the chipIfaceCombo even support the requested type?
1508        if (chipIfaceCombo[ifaceType] == 0) {
1509            if (VDBG) Log.d(TAG, "Requested type not supported by combo");
1510            return null;
1511        }
1512
1513        boolean isChipModeChangeProposed =
1514                chipInfo.currentModeIdValid && chipInfo.currentModeId != chipMode.id;
1515
1516        // short-circuit: can't change chip-mode if an existing interface on this chip has a higher
1517        // priority than the requested interface
1518        if (isChipModeChangeProposed) {
1519            for (int type: IFACE_TYPES_BY_PRIORITY) {
1520                if (chipInfo.ifaces[type].length != 0) {
1521                    if (lowPriority) {
1522                        if (VDBG) {
1523                            Log.d(TAG, "Couldn't delete existing type " + type
1524                                    + " interfaces for a low priority request");
1525                        }
1526                        return null;
1527                    }
1528                    if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType,
1529                            chipInfo.ifaces, chipInfo.ifaces[type].length)) {
1530                        if (VDBG) {
1531                            Log.d(TAG, "Couldn't delete existing type " + type
1532                                    + " interfaces for requested type");
1533                        }
1534                        return null;
1535                    }
1536                }
1537            }
1538
1539            // but if priority allows the mode change then we're good to go
1540            IfaceCreationData ifaceCreationData = new IfaceCreationData();
1541            ifaceCreationData.chipInfo = chipInfo;
1542            ifaceCreationData.chipModeId = chipMode.id;
1543
1544            return ifaceCreationData;
1545        }
1546
1547        // possibly supported
1548        List<WifiIfaceInfo> interfacesToBeRemovedFirst = new ArrayList<>();
1549
1550        for (int type: IFACE_TYPES_BY_PRIORITY) {
1551            int tooManyInterfaces = chipInfo.ifaces[type].length - chipIfaceCombo[type];
1552
1553            // need to count the requested interface as well
1554            if (type == ifaceType) {
1555                tooManyInterfaces += 1;
1556            }
1557
1558            if (tooManyInterfaces > 0) { // may need to delete some
1559                if (lowPriority) {
1560                    if (VDBG) {
1561                        Log.d(TAG, "Couldn't delete existing type " + type
1562                                + " interfaces for a low priority request");
1563                    }
1564                    return null;
1565                }
1566
1567                if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType, chipInfo.ifaces,
1568                        tooManyInterfaces)) {
1569                    if (VDBG) {
1570                        Log.d(TAG, "Would need to delete some higher priority interfaces");
1571                    }
1572                    return null;
1573                }
1574
1575                // delete the most recently created interfaces or LOW priority interfaces
1576                interfacesToBeRemovedFirst = selectInterfacesToDelete(tooManyInterfaces,
1577                        chipInfo.ifaces[type]);
1578            }
1579        }
1580
1581        IfaceCreationData ifaceCreationData = new IfaceCreationData();
1582        ifaceCreationData.chipInfo = chipInfo;
1583        ifaceCreationData.chipModeId = chipMode.id;
1584        ifaceCreationData.interfacesToBeRemovedFirst = interfacesToBeRemovedFirst;
1585
1586        return ifaceCreationData;
1587    }
1588
1589    /**
1590     * Compares two options to create an interface and determines which is the 'best'. Returns
1591     * true if proposal 1 (val1) is better, other false.
1592     *
1593     * Note: both proposals are 'acceptable' bases on priority criteria.
1594     *
1595     * Criteria:
1596     * - Proposal is better if it means removing fewer high priority interfaces
1597     */
1598    private boolean compareIfaceCreationData(IfaceCreationData val1, IfaceCreationData val2) {
1599        if (VDBG) Log.d(TAG, "compareIfaceCreationData: val1=" + val1 + ", val2=" + val2);
1600
1601        // deal with trivial case of one or the other being null
1602        if (val1 == null) {
1603            return false;
1604        } else if (val2 == null) {
1605            return true;
1606        }
1607
1608        for (int type: IFACE_TYPES_BY_PRIORITY) {
1609            // # of interfaces to be deleted: the list or all interfaces of the type if mode change
1610            int numIfacesToDelete1 = 0;
1611            if (val1.chipInfo.currentModeIdValid
1612                    && val1.chipInfo.currentModeId != val1.chipModeId) {
1613                numIfacesToDelete1 = val1.chipInfo.ifaces[type].length;
1614            } else {
1615                numIfacesToDelete1 = val1.interfacesToBeRemovedFirst.size();
1616            }
1617
1618            int numIfacesToDelete2 = 0;
1619            if (val2.chipInfo.currentModeIdValid
1620                    && val2.chipInfo.currentModeId != val2.chipModeId) {
1621                numIfacesToDelete2 = val2.chipInfo.ifaces[type].length;
1622            } else {
1623                numIfacesToDelete2 = val2.interfacesToBeRemovedFirst.size();
1624            }
1625
1626            if (numIfacesToDelete1 < numIfacesToDelete2) {
1627                if (VDBG) {
1628                    Log.d(TAG, "decision based on type=" + type + ": " + numIfacesToDelete1
1629                            + " < " + numIfacesToDelete2);
1630                }
1631                return true;
1632            }
1633        }
1634
1635        // arbitrary - flip a coin
1636        if (VDBG) Log.d(TAG, "proposals identical - flip a coin");
1637        return false;
1638    }
1639
1640    /**
1641     * Returns true if we're allowed to delete the existing interface type for the requested
1642     * interface type.
1643     *
1644     * Rules - applies in order:
1645     *
1646     * General rules:
1647     * 1. No interface will be destroyed for a requested interface of the same type
1648     * 2. No interface will be destroyed if one of the requested interfaces already exists
1649     * 3. If there are >1 interface of an existing type, then it is ok to destroy that type
1650     *    interface
1651     *
1652     * Type-specific rules (but note that the general rules are appied first):
1653     * 4. Request for AP or STA will destroy any other interface
1654     * 5. Request for P2P will destroy NAN-only (but will destroy a second STA per #3)
1655     * 6. Request for NAN will not destroy any interface (but will destroy a second STA per #3)
1656     *
1657     * Note: the 'numNecessaryInterfaces' is used to specify how many interfaces would be needed to
1658     * be deleted. This is used to determine whether there are that many low priority interfaces
1659     * of the requested type to delete.
1660     */
1661    private boolean allowedToDeleteIfaceTypeForRequestedType(int existingIfaceType,
1662            int requestedIfaceType, WifiIfaceInfo[][] currentIfaces, int numNecessaryInterfaces) {
1663        // rule 0: check for any low priority interfaces
1664        int numAvailableLowPriorityInterfaces = 0;
1665        for (InterfaceCacheEntry entry : mInterfaceInfoCache.values()) {
1666            if (entry.type == existingIfaceType && entry.isLowPriority) {
1667                numAvailableLowPriorityInterfaces++;
1668            }
1669        }
1670        if (numAvailableLowPriorityInterfaces >= numNecessaryInterfaces) {
1671            return true;
1672        }
1673
1674        // rule 1
1675        if (existingIfaceType == requestedIfaceType) {
1676            return false;
1677        }
1678
1679        // rule 2
1680        if (currentIfaces[requestedIfaceType].length != 0) {
1681            return false;
1682        }
1683
1684        // rule 3
1685        if (currentIfaces[existingIfaceType].length > 1) {
1686            return true;
1687        }
1688
1689        // rule 6
1690        if (requestedIfaceType == IfaceType.NAN) {
1691            return false;
1692        }
1693
1694        // rule 5
1695        if (requestedIfaceType == IfaceType.P2P) {
1696            return existingIfaceType == IfaceType.NAN;
1697        }
1698
1699        // rule 4, the requestIfaceType is either AP or STA
1700        return true;
1701    }
1702
1703    /**
1704     * Selects the interfaces to delete.
1705     *
1706     * Rule: select low priority interfaces and then other interfaces in order of creation time.
1707     *
1708     * @param excessInterfaces Number of interfaces which need to be selected.
1709     * @param interfaces Array of interfaces.
1710     */
1711    private List<WifiIfaceInfo> selectInterfacesToDelete(int excessInterfaces,
1712            WifiIfaceInfo[] interfaces) {
1713        if (VDBG) {
1714            Log.d(TAG, "selectInterfacesToDelete: excessInterfaces=" + excessInterfaces
1715                    + ", interfaces=" + Arrays.toString(interfaces));
1716        }
1717
1718        boolean lookupError = false;
1719        LongSparseArray<WifiIfaceInfo> orderedListLowPriority = new LongSparseArray<>();
1720        LongSparseArray<WifiIfaceInfo> orderedList = new LongSparseArray<>();
1721        for (WifiIfaceInfo info : interfaces) {
1722            InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(
1723                    Pair.create(info.name, getType(info.iface)));
1724            if (cacheEntry == null) {
1725                Log.e(TAG,
1726                        "selectInterfacesToDelete: can't find cache entry with name=" + info.name);
1727                lookupError = true;
1728                break;
1729            }
1730            if (cacheEntry.isLowPriority) {
1731                orderedListLowPriority.append(cacheEntry.creationTime, info);
1732            } else {
1733                orderedList.append(cacheEntry.creationTime, info);
1734            }
1735        }
1736
1737        if (lookupError) {
1738            Log.e(TAG, "selectInterfacesToDelete: falling back to arbitrary selection");
1739            return Arrays.asList(Arrays.copyOf(interfaces, excessInterfaces));
1740        } else {
1741            List<WifiIfaceInfo> result = new ArrayList<>(excessInterfaces);
1742            for (int i = 0; i < excessInterfaces; ++i) {
1743                int lowPriorityNextIndex = orderedListLowPriority.size() - i - 1;
1744                if (lowPriorityNextIndex >= 0) {
1745                    result.add(orderedListLowPriority.valueAt(lowPriorityNextIndex));
1746                } else {
1747                    result.add(orderedList.valueAt(
1748                            orderedList.size() - i + orderedListLowPriority.size() - 1));
1749                }
1750            }
1751            return result;
1752        }
1753    }
1754
1755    /**
1756     * Performs chip reconfiguration per the input:
1757     * - Removes the specified interfaces
1758     * - Reconfigures the chip to the new chip mode (if necessary)
1759     * - Creates the new interface
1760     *
1761     * Returns the newly created interface or a null on any error.
1762     */
1763    private IWifiIface executeChipReconfiguration(IfaceCreationData ifaceCreationData,
1764            int ifaceType) {
1765        if (mDbg) {
1766            Log.d(TAG, "executeChipReconfiguration: ifaceCreationData=" + ifaceCreationData
1767                    + ", ifaceType=" + ifaceType);
1768        }
1769        synchronized (mLock) {
1770            try {
1771                // is this a mode change?
1772                boolean isModeConfigNeeded = !ifaceCreationData.chipInfo.currentModeIdValid
1773                        || ifaceCreationData.chipInfo.currentModeId != ifaceCreationData.chipModeId;
1774                if (mDbg) Log.d(TAG, "isModeConfigNeeded=" + isModeConfigNeeded);
1775
1776                // first delete interfaces/change modes
1777                if (isModeConfigNeeded) {
1778                    // remove all interfaces pre mode-change
1779                    // TODO: is this necessary? note that even if we don't want to explicitly
1780                    // remove the interfaces we do need to call the onDeleted callbacks - which
1781                    // this does
1782                    for (WifiIfaceInfo[] ifaceInfos: ifaceCreationData.chipInfo.ifaces) {
1783                        for (WifiIfaceInfo ifaceInfo: ifaceInfos) {
1784                            removeIfaceInternal(ifaceInfo.iface); // ignore return value
1785                        }
1786                    }
1787
1788                    WifiStatus status = ifaceCreationData.chipInfo.chip.configureChip(
1789                            ifaceCreationData.chipModeId);
1790                    if (status.code != WifiStatusCode.SUCCESS) {
1791                        Log.e(TAG, "executeChipReconfiguration: configureChip error: "
1792                                + statusString(status));
1793                        return null;
1794                    }
1795                } else {
1796                    // remove all interfaces on the delete list
1797                    for (WifiIfaceInfo ifaceInfo: ifaceCreationData.interfacesToBeRemovedFirst) {
1798                        removeIfaceInternal(ifaceInfo.iface); // ignore return value
1799                    }
1800                }
1801
1802                // create new interface
1803                Mutable<WifiStatus> statusResp = new Mutable<>();
1804                Mutable<IWifiIface> ifaceResp = new Mutable<>();
1805                switch (ifaceType) {
1806                    case IfaceType.STA:
1807                        ifaceCreationData.chipInfo.chip.createStaIface(
1808                                (WifiStatus status, IWifiStaIface iface) -> {
1809                                    statusResp.value = status;
1810                                    ifaceResp.value = iface;
1811                                });
1812                        break;
1813                    case IfaceType.AP:
1814                        ifaceCreationData.chipInfo.chip.createApIface(
1815                                (WifiStatus status, IWifiApIface iface) -> {
1816                                    statusResp.value = status;
1817                                    ifaceResp.value = iface;
1818                                });
1819                        break;
1820                    case IfaceType.P2P:
1821                        ifaceCreationData.chipInfo.chip.createP2pIface(
1822                                (WifiStatus status, IWifiP2pIface iface) -> {
1823                                    statusResp.value = status;
1824                                    ifaceResp.value = iface;
1825                                });
1826                        break;
1827                    case IfaceType.NAN:
1828                        ifaceCreationData.chipInfo.chip.createNanIface(
1829                                (WifiStatus status, IWifiNanIface iface) -> {
1830                                    statusResp.value = status;
1831                                    ifaceResp.value = iface;
1832                                });
1833                        break;
1834                }
1835
1836                if (statusResp.value.code != WifiStatusCode.SUCCESS) {
1837                    Log.e(TAG, "executeChipReconfiguration: failed to create interface ifaceType="
1838                            + ifaceType + ": " + statusString(statusResp.value));
1839                    return null;
1840                }
1841
1842                return ifaceResp.value;
1843            } catch (RemoteException e) {
1844                Log.e(TAG, "executeChipReconfiguration exception: " + e);
1845                return null;
1846            }
1847        }
1848    }
1849
1850    private boolean removeIfaceInternal(IWifiIface iface) {
1851        String name = getName(iface);
1852        int type = getType(iface);
1853        if (mDbg) Log.d(TAG, "removeIfaceInternal: iface(name)=" + name + ", type=" + type);
1854
1855        if (type == -1) {
1856            Log.e(TAG, "removeIfaceInternal: can't get type -- iface(name)=" + name);
1857            return false;
1858        }
1859
1860        synchronized (mLock) {
1861            if (mWifi == null) {
1862                Log.e(TAG, "removeIfaceInternal: null IWifi -- iface(name)=" + name);
1863                return false;
1864            }
1865
1866            IWifiChip chip = getChip(iface);
1867            if (chip == null) {
1868                Log.e(TAG, "removeIfaceInternal: null IWifiChip -- iface(name)=" + name);
1869                return false;
1870            }
1871
1872            if (name == null) {
1873                Log.e(TAG, "removeIfaceInternal: can't get name");
1874                return false;
1875            }
1876
1877            WifiStatus status = null;
1878            try {
1879                switch (type) {
1880                    case IfaceType.STA:
1881                        status = chip.removeStaIface(name);
1882                        break;
1883                    case IfaceType.AP:
1884                        status = chip.removeApIface(name);
1885                        break;
1886                    case IfaceType.P2P:
1887                        status = chip.removeP2pIface(name);
1888                        break;
1889                    case IfaceType.NAN:
1890                        status = chip.removeNanIface(name);
1891                        break;
1892                    default:
1893                        Log.wtf(TAG, "removeIfaceInternal: invalid type=" + type);
1894                        return false;
1895                }
1896            } catch (RemoteException e) {
1897                Log.e(TAG, "IWifiChip.removeXxxIface exception: " + e);
1898            }
1899
1900            // dispatch listeners no matter what status
1901            dispatchDestroyedListeners(name, type);
1902
1903            if (status != null && status.code == WifiStatusCode.SUCCESS) {
1904                return true;
1905            } else {
1906                Log.e(TAG, "IWifiChip.removeXxxIface failed: " + statusString(status));
1907                return false;
1908            }
1909        }
1910    }
1911
1912    // dispatch all available for request listeners of the specified type AND clean-out the list:
1913    // listeners are called once at most!
1914    private boolean dispatchAvailableForRequestListeners() {
1915        if (VDBG) Log.d(TAG, "dispatchAvailableForRequestListeners");
1916
1917        synchronized (mLock) {
1918            WifiChipInfo[] chipInfos = getAllChipInfo();
1919            if (chipInfos == null) {
1920                Log.e(TAG, "dispatchAvailableForRequestListeners: no chip info found");
1921                stopWifi(); // major error: shutting down
1922                return false;
1923            }
1924            if (VDBG) {
1925                Log.d(TAG, "dispatchAvailableForRequestListeners: chipInfos="
1926                        + Arrays.deepToString(chipInfos));
1927            }
1928
1929            for (int ifaceType : IFACE_TYPES_BY_PRIORITY) {
1930                dispatchAvailableForRequestListenersForType(ifaceType, chipInfos);
1931            }
1932        }
1933
1934        return true;
1935    }
1936
1937    private void dispatchAvailableForRequestListenersForType(int ifaceType,
1938            WifiChipInfo[] chipInfos) {
1939        if (VDBG) Log.d(TAG, "dispatchAvailableForRequestListenersForType: ifaceType=" + ifaceType);
1940
1941        synchronized (mLock) {
1942            Map<InterfaceAvailableForRequestListenerProxy, Boolean> listeners =
1943                    mInterfaceAvailableForRequestListeners.get(ifaceType);
1944
1945            if (listeners.size() == 0) {
1946                return;
1947            }
1948
1949            boolean isAvailable = isItPossibleToCreateIface(chipInfos, ifaceType);
1950
1951            if (VDBG) {
1952                Log.d(TAG, "Interface available for: ifaceType=" + ifaceType + " = " + isAvailable);
1953            }
1954            for (Map.Entry<InterfaceAvailableForRequestListenerProxy, Boolean> listenerEntry :
1955                    listeners.entrySet()) {
1956                if (listenerEntry.getValue() == null || listenerEntry.getValue() != isAvailable) {
1957                    if (VDBG) {
1958                        Log.d(TAG, "Interface available listener dispatched: ifaceType=" + ifaceType
1959                                + ", listener=" + listenerEntry.getKey());
1960                    }
1961                    listenerEntry.getKey().triggerWithArg(isAvailable);
1962                }
1963                listenerEntry.setValue(isAvailable);
1964            }
1965        }
1966    }
1967
1968    // dispatch all destroyed listeners registered for the specified interface AND remove the
1969    // cache entry
1970    private void dispatchDestroyedListeners(String name, int type) {
1971        if (VDBG) Log.d(TAG, "dispatchDestroyedListeners: iface(name)=" + name);
1972
1973        synchronized (mLock) {
1974            InterfaceCacheEntry entry = mInterfaceInfoCache.get(Pair.create(name, type));
1975            if (entry == null) {
1976                Log.e(TAG, "dispatchDestroyedListeners: no cache entry for iface(name)=" + name);
1977                return;
1978            }
1979
1980            for (InterfaceDestroyedListenerProxy listener : entry.destroyedListeners) {
1981                listener.trigger();
1982            }
1983            entry.destroyedListeners.clear(); // for insurance (though cache entry is removed)
1984            mInterfaceInfoCache.remove(Pair.create(name, type));
1985        }
1986    }
1987
1988    // dispatch all destroyed listeners registered to all interfaces
1989    private void dispatchAllDestroyedListeners() {
1990        if (VDBG) Log.d(TAG, "dispatchAllDestroyedListeners");
1991
1992        synchronized (mLock) {
1993            Iterator<Map.Entry<Pair<String, Integer>, InterfaceCacheEntry>> it =
1994                    mInterfaceInfoCache.entrySet().iterator();
1995            while (it.hasNext()) {
1996                InterfaceCacheEntry entry = it.next().getValue();
1997                for (InterfaceDestroyedListenerProxy listener : entry.destroyedListeners) {
1998                    listener.trigger();
1999                }
2000                entry.destroyedListeners.clear(); // for insurance (though cache entry is removed)
2001                it.remove();
2002            }
2003        }
2004    }
2005
2006    private abstract class ListenerProxy<LISTENER>  {
2007        protected LISTENER mListener;
2008        private Handler mHandler;
2009
2010        // override equals & hash to make sure that the container HashSet is unique with respect to
2011        // the contained listener
2012        @Override
2013        public boolean equals(Object obj) {
2014            return mListener == ((ListenerProxy<LISTENER>) obj).mListener;
2015        }
2016
2017        @Override
2018        public int hashCode() {
2019            return mListener.hashCode();
2020        }
2021
2022        void trigger() {
2023            if (mHandler != null) {
2024                mHandler.post(() -> {
2025                    action();
2026                });
2027            } else {
2028                action();
2029            }
2030        }
2031
2032        void triggerWithArg(boolean arg) {
2033            if (mHandler != null) {
2034                mHandler.post(() -> {
2035                    actionWithArg(arg);
2036                });
2037            } else {
2038                actionWithArg(arg);
2039            }
2040        }
2041
2042        protected void action() {}
2043        protected void actionWithArg(boolean arg) {}
2044
2045        ListenerProxy(LISTENER listener, Handler handler, String tag) {
2046            mListener = listener;
2047            mHandler = handler;
2048        }
2049    }
2050
2051    private class InterfaceDestroyedListenerProxy extends
2052            ListenerProxy<InterfaceDestroyedListener> {
2053        private final String mIfaceName;
2054        InterfaceDestroyedListenerProxy(@NonNull String ifaceName,
2055                                        InterfaceDestroyedListener destroyedListener,
2056                                        Handler handler) {
2057            super(destroyedListener, handler, "InterfaceDestroyedListenerProxy");
2058            mIfaceName = ifaceName;
2059        }
2060
2061        @Override
2062        protected void action() {
2063            mListener.onDestroyed(mIfaceName);
2064        }
2065    }
2066
2067    private class InterfaceAvailableForRequestListenerProxy extends
2068            ListenerProxy<InterfaceAvailableForRequestListener> {
2069        InterfaceAvailableForRequestListenerProxy(
2070                InterfaceAvailableForRequestListener destroyedListener, Handler handler) {
2071            super(destroyedListener, handler, "InterfaceAvailableForRequestListenerProxy");
2072        }
2073
2074        @Override
2075        protected void actionWithArg(boolean isAvailable) {
2076            mListener.onAvailabilityChanged(isAvailable);
2077        }
2078    }
2079
2080    // general utilities
2081
2082    private static String statusString(WifiStatus status) {
2083        if (status == null) {
2084            return "status=null";
2085        }
2086        StringBuilder sb = new StringBuilder();
2087        sb.append(status.code).append(" (").append(status.description).append(")");
2088        return sb.toString();
2089    }
2090
2091    // Will return -1 for invalid results! Otherwise will return one of the 4 valid values.
2092    private static int getType(IWifiIface iface) {
2093        MutableInt typeResp = new MutableInt(-1);
2094        try {
2095            iface.getType((WifiStatus status, int type) -> {
2096                if (status.code == WifiStatusCode.SUCCESS) {
2097                    typeResp.value = type;
2098                } else {
2099                    Log.e(TAG, "Error on getType: " + statusString(status));
2100                }
2101            });
2102        } catch (RemoteException e) {
2103            Log.e(TAG, "Exception on getType: " + e);
2104        }
2105
2106        return typeResp.value;
2107    }
2108
2109    /**
2110     * Dump the internal state of the class.
2111     */
2112    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2113        pw.println("HalDeviceManager:");
2114        pw.println("  mServiceManager: " + mServiceManager);
2115        pw.println("  mWifi: " + mWifi);
2116        pw.println("  mManagerStatusListeners: " + mManagerStatusListeners);
2117        pw.println("  mInterfaceAvailableForRequestListeners: "
2118                + mInterfaceAvailableForRequestListeners);
2119        pw.println("  mInterfaceInfoCache: " + mInterfaceInfoCache);
2120    }
2121}
2122