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