HalDeviceManager.java revision ba89009ba7554d5073c0b93c04f167c3a11667fa
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                    mWifi = null; // get rid of old copy!
564                    initIWifiIfNecessary();
565                    stopWifi(); // just in case
566                }
567            };
568
569    /**
570     * Failures of IServiceManager are most likely system breaking in any case. Behavior here
571     * will be to WTF and continue.
572     */
573    private void initIServiceManagerIfNecessary() {
574        if (DBG) Log.d(TAG, "initIServiceManagerIfNecessary");
575
576        synchronized (mLock) {
577            if (mServiceManager != null) {
578                return;
579            }
580
581            mServiceManager = getServiceManagerMockable();
582            if (mServiceManager == null) {
583                Log.wtf(TAG, "Failed to get IServiceManager instance");
584            } else {
585                try {
586                    if (!mServiceManager.linkToDeath(
587                            mServiceManagerDeathRecipient, /* don't care */ 0)) {
588                        Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
589                        mServiceManager = null;
590                        return;
591                    }
592
593                    if (!mServiceManager.registerForNotifications(IWifi.kInterfaceName, "",
594                            mServiceNotificationCallback)) {
595                        Log.wtf(TAG, "Failed to register a listener for IWifi service");
596                        mServiceManager = null;
597                    }
598                } catch (RemoteException e) {
599                    Log.wtf(TAG, "Exception while operating on IServiceManager: " + e);
600                    mServiceManager = null;
601                }
602            }
603        }
604    }
605
606    /**
607     * Uses the IServiceManager to query if the vendor HAL is present in the VINTF for the device
608     * or not.
609     * @return true if supported, false otherwise.
610     */
611    private boolean isSupportedInternal() {
612        if (DBG) Log.d(TAG, "isSupportedInternal");
613
614        synchronized (mLock) {
615            if (mServiceManager == null) {
616                Log.e(TAG, "isSupported: called but mServiceManager is null!?");
617                return false;
618            }
619            try {
620                return (mServiceManager.getTransport(IWifi.kInterfaceName, HAL_INSTANCE_NAME)
621                        != IServiceManager.Transport.EMPTY);
622            } catch (RemoteException e) {
623                Log.wtf(TAG, "Exception while operating on IServiceManager: " + e);
624                return false;
625            }
626        }
627    }
628
629    private final HwRemoteBinder.DeathRecipient mIWifiDeathRecipient =
630            cookie -> {
631                Log.e(TAG, "IWifi HAL service died! Have a listener for it ... cookie=" + cookie);
632                synchronized (mLock) { // prevents race condition with surrounding method
633                    mWifi = null;
634                    teardownInternal();
635                    // don't restart: wait for registration notification
636                }
637            };
638
639    /**
640     * Initialize IWifi and register death listener and event callback.
641     *
642     * - It is possible that IWifi is not ready - we have a listener on IServiceManager for it.
643     * - It is not expected that any of the registrations will fail. Possible indication that
644     *   service died after we obtained a handle to it.
645     *
646     * Here and elsewhere we assume that death listener will do the right thing!
647    */
648    private void initIWifiIfNecessary() {
649        if (DBG) Log.d(TAG, "initIWifiIfNecessary");
650
651        synchronized (mLock) {
652            if (mWifi != null) {
653                return;
654            }
655
656            try {
657                mWifi = getWifiServiceMockable();
658                if (mWifi == null) {
659                    Log.e(TAG, "IWifi not (yet) available - but have a listener for it ...");
660                    return;
661                }
662
663                if (!mWifi.linkToDeath(mIWifiDeathRecipient, /* don't care */ 0)) {
664                    Log.e(TAG, "Error on linkToDeath on IWifi - will retry later");
665                    return;
666                }
667
668                WifiStatus status = mWifi.registerEventCallback(mWifiEventCallback);
669                if (status.code != WifiStatusCode.SUCCESS) {
670                    Log.e(TAG, "IWifi.registerEventCallback failed: " + statusString(status));
671                    mWifi = null;
672                    return;
673                }
674                managerStatusListenerDispatch();
675            } catch (RemoteException e) {
676                Log.e(TAG, "Exception while operating on IWifi: " + e);
677            }
678        }
679    }
680
681    /**
682     * Registers event listeners on all IWifiChips after a successful start: DEBUG only!
683     *
684     * We don't need the listeners since any callbacks are just confirmation of status codes we
685     * obtain directly from mode changes or interface creation/deletion.
686     *
687     * Relies (to the degree we care) on the service removing all listeners when Wi-Fi is stopped.
688     */
689    private void initIWifiChipDebugListeners() {
690        if (DBG) Log.d(TAG, "initIWifiChipDebugListeners");
691
692        if (!DBG) {
693            return;
694        }
695
696        synchronized (mLock) {
697            try {
698                MutableBoolean statusOk = new MutableBoolean(false);
699                Mutable<ArrayList<Integer>> chipIdsResp = new Mutable<>();
700
701                // get all chip IDs
702                mWifi.getChipIds((WifiStatus status, ArrayList<Integer> chipIds) -> {
703                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
704                    if (statusOk.value) {
705                        chipIdsResp.value = chipIds;
706                    } else {
707                        Log.e(TAG, "getChipIds failed: " + statusString(status));
708                    }
709                });
710                if (!statusOk.value) {
711                    return;
712                }
713
714                if (DBG) Log.d(TAG, "getChipIds=" + chipIdsResp.value);
715                if (chipIdsResp.value.size() == 0) {
716                    Log.e(TAG, "Should have at least 1 chip!");
717                    return;
718                }
719
720                // register a callback for each chip
721                Mutable<IWifiChip> chipResp = new Mutable<>();
722                for (Integer chipId: chipIdsResp.value) {
723                    mWifi.getChip(chipId, (WifiStatus status, IWifiChip chip) -> {
724                        statusOk.value = status.code == WifiStatusCode.SUCCESS;
725                        if (statusOk.value) {
726                            chipResp.value = chip;
727                        } else {
728                            Log.e(TAG, "getChip failed: " + statusString(status));
729                        }
730                    });
731                    if (!statusOk.value) {
732                        continue; // still try next one?
733                    }
734
735                    IWifiChipEventCallback.Stub callback =
736                            new IWifiChipEventCallback.Stub() {
737                                @Override
738                                public void onChipReconfigured(int modeId) throws RemoteException {
739                                    Log.d(TAG, "onChipReconfigured: modeId=" + modeId);
740                                }
741
742                                @Override
743                                public void onChipReconfigureFailure(WifiStatus status)
744                                        throws RemoteException {
745                                    Log.d(TAG, "onChipReconfigureFailure: status=" + statusString(
746                                            status));
747                                }
748
749                                @Override
750                                public void onIfaceAdded(int type, String name)
751                                        throws RemoteException {
752                                    Log.d(TAG, "onIfaceAdded: type=" + type + ", name=" + name);
753                                }
754
755                                @Override
756                                public void onIfaceRemoved(int type, String name)
757                                        throws RemoteException {
758                                    Log.d(TAG, "onIfaceRemoved: type=" + type + ", name=" + name);
759                                }
760
761                                @Override
762                                public void onDebugRingBufferDataAvailable(
763                                        WifiDebugRingBufferStatus status,
764                                        ArrayList<Byte> data) throws RemoteException {
765                                    Log.d(TAG, "onDebugRingBufferDataAvailable");
766                                }
767
768                                @Override
769                                public void onDebugErrorAlert(int errorCode,
770                                        ArrayList<Byte> debugData)
771                                        throws RemoteException {
772                                    Log.d(TAG, "onDebugErrorAlert");
773                                }
774                            };
775                    mDebugCallbacks.put(chipId, callback); // store to prevent GC: needed by HIDL
776                    WifiStatus status = chipResp.value.registerEventCallback(callback);
777                    if (status.code != WifiStatusCode.SUCCESS) {
778                        Log.e(TAG, "registerEventCallback failed: " + statusString(status));
779                        continue; // still try next one?
780                    }
781                }
782            } catch (RemoteException e) {
783                Log.e(TAG, "initIWifiChipDebugListeners: exception: " + e);
784                return;
785            }
786        }
787    }
788
789    /**
790     * Get current information about all the chips in the system: modes, current mode (if any), and
791     * any existing interfaces.
792     *
793     * Intended to be called whenever we need to configure the chips - information is NOT cached (to
794     * reduce the likelihood that we get out-of-sync).
795     */
796    private WifiChipInfo[] getAllChipInfo() {
797        if (DBG) Log.d(TAG, "getAllChipInfo");
798
799        synchronized (mLock) {
800            if (mWifi == null) {
801                Log.e(TAG, "getAllChipInfo: called but mWifi is null!?");
802                return null;
803            }
804
805            try {
806                MutableBoolean statusOk = new MutableBoolean(false);
807                Mutable<ArrayList<Integer>> chipIdsResp = new Mutable<>();
808
809                // get all chip IDs
810                mWifi.getChipIds((WifiStatus status, ArrayList<Integer> chipIds) -> {
811                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
812                    if (statusOk.value) {
813                        chipIdsResp.value = chipIds;
814                    } else {
815                        Log.e(TAG, "getChipIds failed: " + statusString(status));
816                    }
817                });
818                if (!statusOk.value) {
819                    return null;
820                }
821
822                if (DBG) Log.d(TAG, "getChipIds=" + chipIdsResp.value);
823                if (chipIdsResp.value.size() == 0) {
824                    Log.e(TAG, "Should have at least 1 chip!");
825                    return null;
826                }
827
828                int chipInfoIndex = 0;
829                WifiChipInfo[] chipsInfo = new WifiChipInfo[chipIdsResp.value.size()];
830
831                Mutable<IWifiChip> chipResp = new Mutable<>();
832                for (Integer chipId: chipIdsResp.value) {
833                    mWifi.getChip(chipId, (WifiStatus status, IWifiChip chip) -> {
834                        statusOk.value = status.code == WifiStatusCode.SUCCESS;
835                        if (statusOk.value) {
836                            chipResp.value = chip;
837                        } else {
838                            Log.e(TAG, "getChip failed: " + statusString(status));
839                        }
840                    });
841                    if (!statusOk.value) {
842                        return null;
843                    }
844
845                    Mutable<ArrayList<IWifiChip.ChipMode>> availableModesResp = new Mutable<>();
846                    chipResp.value.getAvailableModes(
847                            (WifiStatus status, ArrayList<IWifiChip.ChipMode> modes) -> {
848                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
849                                if (statusOk.value) {
850                                    availableModesResp.value = modes;
851                                } else {
852                                    Log.e(TAG, "getAvailableModes failed: " + statusString(status));
853                                }
854                            });
855                    if (!statusOk.value) {
856                        return null;
857                    }
858
859                    MutableBoolean currentModeValidResp = new MutableBoolean(false);
860                    MutableInt currentModeResp = new MutableInt(0);
861                    chipResp.value.getMode((WifiStatus status, int modeId) -> {
862                        statusOk.value = status.code == WifiStatusCode.SUCCESS;
863                        if (statusOk.value) {
864                            currentModeValidResp.value = true;
865                            currentModeResp.value = modeId;
866                        } else if (status.code == WifiStatusCode.ERROR_NOT_AVAILABLE) {
867                            statusOk.value = true; // valid response
868                        } else {
869                            Log.e(TAG, "getMode failed: " + statusString(status));
870                        }
871                    });
872                    if (!statusOk.value) {
873                        return null;
874                    }
875
876                    Mutable<ArrayList<String>> ifaceNamesResp = new Mutable<>();
877                    MutableInt ifaceIndex = new MutableInt(0);
878
879                    chipResp.value.getStaIfaceNames(
880                            (WifiStatus status, ArrayList<String> ifnames) -> {
881                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
882                                if (statusOk.value) {
883                                    ifaceNamesResp.value = ifnames;
884                                } else {
885                                    Log.e(TAG, "getStaIfaceNames failed: " + statusString(status));
886                                }
887                            });
888                    if (!statusOk.value) {
889                        return null;
890                    }
891
892                    WifiIfaceInfo[] staIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
893                    for (String ifaceName: ifaceNamesResp.value) {
894                        chipResp.value.getStaIface(ifaceName,
895                                (WifiStatus status, IWifiStaIface iface) -> {
896                                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
897                                    if (statusOk.value) {
898                                        WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
899                                        ifaceInfo.name = ifaceName;
900                                        ifaceInfo.iface = iface;
901                                        staIfaces[ifaceIndex.value++] = ifaceInfo;
902                                    } else {
903                                        Log.e(TAG, "getStaIface failed: " + statusString(status));
904                                    }
905                                });
906                        if (!statusOk.value) {
907                            return null;
908                        }
909                    }
910
911                    ifaceIndex.value = 0;
912                    chipResp.value.getApIfaceNames(
913                            (WifiStatus status, ArrayList<String> ifnames) -> {
914                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
915                                if (statusOk.value) {
916                                    ifaceNamesResp.value = ifnames;
917                                } else {
918                                    Log.e(TAG, "getApIfaceNames failed: " + statusString(status));
919                                }
920                            });
921                    if (!statusOk.value) {
922                        return null;
923                    }
924
925                    WifiIfaceInfo[] apIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
926                    for (String ifaceName: ifaceNamesResp.value) {
927                        chipResp.value.getApIface(ifaceName,
928                                (WifiStatus status, IWifiApIface iface) -> {
929                                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
930                                    if (statusOk.value) {
931                                        WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
932                                        ifaceInfo.name = ifaceName;
933                                        ifaceInfo.iface = iface;
934                                        apIfaces[ifaceIndex.value++] = ifaceInfo;
935                                    } else {
936                                        Log.e(TAG, "getApIface failed: " + statusString(status));
937                                    }
938                                });
939                        if (!statusOk.value) {
940                            return null;
941                        }
942                    }
943
944                    ifaceIndex.value = 0;
945                    chipResp.value.getP2pIfaceNames(
946                            (WifiStatus status, ArrayList<String> ifnames) -> {
947                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
948                                if (statusOk.value) {
949                                    ifaceNamesResp.value = ifnames;
950                                } else {
951                                    Log.e(TAG, "getP2pIfaceNames failed: " + statusString(status));
952                                }
953                            });
954                    if (!statusOk.value) {
955                        return null;
956                    }
957
958                    WifiIfaceInfo[] p2pIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
959                    for (String ifaceName: ifaceNamesResp.value) {
960                        chipResp.value.getP2pIface(ifaceName,
961                                (WifiStatus status, IWifiP2pIface iface) -> {
962                                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
963                                    if (statusOk.value) {
964                                        WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
965                                        ifaceInfo.name = ifaceName;
966                                        ifaceInfo.iface = iface;
967                                        p2pIfaces[ifaceIndex.value++] = ifaceInfo;
968                                    } else {
969                                        Log.e(TAG, "getP2pIface failed: " + statusString(status));
970                                    }
971                                });
972                        if (!statusOk.value) {
973                            return null;
974                        }
975                    }
976
977                    ifaceIndex.value = 0;
978                    chipResp.value.getNanIfaceNames(
979                            (WifiStatus status, ArrayList<String> ifnames) -> {
980                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
981                                if (statusOk.value) {
982                                    ifaceNamesResp.value = ifnames;
983                                } else {
984                                    Log.e(TAG, "getNanIfaceNames failed: " + statusString(status));
985                                }
986                            });
987                    if (!statusOk.value) {
988                        return null;
989                    }
990
991                    WifiIfaceInfo[] nanIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
992                    for (String ifaceName: ifaceNamesResp.value) {
993                        chipResp.value.getNanIface(ifaceName,
994                                (WifiStatus status, IWifiNanIface iface) -> {
995                                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
996                                    if (statusOk.value) {
997                                        WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
998                                        ifaceInfo.name = ifaceName;
999                                        ifaceInfo.iface = iface;
1000                                        nanIfaces[ifaceIndex.value++] = ifaceInfo;
1001                                    } else {
1002                                        Log.e(TAG, "getNanIface failed: " + statusString(status));
1003                                    }
1004                                });
1005                        if (!statusOk.value) {
1006                            return null;
1007                        }
1008                    }
1009
1010                    WifiChipInfo chipInfo = new WifiChipInfo();
1011                    chipsInfo[chipInfoIndex++] = chipInfo;
1012
1013                    chipInfo.chip = chipResp.value;
1014                    chipInfo.chipId = chipId;
1015                    chipInfo.availableModes = availableModesResp.value;
1016                    chipInfo.currentModeIdValid = currentModeValidResp.value;
1017                    chipInfo.currentModeId = currentModeResp.value;
1018                    chipInfo.ifaces[IfaceType.STA] = staIfaces;
1019                    chipInfo.ifaces[IfaceType.AP] = apIfaces;
1020                    chipInfo.ifaces[IfaceType.P2P] = p2pIfaces;
1021                    chipInfo.ifaces[IfaceType.NAN] = nanIfaces;
1022                }
1023
1024                return chipsInfo;
1025            } catch (RemoteException e) {
1026                Log.e(TAG, "getAllChipInfoAndValidateCache exception: " + e);
1027            }
1028        }
1029
1030        return null;
1031    }
1032
1033    /**
1034     * Checks the local state of this object (the cached state) against the input 'chipInfos'
1035     * state (which is a live representation of the Wi-Fi firmware status - read through the HAL).
1036     * Returns 'true' if there are no discrepancies - 'false' otherwise.
1037     *
1038     * A discrepancy is if any local state contains references to a chip or interface which are not
1039     * found on the information read from the chip.
1040     */
1041    private boolean validateInterfaceCache(WifiChipInfo[] chipInfos) {
1042        if (DBG) Log.d(TAG, "validateInterfaceCache");
1043
1044        synchronized (mLock) {
1045            for (InterfaceCacheEntry entry: mInterfaceInfoCache.values()) {
1046                // search for chip
1047                WifiChipInfo matchingChipInfo = null;
1048                for (WifiChipInfo ci: chipInfos) {
1049                    if (ci.chipId == entry.chipId) {
1050                        matchingChipInfo = ci;
1051                        break;
1052                    }
1053                }
1054                if (matchingChipInfo == null) {
1055                    Log.e(TAG, "validateInterfaceCache: no chip found for " + entry);
1056                    return false;
1057                }
1058
1059                // search for interface
1060                WifiIfaceInfo[] ifaceInfoList = matchingChipInfo.ifaces[entry.type];
1061                if (ifaceInfoList == null) {
1062                    Log.e(TAG, "validateInterfaceCache: invalid type on entry " + entry);
1063                    return false;
1064                }
1065
1066                boolean matchFound = false;
1067                for (WifiIfaceInfo ifaceInfo: ifaceInfoList) {
1068                    if (ifaceInfo.name.equals(entry.name)) {
1069                        matchFound = true;
1070                        break;
1071                    }
1072                }
1073                if (!matchFound) {
1074                    Log.e(TAG, "validateInterfaceCache: no interface found for " + entry);
1075                    return false;
1076                }
1077            }
1078        }
1079
1080        return true;
1081    }
1082
1083    private boolean isWifiStarted() {
1084        if (DBG) Log.d(TAG, "isWifiStart");
1085
1086        synchronized (mLock) {
1087            try {
1088                if (mWifi == null) {
1089                    Log.w(TAG, "isWifiStarted called but mWifi is null!?");
1090                    return false;
1091                } else {
1092                    return mWifi.isStarted();
1093                }
1094            } catch (RemoteException e) {
1095                Log.e(TAG, "isWifiStarted exception: " + e);
1096                return false;
1097            }
1098        }
1099    }
1100
1101    private boolean startWifi() {
1102        if (DBG) Log.d(TAG, "startWifi");
1103
1104        synchronized (mLock) {
1105            try {
1106                if (mWifi == null) {
1107                    Log.w(TAG, "startWifi called but mWifi is null!?");
1108                    return false;
1109                } else {
1110                    int triedCount = 0;
1111                    while (triedCount <= START_HAL_RETRY_TIMES) {
1112                        WifiStatus status = mWifi.start();
1113                        if (status.code == WifiStatusCode.SUCCESS) {
1114                            initIWifiChipDebugListeners();
1115                            managerStatusListenerDispatch();
1116                            if (triedCount != 0) {
1117                                Log.d(TAG, "start IWifi succeeded after trying "
1118                                         + triedCount + " times");
1119                            }
1120                            return true;
1121                        } else if (status.code == WifiStatusCode.ERROR_NOT_AVAILABLE) {
1122                            // Should retry. Hal might still be stopping.
1123                            Log.e(TAG, "Cannot start IWifi: " + statusString(status)
1124                                    + ", Retrying...");
1125                            try {
1126                                Thread.sleep(START_HAL_RETRY_INTERVAL_MS);
1127                            } catch (InterruptedException ignore) {
1128                                // no-op
1129                            }
1130                            triedCount++;
1131                        } else {
1132                            // Should not retry on other failures.
1133                            Log.e(TAG, "Cannot start IWifi: " + statusString(status));
1134                            return false;
1135                        }
1136                    }
1137                    Log.e(TAG, "Cannot start IWifi after trying " + triedCount + " times");
1138                    return false;
1139                }
1140            } catch (RemoteException e) {
1141                Log.e(TAG, "startWifi exception: " + e);
1142                return false;
1143            }
1144        }
1145    }
1146
1147    private void stopWifi() {
1148        if (DBG) Log.d(TAG, "stopWifi");
1149
1150        synchronized (mLock) {
1151            try {
1152                if (mWifi == null) {
1153                    Log.w(TAG, "stopWifi called but mWifi is null!?");
1154                } else {
1155                    WifiStatus status = mWifi.stop();
1156                    if (status.code != WifiStatusCode.SUCCESS) {
1157                        Log.e(TAG, "Cannot stop IWifi: " + statusString(status));
1158                    }
1159
1160                    // even on failure since WTF??
1161                    teardownInternal();
1162                }
1163            } catch (RemoteException e) {
1164                Log.e(TAG, "stopWifi exception: " + e);
1165            }
1166        }
1167    }
1168
1169    private class WifiEventCallback extends IWifiEventCallback.Stub {
1170        @Override
1171        public void onStart() throws RemoteException {
1172            if (DBG) Log.d(TAG, "IWifiEventCallback.onStart");
1173            // NOP: only happens in reaction to my calls - will handle directly
1174        }
1175
1176        @Override
1177        public void onStop() throws RemoteException {
1178            if (DBG) Log.d(TAG, "IWifiEventCallback.onStop");
1179            // NOP: only happens in reaction to my calls - will handle directly
1180        }
1181
1182        @Override
1183        public void onFailure(WifiStatus status) throws RemoteException {
1184            Log.e(TAG, "IWifiEventCallback.onFailure: " + statusString(status));
1185            teardownInternal();
1186
1187            // No need to do anything else: listeners may (will) re-start Wi-Fi
1188        }
1189    }
1190
1191    private void managerStatusListenerDispatch() {
1192        synchronized (mLock) {
1193            for (ManagerStatusListenerProxy cb : mManagerStatusListeners) {
1194                cb.trigger();
1195            }
1196        }
1197    }
1198
1199    private class ManagerStatusListenerProxy  extends
1200            ListenerProxy<ManagerStatusListener> {
1201        ManagerStatusListenerProxy(ManagerStatusListener statusListener,
1202                Looper looper) {
1203            super(statusListener, looper, "ManagerStatusListenerProxy");
1204        }
1205
1206        @Override
1207        protected void action() {
1208            mListener.onStatusChanged();
1209        }
1210    }
1211
1212    Set<Integer> getSupportedIfaceTypesInternal(IWifiChip chip) {
1213        Set<Integer> results = new HashSet<>();
1214
1215        WifiChipInfo[] chipInfos = getAllChipInfo();
1216        if (chipInfos == null) {
1217            Log.e(TAG, "getSupportedIfaceTypesInternal: no chip info found");
1218            return results;
1219        }
1220
1221        MutableInt chipIdIfProvided = new MutableInt(0); // NOT using 0 as a magic value
1222        if (chip != null) {
1223            MutableBoolean statusOk = new MutableBoolean(false);
1224            try {
1225                chip.getId((WifiStatus status, int id) -> {
1226                    if (status.code == WifiStatusCode.SUCCESS) {
1227                        chipIdIfProvided.value = id;
1228                        statusOk.value = true;
1229                    } else {
1230                        Log.e(TAG, "getSupportedIfaceTypesInternal: IWifiChip.getId() error: "
1231                                + statusString(status));
1232                        statusOk.value = false;
1233                    }
1234                });
1235            } catch (RemoteException e) {
1236                Log.e(TAG, "getSupportedIfaceTypesInternal IWifiChip.getId() exception: " + e);
1237                return results;
1238            }
1239            if (!statusOk.value) {
1240                return results;
1241            }
1242        }
1243
1244        for (WifiChipInfo wci: chipInfos) {
1245            if (chip != null && wci.chipId != chipIdIfProvided.value) {
1246                continue;
1247            }
1248
1249            for (IWifiChip.ChipMode cm: wci.availableModes) {
1250                for (IWifiChip.ChipIfaceCombination cic: cm.availableCombinations) {
1251                    for (IWifiChip.ChipIfaceCombinationLimit cicl: cic.limits) {
1252                        for (int type: cicl.types) {
1253                            results.add(type);
1254                        }
1255                    }
1256                }
1257            }
1258        }
1259
1260        return results;
1261    }
1262
1263    private IWifiIface createIface(int ifaceType, InterfaceDestroyedListener destroyedListener,
1264            Looper looper) {
1265        if (DBG) Log.d(TAG, "createIface: ifaceType=" + ifaceType);
1266
1267        synchronized (mLock) {
1268            WifiChipInfo[] chipInfos = getAllChipInfo();
1269            if (chipInfos == null) {
1270                Log.e(TAG, "createIface: no chip info found");
1271                stopWifi(); // major error: shutting down
1272                return null;
1273            }
1274
1275            if (!validateInterfaceCache(chipInfos)) {
1276                Log.e(TAG, "createIface: local cache is invalid!");
1277                stopWifi(); // major error: shutting down
1278                return null;
1279            }
1280
1281            IWifiIface iface = createIfaceIfPossible(chipInfos, ifaceType, destroyedListener,
1282                    looper);
1283            if (iface != null) { // means that some configuration has changed
1284                if (!dispatchAvailableForRequestListeners()) {
1285                    return null; // catastrophic failure - shut down
1286                }
1287            }
1288
1289            return iface;
1290        }
1291    }
1292
1293    private IWifiIface createIfaceIfPossible(WifiChipInfo[] chipInfos, int ifaceType,
1294            InterfaceDestroyedListener destroyedListener, Looper looper) {
1295        if (DBG) {
1296            Log.d(TAG, "createIfaceIfPossible: chipInfos=" + Arrays.deepToString(chipInfos)
1297                    + ", ifaceType=" + ifaceType);
1298        }
1299        synchronized (mLock) {
1300            IfaceCreationData bestIfaceCreationProposal = null;
1301            for (WifiChipInfo chipInfo: chipInfos) {
1302                for (IWifiChip.ChipMode chipMode: chipInfo.availableModes) {
1303                    for (IWifiChip.ChipIfaceCombination chipIfaceCombo : chipMode
1304                            .availableCombinations) {
1305                        int[][] expandedIfaceCombos = expandIfaceCombos(chipIfaceCombo);
1306                        if (DBG) {
1307                            Log.d(TAG, chipIfaceCombo + " expands to "
1308                                    + Arrays.deepToString(expandedIfaceCombos));
1309                        }
1310
1311                        for (int[] expandedIfaceCombo: expandedIfaceCombos) {
1312                            IfaceCreationData currentProposal = canIfaceComboSupportRequest(
1313                                    chipInfo, chipMode, expandedIfaceCombo, ifaceType);
1314                            if (compareIfaceCreationData(currentProposal,
1315                                    bestIfaceCreationProposal)) {
1316                                if (DBG) Log.d(TAG, "new proposal accepted");
1317                                bestIfaceCreationProposal = currentProposal;
1318                            }
1319                        }
1320                    }
1321                }
1322            }
1323
1324            if (bestIfaceCreationProposal != null) {
1325                IWifiIface iface = executeChipReconfiguration(bestIfaceCreationProposal, ifaceType);
1326                if (iface != null) {
1327                    InterfaceCacheEntry cacheEntry = new InterfaceCacheEntry();
1328
1329                    cacheEntry.chip = bestIfaceCreationProposal.chipInfo.chip;
1330                    cacheEntry.chipId = bestIfaceCreationProposal.chipInfo.chipId;
1331                    cacheEntry.name = getName(iface);
1332                    cacheEntry.type = ifaceType;
1333                    if (destroyedListener != null) {
1334                        cacheEntry.destroyedListeners.add(
1335                                new InterfaceDestroyedListenerProxy(destroyedListener,
1336                                        looper == null ? Looper.myLooper() : looper));
1337                    }
1338
1339                    if (DBG) Log.d(TAG, "createIfaceIfPossible: added cacheEntry=" + cacheEntry);
1340                    mInterfaceInfoCache.put(cacheEntry.name, cacheEntry);
1341                    return iface;
1342                }
1343            }
1344        }
1345
1346        return null;
1347    }
1348
1349    // similar to createIfaceIfPossible - but simpler code: not looking for best option just
1350    // for any option (so terminates on first one).
1351    private boolean isItPossibleToCreateIface(WifiChipInfo[] chipInfos, int ifaceType) {
1352        if (DBG) {
1353            Log.d(TAG, "isItPossibleToCreateIface: chipInfos=" + Arrays.deepToString(chipInfos)
1354                    + ", ifaceType=" + ifaceType);
1355        }
1356
1357        for (WifiChipInfo chipInfo: chipInfos) {
1358            for (IWifiChip.ChipMode chipMode: chipInfo.availableModes) {
1359                for (IWifiChip.ChipIfaceCombination chipIfaceCombo : chipMode
1360                        .availableCombinations) {
1361                    int[][] expandedIfaceCombos = expandIfaceCombos(chipIfaceCombo);
1362                    if (DBG) {
1363                        Log.d(TAG, chipIfaceCombo + " expands to "
1364                                + Arrays.deepToString(expandedIfaceCombos));
1365                    }
1366
1367                    for (int[] expandedIfaceCombo: expandedIfaceCombos) {
1368                        if (canIfaceComboSupportRequest(chipInfo, chipMode, expandedIfaceCombo,
1369                                ifaceType) != null) {
1370                            return true;
1371                        }
1372                    }
1373                }
1374            }
1375        }
1376
1377        return false;
1378    }
1379
1380    /**
1381     * Expands (or provides an alternative representation) of the ChipIfaceCombination as all
1382     * possible combinations of interface.
1383     *
1384     * Returns [# of combinations][4 (IfaceType)]
1385     *
1386     * Note: there could be duplicates - allow (inefficient but ...).
1387     * TODO: optimize by using a Set as opposed to a []: will remove duplicates. Will need to
1388     * provide correct hashes.
1389     */
1390    private int[][] expandIfaceCombos(IWifiChip.ChipIfaceCombination chipIfaceCombo) {
1391        int numOfCombos = 1;
1392        for (IWifiChip.ChipIfaceCombinationLimit limit: chipIfaceCombo.limits) {
1393            for (int i = 0; i < limit.maxIfaces; ++i) {
1394                numOfCombos *= limit.types.size();
1395            }
1396        }
1397
1398        int[][] expandedIfaceCombos = new int[numOfCombos][IFACE_TYPES_BY_PRIORITY.length];
1399
1400        int span = numOfCombos; // span of an individual type (or sub-tree size)
1401        for (IWifiChip.ChipIfaceCombinationLimit limit: chipIfaceCombo.limits) {
1402            for (int i = 0; i < limit.maxIfaces; ++i) {
1403                span /= limit.types.size();
1404                for (int k = 0; k < numOfCombos; ++k) {
1405                    expandedIfaceCombos[k][limit.types.get((k / span) % limit.types.size())]++;
1406                }
1407            }
1408        }
1409
1410        return expandedIfaceCombos;
1411    }
1412
1413    private class IfaceCreationData {
1414        public WifiChipInfo chipInfo;
1415        public int chipModeId;
1416        public List<WifiIfaceInfo> interfacesToBeRemovedFirst;
1417
1418        @Override
1419        public String toString() {
1420            StringBuilder sb = new StringBuilder();
1421            sb.append("{chipInfo=").append(chipInfo).append(", chipModeId=").append(chipModeId)
1422                    .append(", interfacesToBeRemovedFirst=").append(interfacesToBeRemovedFirst)
1423                    .append(")");
1424            return sb.toString();
1425        }
1426    }
1427
1428    /**
1429     * Checks whether the input chip-iface-combo can support the requested interface type: if not
1430     * then returns null, if yes then returns information containing the list of interfaces which
1431     * would have to be removed first before the requested interface can be created.
1432     *
1433     * Note: the list of interfaces to be removed is EMPTY if a chip mode change is required - in
1434     * that case ALL the interfaces on the current chip have to be removed first.
1435     *
1436     * Response determined based on:
1437     * - Mode configuration: i.e. could the mode support the interface type in principle
1438     * - Priority information: i.e. are we 'allowed' to remove interfaces in order to create the
1439     *   requested interface
1440     */
1441    private IfaceCreationData canIfaceComboSupportRequest(WifiChipInfo chipInfo,
1442            IWifiChip.ChipMode chipMode, int[] chipIfaceCombo, int ifaceType) {
1443        if (DBG) {
1444            Log.d(TAG, "canIfaceComboSupportRequest: chipInfo=" + chipInfo + ", chipMode="
1445                    + chipMode + ", chipIfaceCombo=" + chipIfaceCombo + ", ifaceType=" + ifaceType);
1446        }
1447
1448        // short-circuit: does the chipIfaceCombo even support the requested type?
1449        if (chipIfaceCombo[ifaceType] == 0) {
1450            if (DBG) Log.d(TAG, "Requested type not supported by combo");
1451            return null;
1452        }
1453
1454        boolean isChipModeChangeProposed =
1455                chipInfo.currentModeIdValid && chipInfo.currentModeId != chipMode.id;
1456
1457        // short-circuit: can't change chip-mode if an existing interface on this chip has a higher
1458        // priority than the requested interface
1459        if (isChipModeChangeProposed) {
1460            for (int type: IFACE_TYPES_BY_PRIORITY) {
1461                if (chipInfo.ifaces[type].length != 0) {
1462                    if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType)) {
1463                        if (DBG) {
1464                            Log.d(TAG, "Couldn't delete existing type " + type
1465                                    + " interfaces for requested type");
1466                        }
1467                        return null;
1468                    }
1469                }
1470            }
1471
1472            // but if priority allows the mode change then we're good to go
1473            IfaceCreationData ifaceCreationData = new IfaceCreationData();
1474            ifaceCreationData.chipInfo = chipInfo;
1475            ifaceCreationData.chipModeId = chipMode.id;
1476
1477            return ifaceCreationData;
1478        }
1479
1480        // possibly supported
1481        List<WifiIfaceInfo> interfacesToBeRemovedFirst = new ArrayList<>();
1482
1483        for (int type: IFACE_TYPES_BY_PRIORITY) {
1484            int tooManyInterfaces = chipInfo.ifaces[type].length - chipIfaceCombo[type];
1485
1486            // need to count the requested interface as well
1487            if (type == ifaceType) {
1488                tooManyInterfaces += 1;
1489            }
1490
1491            if (tooManyInterfaces > 0) { // may need to delete some
1492                if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType)) {
1493                    if (DBG) {
1494                        Log.d(TAG, "Would need to delete some higher priority interfaces");
1495                    }
1496                    return null;
1497                }
1498
1499                // arbitrarily pick the first interfaces to delete
1500                for (int i = 0; i < tooManyInterfaces; ++i) {
1501                    interfacesToBeRemovedFirst.add(chipInfo.ifaces[type][i]);
1502                }
1503            }
1504        }
1505
1506        IfaceCreationData ifaceCreationData = new IfaceCreationData();
1507        ifaceCreationData.chipInfo = chipInfo;
1508        ifaceCreationData.chipModeId = chipMode.id;
1509        ifaceCreationData.interfacesToBeRemovedFirst = interfacesToBeRemovedFirst;
1510
1511        return ifaceCreationData;
1512    }
1513
1514    /**
1515     * Compares two options to create an interface and determines which is the 'best'. Returns
1516     * true if proposal 1 (val1) is better, other false.
1517     *
1518     * Note: both proposals are 'acceptable' bases on priority criteria.
1519     *
1520     * Criteria:
1521     * - Proposal is better if it means removing fewer high priority interfaces
1522     */
1523    private boolean compareIfaceCreationData(IfaceCreationData val1, IfaceCreationData val2) {
1524        if (DBG) Log.d(TAG, "compareIfaceCreationData: val1=" + val1 + ", val2=" + val2);
1525
1526        // deal with trivial case of one or the other being null
1527        if (val1 == null) {
1528            return false;
1529        } else if (val2 == null) {
1530            return true;
1531        }
1532
1533        for (int type: IFACE_TYPES_BY_PRIORITY) {
1534            // # of interfaces to be deleted: the list or all interfaces of the type if mode change
1535            int numIfacesToDelete1 = 0;
1536            if (val1.chipInfo.currentModeIdValid
1537                    && val1.chipInfo.currentModeId != val1.chipModeId) {
1538                numIfacesToDelete1 = val1.chipInfo.ifaces[type].length;
1539            } else {
1540                numIfacesToDelete1 = val1.interfacesToBeRemovedFirst.size();
1541            }
1542
1543            int numIfacesToDelete2 = 0;
1544            if (val2.chipInfo.currentModeIdValid
1545                    && val2.chipInfo.currentModeId != val2.chipModeId) {
1546                numIfacesToDelete2 = val2.chipInfo.ifaces[type].length;
1547            } else {
1548                numIfacesToDelete2 = val2.interfacesToBeRemovedFirst.size();
1549            }
1550
1551            if (numIfacesToDelete1 < numIfacesToDelete2) {
1552                if (DBG) {
1553                    Log.d(TAG, "decision based on type=" + type + ": " + numIfacesToDelete1
1554                            + " < " + numIfacesToDelete2);
1555                }
1556                return true;
1557            }
1558        }
1559
1560        // arbitrary - flip a coin
1561        if (DBG) Log.d(TAG, "proposals identical - flip a coin");
1562        return false;
1563    }
1564
1565    /**
1566     * Returns true if we're allowed to delete the existing interface type for the requested
1567     * interface type.
1568     *
1569     * Rules:
1570     * 1. Request for AP or STA will destroy any other interface (except see #4)
1571     * 2. Request for P2P will destroy NAN-only
1572     * 3. Request for NAN will not destroy any interface
1573     * --
1574     * 4. No interface will be destroyed for a requested interface of the same type
1575     */
1576    private boolean allowedToDeleteIfaceTypeForRequestedType(int existingIfaceType,
1577            int requestedIfaceType) {
1578        // rule 4
1579        if (existingIfaceType == requestedIfaceType) {
1580            return false;
1581        }
1582
1583        // rule 3
1584        if (requestedIfaceType == IfaceType.NAN) {
1585            return false;
1586        }
1587
1588        // rule 2
1589        if (requestedIfaceType == IfaceType.P2P) {
1590            return existingIfaceType == IfaceType.NAN;
1591        }
1592
1593        // rule 1, the requestIfaceType is either AP or STA
1594        return true;
1595    }
1596
1597    /**
1598     * Performs chip reconfiguration per the input:
1599     * - Removes the specified interfaces
1600     * - Reconfigures the chip to the new chip mode (if necessary)
1601     * - Creates the new interface
1602     *
1603     * Returns the newly created interface or a null on any error.
1604     */
1605    private IWifiIface executeChipReconfiguration(IfaceCreationData ifaceCreationData,
1606            int ifaceType) {
1607        if (DBG) {
1608            Log.d(TAG, "executeChipReconfiguration: ifaceCreationData=" + ifaceCreationData
1609                    + ", ifaceType=" + ifaceType);
1610        }
1611        synchronized (mLock) {
1612            try {
1613                // is this a mode change?
1614                boolean isModeConfigNeeded = !ifaceCreationData.chipInfo.currentModeIdValid
1615                        || ifaceCreationData.chipInfo.currentModeId != ifaceCreationData.chipModeId;
1616                if (DBG) Log.d(TAG, "isModeConfigNeeded=" + isModeConfigNeeded);
1617
1618                // first delete interfaces/change modes
1619                if (isModeConfigNeeded) {
1620                    // remove all interfaces pre mode-change
1621                    // TODO: is this necessary? note that even if we don't want to explicitly
1622                    // remove the interfaces we do need to call the onDeleted callbacks - which
1623                    // this does
1624                    for (WifiIfaceInfo[] ifaceInfos: ifaceCreationData.chipInfo.ifaces) {
1625                        for (WifiIfaceInfo ifaceInfo: ifaceInfos) {
1626                            removeIfaceInternal(ifaceInfo.iface); // ignore return value
1627                        }
1628                    }
1629
1630                    WifiStatus status = ifaceCreationData.chipInfo.chip.configureChip(
1631                            ifaceCreationData.chipModeId);
1632                    if (status.code != WifiStatusCode.SUCCESS) {
1633                        Log.e(TAG, "executeChipReconfiguration: configureChip error: "
1634                                + statusString(status));
1635                        return null;
1636                    }
1637                } else {
1638                    // remove all interfaces on the delete list
1639                    for (WifiIfaceInfo ifaceInfo: ifaceCreationData.interfacesToBeRemovedFirst) {
1640                        removeIfaceInternal(ifaceInfo.iface); // ignore return value
1641                    }
1642                }
1643
1644                // create new interface
1645                Mutable<WifiStatus> statusResp = new Mutable<>();
1646                Mutable<IWifiIface> ifaceResp = new Mutable<>();
1647                switch (ifaceType) {
1648                    case IfaceType.STA:
1649                        ifaceCreationData.chipInfo.chip.createStaIface(
1650                                (WifiStatus status, IWifiStaIface iface) -> {
1651                                    statusResp.value = status;
1652                                    ifaceResp.value = iface;
1653                                });
1654                        break;
1655                    case IfaceType.AP:
1656                        ifaceCreationData.chipInfo.chip.createApIface(
1657                                (WifiStatus status, IWifiApIface iface) -> {
1658                                    statusResp.value = status;
1659                                    ifaceResp.value = iface;
1660                                });
1661                        break;
1662                    case IfaceType.P2P:
1663                        ifaceCreationData.chipInfo.chip.createP2pIface(
1664                                (WifiStatus status, IWifiP2pIface iface) -> {
1665                                    statusResp.value = status;
1666                                    ifaceResp.value = iface;
1667                                });
1668                        break;
1669                    case IfaceType.NAN:
1670                        ifaceCreationData.chipInfo.chip.createNanIface(
1671                                (WifiStatus status, IWifiNanIface iface) -> {
1672                                    statusResp.value = status;
1673                                    ifaceResp.value = iface;
1674                                });
1675                        break;
1676                }
1677
1678                if (statusResp.value.code != WifiStatusCode.SUCCESS) {
1679                    Log.e(TAG, "executeChipReconfiguration: failed to create interface ifaceType="
1680                            + ifaceType + ": " + statusString(statusResp.value));
1681                    return null;
1682                }
1683
1684                return ifaceResp.value;
1685            } catch (RemoteException e) {
1686                Log.e(TAG, "executeChipReconfiguration exception: " + e);
1687                return null;
1688            }
1689        }
1690    }
1691
1692    private boolean removeIfaceInternal(IWifiIface iface) {
1693        String name = getName(iface);
1694        if (DBG) Log.d(TAG, "removeIfaceInternal: iface(name)=" + name);
1695
1696        synchronized (mLock) {
1697            if (mWifi == null) {
1698                Log.e(TAG, "removeIfaceInternal: null IWifi -- iface(name)=" + name);
1699                return false;
1700            }
1701
1702            IWifiChip chip = getChip(iface);
1703            if (chip == null) {
1704                Log.e(TAG, "removeIfaceInternal: null IWifiChip -- iface(name)=" + name);
1705                return false;
1706            }
1707
1708            if (name == null) {
1709                Log.e(TAG, "removeIfaceInternal: can't get name");
1710                return false;
1711            }
1712
1713            int type = getType(iface);
1714            if (type == -1) {
1715                Log.e(TAG, "removeIfaceInternal: can't get type -- iface(name)=" + name);
1716                return false;
1717            }
1718
1719            WifiStatus status = null;
1720            try {
1721                switch (type) {
1722                    case IfaceType.STA:
1723                        status = chip.removeStaIface(name);
1724                        break;
1725                    case IfaceType.AP:
1726                        status = chip.removeApIface(name);
1727                        break;
1728                    case IfaceType.P2P:
1729                        status = chip.removeP2pIface(name);
1730                        break;
1731                    case IfaceType.NAN:
1732                        status = chip.removeNanIface(name);
1733                        break;
1734                    default:
1735                        Log.wtf(TAG, "removeIfaceInternal: invalid type=" + type);
1736                        return false;
1737                }
1738            } catch (RemoteException e) {
1739                Log.e(TAG, "IWifiChip.removeXxxIface exception: " + e);
1740            }
1741
1742            // dispatch listeners no matter what status
1743            dispatchDestroyedListeners(name);
1744
1745            if (status != null && status.code == WifiStatusCode.SUCCESS) {
1746                return true;
1747            } else {
1748                Log.e(TAG, "IWifiChip.removeXxxIface failed: " + statusString(status));
1749                return false;
1750            }
1751        }
1752    }
1753
1754    // dispatch all available for request listeners of the specified type AND clean-out the list:
1755    // listeners are called once at most!
1756    private boolean dispatchAvailableForRequestListeners() {
1757        if (DBG) Log.d(TAG, "dispatchAvailableForRequestListeners");
1758
1759        synchronized (mLock) {
1760            WifiChipInfo[] chipInfos = getAllChipInfo();
1761            if (chipInfos == null) {
1762                Log.e(TAG, "dispatchAvailableForRequestListeners: no chip info found");
1763                stopWifi(); // major error: shutting down
1764                return false;
1765            }
1766            if (DBG) {
1767                Log.d(TAG, "dispatchAvailableForRequestListeners: chipInfos="
1768                        + Arrays.deepToString(chipInfos));
1769            }
1770
1771            for (int ifaceType : IFACE_TYPES_BY_PRIORITY) {
1772                dispatchAvailableForRequestListenersForType(ifaceType, chipInfos);
1773            }
1774        }
1775
1776        return true;
1777    }
1778
1779    private void dispatchAvailableForRequestListenersForType(int ifaceType,
1780            WifiChipInfo[] chipInfos) {
1781        if (DBG) Log.d(TAG, "dispatchAvailableForRequestListenersForType: ifaceType=" + ifaceType);
1782
1783        Set<InterfaceAvailableForRequestListenerProxy> listeners =
1784                mInterfaceAvailableForRequestListeners.get(ifaceType);
1785
1786        if (listeners.size() == 0) {
1787            return;
1788        }
1789
1790        if (!isItPossibleToCreateIface(chipInfos, ifaceType)) {
1791            if (DBG) Log.d(TAG, "Creating interface type isn't possible: ifaceType=" + ifaceType);
1792            return;
1793        }
1794
1795        if (DBG) Log.d(TAG, "It is possible to create the interface type: ifaceType=" + ifaceType);
1796        for (InterfaceAvailableForRequestListenerProxy listener : listeners) {
1797            listener.trigger();
1798        }
1799    }
1800
1801    // dispatch all destroyed listeners registered for the specified interface AND remove the
1802    // cache entry
1803    private void dispatchDestroyedListeners(String name) {
1804        if (DBG) Log.d(TAG, "dispatchDestroyedListeners: iface(name)=" + name);
1805
1806        synchronized (mLock) {
1807            InterfaceCacheEntry entry = mInterfaceInfoCache.get(name);
1808            if (entry == null) {
1809                Log.e(TAG, "dispatchDestroyedListeners: no cache entry for iface(name)=" + name);
1810                return;
1811            }
1812
1813            for (InterfaceDestroyedListenerProxy listener : entry.destroyedListeners) {
1814                listener.trigger();
1815            }
1816            entry.destroyedListeners.clear(); // for insurance (though cache entry is removed)
1817            mInterfaceInfoCache.remove(name);
1818        }
1819    }
1820
1821    // dispatch all destroyed listeners registered to all interfaces
1822    private void dispatchAllDestroyedListeners() {
1823        if (DBG) Log.d(TAG, "dispatchAllDestroyedListeners");
1824
1825        synchronized (mLock) {
1826            Iterator<Map.Entry<String, InterfaceCacheEntry>> it =
1827                    mInterfaceInfoCache.entrySet().iterator();
1828            while (it.hasNext()) {
1829                InterfaceCacheEntry entry = it.next().getValue();
1830                for (InterfaceDestroyedListenerProxy listener : entry.destroyedListeners) {
1831                    listener.trigger();
1832                }
1833                entry.destroyedListeners.clear(); // for insurance (though cache entry is removed)
1834                it.remove();
1835            }
1836        }
1837    }
1838
1839    private abstract class ListenerProxy<LISTENER>  {
1840        private static final int LISTENER_TRIGGERED = 0;
1841
1842        protected LISTENER mListener;
1843        private Handler mHandler;
1844
1845        // override equals & hash to make sure that the container HashSet is unique with respect to
1846        // the contained listener
1847        @Override
1848        public boolean equals(Object obj) {
1849            return mListener == ((ListenerProxy<LISTENER>) obj).mListener;
1850        }
1851
1852        @Override
1853        public int hashCode() {
1854            return mListener.hashCode();
1855        }
1856
1857        void trigger() {
1858            mHandler.sendMessage(mHandler.obtainMessage(LISTENER_TRIGGERED));
1859        }
1860
1861        protected abstract void action();
1862
1863        ListenerProxy(LISTENER listener, Looper looper, String tag) {
1864            mListener = listener;
1865            mHandler = new Handler(looper) {
1866                @Override
1867                public void handleMessage(Message msg) {
1868                    if (DBG) {
1869                        Log.d(tag, "ListenerProxy.handleMessage: what=" + msg.what);
1870                    }
1871                    switch (msg.what) {
1872                        case LISTENER_TRIGGERED:
1873                            action();
1874                            break;
1875                        default:
1876                            Log.e(tag, "ListenerProxy.handleMessage: unknown message what="
1877                                    + msg.what);
1878                    }
1879                }
1880            };
1881        }
1882    }
1883
1884    private class InterfaceDestroyedListenerProxy extends
1885            ListenerProxy<InterfaceDestroyedListener> {
1886        InterfaceDestroyedListenerProxy(InterfaceDestroyedListener destroyedListener,
1887                Looper looper) {
1888            super(destroyedListener, looper, "InterfaceDestroyedListenerProxy");
1889        }
1890
1891        @Override
1892        protected void action() {
1893            mListener.onDestroyed();
1894        }
1895    }
1896
1897    private class InterfaceAvailableForRequestListenerProxy extends
1898            ListenerProxy<InterfaceAvailableForRequestListener> {
1899        InterfaceAvailableForRequestListenerProxy(
1900                InterfaceAvailableForRequestListener destroyedListener, Looper looper) {
1901            super(destroyedListener, looper, "InterfaceAvailableForRequestListenerProxy");
1902        }
1903
1904        @Override
1905        protected void action() {
1906            mListener.onAvailableForRequest();
1907        }
1908    }
1909
1910    // general utilities
1911
1912    private static String statusString(WifiStatus status) {
1913        if (status == null) {
1914            return "status=null";
1915        }
1916        StringBuilder sb = new StringBuilder();
1917        sb.append(status.code).append(" (").append(status.description).append(")");
1918        return sb.toString();
1919    }
1920
1921    // Will return -1 for invalid results! Otherwise will return one of the 4 valid values.
1922    private static int getType(IWifiIface iface) {
1923        MutableInt typeResp = new MutableInt(-1);
1924        try {
1925            iface.getType((WifiStatus status, int type) -> {
1926                if (status.code == WifiStatusCode.SUCCESS) {
1927                    typeResp.value = type;
1928                } else {
1929                    Log.e(TAG, "Error on getType: " + statusString(status));
1930                }
1931            });
1932        } catch (RemoteException e) {
1933            Log.e(TAG, "Exception on getType: " + e);
1934        }
1935
1936        return typeResp.value;
1937    }
1938
1939    private static class Mutable<E> {
1940        public E value;
1941
1942        Mutable() {
1943            value = null;
1944        }
1945
1946        Mutable(E value) {
1947            this.value = value;
1948        }
1949    }
1950
1951    /**
1952     * Dump the internal state of the class.
1953     */
1954    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1955        pw.println("HalDeviceManager:");
1956        pw.println("  mServiceManager: " + mServiceManager);
1957        pw.println("  mWifi: " + mWifi);
1958        pw.println("  mManagerStatusListeners: " + mManagerStatusListeners);
1959        pw.println("  mInterfaceAvailableForRequestListeners: "
1960                + mInterfaceAvailableForRequestListeners);
1961        pw.println("  mInterfaceInfoCache: " + mInterfaceInfoCache);
1962    }
1963}
1964