HalDeviceManager.java revision 4fe311c774d6880c2eb9aa38ece9f9a5a60f7bd7
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    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    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        if (DBG) Log.d(TAG, "getChip: iface(name)=" + getName(iface));
248
249        synchronized (mLock) {
250            InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(iface);
251            if (cacheEntry == null) {
252                Log.e(TAG, "getChip: no entry for iface(name)=" + getName(iface));
253                return null;
254            }
255
256            return cacheEntry.chip;
257        }
258    }
259
260    /**
261     * Register an InterfaceDestroyedListener to the specified iface - returns true on success
262     * and false on failure. This listener is in addition to the one registered when the interface
263     * was created - allowing non-creators to monitor interface status.
264     *
265     * Listener called-back on the specified looper - or on the current looper if a null is passed.
266     */
267    public boolean registerDestroyedListener(IWifiIface iface,
268            InterfaceDestroyedListener destroyedListener,
269            Looper looper) {
270        if (DBG) Log.d(TAG, "registerDestroyedListener: iface(name)=" + getName(iface));
271
272        synchronized (mLock) {
273            InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(iface);
274            if (cacheEntry == null) {
275                Log.e(TAG, "registerDestroyedListener: no entry for iface(name)="
276                        + getName(iface));
277                return false;
278            }
279
280            return cacheEntry.destroyedListeners.add(
281                    new InterfaceDestroyedListenerProxy(destroyedListener,
282                            looper == null ? Looper.myLooper() : looper));
283        }
284    }
285
286    /**
287     * Register a listener to be called when an interface of the specified type could be requested.
288     * No guarantees are provided (some other entity could request it first). The listener is
289     * active from registration until unregistration - using
290     * unregisterInterfaceAvailableForRequestListener().
291     *
292     * Only a single instance of a listener will be registered (even if the specified looper is
293     * different).
294     *
295     * Note that if it is possible to create the specified interface type at registration time
296     * then the callback will be triggered immediately.
297     *
298     * @param ifaceType The interface type (IfaceType) to be monitored.
299     * @param listener Listener to call when an interface of the requested
300     *                 type could be created
301     * @param looper The looper on which to dispatch the listener. A null value indicates the
302     *               current thread.
303     */
304    public void registerInterfaceAvailableForRequestListener(int ifaceType,
305            InterfaceAvailableForRequestListener listener, Looper looper) {
306        if (DBG) Log.d(TAG, "registerInterfaceAvailableForRequestListener: ifaceType=" + ifaceType);
307
308        synchronized (mLock) {
309            mInterfaceAvailableForRequestListeners.get(ifaceType).add(
310                    new InterfaceAvailableForRequestListenerProxy(listener,
311                            looper == null ? Looper.myLooper() : looper));
312        }
313
314        WifiChipInfo[] chipInfos = getAllChipInfo();
315        if (chipInfos == null) {
316            Log.e(TAG,
317                    "registerInterfaceAvailableForRequestListener: no chip info found - but "
318                            + "possibly registered pre-started - ignoring");
319            return;
320        }
321        dispatchAvailableForRequestListenersForType(ifaceType, chipInfos);
322    }
323
324    /**
325     * Unregisters a listener registered with registerInterfaceAvailableForRequestListener().
326     */
327    public void unregisterInterfaceAvailableForRequestListener(
328            int ifaceType,
329            InterfaceAvailableForRequestListener listener) {
330        if (DBG) {
331            Log.d(TAG, "unregisterInterfaceAvailableForRequestListener: ifaceType=" + ifaceType);
332        }
333
334        synchronized (mLock) {
335            Iterator<InterfaceAvailableForRequestListenerProxy> it =
336                    mInterfaceAvailableForRequestListeners.get(ifaceType).iterator();
337            while (it.hasNext()) {
338                if (it.next().mListener == listener) {
339                    it.remove();
340                    return;
341                }
342            }
343        }
344    }
345
346    /**
347     * Return the name of the input interface or null on error.
348     */
349    public static String getName(IWifiIface iface) {
350        if (iface == null) {
351            return "<null>";
352        }
353
354        Mutable<String> nameResp = new Mutable<>();
355        try {
356            iface.getName((WifiStatus status, String name) -> {
357                if (status.code == WifiStatusCode.SUCCESS) {
358                    nameResp.value = name;
359                } else {
360                    Log.e(TAG, "Error on getName: " + statusString(status));
361                }
362            });
363        } catch (RemoteException e) {
364            Log.e(TAG, "Exception on getName: " + e);
365        }
366
367        return nameResp.value;
368    }
369
370    /**
371     * Called when interface is destroyed.
372     */
373    public interface InterfaceDestroyedListener {
374        /**
375         * Called for every interface on which registered when destroyed - whether
376         * destroyed by releaseIface() or through chip mode change or through Wi-Fi
377         * going down.
378         *
379         * Can be registered when the interface is requested with createXxxIface() - will
380         * only be valid if the interface creation was successful - i.e. a non-null was returned.
381         */
382        void onDestroyed();
383    }
384
385    /**
386     * Called when an interface type is possibly available for creation.
387     */
388    public interface InterfaceAvailableForRequestListener {
389        /**
390         * Registered when an interface type could be requested. Registered with
391         * registerInterfaceAvailableForRequestListener() and unregistered with
392         * unregisterInterfaceAvailableForRequestListener().
393         */
394        void onAvailableForRequest();
395    }
396
397    /**
398     * Creates a IWifiRttController corresponding to the input interface. A direct match to the
399     * IWifiChip.createRttController() method.
400     *
401     * Returns the created IWifiRttController or a null on error.
402     */
403    public IWifiRttController createRttController(IWifiIface boundIface) {
404        if (DBG) Log.d(TAG, "createRttController: boundIface(name)=" + getName(boundIface));
405        synchronized (mLock) {
406            if (mWifi == null) {
407                Log.e(TAG, "createRttController: null IWifi -- boundIface(name)="
408                        + getName(boundIface));
409                return null;
410            }
411
412            IWifiChip chip = getChip(boundIface);
413            if (chip == null) {
414                Log.e(TAG, "createRttController: null IWifiChip -- boundIface(name)="
415                        + getName(boundIface));
416                return null;
417            }
418
419            Mutable<IWifiRttController> rttResp = new Mutable<>();
420            try {
421                chip.createRttController(boundIface,
422                        (WifiStatus status, IWifiRttController rtt) -> {
423                            if (status.code == WifiStatusCode.SUCCESS) {
424                                rttResp.value = rtt;
425                            } else {
426                                Log.e(TAG, "IWifiChip.createRttController failed: " + statusString(
427                                        status));
428                            }
429                        });
430            } catch (RemoteException e) {
431                Log.e(TAG, "IWifiChip.createRttController exception: " + e);
432            }
433
434            return rttResp.value;
435        }
436    }
437
438    // internal state
439
440    /* This "PRIORITY" is not for deciding interface elimination (that is controlled by
441     * allowedToDeleteIfaceTypeForRequestedType. This priority is used for:
442     * - Comparing 2 configuration options
443     * - Order of dispatch of available for request listeners
444     */
445    private static final int[] IFACE_TYPES_BY_PRIORITY =
446            {IfaceType.AP, IfaceType.STA, IfaceType.P2P, IfaceType.NAN};
447
448    private final Object mLock = new Object();
449
450    private IServiceManager mServiceManager;
451    private IWifi mWifi;
452    private final WifiEventCallback mWifiEventCallback = new WifiEventCallback();
453    private final Set<ManagerStatusListenerProxy> mManagerStatusListeners = new HashSet<>();
454    private final SparseArray<Set<InterfaceAvailableForRequestListenerProxy>>
455            mInterfaceAvailableForRequestListeners = new SparseArray<>();
456    private final SparseArray<IWifiChipEventCallback.Stub> mDebugCallbacks = new SparseArray<>();
457
458    /*
459     * This is the only place where we cache HIDL information in this manager. Necessary since
460     * we need to keep a list of registered destroyed listeners. Will be validated regularly
461     * in getAllChipInfoAndValidateCache().
462     */
463    private final Map<IWifiIface, InterfaceCacheEntry> mInterfaceInfoCache = new HashMap<>();
464
465    private class InterfaceCacheEntry {
466        public IWifiChip chip;
467        public int chipId;
468        public String name;
469        public int type;
470        public Set<InterfaceDestroyedListenerProxy> destroyedListeners = new HashSet<>();
471
472        @Override
473        public String toString() {
474            StringBuilder sb = new StringBuilder();
475            sb.append("{name=").append(name).append(", type=").append(type)
476                    .append(", destroyedListeners.size()=").append(destroyedListeners.size())
477                    .append("}");
478            return sb.toString();
479        }
480    }
481
482    private class WifiIfaceInfo {
483        public String name;
484        public IWifiIface iface;
485    }
486
487    private class WifiChipInfo {
488        public IWifiChip chip;
489        public int chipId;
490        public ArrayList<IWifiChip.ChipMode> availableModes;
491        public boolean currentModeIdValid;
492        public int currentModeId;
493        public WifiIfaceInfo[][] ifaces = new WifiIfaceInfo[IFACE_TYPES_BY_PRIORITY.length][];
494
495        @Override
496        public String toString() {
497            StringBuilder sb = new StringBuilder();
498            sb.append("{chipId=").append(chipId).append(", availableModes=").append(availableModes)
499                    .append(", currentModeIdValid=").append(currentModeIdValid)
500                    .append(", currentModeId=").append(currentModeId);
501            for (int type: IFACE_TYPES_BY_PRIORITY) {
502                sb.append(", ifaces[" + type + "].length=").append(ifaces[type].length);
503            }
504            sb.append(")");
505            return sb.toString();
506        }
507    }
508
509    /**
510     * Wrapper function to access the HIDL services. Created to be mockable in unit-tests.
511     */
512    protected IWifi getWifiServiceMockable() {
513        try {
514            return IWifi.getService();
515        } catch (RemoteException e) {
516            Log.e(TAG, "Exception getting IWifi service: " + e);
517            return null;
518        }
519    }
520
521    protected IServiceManager getServiceManagerMockable() {
522        try {
523            return IServiceManager.getService();
524        } catch (RemoteException e) {
525            Log.e(TAG, "Exception getting IServiceManager: " + e);
526            return null;
527        }
528    }
529
530    // internal implementation
531
532    private void initializeInternal() {
533        initIServiceManagerIfNecessary();
534    }
535
536    private void teardownInternal() {
537        managerStatusListenerDispatch();
538        dispatchAllDestroyedListeners();
539        mInterfaceAvailableForRequestListeners.get(IfaceType.STA).clear();
540        mInterfaceAvailableForRequestListeners.get(IfaceType.AP).clear();
541        mInterfaceAvailableForRequestListeners.get(IfaceType.P2P).clear();
542        mInterfaceAvailableForRequestListeners.get(IfaceType.NAN).clear();
543    }
544
545    private final HwRemoteBinder.DeathRecipient mServiceManagerDeathRecipient =
546            cookie -> {
547                Log.wtf(TAG, "IServiceManager died: cookie=" + cookie);
548                synchronized (mLock) {
549                    mServiceManager = null;
550                    // theoretically can call initServiceManager again here - but
551                    // there's no point since most likely system is going to reboot
552                }
553            };
554
555    private final IServiceNotification mServiceNotificationCallback =
556            new IServiceNotification.Stub() {
557                @Override
558                public void onRegistration(String fqName, String name,
559                                           boolean preexisting) {
560                    Log.d(TAG, "IWifi registration notification: fqName=" + fqName
561                            + ", name=" + name + ", preexisting=" + preexisting);
562                    mWifi = null; // get rid of old copy!
563                    initIWifiIfNecessary();
564                    stopWifi(); // just in case
565                }
566            };
567
568    /**
569     * Failures of IServiceManager are most likely system breaking in any case. Behavior here
570     * will be to WTF and continue.
571     */
572    private void initIServiceManagerIfNecessary() {
573        if (DBG) Log.d(TAG, "initIServiceManagerIfNecessary");
574
575        synchronized (mLock) {
576            if (mServiceManager != null) {
577                return;
578            }
579
580            mServiceManager = getServiceManagerMockable();
581            if (mServiceManager == null) {
582                Log.wtf(TAG, "Failed to get IServiceManager instance");
583            } else {
584                try {
585                    if (!mServiceManager.linkToDeath(
586                            mServiceManagerDeathRecipient, /* don't care */ 0)) {
587                        Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
588                        mServiceManager = null;
589                        return;
590                    }
591
592                    if (!mServiceManager.registerForNotifications(IWifi.kInterfaceName, "",
593                            mServiceNotificationCallback)) {
594                        Log.wtf(TAG, "Failed to register a listener for IWifi service");
595                        mServiceManager = null;
596                    }
597                } catch (RemoteException e) {
598                    Log.wtf(TAG, "Exception while operating on IServiceManager: " + e);
599                    mServiceManager = null;
600                }
601            }
602        }
603    }
604
605    /**
606     * Uses the IServiceManager to query if the vendor HAL is present in the VINTF for the device
607     * or not.
608     * @return true if supported, false otherwise.
609     */
610    private boolean isSupportedInternal() {
611        if (DBG) Log.d(TAG, "isSupportedInternal");
612
613        synchronized (mLock) {
614            if (mServiceManager == null) {
615                Log.e(TAG, "isSupported: called but mServiceManager is null!?");
616                return false;
617            }
618            try {
619                return (mServiceManager.getTransport(IWifi.kInterfaceName, HAL_INSTANCE_NAME)
620                        != IServiceManager.Transport.EMPTY);
621            } catch (RemoteException e) {
622                Log.wtf(TAG, "Exception while operating on IServiceManager: " + e);
623                return false;
624            }
625        }
626    }
627
628    private final HwRemoteBinder.DeathRecipient mIWifiDeathRecipient =
629            cookie -> {
630                Log.e(TAG, "IWifi HAL service died! Have a listener for it ... cookie=" + cookie);
631                synchronized (mLock) { // prevents race condition with surrounding method
632                    mWifi = null;
633                    teardownInternal();
634                    // don't restart: wait for registration notification
635                }
636            };
637
638    /**
639     * Initialize IWifi and register death listener and event callback.
640     *
641     * - It is possible that IWifi is not ready - we have a listener on IServiceManager for it.
642     * - It is not expected that any of the registrations will fail. Possible indication that
643     *   service died after we obtained a handle to it.
644     *
645     * Here and elsewhere we assume that death listener will do the right thing!
646    */
647    private void initIWifiIfNecessary() {
648        if (DBG) Log.d(TAG, "initIWifiIfNecessary");
649
650        synchronized (mLock) {
651            if (mWifi != null) {
652                return;
653            }
654
655            try {
656                mWifi = getWifiServiceMockable();
657                if (mWifi == null) {
658                    Log.e(TAG, "IWifi not (yet) available - but have a listener for it ...");
659                    return;
660                }
661
662                if (!mWifi.linkToDeath(mIWifiDeathRecipient, /* don't care */ 0)) {
663                    Log.e(TAG, "Error on linkToDeath on IWifi - will retry later");
664                    return;
665                }
666
667                WifiStatus status = mWifi.registerEventCallback(mWifiEventCallback);
668                if (status.code != WifiStatusCode.SUCCESS) {
669                    Log.e(TAG, "IWifi.registerEventCallback failed: " + statusString(status));
670                    mWifi = null;
671                    return;
672                }
673                managerStatusListenerDispatch();
674            } catch (RemoteException e) {
675                Log.e(TAG, "Exception while operating on IWifi: " + e);
676            }
677        }
678    }
679
680    /**
681     * Registers event listeners on all IWifiChips after a successful start: DEBUG only!
682     *
683     * We don't need the listeners since any callbacks are just confirmation of status codes we
684     * obtain directly from mode changes or interface creation/deletion.
685     *
686     * Relies (to the degree we care) on the service removing all listeners when Wi-Fi is stopped.
687     */
688    private void initIWifiChipDebugListeners() {
689        if (DBG) Log.d(TAG, "initIWifiChipDebugListeners");
690
691        if (!DBG) {
692            return;
693        }
694
695        synchronized (mLock) {
696            try {
697                MutableBoolean statusOk = new MutableBoolean(false);
698                Mutable<ArrayList<Integer>> chipIdsResp = new Mutable<>();
699
700                // get all chip IDs
701                mWifi.getChipIds((WifiStatus status, ArrayList<Integer> chipIds) -> {
702                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
703                    if (statusOk.value) {
704                        chipIdsResp.value = chipIds;
705                    } else {
706                        Log.e(TAG, "getChipIds failed: " + statusString(status));
707                    }
708                });
709                if (!statusOk.value) {
710                    return;
711                }
712
713                if (DBG) Log.d(TAG, "getChipIds=" + chipIdsResp.value);
714                if (chipIdsResp.value.size() == 0) {
715                    Log.e(TAG, "Should have at least 1 chip!");
716                    return;
717                }
718
719                // register a callback for each chip
720                Mutable<IWifiChip> chipResp = new Mutable<>();
721                for (Integer chipId: chipIdsResp.value) {
722                    mWifi.getChip(chipId, (WifiStatus status, IWifiChip chip) -> {
723                        statusOk.value = status.code == WifiStatusCode.SUCCESS;
724                        if (statusOk.value) {
725                            chipResp.value = chip;
726                        } else {
727                            Log.e(TAG, "getChip failed: " + statusString(status));
728                        }
729                    });
730                    if (!statusOk.value) {
731                        continue; // still try next one?
732                    }
733
734                    IWifiChipEventCallback.Stub callback =
735                            new IWifiChipEventCallback.Stub() {
736                                @Override
737                                public void onChipReconfigured(int modeId) throws RemoteException {
738                                    Log.d(TAG, "onChipReconfigured: modeId=" + modeId);
739                                }
740
741                                @Override
742                                public void onChipReconfigureFailure(WifiStatus status)
743                                        throws RemoteException {
744                                    Log.d(TAG, "onChipReconfigureFailure: status=" + statusString(
745                                            status));
746                                }
747
748                                @Override
749                                public void onIfaceAdded(int type, String name)
750                                        throws RemoteException {
751                                    Log.d(TAG, "onIfaceAdded: type=" + type + ", name=" + name);
752                                }
753
754                                @Override
755                                public void onIfaceRemoved(int type, String name)
756                                        throws RemoteException {
757                                    Log.d(TAG, "onIfaceRemoved: type=" + type + ", name=" + name);
758                                }
759
760                                @Override
761                                public void onDebugRingBufferDataAvailable(
762                                        WifiDebugRingBufferStatus status,
763                                        ArrayList<Byte> data) throws RemoteException {
764                                    Log.d(TAG, "onDebugRingBufferDataAvailable");
765                                }
766
767                                @Override
768                                public void onDebugErrorAlert(int errorCode,
769                                        ArrayList<Byte> debugData)
770                                        throws RemoteException {
771                                    Log.d(TAG, "onDebugErrorAlert");
772                                }
773                            };
774                    mDebugCallbacks.put(chipId, callback); // store to prevent GC: needed by HIDL
775                    WifiStatus status = chipResp.value.registerEventCallback(callback);
776                    if (status.code != WifiStatusCode.SUCCESS) {
777                        Log.e(TAG, "registerEventCallback failed: " + statusString(status));
778                        continue; // still try next one?
779                    }
780                }
781            } catch (RemoteException e) {
782                Log.e(TAG, "initIWifiChipDebugListeners: exception: " + e);
783                return;
784            }
785        }
786    }
787
788    /**
789     * Get current information about all the chips in the system: modes, current mode (if any), and
790     * any existing interfaces.
791     *
792     * Intended to be called whenever we need to configure the chips - information is NOT cached (to
793     * reduce the likelihood that we get out-of-sync).
794     */
795    private WifiChipInfo[] getAllChipInfo() {
796        if (DBG) Log.d(TAG, "getAllChipInfo");
797
798        synchronized (mLock) {
799            if (mWifi == null) {
800                Log.e(TAG, "getAllChipInfo: called but mWifi is null!?");
801                return null;
802            }
803
804            try {
805                MutableBoolean statusOk = new MutableBoolean(false);
806                Mutable<ArrayList<Integer>> chipIdsResp = new Mutable<>();
807
808                // get all chip IDs
809                mWifi.getChipIds((WifiStatus status, ArrayList<Integer> chipIds) -> {
810                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
811                    if (statusOk.value) {
812                        chipIdsResp.value = chipIds;
813                    } else {
814                        Log.e(TAG, "getChipIds failed: " + statusString(status));
815                    }
816                });
817                if (!statusOk.value) {
818                    return null;
819                }
820
821                if (DBG) Log.d(TAG, "getChipIds=" + chipIdsResp.value);
822                if (chipIdsResp.value.size() == 0) {
823                    Log.e(TAG, "Should have at least 1 chip!");
824                    return null;
825                }
826
827                int chipInfoIndex = 0;
828                WifiChipInfo[] chipsInfo = new WifiChipInfo[chipIdsResp.value.size()];
829
830                Mutable<IWifiChip> chipResp = new Mutable<>();
831                for (Integer chipId: chipIdsResp.value) {
832                    mWifi.getChip(chipId, (WifiStatus status, IWifiChip chip) -> {
833                        statusOk.value = status.code == WifiStatusCode.SUCCESS;
834                        if (statusOk.value) {
835                            chipResp.value = chip;
836                        } else {
837                            Log.e(TAG, "getChip failed: " + statusString(status));
838                        }
839                    });
840                    if (!statusOk.value) {
841                        return null;
842                    }
843
844                    Mutable<ArrayList<IWifiChip.ChipMode>> availableModesResp = new Mutable<>();
845                    chipResp.value.getAvailableModes(
846                            (WifiStatus status, ArrayList<IWifiChip.ChipMode> modes) -> {
847                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
848                                if (statusOk.value) {
849                                    availableModesResp.value = modes;
850                                } else {
851                                    Log.e(TAG, "getAvailableModes failed: " + statusString(status));
852                                }
853                            });
854                    if (!statusOk.value) {
855                        return null;
856                    }
857
858                    MutableBoolean currentModeValidResp = new MutableBoolean(false);
859                    MutableInt currentModeResp = new MutableInt(0);
860                    chipResp.value.getMode((WifiStatus status, int modeId) -> {
861                        statusOk.value = status.code == WifiStatusCode.SUCCESS;
862                        if (statusOk.value) {
863                            currentModeValidResp.value = true;
864                            currentModeResp.value = modeId;
865                        } else if (status.code == WifiStatusCode.ERROR_NOT_AVAILABLE) {
866                            statusOk.value = true; // valid response
867                        } else {
868                            Log.e(TAG, "getMode failed: " + statusString(status));
869                        }
870                    });
871                    if (!statusOk.value) {
872                        return null;
873                    }
874
875                    Mutable<ArrayList<String>> ifaceNamesResp = new Mutable<>();
876                    MutableInt ifaceIndex = new MutableInt(0);
877
878                    chipResp.value.getStaIfaceNames(
879                            (WifiStatus status, ArrayList<String> ifnames) -> {
880                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
881                                if (statusOk.value) {
882                                    ifaceNamesResp.value = ifnames;
883                                } else {
884                                    Log.e(TAG, "getStaIfaceNames failed: " + statusString(status));
885                                }
886                            });
887                    if (!statusOk.value) {
888                        return null;
889                    }
890
891                    WifiIfaceInfo[] staIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
892                    for (String ifaceName: ifaceNamesResp.value) {
893                        chipResp.value.getStaIface(ifaceName,
894                                (WifiStatus status, IWifiStaIface iface) -> {
895                                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
896                                    if (statusOk.value) {
897                                        WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
898                                        ifaceInfo.name = ifaceName;
899                                        ifaceInfo.iface = iface;
900                                        staIfaces[ifaceIndex.value++] = ifaceInfo;
901                                    } else {
902                                        Log.e(TAG, "getStaIface failed: " + statusString(status));
903                                    }
904                                });
905                        if (!statusOk.value) {
906                            return null;
907                        }
908                    }
909
910                    ifaceIndex.value = 0;
911                    chipResp.value.getApIfaceNames(
912                            (WifiStatus status, ArrayList<String> ifnames) -> {
913                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
914                                if (statusOk.value) {
915                                    ifaceNamesResp.value = ifnames;
916                                } else {
917                                    Log.e(TAG, "getApIfaceNames failed: " + statusString(status));
918                                }
919                            });
920                    if (!statusOk.value) {
921                        return null;
922                    }
923
924                    WifiIfaceInfo[] apIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
925                    for (String ifaceName: ifaceNamesResp.value) {
926                        chipResp.value.getApIface(ifaceName,
927                                (WifiStatus status, IWifiApIface iface) -> {
928                                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
929                                    if (statusOk.value) {
930                                        WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
931                                        ifaceInfo.name = ifaceName;
932                                        ifaceInfo.iface = iface;
933                                        apIfaces[ifaceIndex.value++] = ifaceInfo;
934                                    } else {
935                                        Log.e(TAG, "getApIface failed: " + statusString(status));
936                                    }
937                                });
938                        if (!statusOk.value) {
939                            return null;
940                        }
941                    }
942
943                    ifaceIndex.value = 0;
944                    chipResp.value.getP2pIfaceNames(
945                            (WifiStatus status, ArrayList<String> ifnames) -> {
946                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
947                                if (statusOk.value) {
948                                    ifaceNamesResp.value = ifnames;
949                                } else {
950                                    Log.e(TAG, "getP2pIfaceNames failed: " + statusString(status));
951                                }
952                            });
953                    if (!statusOk.value) {
954                        return null;
955                    }
956
957                    WifiIfaceInfo[] p2pIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
958                    for (String ifaceName: ifaceNamesResp.value) {
959                        chipResp.value.getP2pIface(ifaceName,
960                                (WifiStatus status, IWifiP2pIface iface) -> {
961                                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
962                                    if (statusOk.value) {
963                                        WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
964                                        ifaceInfo.name = ifaceName;
965                                        ifaceInfo.iface = iface;
966                                        p2pIfaces[ifaceIndex.value++] = ifaceInfo;
967                                    } else {
968                                        Log.e(TAG, "getP2pIface failed: " + statusString(status));
969                                    }
970                                });
971                        if (!statusOk.value) {
972                            return null;
973                        }
974                    }
975
976                    ifaceIndex.value = 0;
977                    chipResp.value.getNanIfaceNames(
978                            (WifiStatus status, ArrayList<String> ifnames) -> {
979                                statusOk.value = status.code == WifiStatusCode.SUCCESS;
980                                if (statusOk.value) {
981                                    ifaceNamesResp.value = ifnames;
982                                } else {
983                                    Log.e(TAG, "getNanIfaceNames failed: " + statusString(status));
984                                }
985                            });
986                    if (!statusOk.value) {
987                        return null;
988                    }
989
990                    WifiIfaceInfo[] nanIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
991                    for (String ifaceName: ifaceNamesResp.value) {
992                        chipResp.value.getNanIface(ifaceName,
993                                (WifiStatus status, IWifiNanIface iface) -> {
994                                    statusOk.value = status.code == WifiStatusCode.SUCCESS;
995                                    if (statusOk.value) {
996                                        WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
997                                        ifaceInfo.name = ifaceName;
998                                        ifaceInfo.iface = iface;
999                                        nanIfaces[ifaceIndex.value++] = ifaceInfo;
1000                                    } else {
1001                                        Log.e(TAG, "getNanIface failed: " + statusString(status));
1002                                    }
1003                                });
1004                        if (!statusOk.value) {
1005                            return null;
1006                        }
1007                    }
1008
1009                    WifiChipInfo chipInfo = new WifiChipInfo();
1010                    chipsInfo[chipInfoIndex++] = chipInfo;
1011
1012                    chipInfo.chip = chipResp.value;
1013                    chipInfo.chipId = chipId;
1014                    chipInfo.availableModes = availableModesResp.value;
1015                    chipInfo.currentModeIdValid = currentModeValidResp.value;
1016                    chipInfo.currentModeId = currentModeResp.value;
1017                    chipInfo.ifaces[IfaceType.STA] = staIfaces;
1018                    chipInfo.ifaces[IfaceType.AP] = apIfaces;
1019                    chipInfo.ifaces[IfaceType.P2P] = p2pIfaces;
1020                    chipInfo.ifaces[IfaceType.NAN] = nanIfaces;
1021                }
1022
1023                return chipsInfo;
1024            } catch (RemoteException e) {
1025                Log.e(TAG, "getAllChipInfoAndValidateCache exception: " + e);
1026            }
1027        }
1028
1029        return null;
1030    }
1031
1032    /**
1033     * Checks the local state of this object (the cached state) against the input 'chipInfos'
1034     * state (which is a live representation of the Wi-Fi firmware status - read through the HAL).
1035     * Returns 'true' if there are no discrepancies - 'false' otherwise.
1036     *
1037     * A discrepancy is if any local state contains references to a chip or interface which are not
1038     * found on the information read from the chip.
1039     */
1040    private boolean validateInterfaceCache(WifiChipInfo[] chipInfos) {
1041        if (DBG) Log.d(TAG, "validateInterfaceCache");
1042
1043        synchronized (mLock) {
1044            for (Map.Entry<IWifiIface, InterfaceCacheEntry> entry: mInterfaceInfoCache.entrySet()) {
1045                // search for chip
1046                WifiChipInfo matchingChipInfo = null;
1047                for (WifiChipInfo ci: chipInfos) {
1048                    if (ci.chipId == entry.getValue().chipId) {
1049                        matchingChipInfo = ci;
1050                        break;
1051                    }
1052                }
1053                if (matchingChipInfo == null) {
1054                    Log.e(TAG, "validateInterfaceCache: no chip found for " + entry.getValue());
1055                    return false;
1056                }
1057
1058                // search for interface
1059                WifiIfaceInfo[] ifaceInfoList = matchingChipInfo.ifaces[entry.getValue().type];
1060                if (ifaceInfoList == null) {
1061                    Log.e(TAG, "validateInterfaceCache: invalid type on entry " + entry.getValue());
1062                    return false;
1063                }
1064
1065                boolean matchFound = false;
1066                for (WifiIfaceInfo ifaceInfo: ifaceInfoList) {
1067                    if (ifaceInfo.name.equals(entry.getValue().name)) {
1068                        matchFound = true;
1069                        break;
1070                    }
1071                }
1072                if (!matchFound) {
1073                    Log.e(TAG, "validateInterfaceCache: no interface found for "
1074                            + entry.getValue());
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                    mInterfaceInfoCache.put(iface, cacheEntry);
1340                    return iface;
1341                }
1342            }
1343        }
1344
1345        return null;
1346    }
1347
1348    // similar to createIfaceIfPossible - but simpler code: not looking for best option just
1349    // for any option (so terminates on first one).
1350    private boolean isItPossibleToCreateIface(WifiChipInfo[] chipInfos, int ifaceType) {
1351        if (DBG) {
1352            Log.d(TAG, "isItPossibleToCreateIface: chipInfos=" + Arrays.deepToString(chipInfos)
1353                    + ", ifaceType=" + ifaceType);
1354        }
1355
1356        for (WifiChipInfo chipInfo: chipInfos) {
1357            for (IWifiChip.ChipMode chipMode: chipInfo.availableModes) {
1358                for (IWifiChip.ChipIfaceCombination chipIfaceCombo : chipMode
1359                        .availableCombinations) {
1360                    int[][] expandedIfaceCombos = expandIfaceCombos(chipIfaceCombo);
1361                    if (DBG) {
1362                        Log.d(TAG, chipIfaceCombo + " expands to "
1363                                + Arrays.deepToString(expandedIfaceCombos));
1364                    }
1365
1366                    for (int[] expandedIfaceCombo: expandedIfaceCombos) {
1367                        if (canIfaceComboSupportRequest(chipInfo, chipMode, expandedIfaceCombo,
1368                                ifaceType) != null) {
1369                            return true;
1370                        }
1371                    }
1372                }
1373            }
1374        }
1375
1376        return false;
1377    }
1378
1379    /**
1380     * Expands (or provides an alternative representation) of the ChipIfaceCombination as all
1381     * possible combinations of interface.
1382     *
1383     * Returns [# of combinations][4 (IfaceType)]
1384     *
1385     * Note: there could be duplicates - allow (inefficient but ...).
1386     * TODO: optimize by using a Set as opposed to a []: will remove duplicates. Will need to
1387     * provide correct hashes.
1388     */
1389    private int[][] expandIfaceCombos(IWifiChip.ChipIfaceCombination chipIfaceCombo) {
1390        int numOfCombos = 1;
1391        for (IWifiChip.ChipIfaceCombinationLimit limit: chipIfaceCombo.limits) {
1392            for (int i = 0; i < limit.maxIfaces; ++i) {
1393                numOfCombos *= limit.types.size();
1394            }
1395        }
1396
1397        int[][] expandedIfaceCombos = new int[numOfCombos][IFACE_TYPES_BY_PRIORITY.length];
1398
1399        int span = numOfCombos; // span of an individual type (or sub-tree size)
1400        for (IWifiChip.ChipIfaceCombinationLimit limit: chipIfaceCombo.limits) {
1401            for (int i = 0; i < limit.maxIfaces; ++i) {
1402                span /= limit.types.size();
1403                for (int k = 0; k < numOfCombos; ++k) {
1404                    expandedIfaceCombos[k][limit.types.get((k / span) % limit.types.size())]++;
1405                }
1406            }
1407        }
1408
1409        return expandedIfaceCombos;
1410    }
1411
1412    private class IfaceCreationData {
1413        public WifiChipInfo chipInfo;
1414        public int chipModeId;
1415        public List<WifiIfaceInfo> interfacesToBeRemovedFirst;
1416
1417        @Override
1418        public String toString() {
1419            StringBuilder sb = new StringBuilder();
1420            sb.append("{chipInfo=").append(chipInfo).append(", chipModeId=").append(chipModeId)
1421                    .append(", interfacesToBeRemovedFirst=").append(interfacesToBeRemovedFirst)
1422                    .append(")");
1423            return sb.toString();
1424        }
1425    }
1426
1427    /**
1428     * Checks whether the input chip-iface-combo can support the requested interface type: if not
1429     * then returns null, if yes then returns information containing the list of interfaces which
1430     * would have to be removed first before the requested interface can be created.
1431     *
1432     * Note: the list of interfaces to be removed is EMPTY if a chip mode change is required - in
1433     * that case ALL the interfaces on the current chip have to be removed first.
1434     *
1435     * Response determined based on:
1436     * - Mode configuration: i.e. could the mode support the interface type in principle
1437     * - Priority information: i.e. are we 'allowed' to remove interfaces in order to create the
1438     *   requested interface
1439     */
1440    private IfaceCreationData canIfaceComboSupportRequest(WifiChipInfo chipInfo,
1441            IWifiChip.ChipMode chipMode, int[] chipIfaceCombo, int ifaceType) {
1442        if (DBG) {
1443            Log.d(TAG, "canIfaceComboSupportRequest: chipInfo=" + chipInfo + ", chipMode="
1444                    + chipMode + ", chipIfaceCombo=" + chipIfaceCombo + ", ifaceType=" + ifaceType);
1445        }
1446
1447        // short-circuit: does the chipIfaceCombo even support the requested type?
1448        if (chipIfaceCombo[ifaceType] == 0) {
1449            if (DBG) Log.d(TAG, "Requested type not supported by combo");
1450            return null;
1451        }
1452
1453        boolean isChipModeChangeProposed =
1454                chipInfo.currentModeIdValid && chipInfo.currentModeId != chipMode.id;
1455
1456        // short-circuit: can't change chip-mode if an existing interface on this chip has a higher
1457        // priority than the requested interface
1458        if (isChipModeChangeProposed) {
1459            for (int type: IFACE_TYPES_BY_PRIORITY) {
1460                if (chipInfo.ifaces[type].length != 0) {
1461                    if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType)) {
1462                        if (DBG) {
1463                            Log.d(TAG, "Couldn't delete existing type " + type
1464                                    + " interfaces for requested type");
1465                        }
1466                        return null;
1467                    }
1468                }
1469            }
1470
1471            // but if priority allows the mode change then we're good to go
1472            IfaceCreationData ifaceCreationData = new IfaceCreationData();
1473            ifaceCreationData.chipInfo = chipInfo;
1474            ifaceCreationData.chipModeId = chipMode.id;
1475
1476            return ifaceCreationData;
1477        }
1478
1479        // possibly supported
1480        List<WifiIfaceInfo> interfacesToBeRemovedFirst = new ArrayList<>();
1481
1482        for (int type: IFACE_TYPES_BY_PRIORITY) {
1483            int tooManyInterfaces = chipInfo.ifaces[type].length - chipIfaceCombo[type];
1484
1485            // need to count the requested interface as well
1486            if (type == ifaceType) {
1487                tooManyInterfaces += 1;
1488            }
1489
1490            if (tooManyInterfaces > 0) { // may need to delete some
1491                if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType)) {
1492                    if (DBG) {
1493                        Log.d(TAG, "Would need to delete some higher priority interfaces");
1494                    }
1495                    return null;
1496                }
1497
1498                // arbitrarily pick the first interfaces to delete
1499                for (int i = 0; i < tooManyInterfaces; ++i) {
1500                    interfacesToBeRemovedFirst.add(chipInfo.ifaces[type][i]);
1501                }
1502            }
1503        }
1504
1505        IfaceCreationData ifaceCreationData = new IfaceCreationData();
1506        ifaceCreationData.chipInfo = chipInfo;
1507        ifaceCreationData.chipModeId = chipMode.id;
1508        ifaceCreationData.interfacesToBeRemovedFirst = interfacesToBeRemovedFirst;
1509
1510        return ifaceCreationData;
1511    }
1512
1513    /**
1514     * Compares two options to create an interface and determines which is the 'best'. Returns
1515     * true if proposal 1 (val1) is better, other false.
1516     *
1517     * Note: both proposals are 'acceptable' bases on priority criteria.
1518     *
1519     * Criteria:
1520     * - Proposal is better if it means removing fewer high priority interfaces
1521     */
1522    private boolean compareIfaceCreationData(IfaceCreationData val1, IfaceCreationData val2) {
1523        if (DBG) Log.d(TAG, "compareIfaceCreationData: val1=" + val1 + ", val2=" + val2);
1524
1525        // deal with trivial case of one or the other being null
1526        if (val1 == null) {
1527            return false;
1528        } else if (val2 == null) {
1529            return true;
1530        }
1531
1532        for (int type: IFACE_TYPES_BY_PRIORITY) {
1533            // # of interfaces to be deleted: the list or all interfaces of the type if mode change
1534            int numIfacesToDelete1 = 0;
1535            if (val1.chipInfo.currentModeIdValid
1536                    && val1.chipInfo.currentModeId != val1.chipModeId) {
1537                numIfacesToDelete1 = val1.chipInfo.ifaces[type].length;
1538            } else {
1539                numIfacesToDelete1 = val1.interfacesToBeRemovedFirst.size();
1540            }
1541
1542            int numIfacesToDelete2 = 0;
1543            if (val2.chipInfo.currentModeIdValid
1544                    && val2.chipInfo.currentModeId != val2.chipModeId) {
1545                numIfacesToDelete2 = val2.chipInfo.ifaces[type].length;
1546            } else {
1547                numIfacesToDelete2 = val2.interfacesToBeRemovedFirst.size();
1548            }
1549
1550            if (numIfacesToDelete1 < numIfacesToDelete2) {
1551                if (DBG) {
1552                    Log.d(TAG, "decision based on type=" + type + ": " + numIfacesToDelete1
1553                            + " < " + numIfacesToDelete2);
1554                }
1555                return true;
1556            }
1557        }
1558
1559        // arbitrary - flip a coin
1560        if (DBG) Log.d(TAG, "proposals identical - flip a coin");
1561        return false;
1562    }
1563
1564    /**
1565     * Returns true if we're allowed to delete the existing interface type for the requested
1566     * interface type.
1567     *
1568     * Rules:
1569     * 1. Request for AP or STA will destroy any other interface (except see #4)
1570     * 2. Request for P2P will destroy NAN-only
1571     * 3. Request for NAN will not destroy any interface
1572     * --
1573     * 4. No interface will be destroyed for a requested interface of the same type
1574     */
1575    private boolean allowedToDeleteIfaceTypeForRequestedType(int existingIfaceType,
1576            int requestedIfaceType) {
1577        // rule 4
1578        if (existingIfaceType == requestedIfaceType) {
1579            return false;
1580        }
1581
1582        // rule 3
1583        if (requestedIfaceType == IfaceType.NAN) {
1584            return false;
1585        }
1586
1587        // rule 2
1588        if (requestedIfaceType == IfaceType.P2P) {
1589            return existingIfaceType == IfaceType.NAN;
1590        }
1591
1592        // rule 1, the requestIfaceType is either AP or STA
1593        return true;
1594    }
1595
1596    /**
1597     * Performs chip reconfiguration per the input:
1598     * - Removes the specified interfaces
1599     * - Reconfigures the chip to the new chip mode (if necessary)
1600     * - Creates the new interface
1601     *
1602     * Returns the newly created interface or a null on any error.
1603     */
1604    private IWifiIface executeChipReconfiguration(IfaceCreationData ifaceCreationData,
1605            int ifaceType) {
1606        if (DBG) {
1607            Log.d(TAG, "executeChipReconfiguration: ifaceCreationData=" + ifaceCreationData
1608                    + ", ifaceType=" + ifaceType);
1609        }
1610        synchronized (mLock) {
1611            try {
1612                // is this a mode change?
1613                boolean isModeConfigNeeded = !ifaceCreationData.chipInfo.currentModeIdValid
1614                        || ifaceCreationData.chipInfo.currentModeId != ifaceCreationData.chipModeId;
1615                if (DBG) Log.d(TAG, "isModeConfigNeeded=" + isModeConfigNeeded);
1616
1617                // first delete interfaces/change modes
1618                if (isModeConfigNeeded) {
1619                    // remove all interfaces pre mode-change
1620                    // TODO: is this necessary? note that even if we don't want to explicitly
1621                    // remove the interfaces we do need to call the onDeleted callbacks - which
1622                    // this does
1623                    for (WifiIfaceInfo[] ifaceInfos: ifaceCreationData.chipInfo.ifaces) {
1624                        for (WifiIfaceInfo ifaceInfo: ifaceInfos) {
1625                            removeIfaceInternal(ifaceInfo.iface); // ignore return value
1626                        }
1627                    }
1628
1629                    WifiStatus status = ifaceCreationData.chipInfo.chip.configureChip(
1630                            ifaceCreationData.chipModeId);
1631                    if (status.code != WifiStatusCode.SUCCESS) {
1632                        Log.e(TAG, "executeChipReconfiguration: configureChip error: "
1633                                + statusString(status));
1634                        return null;
1635                    }
1636                } else {
1637                    // remove all interfaces on the delete list
1638                    for (WifiIfaceInfo ifaceInfo: ifaceCreationData.interfacesToBeRemovedFirst) {
1639                        removeIfaceInternal(ifaceInfo.iface); // ignore return value
1640                    }
1641                }
1642
1643                // create new interface
1644                Mutable<WifiStatus> statusResp = new Mutable<>();
1645                Mutable<IWifiIface> ifaceResp = new Mutable<>();
1646                switch (ifaceType) {
1647                    case IfaceType.STA:
1648                        ifaceCreationData.chipInfo.chip.createStaIface(
1649                                (WifiStatus status, IWifiStaIface iface) -> {
1650                                    statusResp.value = status;
1651                                    ifaceResp.value = iface;
1652                                });
1653                        break;
1654                    case IfaceType.AP:
1655                        ifaceCreationData.chipInfo.chip.createApIface(
1656                                (WifiStatus status, IWifiApIface iface) -> {
1657                                    statusResp.value = status;
1658                                    ifaceResp.value = iface;
1659                                });
1660                        break;
1661                    case IfaceType.P2P:
1662                        ifaceCreationData.chipInfo.chip.createP2pIface(
1663                                (WifiStatus status, IWifiP2pIface iface) -> {
1664                                    statusResp.value = status;
1665                                    ifaceResp.value = iface;
1666                                });
1667                        break;
1668                    case IfaceType.NAN:
1669                        ifaceCreationData.chipInfo.chip.createNanIface(
1670                                (WifiStatus status, IWifiNanIface iface) -> {
1671                                    statusResp.value = status;
1672                                    ifaceResp.value = iface;
1673                                });
1674                        break;
1675                }
1676
1677                if (statusResp.value.code != WifiStatusCode.SUCCESS) {
1678                    Log.e(TAG, "executeChipReconfiguration: failed to create interface ifaceType="
1679                            + ifaceType + ": " + statusString(statusResp.value));
1680                    return null;
1681                }
1682
1683                return ifaceResp.value;
1684            } catch (RemoteException e) {
1685                Log.e(TAG, "executeChipReconfiguration exception: " + e);
1686                return null;
1687            }
1688        }
1689    }
1690
1691    private boolean removeIfaceInternal(IWifiIface iface) {
1692        if (DBG) Log.d(TAG, "removeIfaceInternal: iface(name)=" + getName(iface));
1693
1694        synchronized (mLock) {
1695            if (mWifi == null) {
1696                Log.e(TAG, "removeIfaceInternal: null IWifi -- iface(name)=" + getName(iface));
1697                return false;
1698            }
1699
1700            IWifiChip chip = getChip(iface);
1701            if (chip == null) {
1702                Log.e(TAG, "removeIfaceInternal: null IWifiChip -- iface(name)=" + getName(iface));
1703                return false;
1704            }
1705
1706            String name = getName(iface);
1707            if (name == null) {
1708                Log.e(TAG, "removeIfaceInternal: can't get name");
1709                return false;
1710            }
1711
1712            int type = getType(iface);
1713            if (type == -1) {
1714                Log.e(TAG, "removeIfaceInternal: can't get type -- iface(name)=" + getName(iface));
1715                return false;
1716            }
1717
1718            WifiStatus status = null;
1719            try {
1720                switch (type) {
1721                    case IfaceType.STA:
1722                        status = chip.removeStaIface(name);
1723                        break;
1724                    case IfaceType.AP:
1725                        status = chip.removeApIface(name);
1726                        break;
1727                    case IfaceType.P2P:
1728                        status = chip.removeP2pIface(name);
1729                        break;
1730                    case IfaceType.NAN:
1731                        status = chip.removeNanIface(name);
1732                        break;
1733                    default:
1734                        Log.wtf(TAG, "removeIfaceInternal: invalid type=" + type);
1735                        return false;
1736                }
1737            } catch (RemoteException e) {
1738                Log.e(TAG, "IWifiChip.removeXxxIface exception: " + e);
1739            }
1740
1741            // dispatch listeners no matter what status
1742            dispatchDestroyedListeners(iface);
1743
1744            if (status != null && status.code == WifiStatusCode.SUCCESS) {
1745                return true;
1746            } else {
1747                Log.e(TAG, "IWifiChip.removeXxxIface failed: " + statusString(status));
1748                return false;
1749            }
1750        }
1751    }
1752
1753    // dispatch all available for request listeners of the specified type AND clean-out the list:
1754    // listeners are called once at most!
1755    private boolean dispatchAvailableForRequestListeners() {
1756        if (DBG) Log.d(TAG, "dispatchAvailableForRequestListeners");
1757
1758        synchronized (mLock) {
1759            WifiChipInfo[] chipInfos = getAllChipInfo();
1760            if (chipInfos == null) {
1761                Log.e(TAG, "dispatchAvailableForRequestListeners: no chip info found");
1762                stopWifi(); // major error: shutting down
1763                return false;
1764            }
1765            if (DBG) {
1766                Log.d(TAG, "dispatchAvailableForRequestListeners: chipInfos="
1767                        + Arrays.deepToString(chipInfos));
1768            }
1769
1770            for (int ifaceType : IFACE_TYPES_BY_PRIORITY) {
1771                dispatchAvailableForRequestListenersForType(ifaceType, chipInfos);
1772            }
1773        }
1774
1775        return true;
1776    }
1777
1778    private void dispatchAvailableForRequestListenersForType(int ifaceType,
1779            WifiChipInfo[] chipInfos) {
1780        if (DBG) Log.d(TAG, "dispatchAvailableForRequestListenersForType: ifaceType=" + ifaceType);
1781
1782        Set<InterfaceAvailableForRequestListenerProxy> listeners =
1783                mInterfaceAvailableForRequestListeners.get(ifaceType);
1784
1785        if (listeners.size() == 0) {
1786            return;
1787        }
1788
1789        if (!isItPossibleToCreateIface(chipInfos, ifaceType)) {
1790            if (DBG) Log.d(TAG, "Creating interface type isn't possible: ifaceType=" + ifaceType);
1791            return;
1792        }
1793
1794        if (DBG) Log.d(TAG, "It is possible to create the interface type: ifaceType=" + ifaceType);
1795        for (InterfaceAvailableForRequestListenerProxy listener : listeners) {
1796            listener.trigger();
1797        }
1798    }
1799
1800    // dispatch all destroyed listeners registered for the specified interface AND remove the
1801    // cache entry
1802    private void dispatchDestroyedListeners(IWifiIface iface) {
1803        if (DBG) Log.d(TAG, "dispatchDestroyedListeners: iface(name)=" + getName(iface));
1804
1805        synchronized (mLock) {
1806            InterfaceCacheEntry entry = mInterfaceInfoCache.get(iface);
1807            if (entry == null) {
1808                Log.e(TAG, "dispatchDestroyedListeners: no cache entry for iface(name)="
1809                        + getName(iface));
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(iface);
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<IWifiIface, 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