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