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