WifiScanningServiceImpl.java revision 11bb0926a6cc3380f2217532fefb9605a1fdc9e8
1/*
2 * Copyright (C) 2008 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.scanner;
18
19import android.Manifest;
20import android.app.AlarmManager;
21import android.app.PendingIntent;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.PackageManager;
27import android.net.wifi.IWifiScanner;
28import android.net.wifi.ScanResult;
29import android.net.wifi.WifiManager;
30import android.net.wifi.WifiScanner;
31import android.net.wifi.WifiScanner.BssidInfo;
32import android.net.wifi.WifiScanner.ChannelSpec;
33import android.net.wifi.WifiScanner.PnoSettings;
34import android.net.wifi.WifiScanner.ScanData;
35import android.net.wifi.WifiScanner.ScanSettings;
36import android.os.Binder;
37import android.os.Bundle;
38import android.os.Handler;
39import android.os.Looper;
40import android.os.Message;
41import android.os.Messenger;
42import android.os.RemoteException;
43import android.os.WorkSource;
44import android.util.ArrayMap;
45import android.util.LocalLog;
46import android.util.Log;
47import android.util.Pair;
48
49import com.android.internal.app.IBatteryStats;
50import com.android.internal.util.AsyncChannel;
51import com.android.internal.util.Protocol;
52import com.android.internal.util.State;
53import com.android.internal.util.StateMachine;
54import com.android.server.wifi.Clock;
55import com.android.server.wifi.WifiInjector;
56import com.android.server.wifi.WifiMetrics;
57import com.android.server.wifi.WifiMetricsProto;
58import com.android.server.wifi.WifiNative;
59import com.android.server.wifi.WifiStateMachine;
60import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection;
61
62import java.io.FileDescriptor;
63import java.io.PrintWriter;
64import java.util.ArrayList;
65import java.util.Collection;
66import java.util.HashMap;
67import java.util.HashSet;
68import java.util.Iterator;
69import java.util.Set;
70
71public class WifiScanningServiceImpl extends IWifiScanner.Stub {
72
73    private static final String TAG = WifiScanningService.TAG;
74    private static final boolean DBG = false;
75
76    private static final int MIN_PERIOD_PER_CHANNEL_MS = 200;               // DFS needs 120 ms
77    private static final int UNKNOWN_PID = -1;
78
79    private final LocalLog mLocalLog = new LocalLog(1024);
80
81    private void localLog(String message) {
82        mLocalLog.log(message);
83    }
84
85    private void logw(String message) {
86        Log.w(TAG, message);
87        mLocalLog.log(message);
88    }
89
90    private void loge(String message) {
91        Log.e(TAG, message);
92        mLocalLog.log(message);
93    }
94
95    private WifiScannerImpl mScannerImpl;
96
97    @Override
98    public Messenger getMessenger() {
99        if (mClientHandler != null) {
100            return new Messenger(mClientHandler);
101        } else {
102            loge("WifiScanningServiceImpl trying to get messenger w/o initialization");
103            return null;
104        }
105    }
106
107    @Override
108    public Bundle getAvailableChannels(int band) {
109        mChannelHelper.updateChannels();
110        ChannelSpec[] channelSpecs = mChannelHelper.getAvailableScanChannels(band);
111        ArrayList<Integer> list = new ArrayList<Integer>(channelSpecs.length);
112        for (ChannelSpec channelSpec : channelSpecs) {
113            list.add(channelSpec.frequency);
114        }
115        Bundle b = new Bundle();
116        b.putIntegerArrayList(WifiScanner.GET_AVAILABLE_CHANNELS_EXTRA, list);
117        return b;
118    }
119
120    private void enforceLocationHardwarePermission(int uid) {
121        mContext.enforcePermission(
122                Manifest.permission.LOCATION_HARDWARE,
123                UNKNOWN_PID, uid,
124                "LocationHardware");
125    }
126
127    private class ClientHandler extends Handler {
128
129        ClientHandler(Looper looper) {
130            super(looper);
131        }
132
133        @Override
134        public void handleMessage(Message msg) {
135            switch (msg.what) {
136                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
137                    ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo);
138                    if (client != null) {
139                        logw("duplicate client connection: " + msg.sendingUid);
140                        client.mChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
141                                AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
142                        return;
143                    }
144
145                    AsyncChannel ac = new AsyncChannel();
146                    ac.connected(mContext, this, msg.replyTo);
147
148                    client = new ExternalClientInfo(msg.sendingUid, msg.replyTo, ac);
149                    client.register();
150
151                    ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
152                            AsyncChannel.STATUS_SUCCESSFUL);
153
154                    if (DBG) Log.d(TAG, "client connected: " + client);
155                    return;
156                }
157                case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
158                    ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo);
159                    if (client != null) {
160                        client.mChannel.disconnect();
161                    }
162                    return;
163                }
164                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
165                    ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo);
166                    if (client != null) {
167                        if (DBG) {
168                            Log.d(TAG, "client disconnected: " + client + ", reason: " + msg.arg1);
169                        }
170                        client.cleanup();
171                    }
172                    return;
173                }
174            }
175
176            try {
177                enforceLocationHardwarePermission(msg.sendingUid);
178            } catch (SecurityException e) {
179                localLog("failed to authorize app: " + e);
180                replyFailed(msg, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
181                return;
182            }
183
184            // Since this message is sent from WifiScanner using |sendMessageSynchronously| which
185            // doesn't set the correct |msg.replyTo| field.
186            if (msg.what == WifiScanner.CMD_GET_SCAN_RESULTS) {
187                mBackgroundScanStateMachine.sendMessage(Message.obtain(msg));
188                return;
189            }
190
191            ClientInfo ci = mClients.get(msg.replyTo);
192            if (ci == null) {
193                loge("Could not find client info for message " + msg.replyTo);
194                replyFailed(msg, WifiScanner.REASON_INVALID_LISTENER, "Could not find listener");
195                return;
196            }
197
198            switch (msg.what) {
199                case WifiScanner.CMD_START_BACKGROUND_SCAN:
200                case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
201                case WifiScanner.CMD_SET_HOTLIST:
202                case WifiScanner.CMD_RESET_HOTLIST:
203                    mBackgroundScanStateMachine.sendMessage(Message.obtain(msg));
204                    break;
205                case WifiScanner.CMD_START_PNO_SCAN:
206                case WifiScanner.CMD_STOP_PNO_SCAN:
207                    mPnoScanStateMachine.sendMessage(Message.obtain(msg));
208                    break;
209                case WifiScanner.CMD_START_SINGLE_SCAN:
210                case WifiScanner.CMD_STOP_SINGLE_SCAN:
211                    mSingleScanStateMachine.sendMessage(Message.obtain(msg));
212                    break;
213                case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
214                case WifiScanner.CMD_START_TRACKING_CHANGE:
215                case WifiScanner.CMD_STOP_TRACKING_CHANGE:
216                    mWifiChangeStateMachine.sendMessage(Message.obtain(msg));
217                    break;
218                default:
219                    replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "Invalid request");
220                    break;
221            }
222        }
223    }
224
225    private static final int BASE = Protocol.BASE_WIFI_SCANNER_SERVICE;
226
227    private static final int CMD_SCAN_RESULTS_AVAILABLE              = BASE + 0;
228    private static final int CMD_FULL_SCAN_RESULTS                   = BASE + 1;
229    private static final int CMD_HOTLIST_AP_FOUND                    = BASE + 2;
230    private static final int CMD_HOTLIST_AP_LOST                     = BASE + 3;
231    private static final int CMD_WIFI_CHANGE_DETECTED                = BASE + 4;
232    private static final int CMD_WIFI_CHANGE_TIMEOUT                 = BASE + 5;
233    private static final int CMD_DRIVER_LOADED                       = BASE + 6;
234    private static final int CMD_DRIVER_UNLOADED                     = BASE + 7;
235    private static final int CMD_SCAN_PAUSED                         = BASE + 8;
236    private static final int CMD_SCAN_RESTARTED                      = BASE + 9;
237    private static final int CMD_SCAN_FAILED                         = BASE + 10;
238    private static final int CMD_PNO_NETWORK_FOUND                   = BASE + 11;
239    private static final int CMD_PNO_SCAN_FAILED                     = BASE + 12;
240
241    private final Context mContext;
242    private final Looper mLooper;
243    private final WifiScannerImpl.WifiScannerImplFactory mScannerImplFactory;
244    private final ArrayMap<Messenger, ClientInfo> mClients;
245
246    private ChannelHelper mChannelHelper;
247    private BackgroundScanScheduler mBackgroundScheduler;
248    private WifiNative.ScanSettings mPreviousSchedule;
249
250    private WifiBackgroundScanStateMachine mBackgroundScanStateMachine;
251    private WifiSingleScanStateMachine mSingleScanStateMachine;
252    private WifiChangeStateMachine mWifiChangeStateMachine;
253    private WifiPnoScanStateMachine mPnoScanStateMachine;
254    private ClientHandler mClientHandler;
255    private final IBatteryStats mBatteryStats;
256    private final AlarmManager mAlarmManager;
257    private final WifiMetrics mWifiMetrics;
258    private final Clock mClock;
259
260    WifiScanningServiceImpl(Context context, Looper looper,
261            WifiScannerImpl.WifiScannerImplFactory scannerImplFactory, IBatteryStats batteryStats,
262            WifiInjector wifiInjector) {
263        mContext = context;
264        mLooper = looper;
265        mScannerImplFactory = scannerImplFactory;
266        mBatteryStats = batteryStats;
267        mClients = new ArrayMap<>();
268        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
269        mWifiMetrics = wifiInjector.getWifiMetrics();
270        mClock = wifiInjector.getClock();
271
272        mPreviousSchedule = null;
273    }
274
275    public void startService() {
276        mClientHandler = new ClientHandler(mLooper);
277        mBackgroundScanStateMachine = new WifiBackgroundScanStateMachine(mLooper);
278        mWifiChangeStateMachine = new WifiChangeStateMachine(mLooper);
279        mSingleScanStateMachine = new WifiSingleScanStateMachine(mLooper);
280        mPnoScanStateMachine = new WifiPnoScanStateMachine(mLooper);
281
282        mContext.registerReceiver(
283                new BroadcastReceiver() {
284                    @Override
285                    public void onReceive(Context context, Intent intent) {
286                        int state = intent.getIntExtra(
287                                WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED);
288                        if (DBG) localLog("SCAN_AVAILABLE : " + state);
289                        if (state == WifiManager.WIFI_STATE_ENABLED) {
290                            mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_LOADED);
291                            mSingleScanStateMachine.sendMessage(CMD_DRIVER_LOADED);
292                            mPnoScanStateMachine.sendMessage(CMD_DRIVER_LOADED);
293                        } else if (state == WifiManager.WIFI_STATE_DISABLED) {
294                            mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
295                            mSingleScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
296                            mPnoScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
297                        }
298                    }
299                }, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE));
300
301        mBackgroundScanStateMachine.start();
302        mWifiChangeStateMachine.start();
303        mSingleScanStateMachine.start();
304        mPnoScanStateMachine.start();
305    }
306
307    private static boolean isWorkSourceValid(WorkSource workSource) {
308        return workSource != null && workSource.size() > 0 && workSource.get(0) >= 0;
309    }
310
311    private WorkSource computeWorkSource(ClientInfo ci, WorkSource requestedWorkSource) {
312        if (requestedWorkSource != null) {
313            if (isWorkSourceValid(requestedWorkSource)) {
314                // Wifi currently doesn't use names, so need to clear names out of the
315                // supplied WorkSource to allow future WorkSource combining.
316                requestedWorkSource.clearNames();
317                return requestedWorkSource;
318            } else {
319                loge("Got invalid work source request: " + requestedWorkSource.toString() +
320                        " from " + ci);
321            }
322        }
323        WorkSource callingWorkSource = new WorkSource(ci.getUid());
324        if (isWorkSourceValid(callingWorkSource)) {
325            return callingWorkSource;
326        } else {
327            loge("Client has invalid work source: " + callingWorkSource);
328            return new WorkSource();
329        }
330    }
331
332    private class RequestInfo<T> {
333        final ClientInfo clientInfo;
334        final int handlerId;
335        final WorkSource workSource;
336        final T settings;
337
338        RequestInfo(ClientInfo clientInfo, int handlerId, WorkSource requestedWorkSource,
339                T settings) {
340            this.clientInfo = clientInfo;
341            this.handlerId = handlerId;
342            this.settings = settings;
343            this.workSource = computeWorkSource(clientInfo, requestedWorkSource);
344        }
345
346        void reportEvent(int what, int arg1, Object obj) {
347            clientInfo.reportEvent(what, arg1, handlerId, obj);
348        }
349    }
350
351    private class RequestList<T> extends ArrayList<RequestInfo<T>> {
352        void addRequest(ClientInfo ci, int handler, WorkSource reqworkSource, T settings) {
353            add(new RequestInfo<T>(ci, handler, reqworkSource, settings));
354        }
355
356        T removeRequest(ClientInfo ci, int handlerId) {
357            T removed = null;
358            Iterator<RequestInfo<T>> iter = iterator();
359            while (iter.hasNext()) {
360                RequestInfo<T> entry = iter.next();
361                if (entry.clientInfo == ci && entry.handlerId == handlerId) {
362                    removed = entry.settings;
363                    iter.remove();
364                }
365            }
366            return removed;
367        }
368
369        Collection<T> getAllSettings() {
370            ArrayList<T> settingsList = new ArrayList<>();
371            Iterator<RequestInfo<T>> iter = iterator();
372            while (iter.hasNext()) {
373                RequestInfo<T> entry = iter.next();
374                settingsList.add(entry.settings);
375            }
376            return settingsList;
377        }
378
379        Collection<T> getAllSettingsForClient(ClientInfo ci) {
380            ArrayList<T> settingsList = new ArrayList<>();
381            Iterator<RequestInfo<T>> iter = iterator();
382            while (iter.hasNext()) {
383                RequestInfo<T> entry = iter.next();
384                if (entry.clientInfo == ci) {
385                    settingsList.add(entry.settings);
386                }
387            }
388            return settingsList;
389        }
390
391        void removeAllForClient(ClientInfo ci) {
392            Iterator<RequestInfo<T>> iter = iterator();
393            while (iter.hasNext()) {
394                RequestInfo<T> entry = iter.next();
395                if (entry.clientInfo == ci) {
396                    iter.remove();
397                }
398            }
399        }
400
401        WorkSource createMergedWorkSource() {
402            WorkSource mergedSource = new WorkSource();
403            for (RequestInfo<T> entry : this) {
404                mergedSource.add(entry.workSource);
405            }
406            return mergedSource;
407        }
408    }
409
410    /**
411     * State machine that holds the state of single scans. Scans should only be active in the
412     * ScanningState. The pending scans and active scans maps are swaped when entering
413     * ScanningState. Any requests queued while scanning will be placed in the pending queue and
414     * executed after transitioning back to IdleState.
415     */
416    class WifiSingleScanStateMachine extends StateMachine implements WifiNative.ScanEventHandler {
417        private final DefaultState mDefaultState = new DefaultState();
418        private final DriverStartedState mDriverStartedState = new DriverStartedState();
419        private final IdleState  mIdleState  = new IdleState();
420        private final ScanningState  mScanningState  = new ScanningState();
421
422        private RequestList<ScanSettings> mActiveScans = new RequestList<>();
423        private RequestList<ScanSettings> mPendingScans = new RequestList<>();
424
425        WifiSingleScanStateMachine(Looper looper) {
426            super("WifiSingleScanStateMachine", looper);
427
428            setLogRecSize(128);
429            setLogOnlyTransitions(false);
430
431            // CHECKSTYLE:OFF IndentationCheck
432            addState(mDefaultState);
433                addState(mDriverStartedState, mDefaultState);
434                    addState(mIdleState, mDriverStartedState);
435                    addState(mScanningState, mDriverStartedState);
436            // CHECKSTYLE:ON IndentationCheck
437
438            setInitialState(mDefaultState);
439        }
440
441        /**
442         * Called to indicate a change in state for the current scan.
443         * Will dispatch a coresponding event to the state machine
444         */
445        @Override
446        public void onScanStatus(int event) {
447            if (DBG) localLog("onScanStatus event received, event=" + event);
448            switch(event) {
449                case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE:
450                case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS:
451                case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT:
452                    sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
453                    break;
454                case WifiNative.WIFI_SCAN_FAILED:
455                    sendMessage(CMD_SCAN_FAILED);
456                    break;
457                default:
458                    Log.e(TAG, "Unknown scan status event: " + event);
459                    break;
460            }
461        }
462
463        /**
464         * Called for each full scan result if requested
465         */
466        @Override
467        public void onFullScanResult(ScanResult fullScanResult, int bucketsScanned) {
468            if (DBG) localLog("onFullScanResult received");
469            sendMessage(CMD_FULL_SCAN_RESULTS, 0, bucketsScanned, fullScanResult);
470        }
471
472        @Override
473        public void onScanPaused(ScanData[] scanData) {
474            // should not happen for single scan
475            Log.e(TAG, "Got scan paused for single scan");
476        }
477
478        @Override
479        public void onScanRestarted() {
480            // should not happen for single scan
481            Log.e(TAG, "Got scan restarted for single scan");
482        }
483
484        class DefaultState extends State {
485            @Override
486            public void enter() {
487                mActiveScans.clear();
488                mPendingScans.clear();
489            }
490            @Override
491            public boolean processMessage(Message msg) {
492                switch (msg.what) {
493                    case CMD_DRIVER_LOADED:
494                        transitionTo(mIdleState);
495                        return HANDLED;
496                    case CMD_DRIVER_UNLOADED:
497                        transitionTo(mDefaultState);
498                        return HANDLED;
499                    case WifiScanner.CMD_START_SINGLE_SCAN:
500                    case WifiScanner.CMD_STOP_SINGLE_SCAN:
501                        replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
502                        return HANDLED;
503                    case CMD_SCAN_RESULTS_AVAILABLE:
504                        if (DBG) localLog("ignored scan results available event");
505                        return HANDLED;
506                    case CMD_FULL_SCAN_RESULTS:
507                        if (DBG) localLog("ignored full scan result event");
508                        return HANDLED;
509                    default:
510                        return NOT_HANDLED;
511                }
512
513            }
514        }
515
516        /**
517         * State representing when the driver is running. This state is not meant to be transitioned
518         * directly, but is instead indented as a parent state of ScanningState and IdleState
519         * to hold common functionality and handle cleaning up scans when the driver is shut down.
520         */
521        class DriverStartedState extends State {
522            @Override
523            public void exit() {
524                mWifiMetrics.incrementScanReturnEntry(
525                        WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED,
526                        mPendingScans.size());
527                sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED,
528                        "Scan was interrupted");
529            }
530
531            @Override
532            public boolean processMessage(Message msg) {
533                ClientInfo ci = mClients.get(msg.replyTo);
534
535                switch (msg.what) {
536                    case WifiScanner.CMD_START_SINGLE_SCAN:
537                        mWifiMetrics.incrementOneshotScanCount();
538                        int handler = msg.arg2;
539                        Bundle scanParams = (Bundle) msg.obj;
540                        if (scanParams == null) {
541                            logCallback("singleScanInvalidRequest",  ci, handler, "null params");
542                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
543                            return HANDLED;
544                        }
545                        scanParams.setDefusable(true);
546                        ScanSettings scanSettings =
547                                scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY);
548                        WorkSource workSource =
549                                scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY);
550                        if (validateAndAddToScanQueue(ci, handler, scanSettings, workSource)) {
551                            replySucceeded(msg);
552                            // If were not currently scanning then try to start a scan. Otherwise
553                            // this scan will be scheduled when transitioning back to IdleState
554                            // after finishing the current scan.
555                            if (getCurrentState() != mScanningState) {
556                                tryToStartNewScan();
557                            }
558                        } else {
559                            logCallback("singleScanInvalidRequest",  ci, handler, "bad request");
560                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
561                            mWifiMetrics.incrementScanReturnEntry(
562                                    WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION, 1);
563                        }
564                        return HANDLED;
565                    case WifiScanner.CMD_STOP_SINGLE_SCAN:
566                        removeSingleScanRequest(ci, msg.arg2);
567                        return HANDLED;
568                    default:
569                        return NOT_HANDLED;
570                }
571            }
572        }
573
574        class IdleState extends State {
575            @Override
576            public void enter() {
577                tryToStartNewScan();
578            }
579
580            @Override
581            public boolean processMessage(Message msg) {
582                return NOT_HANDLED;
583            }
584        }
585
586        class ScanningState extends State {
587            private WorkSource mScanWorkSource;
588
589            @Override
590            public void enter() {
591                mScanWorkSource = mActiveScans.createMergedWorkSource();
592                try {
593                    mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource);
594                } catch (RemoteException e) {
595                    loge(e.toString());
596                }
597            }
598
599            @Override
600            public void exit() {
601                try {
602                    mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource);
603                } catch (RemoteException e) {
604                    loge(e.toString());
605                }
606
607                // if any scans are still active (never got results available then indicate failure)
608                mWifiMetrics.incrementScanReturnEntry(
609                                WifiMetricsProto.WifiLog.SCAN_UNKNOWN,
610                                mActiveScans.size());
611                sendOpFailedToAllAndClear(mActiveScans, WifiScanner.REASON_UNSPECIFIED,
612                        "Scan was interrupted");
613            }
614
615            @Override
616            public boolean processMessage(Message msg) {
617                switch (msg.what) {
618                    case CMD_SCAN_RESULTS_AVAILABLE:
619                        mWifiMetrics.incrementScanReturnEntry(
620                                WifiMetricsProto.WifiLog.SCAN_SUCCESS,
621                                mActiveScans.size());
622                        reportScanResults(mScannerImpl.getLatestSingleScanResults());
623                        mActiveScans.clear();
624                        transitionTo(mIdleState);
625                        return HANDLED;
626                    case CMD_FULL_SCAN_RESULTS:
627                        reportFullScanResult((ScanResult) msg.obj, /* bucketsScanned */ msg.arg2);
628                        return HANDLED;
629                    case CMD_SCAN_FAILED:
630                        mWifiMetrics.incrementScanReturnEntry(
631                                WifiMetricsProto.WifiLog.SCAN_UNKNOWN, mActiveScans.size());
632                        sendOpFailedToAllAndClear(mActiveScans, WifiScanner.REASON_UNSPECIFIED,
633                                "Scan failed");
634                        transitionTo(mIdleState);
635                        return HANDLED;
636                    default:
637                        return NOT_HANDLED;
638                }
639            }
640        }
641
642        boolean validateAndAddToScanQueue(ClientInfo ci, int handler, ScanSettings settings,
643                WorkSource workSource) {
644            if (ci == null) {
645                Log.d(TAG, "Failing single scan request ClientInfo not found " + handler);
646                return false;
647            }
648            if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
649                if (settings.channels == null || settings.channels.length == 0) {
650                    Log.d(TAG, "Failing single scan because channel list was empty");
651                    return false;
652                }
653            }
654            logScanRequest("addSingleScanRequest", ci, handler, workSource, settings, null);
655            mPendingScans.addRequest(ci, handler, workSource, settings);
656            return true;
657        }
658
659        void removeSingleScanRequest(ClientInfo ci, int handler) {
660            if (ci != null) {
661                logScanRequest("removeSingleScanRequest", ci, handler, null, null, null);
662                mPendingScans.removeRequest(ci, handler);
663                mActiveScans.removeRequest(ci, handler);
664            }
665        }
666
667        void removeSingleScanRequests(ClientInfo ci) {
668            if (ci != null) {
669                logScanRequest("removeSingleScanRequests", ci, -1, null, null, null);
670                mPendingScans.removeAllForClient(ci);
671                mActiveScans.removeAllForClient(ci);
672            }
673        }
674
675        void tryToStartNewScan() {
676            if (mPendingScans.size() == 0) { // no pending requests
677                return;
678            }
679            mChannelHelper.updateChannels();
680            // TODO move merging logic to a scheduler
681            WifiNative.ScanSettings settings = new WifiNative.ScanSettings();
682            settings.num_buckets = 1;
683            WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings();
684            bucketSettings.bucket = 0;
685            bucketSettings.period_ms = 0;
686            bucketSettings.report_events = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
687
688            ChannelCollection channels = mChannelHelper.createChannelCollection();
689            HashSet<Integer> hiddenNetworkIdSet = new HashSet<>();
690            for (RequestInfo<ScanSettings> entry : mPendingScans) {
691                channels.addChannels(entry.settings);
692                if (entry.settings.hiddenNetworkIds != null) {
693                    for (int i = 0; i < entry.settings.hiddenNetworkIds.length; i++) {
694                        hiddenNetworkIdSet.add(entry.settings.hiddenNetworkIds[i]);
695                    }
696                }
697                if ((entry.settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)
698                        != 0) {
699                    bucketSettings.report_events |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
700                }
701            }
702            if (hiddenNetworkIdSet.size() > 0) {
703                settings.hiddenNetworkIds = new int[hiddenNetworkIdSet.size()];
704                int numHiddenNetworks = 0;
705                for (Integer hiddenNetworkId : hiddenNetworkIdSet) {
706                    settings.hiddenNetworkIds[numHiddenNetworks++] = hiddenNetworkId;
707                }
708            }
709
710            channels.fillBucketSettings(bucketSettings, Integer.MAX_VALUE);
711
712            settings.buckets = new WifiNative.BucketSettings[] {bucketSettings};
713            if (mScannerImpl.startSingleScan(settings, this)) {
714                // swap pending and active scan requests
715                RequestList<ScanSettings> tmp = mActiveScans;
716                mActiveScans = mPendingScans;
717                mPendingScans = tmp;
718                // make sure that the pending list is clear
719                mPendingScans.clear();
720                transitionTo(mScanningState);
721            } else {
722                mWifiMetrics.incrementScanReturnEntry(
723                        WifiMetricsProto.WifiLog.SCAN_UNKNOWN, mPendingScans.size());
724                // notify and cancel failed scans
725                sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED,
726                        "Failed to start single scan");
727            }
728        }
729
730        void sendOpFailedToAllAndClear(RequestList<?> clientHandlers, int reason,
731                String description) {
732            for (RequestInfo<?> entry : clientHandlers) {
733                logCallback("singleScanFailed",  entry.clientInfo, entry.handlerId,
734                        "reason=" + reason + ", " + description);
735                entry.reportEvent(WifiScanner.CMD_OP_FAILED, 0,
736                        new WifiScanner.OperationResult(reason, description));
737            }
738            clientHandlers.clear();
739        }
740
741        void reportFullScanResult(ScanResult result, int bucketsScanned) {
742            for (RequestInfo<ScanSettings> entry : mActiveScans) {
743                if (ScanScheduleUtil.shouldReportFullScanResultForSettings(mChannelHelper,
744                                result, bucketsScanned, entry.settings, -1)) {
745                    entry.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, result);
746                }
747            }
748        }
749
750        void reportScanResults(ScanData results) {
751            if (results != null && results.getResults() != null) {
752                if (results.getResults().length > 0) {
753                    mWifiMetrics.incrementNonEmptyScanResultCount();
754                } else {
755                    mWifiMetrics.incrementEmptyScanResultCount();
756                }
757            }
758            for (RequestInfo<ScanSettings> entry : mActiveScans) {
759                ScanData[] resultsArray = new ScanData[] {results};
760                ScanData[] resultsToDeliver = ScanScheduleUtil.filterResultsForSettings(
761                        mChannelHelper, resultsArray, entry.settings, -1);
762                WifiScanner.ParcelableScanData parcelableScanData =
763                        new WifiScanner.ParcelableScanData(resultsToDeliver);
764                logCallback("singleScanResults",  entry.clientInfo, entry.handlerId,
765                        describeForLog(resultsToDeliver));
766                entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableScanData);
767                // make sure the handler is removed
768                entry.reportEvent(WifiScanner.CMD_SINGLE_SCAN_COMPLETED, 0, null);
769            }
770        }
771    }
772
773    class WifiBackgroundScanStateMachine extends StateMachine
774            implements WifiNative.ScanEventHandler, WifiNative.HotlistEventHandler {
775
776        private final DefaultState mDefaultState = new DefaultState();
777        private final StartedState mStartedState = new StartedState();
778        private final PausedState  mPausedState  = new PausedState();
779
780        private final RequestList<ScanSettings> mActiveBackgroundScans = new RequestList<>();
781        private final RequestList<WifiScanner.HotlistSettings> mActiveHotlistSettings =
782                new RequestList<>();
783
784        WifiBackgroundScanStateMachine(Looper looper) {
785            super("WifiBackgroundScanStateMachine", looper);
786
787            setLogRecSize(512);
788            setLogOnlyTransitions(false);
789
790            // CHECKSTYLE:OFF IndentationCheck
791            addState(mDefaultState);
792                addState(mStartedState, mDefaultState);
793                addState(mPausedState, mDefaultState);
794            // CHECKSTYLE:ON IndentationCheck
795
796            setInitialState(mDefaultState);
797        }
798
799        public Collection<ScanSettings> getBackgroundScanSettings(ClientInfo ci) {
800            return mActiveBackgroundScans.getAllSettingsForClient(ci);
801        }
802
803        public void removeBackgroundScanSettings(ClientInfo ci) {
804            mActiveBackgroundScans.removeAllForClient(ci);
805            updateSchedule();
806        }
807
808        public void removeHotlistSettings(ClientInfo ci) {
809            mActiveHotlistSettings.removeAllForClient(ci);
810            resetHotlist();
811        }
812
813        @Override
814        public void onScanStatus(int event) {
815            if (DBG) localLog("onScanStatus event received, event=" + event);
816            switch(event) {
817                case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE:
818                case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS:
819                case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT:
820                    sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
821                    break;
822                case WifiNative.WIFI_SCAN_FAILED:
823                    sendMessage(CMD_SCAN_FAILED);
824                    break;
825                default:
826                    Log.e(TAG, "Unknown scan status event: " + event);
827                    break;
828            }
829        }
830
831        @Override
832        public void onFullScanResult(ScanResult fullScanResult, int bucketsScanned) {
833            if (DBG) localLog("onFullScanResult received");
834            sendMessage(CMD_FULL_SCAN_RESULTS, 0, bucketsScanned, fullScanResult);
835        }
836
837        @Override
838        public void onScanPaused(ScanData scanData[]) {
839            if (DBG) localLog("onScanPaused received");
840            sendMessage(CMD_SCAN_PAUSED, scanData);
841        }
842
843        @Override
844        public void onScanRestarted() {
845            if (DBG) localLog("onScanRestarted received");
846            sendMessage(CMD_SCAN_RESTARTED);
847        }
848
849        @Override
850        public void onHotlistApFound(ScanResult[] results) {
851            if (DBG) localLog("onHotlistApFound event received");
852            sendMessage(CMD_HOTLIST_AP_FOUND, 0, 0, results);
853        }
854
855        @Override
856        public void onHotlistApLost(ScanResult[] results) {
857            if (DBG) localLog("onHotlistApLost event received");
858            sendMessage(CMD_HOTLIST_AP_LOST, 0, 0, results);
859        }
860
861        class DefaultState extends State {
862            @Override
863            public void enter() {
864                if (DBG) localLog("DefaultState");
865                mActiveBackgroundScans.clear();
866                mActiveHotlistSettings.clear();
867            }
868
869            @Override
870            public boolean processMessage(Message msg) {
871                switch (msg.what) {
872                    case CMD_DRIVER_LOADED:
873                        // TODO this should be moved to a common location since it is used outside
874                        // of this state machine. It is ok right now because the driver loaded event
875                        // is sent to this state machine first.
876                        if (mScannerImpl == null) {
877                            mScannerImpl = mScannerImplFactory.create(mContext, mLooper, mClock);
878                            mChannelHelper = mScannerImpl.getChannelHelper();
879                        }
880
881                        mBackgroundScheduler = new BackgroundScanScheduler(mChannelHelper);
882
883                        WifiNative.ScanCapabilities capabilities =
884                                new WifiNative.ScanCapabilities();
885                        if (!mScannerImpl.getScanCapabilities(capabilities)) {
886                            loge("could not get scan capabilities");
887                            return HANDLED;
888                        }
889                        mBackgroundScheduler.setMaxBuckets(capabilities.max_scan_buckets);
890                        mBackgroundScheduler.setMaxApPerScan(capabilities.max_ap_cache_per_scan);
891
892                        Log.i(TAG, "wifi driver loaded with scan capabilities: "
893                                + "max buckets=" + capabilities.max_scan_buckets);
894
895                        transitionTo(mStartedState);
896                        return HANDLED;
897                    case CMD_DRIVER_UNLOADED:
898                        Log.i(TAG, "wifi driver unloaded");
899                        transitionTo(mDefaultState);
900                        break;
901                    case WifiScanner.CMD_START_BACKGROUND_SCAN:
902                    case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
903                    case WifiScanner.CMD_START_SINGLE_SCAN:
904                    case WifiScanner.CMD_STOP_SINGLE_SCAN:
905                    case WifiScanner.CMD_SET_HOTLIST:
906                    case WifiScanner.CMD_RESET_HOTLIST:
907                    case WifiScanner.CMD_GET_SCAN_RESULTS:
908                        replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
909                        break;
910
911                    case CMD_SCAN_RESULTS_AVAILABLE:
912                        if (DBG) localLog("ignored scan results available event");
913                        break;
914
915                    case CMD_FULL_SCAN_RESULTS:
916                        if (DBG) localLog("ignored full scan result event");
917                        break;
918
919                    default:
920                        break;
921                }
922
923                return HANDLED;
924            }
925        }
926
927        class StartedState extends State {
928
929            @Override
930            public void enter() {
931                if (DBG) localLog("StartedState");
932            }
933
934            @Override
935            public void exit() {
936                sendBackgroundScanFailedToAllAndClear(
937                        WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted");
938                sendHotlistFailedToAllAndClear(
939                        WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted");
940                mScannerImpl.cleanup();
941            }
942
943            @Override
944            public boolean processMessage(Message msg) {
945                ClientInfo ci = mClients.get(msg.replyTo);
946
947                switch (msg.what) {
948                    case CMD_DRIVER_LOADED:
949                        return NOT_HANDLED;
950                    case CMD_DRIVER_UNLOADED:
951                        return NOT_HANDLED;
952                    case WifiScanner.CMD_START_BACKGROUND_SCAN: {
953                        mWifiMetrics.incrementBackgroundScanCount();
954                        Bundle scanParams = (Bundle) msg.obj;
955                        if (scanParams == null) {
956                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
957                            return HANDLED;
958                        }
959                        scanParams.setDefusable(true);
960                        ScanSettings scanSettings =
961                                scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY);
962                        WorkSource workSource =
963                                scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY);
964                        if (addBackgroundScanRequest(ci, msg.arg2, scanSettings, workSource)) {
965                            replySucceeded(msg);
966                        } else {
967                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
968                        }
969                        break;
970                    }
971                    case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
972                        removeBackgroundScanRequest(ci, msg.arg2);
973                        break;
974                    case WifiScanner.CMD_GET_SCAN_RESULTS:
975                        reportScanResults(mScannerImpl.getLatestBatchedScanResults(true));
976                        replySucceeded(msg);
977                        break;
978                    case WifiScanner.CMD_SET_HOTLIST:
979                        addHotlist(ci, msg.arg2, (WifiScanner.HotlistSettings) msg.obj);
980                        replySucceeded(msg);
981                        break;
982                    case WifiScanner.CMD_RESET_HOTLIST:
983                        removeHotlist(ci, msg.arg2);
984                        break;
985                    case CMD_SCAN_RESULTS_AVAILABLE:
986                        reportScanResults(mScannerImpl.getLatestBatchedScanResults(true));
987                        break;
988                    case CMD_FULL_SCAN_RESULTS:
989                        reportFullScanResult((ScanResult) msg.obj, /* bucketsScanned */ msg.arg2);
990                        break;
991                    case CMD_HOTLIST_AP_FOUND:
992                        reportHotlistResults(WifiScanner.CMD_AP_FOUND, (ScanResult[]) msg.obj);
993                        break;
994                    case CMD_HOTLIST_AP_LOST:
995                        reportHotlistResults(WifiScanner.CMD_AP_LOST, (ScanResult[]) msg.obj);
996                        break;
997                    case CMD_SCAN_PAUSED:
998                        reportScanResults((ScanData[]) msg.obj);
999                        transitionTo(mPausedState);
1000                        break;
1001                    case CMD_SCAN_FAILED:
1002                        Log.e(TAG, "WifiScanner background scan gave CMD_SCAN_FAILED");
1003                        sendBackgroundScanFailedToAllAndClear(
1004                                WifiScanner.REASON_UNSPECIFIED, "Background Scan failed");
1005                        break;
1006                    default:
1007                        return NOT_HANDLED;
1008                }
1009
1010                return HANDLED;
1011            }
1012        }
1013
1014        class PausedState extends State {
1015            @Override
1016            public void enter() {
1017                if (DBG) localLog("PausedState");
1018            }
1019
1020            @Override
1021            public boolean processMessage(Message msg) {
1022                switch (msg.what) {
1023                    case CMD_SCAN_RESTARTED:
1024                        transitionTo(mStartedState);
1025                        break;
1026                    default:
1027                        deferMessage(msg);
1028                        break;
1029                }
1030                return HANDLED;
1031            }
1032        }
1033
1034        private boolean addBackgroundScanRequest(ClientInfo ci, int handler,
1035                ScanSettings settings, WorkSource workSource) {
1036            // sanity check the input
1037            if (ci == null) {
1038                Log.d(TAG, "Failing scan request ClientInfo not found " + handler);
1039                return false;
1040            }
1041            if (settings.periodInMs < WifiScanner.MIN_SCAN_PERIOD_MS) {
1042                loge("Failing scan request because periodInMs is " + settings.periodInMs
1043                        + ", min scan period is: " + WifiScanner.MIN_SCAN_PERIOD_MS);
1044                return false;
1045            }
1046
1047            if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED && settings.channels == null) {
1048                loge("Channels was null with unspecified band");
1049                return false;
1050            }
1051
1052            if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED
1053                    && settings.channels.length == 0) {
1054                loge("No channels specified");
1055                return false;
1056            }
1057
1058            int minSupportedPeriodMs = mChannelHelper.estimateScanDuration(settings);
1059            if (settings.periodInMs < minSupportedPeriodMs) {
1060                loge("Failing scan request because minSupportedPeriodMs is "
1061                        + minSupportedPeriodMs + " but the request wants " + settings.periodInMs);
1062                return false;
1063            }
1064
1065            // check truncated binary exponential back off scan settings
1066            if (settings.maxPeriodInMs != 0 && settings.maxPeriodInMs != settings.periodInMs) {
1067                if (settings.maxPeriodInMs < settings.periodInMs) {
1068                    loge("Failing scan request because maxPeriodInMs is " + settings.maxPeriodInMs
1069                            + " but less than periodInMs " + settings.periodInMs);
1070                    return false;
1071                }
1072                if (settings.maxPeriodInMs > WifiScanner.MAX_SCAN_PERIOD_MS) {
1073                    loge("Failing scan request because maxSupportedPeriodMs is "
1074                            + WifiScanner.MAX_SCAN_PERIOD_MS + " but the request wants "
1075                            + settings.maxPeriodInMs);
1076                    return false;
1077                }
1078                if (settings.stepCount < 1) {
1079                    loge("Failing scan request because stepCount is " + settings.stepCount
1080                            + " which is less than 1");
1081                    return false;
1082                }
1083            }
1084
1085            logScanRequest("addBackgroundScanRequest", ci, handler, null, settings, null);
1086            mActiveBackgroundScans.addRequest(ci, handler, workSource, settings);
1087
1088            if (updateSchedule()) {
1089                return true;
1090            } else {
1091                mActiveBackgroundScans.removeRequest(ci, handler);
1092                localLog("Failing scan request because failed to reset scan");
1093                return false;
1094            }
1095        }
1096
1097        private boolean updateSchedule() {
1098            mChannelHelper.updateChannels();
1099            Collection<ScanSettings> settings = mActiveBackgroundScans.getAllSettings();
1100
1101            mBackgroundScheduler.updateSchedule(settings);
1102            WifiNative.ScanSettings schedule = mBackgroundScheduler.getSchedule();
1103
1104            if (ScanScheduleUtil.scheduleEquals(mPreviousSchedule, schedule)) {
1105                if (DBG) Log.d(TAG, "schedule updated with no change");
1106                return true;
1107            }
1108
1109            mPreviousSchedule = schedule;
1110
1111            if (schedule.num_buckets == 0) {
1112                mScannerImpl.stopBatchedScan();
1113                if (DBG) Log.d(TAG, "scan stopped");
1114                return true;
1115            } else {
1116                Log.d(TAG, "starting scan: "
1117                        + "base period=" + schedule.base_period_ms
1118                        + ", max ap per scan=" + schedule.max_ap_per_scan
1119                        + ", batched scans=" + schedule.report_threshold_num_scans);
1120                for (int b = 0; b < schedule.num_buckets; b++) {
1121                    WifiNative.BucketSettings bucket = schedule.buckets[b];
1122                    Log.d(TAG, "bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)"
1123                            + "[" + bucket.report_events + "]: "
1124                            + ChannelHelper.toString(bucket));
1125                }
1126
1127                if (mScannerImpl.startBatchedScan(schedule, this)) {
1128                    if (DBG) {
1129                        Log.d(TAG, "scan restarted with " + schedule.num_buckets
1130                                + " bucket(s) and base period: " + schedule.base_period_ms);
1131                    }
1132                    return true;
1133                } else {
1134                    mPreviousSchedule = null;
1135                    loge("error starting scan: "
1136                            + "base period=" + schedule.base_period_ms
1137                            + ", max ap per scan=" + schedule.max_ap_per_scan
1138                            + ", batched scans=" + schedule.report_threshold_num_scans);
1139                    for (int b = 0; b < schedule.num_buckets; b++) {
1140                        WifiNative.BucketSettings bucket = schedule.buckets[b];
1141                        loge("bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)"
1142                                + "[" + bucket.report_events + "]: "
1143                                + ChannelHelper.toString(bucket));
1144                    }
1145                    return false;
1146                }
1147            }
1148        }
1149
1150        private void removeBackgroundScanRequest(ClientInfo ci, int handler) {
1151            if (ci != null) {
1152                ScanSettings settings = mActiveBackgroundScans.removeRequest(ci, handler);
1153                logScanRequest("removeBackgroundScanRequest", ci, handler, null, settings, null);
1154                updateSchedule();
1155            }
1156        }
1157
1158        private void reportFullScanResult(ScanResult result, int bucketsScanned) {
1159            for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) {
1160                ClientInfo ci = entry.clientInfo;
1161                int handler = entry.handlerId;
1162                ScanSettings settings = entry.settings;
1163                if (mBackgroundScheduler.shouldReportFullScanResultForSettings(
1164                                result, bucketsScanned, settings)) {
1165                    ScanResult newResult = new ScanResult(result);
1166                    if (result.informationElements != null) {
1167                        newResult.informationElements = result.informationElements.clone();
1168                    }
1169                    else {
1170                        newResult.informationElements = null;
1171                    }
1172                    ci.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, handler, newResult);
1173                }
1174            }
1175        }
1176
1177        private void reportScanResults(ScanData[] results) {
1178            for (ScanData result : results) {
1179                if (result != null && result.getResults() != null) {
1180                    if (result.getResults().length > 0) {
1181                        mWifiMetrics.incrementNonEmptyScanResultCount();
1182                    } else {
1183                        mWifiMetrics.incrementEmptyScanResultCount();
1184                    }
1185                }
1186            }
1187            for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) {
1188                ClientInfo ci = entry.clientInfo;
1189                int handler = entry.handlerId;
1190                ScanSettings settings = entry.settings;
1191                ScanData[] resultsToDeliver =
1192                        mBackgroundScheduler.filterResultsForSettings(results, settings);
1193                if (resultsToDeliver != null) {
1194                    logCallback("backgroundScanResults", ci, handler,
1195                            describeForLog(resultsToDeliver));
1196                    WifiScanner.ParcelableScanData parcelableScanData =
1197                            new WifiScanner.ParcelableScanData(resultsToDeliver);
1198                    ci.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, handler, parcelableScanData);
1199                }
1200            }
1201        }
1202
1203        private void sendBackgroundScanFailedToAllAndClear(int reason, String description) {
1204            for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) {
1205                ClientInfo ci = entry.clientInfo;
1206                int handler = entry.handlerId;
1207                ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler,
1208                        new WifiScanner.OperationResult(reason, description));
1209            }
1210            mActiveBackgroundScans.clear();
1211        }
1212
1213        private void addHotlist(ClientInfo ci, int handler, WifiScanner.HotlistSettings settings) {
1214            mActiveHotlistSettings.addRequest(ci, handler, null, settings);
1215            resetHotlist();
1216        }
1217
1218        private void removeHotlist(ClientInfo ci, int handler) {
1219            mActiveHotlistSettings.removeRequest(ci, handler);
1220            resetHotlist();
1221        }
1222
1223        private void resetHotlist() {
1224            Collection<WifiScanner.HotlistSettings> settings =
1225                    mActiveHotlistSettings.getAllSettings();
1226            int num_hotlist_ap = 0;
1227
1228            for (WifiScanner.HotlistSettings s : settings) {
1229                num_hotlist_ap +=  s.bssidInfos.length;
1230            }
1231
1232            if (num_hotlist_ap == 0) {
1233                mScannerImpl.resetHotlist();
1234            } else {
1235                BssidInfo[] bssidInfos = new BssidInfo[num_hotlist_ap];
1236                int apLostThreshold = Integer.MAX_VALUE;
1237                int index = 0;
1238                for (WifiScanner.HotlistSettings s : settings) {
1239                    for (int i = 0; i < s.bssidInfos.length; i++, index++) {
1240                        bssidInfos[index] = s.bssidInfos[i];
1241                    }
1242                    if (s.apLostThreshold < apLostThreshold) {
1243                        apLostThreshold = s.apLostThreshold;
1244                    }
1245                }
1246
1247                WifiScanner.HotlistSettings mergedSettings = new WifiScanner.HotlistSettings();
1248                mergedSettings.bssidInfos = bssidInfos;
1249                mergedSettings.apLostThreshold = apLostThreshold;
1250                mScannerImpl.setHotlist(mergedSettings, this);
1251            }
1252        }
1253
1254        private void reportHotlistResults(int what, ScanResult[] results) {
1255            if (DBG) localLog("reportHotlistResults " + what + " results " + results.length);
1256            for (RequestInfo<WifiScanner.HotlistSettings> entry : mActiveHotlistSettings) {
1257                ClientInfo ci = entry.clientInfo;
1258                int handler = entry.handlerId;
1259                WifiScanner.HotlistSettings settings = entry.settings;
1260                int num_results = 0;
1261                for (ScanResult result : results) {
1262                    for (BssidInfo BssidInfo : settings.bssidInfos) {
1263                        if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) {
1264                            num_results++;
1265                            break;
1266                        }
1267                    }
1268                }
1269                if (num_results == 0) {
1270                    // nothing to report
1271                    return;
1272                }
1273                ScanResult[] results2 = new ScanResult[num_results];
1274                int index = 0;
1275                for (ScanResult result : results) {
1276                    for (BssidInfo BssidInfo : settings.bssidInfos) {
1277                        if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) {
1278                            results2[index] = result;
1279                            index++;
1280                        }
1281                    }
1282                }
1283                WifiScanner.ParcelableScanResults parcelableScanResults =
1284                        new WifiScanner.ParcelableScanResults(results2);
1285
1286                ci.reportEvent(what, 0, handler, parcelableScanResults);
1287            }
1288        }
1289
1290        private void sendHotlistFailedToAllAndClear(int reason, String description) {
1291            for (RequestInfo<WifiScanner.HotlistSettings> entry : mActiveHotlistSettings) {
1292                ClientInfo ci = entry.clientInfo;
1293                int handler = entry.handlerId;
1294                ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler,
1295                        new WifiScanner.OperationResult(reason, description));
1296            }
1297            mActiveHotlistSettings.clear();
1298        }
1299    }
1300
1301    /**
1302     * PNO scan state machine has 5 states:
1303     * -Default State
1304     *   -Started State
1305     *     -Hw Pno Scan state
1306     *       -Single Scan state
1307     *     -Sw Pno Scan state
1308     *
1309     * These are the main state transitions:
1310     * 1. Start at |Default State|
1311     * 2. Move to |Started State| when we get the |WIFI_SCAN_AVAILABLE| broadcast from WifiManager.
1312     * 3. When a new PNO scan request comes in:
1313     *   a.1. Switch to |Hw Pno Scan state| when the device supports HW PNO
1314     *        (This could either be HAL based ePNO or supplicant based PNO).
1315     *   a.2. In |Hw Pno Scan state| when PNO scan results are received, check if the result
1316     *        contains IE (information elements). If yes, send the results to the client, else
1317     *        switch to |Single Scan state| and send the result to the client when the scan result
1318     *        is obtained.
1319     *   b.1. Switch to |Sw Pno Scan state| when the device does not supports HW PNO
1320     *        (This is for older devices which do not support HW PNO and for connected PNO on
1321     *         devices which support supplicant based PNO)
1322     *   b.2. In |Sw Pno Scan state| send the result to the client when the background scan result
1323     *        is obtained
1324     *
1325     * Note: PNO scans only work for a single client today. We don't have support in HW to support
1326     * multiple requests at the same time, so will need non-trivial changes to support (if at all
1327     * possible) in WifiScanningService.
1328     */
1329    class WifiPnoScanStateMachine extends StateMachine implements WifiNative.PnoEventHandler {
1330
1331        private final DefaultState mDefaultState = new DefaultState();
1332        private final StartedState mStartedState = new StartedState();
1333        private final HwPnoScanState mHwPnoScanState = new HwPnoScanState();
1334        private final SwPnoScanState mSwPnoScanState = new SwPnoScanState();
1335        private final SingleScanState mSingleScanState = new SingleScanState();
1336        private InternalClientInfo mInternalClientInfo;
1337
1338        private final RequestList<Pair<PnoSettings, ScanSettings>> mActivePnoScans =
1339                new RequestList<>();
1340
1341        WifiPnoScanStateMachine(Looper looper) {
1342            super("WifiPnoScanStateMachine", looper);
1343
1344            setLogRecSize(512);
1345            setLogOnlyTransitions(false);
1346
1347            // CHECKSTYLE:OFF IndentationCheck
1348            addState(mDefaultState);
1349                addState(mStartedState, mDefaultState);
1350                    addState(mHwPnoScanState, mStartedState);
1351                        addState(mSingleScanState, mHwPnoScanState);
1352                    addState(mSwPnoScanState, mStartedState);
1353            // CHECKSTYLE:ON IndentationCheck
1354
1355            setInitialState(mDefaultState);
1356        }
1357
1358        public void removePnoSettings(ClientInfo ci) {
1359            mActivePnoScans.removeAllForClient(ci);
1360            transitionTo(mStartedState);
1361        }
1362
1363        @Override
1364        public void onPnoNetworkFound(ScanResult[] results) {
1365            if (DBG) localLog("onWifiPnoNetworkFound event received");
1366            sendMessage(CMD_PNO_NETWORK_FOUND, 0, 0, results);
1367        }
1368
1369        @Override
1370        public void onPnoScanFailed() {
1371            if (DBG) localLog("onWifiPnoScanFailed event received");
1372            sendMessage(CMD_PNO_SCAN_FAILED, 0, 0, null);
1373        }
1374
1375        class DefaultState extends State {
1376            @Override
1377            public void enter() {
1378                if (DBG) localLog("DefaultState");
1379            }
1380
1381            @Override
1382            public boolean processMessage(Message msg) {
1383                switch (msg.what) {
1384                    case CMD_DRIVER_LOADED:
1385                        transitionTo(mStartedState);
1386                        break;
1387                    case CMD_DRIVER_UNLOADED:
1388                        transitionTo(mDefaultState);
1389                        break;
1390                    case WifiScanner.CMD_START_PNO_SCAN:
1391                    case WifiScanner.CMD_STOP_PNO_SCAN:
1392                        replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
1393                        break;
1394                    case CMD_PNO_NETWORK_FOUND:
1395                    case CMD_PNO_SCAN_FAILED:
1396                    case WifiScanner.CMD_SCAN_RESULT:
1397                    case WifiScanner.CMD_OP_FAILED:
1398                        loge("Unexpected message " + msg.what);
1399                        break;
1400                    default:
1401                        return NOT_HANDLED;
1402                }
1403                return HANDLED;
1404            }
1405        }
1406
1407        class StartedState extends State {
1408            @Override
1409            public void enter() {
1410                if (DBG) localLog("StartedState");
1411            }
1412
1413            @Override
1414            public void exit() {
1415                sendPnoScanFailedToAllAndClear(
1416                        WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted");
1417            }
1418
1419            @Override
1420            public boolean processMessage(Message msg) {
1421                ClientInfo ci = mClients.get(msg.replyTo);
1422                switch (msg.what) {
1423                    case WifiScanner.CMD_START_PNO_SCAN:
1424                        Bundle pnoParams = (Bundle) msg.obj;
1425                        if (pnoParams == null) {
1426                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
1427                            return HANDLED;
1428                        }
1429                        pnoParams.setDefusable(true);
1430                        PnoSettings pnoSettings =
1431                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
1432                        // This message is handled after the transition to SwPnoScan/HwPnoScan state
1433                        deferMessage(msg);
1434                        if (mScannerImpl.isHwPnoSupported(pnoSettings.isConnected)) {
1435                            transitionTo(mHwPnoScanState);
1436                        } else {
1437                            transitionTo(mSwPnoScanState);
1438                        }
1439                        break;
1440                    case WifiScanner.CMD_STOP_PNO_SCAN:
1441                        replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "no scan running");
1442                        break;
1443                    default:
1444                        return NOT_HANDLED;
1445                }
1446                return HANDLED;
1447            }
1448        }
1449
1450        class HwPnoScanState extends State {
1451            @Override
1452            public void enter() {
1453                if (DBG) localLog("HwPnoScanState");
1454            }
1455
1456            @Override
1457            public void exit() {
1458                // Reset PNO scan in ScannerImpl before we exit.
1459                mScannerImpl.resetHwPnoList();
1460                removeInternalClient();
1461            }
1462
1463            @Override
1464            public boolean processMessage(Message msg) {
1465                ClientInfo ci = mClients.get(msg.replyTo);
1466                switch (msg.what) {
1467                    case WifiScanner.CMD_START_PNO_SCAN:
1468                        Bundle pnoParams = (Bundle) msg.obj;
1469                        if (pnoParams == null) {
1470                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
1471                            return HANDLED;
1472                        }
1473                        pnoParams.setDefusable(true);
1474                        PnoSettings pnoSettings =
1475                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
1476                        ScanSettings scanSettings =
1477                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY);
1478                        if (addHwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) {
1479                            replySucceeded(msg);
1480                        } else {
1481                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
1482                            transitionTo(mStartedState);
1483                        }
1484                        break;
1485                    case WifiScanner.CMD_STOP_PNO_SCAN:
1486                        removeHwPnoScanRequest(ci, msg.arg2);
1487                        transitionTo(mStartedState);
1488                        break;
1489                    case CMD_PNO_NETWORK_FOUND:
1490                        ScanResult[] scanResults = ((ScanResult[]) msg.obj);
1491                        if (isSingleScanNeeded(scanResults)) {
1492                            ScanSettings activeScanSettings = getScanSettings();
1493                            if (activeScanSettings == null) {
1494                                sendPnoScanFailedToAllAndClear(
1495                                        WifiScanner.REASON_UNSPECIFIED,
1496                                        "couldn't retrieve setting");
1497                                transitionTo(mStartedState);
1498                            } else {
1499                                addSingleScanRequest(activeScanSettings);
1500                                transitionTo(mSingleScanState);
1501                            }
1502                        } else {
1503                            reportPnoNetworkFound((ScanResult[]) msg.obj);
1504                        }
1505                        break;
1506                    case CMD_PNO_SCAN_FAILED:
1507                        sendPnoScanFailedToAllAndClear(
1508                                WifiScanner.REASON_UNSPECIFIED, "pno scan failed");
1509                        transitionTo(mStartedState);
1510                        break;
1511                    default:
1512                        return NOT_HANDLED;
1513                }
1514                return HANDLED;
1515            }
1516        }
1517
1518        class SingleScanState extends State {
1519            @Override
1520            public void enter() {
1521                if (DBG) localLog("SingleScanState");
1522            }
1523
1524            @Override
1525            public boolean processMessage(Message msg) {
1526                ClientInfo ci = mClients.get(msg.replyTo);
1527                switch (msg.what) {
1528                    case WifiScanner.CMD_SCAN_RESULT:
1529                        WifiScanner.ParcelableScanData parcelableScanData =
1530                                (WifiScanner.ParcelableScanData) msg.obj;
1531                        ScanData[] scanDatas = parcelableScanData.getResults();
1532                        ScanData lastScanData = scanDatas[scanDatas.length - 1];
1533                        reportPnoNetworkFound(lastScanData.getResults());
1534                        transitionTo(mHwPnoScanState);
1535                        break;
1536                    case WifiScanner.CMD_OP_FAILED:
1537                        sendPnoScanFailedToAllAndClear(
1538                                WifiScanner.REASON_UNSPECIFIED, "single scan failed");
1539                        transitionTo(mStartedState);
1540                        break;
1541                    default:
1542                        return NOT_HANDLED;
1543                }
1544                return HANDLED;
1545            }
1546        }
1547
1548        class SwPnoScanState extends State {
1549            private final ArrayList<ScanResult> mSwPnoFullScanResults = new ArrayList<>();
1550
1551            @Override
1552            public void enter() {
1553                if (DBG) localLog("SwPnoScanState");
1554                mSwPnoFullScanResults.clear();
1555            }
1556
1557            @Override
1558            public void exit() {
1559                removeInternalClient();
1560            }
1561
1562            @Override
1563            public boolean processMessage(Message msg) {
1564                ClientInfo ci = mClients.get(msg.replyTo);
1565                switch (msg.what) {
1566                    case WifiScanner.CMD_START_PNO_SCAN:
1567                        Bundle pnoParams = (Bundle) msg.obj;
1568                        if (pnoParams == null) {
1569                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
1570                            return HANDLED;
1571                        }
1572                        pnoParams.setDefusable(true);
1573                        PnoSettings pnoSettings =
1574                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
1575                        ScanSettings scanSettings =
1576                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY);
1577                        if (addSwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) {
1578                            replySucceeded(msg);
1579                        } else {
1580                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
1581                            transitionTo(mStartedState);
1582                        }
1583                        break;
1584                    case WifiScanner.CMD_STOP_PNO_SCAN:
1585                        removeSwPnoScanRequest(ci, msg.arg2);
1586                        transitionTo(mStartedState);
1587                        break;
1588                    case WifiScanner.CMD_FULL_SCAN_RESULT:
1589                        // Aggregate full scan results until we get the |CMD_SCAN_RESULT| message
1590                        mSwPnoFullScanResults.add((ScanResult) msg.obj);
1591                        break;
1592                    case WifiScanner.CMD_SCAN_RESULT:
1593                        ScanResult[] scanResults = mSwPnoFullScanResults.toArray(
1594                                new ScanResult[mSwPnoFullScanResults.size()]);
1595                        reportPnoNetworkFound(scanResults);
1596                        mSwPnoFullScanResults.clear();
1597                        break;
1598                    case WifiScanner.CMD_OP_FAILED:
1599                        sendPnoScanFailedToAllAndClear(
1600                                WifiScanner.REASON_UNSPECIFIED, "background scan failed");
1601                        transitionTo(mStartedState);
1602                        break;
1603                    default:
1604                        return NOT_HANDLED;
1605                }
1606                return HANDLED;
1607            }
1608        }
1609
1610        private WifiNative.PnoSettings convertPnoSettingsToNative(PnoSettings pnoSettings) {
1611            WifiNative.PnoSettings nativePnoSetting = new WifiNative.PnoSettings();
1612            nativePnoSetting.min5GHzRssi = pnoSettings.min5GHzRssi;
1613            nativePnoSetting.min24GHzRssi = pnoSettings.min24GHzRssi;
1614            nativePnoSetting.initialScoreMax = pnoSettings.initialScoreMax;
1615            nativePnoSetting.currentConnectionBonus = pnoSettings.currentConnectionBonus;
1616            nativePnoSetting.sameNetworkBonus = pnoSettings.sameNetworkBonus;
1617            nativePnoSetting.secureBonus = pnoSettings.secureBonus;
1618            nativePnoSetting.band5GHzBonus = pnoSettings.band5GHzBonus;
1619            nativePnoSetting.isConnected = pnoSettings.isConnected;
1620            nativePnoSetting.networkList =
1621                    new WifiNative.PnoNetwork[pnoSettings.networkList.length];
1622            for (int i = 0; i < pnoSettings.networkList.length; i++) {
1623                nativePnoSetting.networkList[i] = new WifiNative.PnoNetwork();
1624                nativePnoSetting.networkList[i].ssid = pnoSettings.networkList[i].ssid;
1625                nativePnoSetting.networkList[i].networkId = pnoSettings.networkList[i].networkId;
1626                nativePnoSetting.networkList[i].priority = pnoSettings.networkList[i].priority;
1627                nativePnoSetting.networkList[i].flags = pnoSettings.networkList[i].flags;
1628                nativePnoSetting.networkList[i].auth_bit_field =
1629                        pnoSettings.networkList[i].authBitField;
1630            }
1631            return nativePnoSetting;
1632        }
1633
1634        // Retrieve the only active scan settings.
1635        private ScanSettings getScanSettings() {
1636            for (Pair<PnoSettings, ScanSettings> settingsPair : mActivePnoScans.getAllSettings()) {
1637                return settingsPair.second;
1638            }
1639            return null;
1640        }
1641
1642        private void removeInternalClient() {
1643            if (mInternalClientInfo != null) {
1644                mInternalClientInfo.cleanup();
1645                mInternalClientInfo = null;
1646            } else {
1647                Log.w(TAG, "No Internal client for PNO");
1648            }
1649        }
1650
1651        private void addInternalClient(ClientInfo ci) {
1652            if (mInternalClientInfo == null) {
1653                mInternalClientInfo =
1654                        new InternalClientInfo(ci.getUid(), new Messenger(this.getHandler()));
1655                mInternalClientInfo.register();
1656            } else {
1657                Log.w(TAG, "Internal client for PNO already exists");
1658            }
1659        }
1660
1661        private void addPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings,
1662                PnoSettings pnoSettings) {
1663            mActivePnoScans.addRequest(ci, handler, WifiStateMachine.WIFI_WORK_SOURCE,
1664                    Pair.create(pnoSettings, scanSettings));
1665            addInternalClient(ci);
1666        }
1667
1668        private Pair<PnoSettings, ScanSettings> removePnoScanRequest(ClientInfo ci, int handler) {
1669            Pair<PnoSettings, ScanSettings> settings = mActivePnoScans.removeRequest(ci, handler);
1670            return settings;
1671        }
1672
1673        private boolean addHwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings,
1674                PnoSettings pnoSettings) {
1675            if (ci == null) {
1676                Log.d(TAG, "Failing scan request ClientInfo not found " + handler);
1677                return false;
1678            }
1679            if (!mActivePnoScans.isEmpty()) {
1680                loge("Failing scan request because there is already an active scan");
1681                return false;
1682            }
1683            WifiNative.PnoSettings nativePnoSettings = convertPnoSettingsToNative(pnoSettings);
1684            if (!mScannerImpl.setHwPnoList(nativePnoSettings, mPnoScanStateMachine)) {
1685                return false;
1686            }
1687            logScanRequest("addHwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings);
1688            addPnoScanRequest(ci, handler, scanSettings, pnoSettings);
1689            // HW PNO is supported, check if we need a background scan running for this.
1690            if (mScannerImpl.shouldScheduleBackgroundScanForHwPno()) {
1691                addBackgroundScanRequest(scanSettings);
1692            }
1693            return true;
1694        }
1695
1696        private void removeHwPnoScanRequest(ClientInfo ci, int handler) {
1697            if (ci != null) {
1698                Pair<PnoSettings, ScanSettings> settings = removePnoScanRequest(ci, handler);
1699                logScanRequest("removeHwPnoScanRequest", ci, handler, null,
1700                        settings.second, settings.first);
1701            }
1702        }
1703
1704        private boolean addSwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings,
1705                PnoSettings pnoSettings) {
1706            if (ci == null) {
1707                Log.d(TAG, "Failing scan request ClientInfo not found " + handler);
1708                return false;
1709            }
1710            if (!mActivePnoScans.isEmpty()) {
1711                loge("Failing scan request because there is already an active scan");
1712                return false;
1713            }
1714            logScanRequest("addSwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings);
1715            addPnoScanRequest(ci, handler, scanSettings, pnoSettings);
1716            // HW PNO is not supported, we need to revert to normal background scans and
1717            // report events after each scan and we need full scan results to get the IE information
1718            scanSettings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
1719                    | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
1720            addBackgroundScanRequest(scanSettings);
1721            return true;
1722        }
1723
1724        private void removeSwPnoScanRequest(ClientInfo ci, int handler) {
1725            if (ci != null) {
1726                Pair<PnoSettings, ScanSettings> settings = removePnoScanRequest(ci, handler);
1727                logScanRequest("removeSwPnoScanRequest", ci, handler, null,
1728                        settings.second, settings.first);
1729            }
1730        }
1731
1732        private void reportPnoNetworkFound(ScanResult[] results) {
1733            WifiScanner.ParcelableScanResults parcelableScanResults =
1734                    new WifiScanner.ParcelableScanResults(results);
1735            for (RequestInfo<Pair<PnoSettings, ScanSettings>> entry : mActivePnoScans) {
1736                ClientInfo ci = entry.clientInfo;
1737                int handler = entry.handlerId;
1738                logCallback("pnoNetworkFound", ci, handler, describeForLog(results));
1739                ci.reportEvent(
1740                        WifiScanner.CMD_PNO_NETWORK_FOUND, 0, handler, parcelableScanResults);
1741            }
1742        }
1743
1744        private void sendPnoScanFailedToAllAndClear(int reason, String description) {
1745            for (RequestInfo<Pair<PnoSettings, ScanSettings>> entry : mActivePnoScans) {
1746                ClientInfo ci = entry.clientInfo;
1747                int handler = entry.handlerId;
1748                ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler,
1749                        new WifiScanner.OperationResult(reason, description));
1750            }
1751            mActivePnoScans.clear();
1752        }
1753
1754        private void addBackgroundScanRequest(ScanSettings settings) {
1755            if (DBG) localLog("Starting background scan");
1756            if (mInternalClientInfo != null) {
1757                mInternalClientInfo.sendRequestToClientHandler(
1758                        WifiScanner.CMD_START_BACKGROUND_SCAN, settings,
1759                        WifiStateMachine.WIFI_WORK_SOURCE);
1760            }
1761        }
1762
1763        private void addSingleScanRequest(ScanSettings settings) {
1764            if (DBG) localLog("Starting single scan");
1765            if (mInternalClientInfo != null) {
1766                mInternalClientInfo.sendRequestToClientHandler(
1767                        WifiScanner.CMD_START_SINGLE_SCAN, settings,
1768                        WifiStateMachine.WIFI_WORK_SOURCE);
1769            }
1770        }
1771
1772        /**
1773         * Checks if IE are present in scan data, if no single scan is needed to report event to
1774         * client
1775         */
1776        private boolean isSingleScanNeeded(ScanResult[] scanResults) {
1777            for (ScanResult scanResult : scanResults) {
1778                if (scanResult.informationElements != null
1779                        && scanResult.informationElements.length > 0) {
1780                    return false;
1781                }
1782            }
1783            return true;
1784        }
1785    }
1786
1787    private abstract class ClientInfo {
1788        private final int mUid;
1789        private final WorkSource mWorkSource;
1790        private boolean mScanWorkReported = false;
1791        protected final Messenger mMessenger;
1792
1793        ClientInfo(int uid, Messenger messenger) {
1794            mUid = uid;
1795            mMessenger = messenger;
1796            mWorkSource = new WorkSource(uid);
1797        }
1798
1799        /**
1800         * Register this client to main client map.
1801         */
1802        public void register() {
1803            mClients.put(mMessenger, this);
1804        }
1805
1806        /**
1807         * Unregister this client from main client map.
1808         */
1809        private void unregister() {
1810            mClients.remove(mMessenger);
1811        }
1812
1813        public void cleanup() {
1814            mSingleScanStateMachine.removeSingleScanRequests(this);
1815            mBackgroundScanStateMachine.removeBackgroundScanSettings(this);
1816            mBackgroundScanStateMachine.removeHotlistSettings(this);
1817            unregister();
1818            localLog("Successfully stopped all requests for client " + this);
1819        }
1820
1821        public int getUid() {
1822            return mUid;
1823        }
1824
1825        public void reportEvent(int what, int arg1, int arg2) {
1826            reportEvent(what, arg1, arg2, null);
1827        }
1828
1829        // This has to be implemented by subclasses to report events back to clients.
1830        public abstract void reportEvent(int what, int arg1, int arg2, Object obj);
1831
1832        // TODO(b/27903217): Blame scan on provided work source
1833        private void reportBatchedScanStart() {
1834            if (mUid == 0)
1835                return;
1836
1837            int csph = getCsph();
1838
1839            try {
1840                mBatteryStats.noteWifiBatchedScanStartedFromSource(mWorkSource, csph);
1841            } catch (RemoteException e) {
1842                logw("failed to report scan work: " + e.toString());
1843            }
1844        }
1845
1846        private void reportBatchedScanStop() {
1847            if (mUid == 0)
1848                return;
1849
1850            try {
1851                mBatteryStats.noteWifiBatchedScanStoppedFromSource(mWorkSource);
1852            } catch (RemoteException e) {
1853                logw("failed to cleanup scan work: " + e.toString());
1854            }
1855        }
1856
1857        // TODO migrate batterystats to accept scan duration per hour instead of csph
1858        private int getCsph() {
1859            int totalScanDurationPerHour = 0;
1860            Collection<ScanSettings> settingsList =
1861                    mBackgroundScanStateMachine.getBackgroundScanSettings(this);
1862            for (ScanSettings settings : settingsList) {
1863                int scanDurationMs = mChannelHelper.estimateScanDuration(settings);
1864                int scans_per_Hour = settings.periodInMs == 0 ? 1 : (3600 * 1000) /
1865                        settings.periodInMs;
1866                totalScanDurationPerHour += scanDurationMs * scans_per_Hour;
1867            }
1868
1869            return totalScanDurationPerHour / ChannelHelper.SCAN_PERIOD_PER_CHANNEL_MS;
1870        }
1871
1872        public void reportScanWorkUpdate() {
1873            if (mScanWorkReported) {
1874                reportBatchedScanStop();
1875                mScanWorkReported = false;
1876            }
1877            if (mBackgroundScanStateMachine.getBackgroundScanSettings(this).isEmpty()) {
1878                reportBatchedScanStart();
1879                mScanWorkReported = true;
1880            }
1881        }
1882
1883        @Override
1884        public String toString() {
1885            return "ClientInfo[uid=" + mUid + "]";
1886        }
1887    }
1888
1889    /**
1890     * This class is used to represent external clients to the WifiScanning Service.
1891     */
1892    private class ExternalClientInfo extends ClientInfo {
1893        private final AsyncChannel mChannel;
1894        /**
1895         * Indicates if the client is still connected
1896         * If the client is no longer connected then messages to it will be silently dropped
1897         */
1898        private boolean mDisconnected = false;
1899
1900        ExternalClientInfo(int uid, Messenger messenger, AsyncChannel c) {
1901            super(uid, messenger);
1902            mChannel = c;
1903            if (DBG) localLog("New client, channel: " + c);
1904        }
1905
1906        @Override
1907        public void reportEvent(int what, int arg1, int arg2, Object obj) {
1908            if (!mDisconnected) {
1909                mChannel.sendMessage(what, arg1, arg2, obj);
1910            }
1911        }
1912
1913        @Override
1914        public void cleanup() {
1915            mDisconnected = true;
1916            // Internal clients should not have any wifi change requests. So, keeping this cleanup
1917            // only for external client because this will otherwise cause an infinite recursion
1918            // when the internal client in WifiChangeStateMachine is cleaned up.
1919            mWifiChangeStateMachine.removeWifiChangeHandler(this);
1920            mPnoScanStateMachine.removePnoSettings(this);
1921            super.cleanup();
1922        }
1923    }
1924
1925    /**
1926     * This class is used to represent internal clients to the WifiScanning Service. This is needed
1927     * for communicating between State Machines.
1928     * This leaves the onReportEvent method unimplemented, so that the clients have the freedom
1929     * to handle the events as they need.
1930     */
1931    private class InternalClientInfo extends ClientInfo {
1932        private static final int INTERNAL_CLIENT_HANDLER = 0;
1933
1934        /**
1935         * The UID here is used to proxy the original external requester UID.
1936         */
1937        InternalClientInfo(int requesterUid, Messenger messenger) {
1938            super(requesterUid, messenger);
1939        }
1940
1941        @Override
1942        public void reportEvent(int what, int arg1, int arg2, Object obj) {
1943            Message message = Message.obtain();
1944            message.what = what;
1945            message.arg1 = arg1;
1946            message.arg2 = arg2;
1947            message.obj = obj;
1948            try {
1949                mMessenger.send(message);
1950            } catch (RemoteException e) {
1951                loge("Failed to send message: " + what);
1952            }
1953        }
1954
1955        /**
1956         * Send a message to the client handler which should reroute the message to the appropriate
1957         * state machine.
1958         */
1959        public void sendRequestToClientHandler(int what, ScanSettings settings,
1960                WorkSource workSource) {
1961            Message msg = Message.obtain();
1962            msg.what = what;
1963            msg.arg2 = INTERNAL_CLIENT_HANDLER;
1964            if (settings != null) {
1965                Bundle bundle = new Bundle();
1966                bundle.putParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
1967                bundle.putParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
1968                msg.obj = bundle;
1969            }
1970            msg.replyTo = mMessenger;
1971            msg.sendingUid = getUid();
1972            mClientHandler.sendMessage(msg);
1973        }
1974
1975        /**
1976         * Send a message to the client handler which should reroute the message to the appropriate
1977         * state machine.
1978         */
1979        public void sendRequestToClientHandler(int what) {
1980            sendRequestToClientHandler(what, null, null);
1981        }
1982    }
1983
1984    void replySucceeded(Message msg) {
1985        if (msg.replyTo != null) {
1986            Message reply = Message.obtain();
1987            reply.what = WifiScanner.CMD_OP_SUCCEEDED;
1988            reply.arg2 = msg.arg2;
1989            try {
1990                msg.replyTo.send(reply);
1991            } catch (RemoteException e) {
1992                // There's not much we can do if reply can't be sent!
1993            }
1994        } else {
1995            // locally generated message; doesn't need a reply!
1996        }
1997    }
1998
1999    void replyFailed(Message msg, int reason, String description) {
2000        if (msg.replyTo != null) {
2001            Message reply = Message.obtain();
2002            reply.what = WifiScanner.CMD_OP_FAILED;
2003            reply.arg2 = msg.arg2;
2004            reply.obj = new WifiScanner.OperationResult(reason, description);
2005            try {
2006                msg.replyTo.send(reply);
2007            } catch (RemoteException e) {
2008                // There's not much we can do if reply can't be sent!
2009            }
2010        } else {
2011            // locally generated message; doesn't need a reply!
2012        }
2013    }
2014
2015    /**
2016     * Wifi Change state machine is used to handle any wifi change tracking requests.
2017     * TODO: This state machine doesn't handle driver loading/unloading yet.
2018     */
2019    class WifiChangeStateMachine extends StateMachine
2020            implements WifiNative.SignificantWifiChangeEventHandler {
2021
2022        private static final int MAX_APS_TO_TRACK = 3;
2023        private static final int MOVING_SCAN_PERIOD_MS      = 10000;
2024        private static final int STATIONARY_SCAN_PERIOD_MS  =  5000;
2025        private static final int MOVING_STATE_TIMEOUT_MS    = 30000;
2026
2027        State mDefaultState = new DefaultState();
2028        State mStationaryState = new StationaryState();
2029        State mMovingState = new MovingState();
2030
2031        private static final String ACTION_TIMEOUT =
2032                "com.android.server.WifiScanningServiceImpl.action.TIMEOUT";
2033        private PendingIntent mTimeoutIntent;
2034        private ScanResult[] mCurrentBssids;
2035        private InternalClientInfo mInternalClientInfo;
2036
2037        private final Set<Pair<ClientInfo, Integer>> mActiveWifiChangeHandlers = new HashSet<>();
2038
2039        WifiChangeStateMachine(Looper looper) {
2040            super("SignificantChangeStateMachine", looper);
2041
2042            // CHECKSTYLE:OFF IndentationCheck
2043            addState(mDefaultState);
2044                addState(mStationaryState, mDefaultState);
2045                addState(mMovingState, mDefaultState);
2046            // CHECKSTYLE:ON IndentationCheck
2047
2048            setInitialState(mDefaultState);
2049        }
2050
2051        public void removeWifiChangeHandler(ClientInfo ci) {
2052            Iterator<Pair<ClientInfo, Integer>> iter = mActiveWifiChangeHandlers.iterator();
2053            while (iter.hasNext()) {
2054                Pair<ClientInfo, Integer> entry = iter.next();
2055                if (entry.first == ci) {
2056                    iter.remove();
2057                }
2058            }
2059            untrackSignificantWifiChangeOnEmpty();
2060        }
2061
2062        class DefaultState extends State {
2063            @Override
2064            public void enter() {
2065                if (DBG) localLog("Entering IdleState");
2066            }
2067
2068            @Override
2069            public boolean processMessage(Message msg) {
2070                if (DBG) localLog("DefaultState state got " + msg);
2071                ClientInfo ci = mClients.get(msg.replyTo);
2072                switch (msg.what) {
2073                    case WifiScanner.CMD_START_TRACKING_CHANGE:
2074                        addWifiChangeHandler(ci, msg.arg2);
2075                        replySucceeded(msg);
2076                        transitionTo(mMovingState);
2077                        break;
2078                    case WifiScanner.CMD_STOP_TRACKING_CHANGE:
2079                        // nothing to do
2080                        break;
2081                    case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
2082                        /* save configuration till we transition to moving state */
2083                        deferMessage(msg);
2084                        break;
2085                    case WifiScanner.CMD_SCAN_RESULT:
2086                        // nothing to do
2087                        break;
2088                    default:
2089                        return NOT_HANDLED;
2090                }
2091                return HANDLED;
2092            }
2093        }
2094
2095        class StationaryState extends State {
2096            @Override
2097            public void enter() {
2098                if (DBG) localLog("Entering StationaryState");
2099                reportWifiStabilized(mCurrentBssids);
2100            }
2101
2102            @Override
2103            public boolean processMessage(Message msg) {
2104                if (DBG) localLog("Stationary state got " + msg);
2105                ClientInfo ci = mClients.get(msg.replyTo);
2106                switch (msg.what) {
2107                    case WifiScanner.CMD_START_TRACKING_CHANGE:
2108                        addWifiChangeHandler(ci, msg.arg2);
2109                        replySucceeded(msg);
2110                        break;
2111                    case WifiScanner.CMD_STOP_TRACKING_CHANGE:
2112                        removeWifiChangeHandler(ci, msg.arg2);
2113                        break;
2114                    case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
2115                        /* save configuration till we transition to moving state */
2116                        deferMessage(msg);
2117                        break;
2118                    case CMD_WIFI_CHANGE_DETECTED:
2119                        if (DBG) localLog("Got wifi change detected");
2120                        reportWifiChanged((ScanResult[]) msg.obj);
2121                        transitionTo(mMovingState);
2122                        break;
2123                    case WifiScanner.CMD_SCAN_RESULT:
2124                        // nothing to do
2125                        break;
2126                    default:
2127                        return NOT_HANDLED;
2128                }
2129                return HANDLED;
2130            }
2131        }
2132
2133        class MovingState extends State {
2134            boolean mWifiChangeDetected = false;
2135            boolean mScanResultsPending = false;
2136
2137            @Override
2138            public void enter() {
2139                if (DBG) localLog("Entering MovingState");
2140                if (mTimeoutIntent == null) {
2141                    Intent intent = new Intent(ACTION_TIMEOUT, null);
2142                    mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
2143
2144                    mContext.registerReceiver(
2145                            new BroadcastReceiver() {
2146                                @Override
2147                                public void onReceive(Context context, Intent intent) {
2148                                    sendMessage(CMD_WIFI_CHANGE_TIMEOUT);
2149                                }
2150                            }, new IntentFilter(ACTION_TIMEOUT));
2151                }
2152                issueFullScan();
2153            }
2154
2155            @Override
2156            public boolean processMessage(Message msg) {
2157                if (DBG) localLog("MovingState state got " + msg);
2158                ClientInfo ci = mClients.get(msg.replyTo);
2159                switch (msg.what) {
2160                    case WifiScanner.CMD_START_TRACKING_CHANGE:
2161                        addWifiChangeHandler(ci, msg.arg2);
2162                        replySucceeded(msg);
2163                        break;
2164                    case WifiScanner.CMD_STOP_TRACKING_CHANGE:
2165                        removeWifiChangeHandler(ci, msg.arg2);
2166                        break;
2167                    case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
2168                        if (DBG) localLog("Got configuration from app");
2169                        WifiScanner.WifiChangeSettings settings =
2170                                (WifiScanner.WifiChangeSettings) msg.obj;
2171                        reconfigureScan(settings);
2172                        mWifiChangeDetected = false;
2173                        long unchangedDelay = settings.unchangedSampleSize * settings.periodInMs;
2174                        mAlarmManager.cancel(mTimeoutIntent);
2175                        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
2176                                mClock.elapsedRealtime() + unchangedDelay,
2177                                mTimeoutIntent);
2178                        break;
2179                    case WifiScanner.CMD_SCAN_RESULT:
2180                        if (DBG) localLog("Got scan results");
2181                        if (mScanResultsPending) {
2182                            if (DBG) localLog("reconfiguring scan");
2183                            reconfigureScan((ScanData[])msg.obj,
2184                                    STATIONARY_SCAN_PERIOD_MS);
2185                            mWifiChangeDetected = false;
2186                            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
2187                                    mClock.elapsedRealtime() + MOVING_STATE_TIMEOUT_MS,
2188                                    mTimeoutIntent);
2189                            mScanResultsPending = false;
2190                        }
2191                        break;
2192                    case CMD_WIFI_CHANGE_DETECTED:
2193                        if (DBG) localLog("Change detected");
2194                        mAlarmManager.cancel(mTimeoutIntent);
2195                        reportWifiChanged((ScanResult[])msg.obj);
2196                        mWifiChangeDetected = true;
2197                        issueFullScan();
2198                        break;
2199                    case CMD_WIFI_CHANGE_TIMEOUT:
2200                        if (DBG) localLog("Got timeout event");
2201                        if (mWifiChangeDetected == false) {
2202                            transitionTo(mStationaryState);
2203                        }
2204                        break;
2205                    default:
2206                        return NOT_HANDLED;
2207                }
2208                return HANDLED;
2209            }
2210
2211            @Override
2212            public void exit() {
2213                mAlarmManager.cancel(mTimeoutIntent);
2214            }
2215
2216            void issueFullScan() {
2217                if (DBG) localLog("Issuing full scan");
2218                ScanSettings settings = new ScanSettings();
2219                settings.band = WifiScanner.WIFI_BAND_BOTH;
2220                settings.periodInMs = MOVING_SCAN_PERIOD_MS;
2221                settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
2222                addScanRequest(settings);
2223                mScanResultsPending = true;
2224            }
2225
2226        }
2227
2228        private void reconfigureScan(ScanData[] results, int period) {
2229            // find brightest APs and set them as sentinels
2230            if (results.length < MAX_APS_TO_TRACK) {
2231                localLog("too few APs (" + results.length + ") available to track wifi change");
2232                return;
2233            }
2234
2235            removeScanRequest();
2236
2237            // remove duplicate BSSIDs
2238            HashMap<String, ScanResult> bssidToScanResult = new HashMap<String, ScanResult>();
2239            for (ScanResult result : results[0].getResults()) {
2240                ScanResult saved = bssidToScanResult.get(result.BSSID);
2241                if (saved == null) {
2242                    bssidToScanResult.put(result.BSSID, result);
2243                } else if (saved.level > result.level) {
2244                    bssidToScanResult.put(result.BSSID, result);
2245                }
2246            }
2247
2248            // find brightest BSSIDs
2249            ScanResult brightest[] = new ScanResult[MAX_APS_TO_TRACK];
2250            Collection<ScanResult> results2 = bssidToScanResult.values();
2251            for (ScanResult result : results2) {
2252                for (int j = 0; j < brightest.length; j++) {
2253                    if (brightest[j] == null
2254                            || (brightest[j].level < result.level)) {
2255                        for (int k = brightest.length; k > (j + 1); k--) {
2256                            brightest[k - 1] = brightest[k - 2];
2257                        }
2258                        brightest[j] = result;
2259                        break;
2260                    }
2261                }
2262            }
2263
2264            // Get channels to scan for
2265            ArrayList<Integer> channels = new ArrayList<Integer>();
2266            for (int i = 0; i < brightest.length; i++) {
2267                boolean found = false;
2268                for (int j = i + 1; j < brightest.length; j++) {
2269                    if (brightest[j].frequency == brightest[i].frequency) {
2270                        found = true;
2271                    }
2272                }
2273                if (!found) {
2274                    channels.add(brightest[i].frequency);
2275                }
2276            }
2277
2278            if (DBG) localLog("Found " + channels.size() + " channels");
2279
2280            // set scanning schedule
2281            ScanSettings settings = new ScanSettings();
2282            settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
2283            settings.channels = new ChannelSpec[channels.size()];
2284            for (int i = 0; i < channels.size(); i++) {
2285                settings.channels[i] = new ChannelSpec(channels.get(i));
2286            }
2287
2288            settings.periodInMs = period;
2289            addScanRequest(settings);
2290
2291            WifiScanner.WifiChangeSettings settings2 = new WifiScanner.WifiChangeSettings();
2292            settings2.rssiSampleSize = 3;
2293            settings2.lostApSampleSize = 3;
2294            settings2.unchangedSampleSize = 3;
2295            settings2.minApsBreachingThreshold = 2;
2296            settings2.bssidInfos = new BssidInfo[brightest.length];
2297
2298            for (int i = 0; i < brightest.length; i++) {
2299                BssidInfo BssidInfo = new BssidInfo();
2300                BssidInfo.bssid = brightest[i].BSSID;
2301                int threshold = (100 + brightest[i].level) / 32 + 2;
2302                BssidInfo.low = brightest[i].level - threshold;
2303                BssidInfo.high = brightest[i].level + threshold;
2304                settings2.bssidInfos[i] = BssidInfo;
2305
2306                if (DBG) localLog("Setting bssid=" + BssidInfo.bssid + ", " +
2307                        "low=" + BssidInfo.low + ", high=" + BssidInfo.high);
2308            }
2309
2310            trackSignificantWifiChange(settings2);
2311            mCurrentBssids = brightest;
2312        }
2313
2314        private void reconfigureScan(WifiScanner.WifiChangeSettings settings) {
2315
2316            if (settings.bssidInfos.length < MAX_APS_TO_TRACK) {
2317                localLog("too few APs (" + settings.bssidInfos.length
2318                        + ") available to track wifi change");
2319                return;
2320            }
2321
2322            if (DBG) localLog("Setting configuration specified by app");
2323
2324            mCurrentBssids = new ScanResult[settings.bssidInfos.length];
2325            HashSet<Integer> channels = new HashSet<Integer>();
2326
2327            for (int i = 0; i < settings.bssidInfos.length; i++) {
2328                ScanResult result = new ScanResult();
2329                result.BSSID = settings.bssidInfos[i].bssid;
2330                mCurrentBssids[i] = result;
2331                channels.add(settings.bssidInfos[i].frequencyHint);
2332            }
2333
2334            // cancel previous scan
2335            removeScanRequest();
2336
2337            // set new scanning schedule
2338            ScanSettings settings2 = new ScanSettings();
2339            settings2.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
2340            settings2.channels = new ChannelSpec[channels.size()];
2341            int i = 0;
2342            for (Integer channel : channels) {
2343                settings2.channels[i++] = new ChannelSpec(channel);
2344            }
2345
2346            settings2.periodInMs = settings.periodInMs;
2347            addScanRequest(settings2);
2348
2349            // start tracking new APs
2350            trackSignificantWifiChange(settings);
2351        }
2352
2353
2354        @Override
2355        public void onChangesFound(ScanResult results[]) {
2356            sendMessage(CMD_WIFI_CHANGE_DETECTED, 0, 0, results);
2357        }
2358
2359        private void addScanRequest(ScanSettings settings) {
2360            if (DBG) localLog("Starting scans");
2361            if (mInternalClientInfo != null) {
2362                mInternalClientInfo.sendRequestToClientHandler(
2363                        WifiScanner.CMD_START_BACKGROUND_SCAN, settings, null);
2364            }
2365        }
2366
2367        private void removeScanRequest() {
2368            if (DBG) localLog("Stopping scans");
2369            if (mInternalClientInfo != null) {
2370                mInternalClientInfo.sendRequestToClientHandler(
2371                        WifiScanner.CMD_STOP_BACKGROUND_SCAN);
2372            }
2373        }
2374
2375        private void trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings) {
2376            if (mScannerImpl != null) {
2377                mScannerImpl.untrackSignificantWifiChange();
2378                mScannerImpl.trackSignificantWifiChange(settings, this);
2379            }
2380        }
2381
2382        private void untrackSignificantWifiChange() {
2383            if (mScannerImpl != null) {
2384                mScannerImpl.untrackSignificantWifiChange();
2385            }
2386        }
2387
2388        private void addWifiChangeHandler(ClientInfo ci, int handler) {
2389            mActiveWifiChangeHandlers.add(Pair.create(ci, handler));
2390            // Add an internal client to make background scan requests.
2391            if (mInternalClientInfo == null) {
2392                mInternalClientInfo =
2393                        new InternalClientInfo(ci.getUid(), new Messenger(this.getHandler()));
2394                mInternalClientInfo.register();
2395            }
2396        }
2397
2398        private void removeWifiChangeHandler(ClientInfo ci, int handler) {
2399            mActiveWifiChangeHandlers.remove(Pair.create(ci, handler));
2400            untrackSignificantWifiChangeOnEmpty();
2401        }
2402
2403        private void untrackSignificantWifiChangeOnEmpty() {
2404            if (mActiveWifiChangeHandlers.isEmpty()) {
2405                if (DBG) localLog("Got Disable Wifi Change");
2406                mCurrentBssids = null;
2407                untrackSignificantWifiChange();
2408                // Remove the internal client when there are no more external clients.
2409                if (mInternalClientInfo != null) {
2410                    mInternalClientInfo.cleanup();
2411                    mInternalClientInfo = null;
2412                }
2413                transitionTo(mDefaultState);
2414            }
2415        }
2416
2417        private void reportWifiChanged(ScanResult[] results) {
2418            WifiScanner.ParcelableScanResults parcelableScanResults =
2419                    new WifiScanner.ParcelableScanResults(results);
2420            Iterator<Pair<ClientInfo, Integer>> it = mActiveWifiChangeHandlers.iterator();
2421            while (it.hasNext()) {
2422                Pair<ClientInfo, Integer> entry = it.next();
2423                ClientInfo ci = entry.first;
2424                int handler = entry.second;
2425                ci.reportEvent(WifiScanner.CMD_WIFI_CHANGE_DETECTED, 0, handler,
2426                        parcelableScanResults);
2427            }
2428        }
2429
2430        private void reportWifiStabilized(ScanResult[] results) {
2431            WifiScanner.ParcelableScanResults parcelableScanResults =
2432                    new WifiScanner.ParcelableScanResults(results);
2433            Iterator<Pair<ClientInfo, Integer>> it = mActiveWifiChangeHandlers.iterator();
2434            while (it.hasNext()) {
2435                Pair<ClientInfo, Integer> entry = it.next();
2436                ClientInfo ci = entry.first;
2437                int handler = entry.second;
2438                ci.reportEvent(WifiScanner.CMD_WIFI_CHANGES_STABILIZED, 0, handler,
2439                        parcelableScanResults);
2440            }
2441        }
2442    }
2443
2444    private static String toString(int uid, ScanSettings settings) {
2445        StringBuilder sb = new StringBuilder();
2446        sb.append("ScanSettings[uid=").append(uid);
2447        sb.append(", period=").append(settings.periodInMs);
2448        sb.append(", report=").append(settings.reportEvents);
2449        if (settings.reportEvents == WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL
2450                && settings.numBssidsPerScan > 0
2451                && settings.maxScansToCache > 1) {
2452            sb.append(", batch=").append(settings.maxScansToCache);
2453            sb.append(", numAP=").append(settings.numBssidsPerScan);
2454        }
2455        sb.append(", ").append(ChannelHelper.toString(settings));
2456        sb.append("]");
2457
2458        return sb.toString();
2459    }
2460
2461    @Override
2462    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2463        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2464                != PackageManager.PERMISSION_GRANTED) {
2465            pw.println("Permission Denial: can't dump WifiScanner from from pid="
2466                    + Binder.getCallingPid()
2467                    + ", uid=" + Binder.getCallingUid()
2468                    + " without permission "
2469                    + android.Manifest.permission.DUMP);
2470            return;
2471        }
2472        pw.println("WifiScanningService - Log Begin ----");
2473        mLocalLog.dump(fd, pw, args);
2474        pw.println("WifiScanningService - Log End ----");
2475        pw.println();
2476        pw.println("clients:");
2477        for (ClientInfo client : mClients.values()) {
2478            pw.println("  " + client);
2479        }
2480        pw.println("listeners:");
2481        for (ClientInfo client : mClients.values()) {
2482            Collection<ScanSettings> settingsList =
2483                    mBackgroundScanStateMachine.getBackgroundScanSettings(client);
2484            for (ScanSettings settings : settingsList) {
2485                pw.println("  " + toString(client.mUid, settings));
2486            }
2487        }
2488        if (mBackgroundScheduler != null) {
2489            WifiNative.ScanSettings schedule = mBackgroundScheduler.getSchedule();
2490            if (schedule != null) {
2491                pw.println("schedule:");
2492                pw.println("  base period: " + schedule.base_period_ms);
2493                pw.println("  max ap per scan: " + schedule.max_ap_per_scan);
2494                pw.println("  batched scans: " + schedule.report_threshold_num_scans);
2495                pw.println("  buckets:");
2496                for (int b = 0; b < schedule.num_buckets; b++) {
2497                    WifiNative.BucketSettings bucket = schedule.buckets[b];
2498                    pw.println("    bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)["
2499                            + bucket.report_events + "]: "
2500                            + ChannelHelper.toString(bucket));
2501                }
2502            }
2503        }
2504        if (mPnoScanStateMachine != null) {
2505            mPnoScanStateMachine.dump(fd, pw, args);
2506        }
2507    }
2508
2509    void logScanRequest(String request, ClientInfo ci, int id, WorkSource workSource,
2510            ScanSettings settings, PnoSettings pnoSettings) {
2511        StringBuilder sb = new StringBuilder();
2512        sb.append(request)
2513                .append(": ")
2514                .append(ci.toString())
2515                .append(",Id=")
2516                .append(id);
2517        if (workSource != null) {
2518            sb.append(",").append(workSource);
2519        }
2520        if (settings != null) {
2521            sb.append(", ");
2522            describeTo(sb, settings);
2523        }
2524        if (pnoSettings != null) {
2525            sb.append(", ");
2526            describeTo(sb, pnoSettings);
2527        }
2528        localLog(sb.toString());
2529    }
2530
2531    void logCallback(String callback, ClientInfo ci, int id, String extra) {
2532        StringBuilder sb = new StringBuilder();
2533        sb.append(callback)
2534                .append(": ")
2535                .append(ci.toString())
2536                .append(",Id=")
2537                .append(id);
2538        if (extra != null) {
2539            sb.append(",").append(extra);
2540        }
2541        localLog(sb.toString());
2542    }
2543
2544    static String describeForLog(ScanData[] results) {
2545        StringBuilder sb = new StringBuilder();
2546        sb.append("results=");
2547        for (int i = 0; i < results.length; ++i) {
2548            if (i > 0) sb.append(";");
2549            sb.append(results[i].getResults().length);
2550        }
2551        return sb.toString();
2552    }
2553
2554    static String describeForLog(ScanResult[] results) {
2555        return "results=" + results.length;
2556    }
2557
2558    static String describeTo(StringBuilder sb, ScanSettings scanSettings) {
2559        sb.append("ScanSettings { ")
2560          .append(" band:").append(scanSettings.band)
2561          .append(" period:").append(scanSettings.periodInMs)
2562          .append(" reportEvents:").append(scanSettings.reportEvents)
2563          .append(" numBssidsPerScan:").append(scanSettings.numBssidsPerScan)
2564          .append(" maxScansToCache:").append(scanSettings.maxScansToCache)
2565          .append(" channels:[ ");
2566        if (scanSettings.channels != null) {
2567            for (int i = 0; i < scanSettings.channels.length; i++) {
2568                sb.append(scanSettings.channels[i].frequency)
2569                  .append(" ");
2570            }
2571        }
2572        sb.append(" ] ")
2573          .append(" } ");
2574        return sb.toString();
2575    }
2576
2577    static String describeTo(StringBuilder sb, PnoSettings pnoSettings) {
2578        sb.append("PnoSettings { ")
2579          .append(" min5GhzRssi:").append(pnoSettings.min5GHzRssi)
2580          .append(" min24GhzRssi:").append(pnoSettings.min24GHzRssi)
2581          .append(" initialScoreMax:").append(pnoSettings.initialScoreMax)
2582          .append(" currentConnectionBonus:").append(pnoSettings.currentConnectionBonus)
2583          .append(" sameNetworkBonus:").append(pnoSettings.sameNetworkBonus)
2584          .append(" secureBonus:").append(pnoSettings.secureBonus)
2585          .append(" band5GhzBonus:").append(pnoSettings.band5GHzBonus)
2586          .append(" isConnected:").append(pnoSettings.isConnected)
2587          .append(" networks:[ ");
2588        if (pnoSettings.networkList != null) {
2589            for (int i = 0; i < pnoSettings.networkList.length; i++) {
2590                sb.append(pnoSettings.networkList[i].ssid)
2591                  .append(",")
2592                  .append(pnoSettings.networkList[i].networkId)
2593                  .append(" ");
2594            }
2595        }
2596        sb.append(" ] ")
2597          .append(" } ");
2598        return sb.toString();
2599    }
2600}
2601