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            if (mChannelHelper == null || mBackgroundScheduler == null || mScannerImpl == null) {
1099                loge("Failed to update schedule because WifiScanningService is not initialized");
1100                return false;
1101            }
1102            mChannelHelper.updateChannels();
1103            Collection<ScanSettings> settings = mActiveBackgroundScans.getAllSettings();
1104
1105            mBackgroundScheduler.updateSchedule(settings);
1106            WifiNative.ScanSettings schedule = mBackgroundScheduler.getSchedule();
1107
1108            if (ScanScheduleUtil.scheduleEquals(mPreviousSchedule, schedule)) {
1109                if (DBG) Log.d(TAG, "schedule updated with no change");
1110                return true;
1111            }
1112
1113            mPreviousSchedule = schedule;
1114
1115            if (schedule.num_buckets == 0) {
1116                mScannerImpl.stopBatchedScan();
1117                if (DBG) Log.d(TAG, "scan stopped");
1118                return true;
1119            } else {
1120                Log.d(TAG, "starting scan: "
1121                        + "base period=" + schedule.base_period_ms
1122                        + ", max ap per scan=" + schedule.max_ap_per_scan
1123                        + ", batched scans=" + schedule.report_threshold_num_scans);
1124                for (int b = 0; b < schedule.num_buckets; b++) {
1125                    WifiNative.BucketSettings bucket = schedule.buckets[b];
1126                    Log.d(TAG, "bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)"
1127                            + "[" + bucket.report_events + "]: "
1128                            + ChannelHelper.toString(bucket));
1129                }
1130
1131                if (mScannerImpl.startBatchedScan(schedule, this)) {
1132                    if (DBG) {
1133                        Log.d(TAG, "scan restarted with " + schedule.num_buckets
1134                                + " bucket(s) and base period: " + schedule.base_period_ms);
1135                    }
1136                    return true;
1137                } else {
1138                    mPreviousSchedule = null;
1139                    loge("error starting scan: "
1140                            + "base period=" + schedule.base_period_ms
1141                            + ", max ap per scan=" + schedule.max_ap_per_scan
1142                            + ", batched scans=" + schedule.report_threshold_num_scans);
1143                    for (int b = 0; b < schedule.num_buckets; b++) {
1144                        WifiNative.BucketSettings bucket = schedule.buckets[b];
1145                        loge("bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)"
1146                                + "[" + bucket.report_events + "]: "
1147                                + ChannelHelper.toString(bucket));
1148                    }
1149                    return false;
1150                }
1151            }
1152        }
1153
1154        private void removeBackgroundScanRequest(ClientInfo ci, int handler) {
1155            if (ci != null) {
1156                ScanSettings settings = mActiveBackgroundScans.removeRequest(ci, handler);
1157                logScanRequest("removeBackgroundScanRequest", ci, handler, null, settings, null);
1158                updateSchedule();
1159            }
1160        }
1161
1162        private void reportFullScanResult(ScanResult result, int bucketsScanned) {
1163            for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) {
1164                ClientInfo ci = entry.clientInfo;
1165                int handler = entry.handlerId;
1166                ScanSettings settings = entry.settings;
1167                if (mBackgroundScheduler.shouldReportFullScanResultForSettings(
1168                                result, bucketsScanned, settings)) {
1169                    ScanResult newResult = new ScanResult(result);
1170                    if (result.informationElements != null) {
1171                        newResult.informationElements = result.informationElements.clone();
1172                    }
1173                    else {
1174                        newResult.informationElements = null;
1175                    }
1176                    ci.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, handler, newResult);
1177                }
1178            }
1179        }
1180
1181        private void reportScanResults(ScanData[] results) {
1182            for (ScanData result : results) {
1183                if (result != null && result.getResults() != null) {
1184                    if (result.getResults().length > 0) {
1185                        mWifiMetrics.incrementNonEmptyScanResultCount();
1186                    } else {
1187                        mWifiMetrics.incrementEmptyScanResultCount();
1188                    }
1189                }
1190            }
1191            for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) {
1192                ClientInfo ci = entry.clientInfo;
1193                int handler = entry.handlerId;
1194                ScanSettings settings = entry.settings;
1195                ScanData[] resultsToDeliver =
1196                        mBackgroundScheduler.filterResultsForSettings(results, settings);
1197                if (resultsToDeliver != null) {
1198                    logCallback("backgroundScanResults", ci, handler,
1199                            describeForLog(resultsToDeliver));
1200                    WifiScanner.ParcelableScanData parcelableScanData =
1201                            new WifiScanner.ParcelableScanData(resultsToDeliver);
1202                    ci.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, handler, parcelableScanData);
1203                }
1204            }
1205        }
1206
1207        private void sendBackgroundScanFailedToAllAndClear(int reason, String description) {
1208            for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) {
1209                ClientInfo ci = entry.clientInfo;
1210                int handler = entry.handlerId;
1211                ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler,
1212                        new WifiScanner.OperationResult(reason, description));
1213            }
1214            mActiveBackgroundScans.clear();
1215        }
1216
1217        private void addHotlist(ClientInfo ci, int handler, WifiScanner.HotlistSettings settings) {
1218            mActiveHotlistSettings.addRequest(ci, handler, null, settings);
1219            resetHotlist();
1220        }
1221
1222        private void removeHotlist(ClientInfo ci, int handler) {
1223            mActiveHotlistSettings.removeRequest(ci, handler);
1224            resetHotlist();
1225        }
1226
1227        private void resetHotlist() {
1228            if (mScannerImpl == null) {
1229                loge("Failed to update hotlist because WifiScanningService is not initialized");
1230                return;
1231            }
1232
1233            Collection<WifiScanner.HotlistSettings> settings =
1234                    mActiveHotlistSettings.getAllSettings();
1235            int num_hotlist_ap = 0;
1236
1237            for (WifiScanner.HotlistSettings s : settings) {
1238                num_hotlist_ap +=  s.bssidInfos.length;
1239            }
1240
1241            if (num_hotlist_ap == 0) {
1242                mScannerImpl.resetHotlist();
1243            } else {
1244                BssidInfo[] bssidInfos = new BssidInfo[num_hotlist_ap];
1245                int apLostThreshold = Integer.MAX_VALUE;
1246                int index = 0;
1247                for (WifiScanner.HotlistSettings s : settings) {
1248                    for (int i = 0; i < s.bssidInfos.length; i++, index++) {
1249                        bssidInfos[index] = s.bssidInfos[i];
1250                    }
1251                    if (s.apLostThreshold < apLostThreshold) {
1252                        apLostThreshold = s.apLostThreshold;
1253                    }
1254                }
1255
1256                WifiScanner.HotlistSettings mergedSettings = new WifiScanner.HotlistSettings();
1257                mergedSettings.bssidInfos = bssidInfos;
1258                mergedSettings.apLostThreshold = apLostThreshold;
1259                mScannerImpl.setHotlist(mergedSettings, this);
1260            }
1261        }
1262
1263        private void reportHotlistResults(int what, ScanResult[] results) {
1264            if (DBG) localLog("reportHotlistResults " + what + " results " + results.length);
1265            for (RequestInfo<WifiScanner.HotlistSettings> entry : mActiveHotlistSettings) {
1266                ClientInfo ci = entry.clientInfo;
1267                int handler = entry.handlerId;
1268                WifiScanner.HotlistSettings settings = entry.settings;
1269                int num_results = 0;
1270                for (ScanResult result : results) {
1271                    for (BssidInfo BssidInfo : settings.bssidInfos) {
1272                        if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) {
1273                            num_results++;
1274                            break;
1275                        }
1276                    }
1277                }
1278                if (num_results == 0) {
1279                    // nothing to report
1280                    return;
1281                }
1282                ScanResult[] results2 = new ScanResult[num_results];
1283                int index = 0;
1284                for (ScanResult result : results) {
1285                    for (BssidInfo BssidInfo : settings.bssidInfos) {
1286                        if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) {
1287                            results2[index] = result;
1288                            index++;
1289                        }
1290                    }
1291                }
1292                WifiScanner.ParcelableScanResults parcelableScanResults =
1293                        new WifiScanner.ParcelableScanResults(results2);
1294
1295                ci.reportEvent(what, 0, handler, parcelableScanResults);
1296            }
1297        }
1298
1299        private void sendHotlistFailedToAllAndClear(int reason, String description) {
1300            for (RequestInfo<WifiScanner.HotlistSettings> entry : mActiveHotlistSettings) {
1301                ClientInfo ci = entry.clientInfo;
1302                int handler = entry.handlerId;
1303                ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler,
1304                        new WifiScanner.OperationResult(reason, description));
1305            }
1306            mActiveHotlistSettings.clear();
1307        }
1308    }
1309
1310    /**
1311     * PNO scan state machine has 5 states:
1312     * -Default State
1313     *   -Started State
1314     *     -Hw Pno Scan state
1315     *       -Single Scan state
1316     *     -Sw Pno Scan state
1317     *
1318     * These are the main state transitions:
1319     * 1. Start at |Default State|
1320     * 2. Move to |Started State| when we get the |WIFI_SCAN_AVAILABLE| broadcast from WifiManager.
1321     * 3. When a new PNO scan request comes in:
1322     *   a.1. Switch to |Hw Pno Scan state| when the device supports HW PNO
1323     *        (This could either be HAL based ePNO or supplicant based PNO).
1324     *   a.2. In |Hw Pno Scan state| when PNO scan results are received, check if the result
1325     *        contains IE (information elements). If yes, send the results to the client, else
1326     *        switch to |Single Scan state| and send the result to the client when the scan result
1327     *        is obtained.
1328     *   b.1. Switch to |Sw Pno Scan state| when the device does not supports HW PNO
1329     *        (This is for older devices which do not support HW PNO and for connected PNO on
1330     *         devices which support supplicant based PNO)
1331     *   b.2. In |Sw Pno Scan state| send the result to the client when the background scan result
1332     *        is obtained
1333     *
1334     * Note: PNO scans only work for a single client today. We don't have support in HW to support
1335     * multiple requests at the same time, so will need non-trivial changes to support (if at all
1336     * possible) in WifiScanningService.
1337     */
1338    class WifiPnoScanStateMachine extends StateMachine implements WifiNative.PnoEventHandler {
1339
1340        private final DefaultState mDefaultState = new DefaultState();
1341        private final StartedState mStartedState = new StartedState();
1342        private final HwPnoScanState mHwPnoScanState = new HwPnoScanState();
1343        private final SwPnoScanState mSwPnoScanState = new SwPnoScanState();
1344        private final SingleScanState mSingleScanState = new SingleScanState();
1345        private InternalClientInfo mInternalClientInfo;
1346
1347        private final RequestList<Pair<PnoSettings, ScanSettings>> mActivePnoScans =
1348                new RequestList<>();
1349
1350        WifiPnoScanStateMachine(Looper looper) {
1351            super("WifiPnoScanStateMachine", looper);
1352
1353            setLogRecSize(512);
1354            setLogOnlyTransitions(false);
1355
1356            // CHECKSTYLE:OFF IndentationCheck
1357            addState(mDefaultState);
1358                addState(mStartedState, mDefaultState);
1359                    addState(mHwPnoScanState, mStartedState);
1360                        addState(mSingleScanState, mHwPnoScanState);
1361                    addState(mSwPnoScanState, mStartedState);
1362            // CHECKSTYLE:ON IndentationCheck
1363
1364            setInitialState(mDefaultState);
1365        }
1366
1367        public void removePnoSettings(ClientInfo ci) {
1368            mActivePnoScans.removeAllForClient(ci);
1369            transitionTo(mStartedState);
1370        }
1371
1372        @Override
1373        public void onPnoNetworkFound(ScanResult[] results) {
1374            if (DBG) localLog("onWifiPnoNetworkFound event received");
1375            sendMessage(CMD_PNO_NETWORK_FOUND, 0, 0, results);
1376        }
1377
1378        @Override
1379        public void onPnoScanFailed() {
1380            if (DBG) localLog("onWifiPnoScanFailed event received");
1381            sendMessage(CMD_PNO_SCAN_FAILED, 0, 0, null);
1382        }
1383
1384        class DefaultState extends State {
1385            @Override
1386            public void enter() {
1387                if (DBG) localLog("DefaultState");
1388            }
1389
1390            @Override
1391            public boolean processMessage(Message msg) {
1392                switch (msg.what) {
1393                    case CMD_DRIVER_LOADED:
1394                        transitionTo(mStartedState);
1395                        break;
1396                    case CMD_DRIVER_UNLOADED:
1397                        transitionTo(mDefaultState);
1398                        break;
1399                    case WifiScanner.CMD_START_PNO_SCAN:
1400                    case WifiScanner.CMD_STOP_PNO_SCAN:
1401                        replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
1402                        break;
1403                    case CMD_PNO_NETWORK_FOUND:
1404                    case CMD_PNO_SCAN_FAILED:
1405                    case WifiScanner.CMD_SCAN_RESULT:
1406                    case WifiScanner.CMD_OP_FAILED:
1407                        loge("Unexpected message " + msg.what);
1408                        break;
1409                    default:
1410                        return NOT_HANDLED;
1411                }
1412                return HANDLED;
1413            }
1414        }
1415
1416        class StartedState extends State {
1417            @Override
1418            public void enter() {
1419                if (DBG) localLog("StartedState");
1420            }
1421
1422            @Override
1423            public void exit() {
1424                sendPnoScanFailedToAllAndClear(
1425                        WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted");
1426            }
1427
1428            @Override
1429            public boolean processMessage(Message msg) {
1430                ClientInfo ci = mClients.get(msg.replyTo);
1431                switch (msg.what) {
1432                    case WifiScanner.CMD_START_PNO_SCAN:
1433                        Bundle pnoParams = (Bundle) msg.obj;
1434                        if (pnoParams == null) {
1435                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
1436                            return HANDLED;
1437                        }
1438                        pnoParams.setDefusable(true);
1439                        PnoSettings pnoSettings =
1440                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
1441                        // This message is handled after the transition to SwPnoScan/HwPnoScan state
1442                        deferMessage(msg);
1443                        if (mScannerImpl.isHwPnoSupported(pnoSettings.isConnected)) {
1444                            transitionTo(mHwPnoScanState);
1445                        } else {
1446                            transitionTo(mSwPnoScanState);
1447                        }
1448                        break;
1449                    case WifiScanner.CMD_STOP_PNO_SCAN:
1450                        replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "no scan running");
1451                        break;
1452                    default:
1453                        return NOT_HANDLED;
1454                }
1455                return HANDLED;
1456            }
1457        }
1458
1459        class HwPnoScanState extends State {
1460            @Override
1461            public void enter() {
1462                if (DBG) localLog("HwPnoScanState");
1463            }
1464
1465            @Override
1466            public void exit() {
1467                // Reset PNO scan in ScannerImpl before we exit.
1468                mScannerImpl.resetHwPnoList();
1469                removeInternalClient();
1470            }
1471
1472            @Override
1473            public boolean processMessage(Message msg) {
1474                ClientInfo ci = mClients.get(msg.replyTo);
1475                switch (msg.what) {
1476                    case WifiScanner.CMD_START_PNO_SCAN:
1477                        Bundle pnoParams = (Bundle) msg.obj;
1478                        if (pnoParams == null) {
1479                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
1480                            return HANDLED;
1481                        }
1482                        pnoParams.setDefusable(true);
1483                        PnoSettings pnoSettings =
1484                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
1485                        ScanSettings scanSettings =
1486                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY);
1487                        if (addHwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) {
1488                            replySucceeded(msg);
1489                        } else {
1490                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
1491                            transitionTo(mStartedState);
1492                        }
1493                        break;
1494                    case WifiScanner.CMD_STOP_PNO_SCAN:
1495                        removeHwPnoScanRequest(ci, msg.arg2);
1496                        transitionTo(mStartedState);
1497                        break;
1498                    case CMD_PNO_NETWORK_FOUND:
1499                        ScanResult[] scanResults = ((ScanResult[]) msg.obj);
1500                        if (isSingleScanNeeded(scanResults)) {
1501                            ScanSettings activeScanSettings = getScanSettings();
1502                            if (activeScanSettings == null) {
1503                                sendPnoScanFailedToAllAndClear(
1504                                        WifiScanner.REASON_UNSPECIFIED,
1505                                        "couldn't retrieve setting");
1506                                transitionTo(mStartedState);
1507                            } else {
1508                                addSingleScanRequest(activeScanSettings);
1509                                transitionTo(mSingleScanState);
1510                            }
1511                        } else {
1512                            reportPnoNetworkFound((ScanResult[]) msg.obj);
1513                        }
1514                        break;
1515                    case CMD_PNO_SCAN_FAILED:
1516                        sendPnoScanFailedToAllAndClear(
1517                                WifiScanner.REASON_UNSPECIFIED, "pno scan failed");
1518                        transitionTo(mStartedState);
1519                        break;
1520                    default:
1521                        return NOT_HANDLED;
1522                }
1523                return HANDLED;
1524            }
1525        }
1526
1527        class SingleScanState extends State {
1528            @Override
1529            public void enter() {
1530                if (DBG) localLog("SingleScanState");
1531            }
1532
1533            @Override
1534            public boolean processMessage(Message msg) {
1535                ClientInfo ci = mClients.get(msg.replyTo);
1536                switch (msg.what) {
1537                    case WifiScanner.CMD_SCAN_RESULT:
1538                        WifiScanner.ParcelableScanData parcelableScanData =
1539                                (WifiScanner.ParcelableScanData) msg.obj;
1540                        ScanData[] scanDatas = parcelableScanData.getResults();
1541                        ScanData lastScanData = scanDatas[scanDatas.length - 1];
1542                        reportPnoNetworkFound(lastScanData.getResults());
1543                        transitionTo(mHwPnoScanState);
1544                        break;
1545                    case WifiScanner.CMD_OP_FAILED:
1546                        sendPnoScanFailedToAllAndClear(
1547                                WifiScanner.REASON_UNSPECIFIED, "single scan failed");
1548                        transitionTo(mStartedState);
1549                        break;
1550                    default:
1551                        return NOT_HANDLED;
1552                }
1553                return HANDLED;
1554            }
1555        }
1556
1557        class SwPnoScanState extends State {
1558            private final ArrayList<ScanResult> mSwPnoFullScanResults = new ArrayList<>();
1559
1560            @Override
1561            public void enter() {
1562                if (DBG) localLog("SwPnoScanState");
1563                mSwPnoFullScanResults.clear();
1564            }
1565
1566            @Override
1567            public void exit() {
1568                removeInternalClient();
1569            }
1570
1571            @Override
1572            public boolean processMessage(Message msg) {
1573                ClientInfo ci = mClients.get(msg.replyTo);
1574                switch (msg.what) {
1575                    case WifiScanner.CMD_START_PNO_SCAN:
1576                        Bundle pnoParams = (Bundle) msg.obj;
1577                        if (pnoParams == null) {
1578                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
1579                            return HANDLED;
1580                        }
1581                        pnoParams.setDefusable(true);
1582                        PnoSettings pnoSettings =
1583                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
1584                        ScanSettings scanSettings =
1585                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY);
1586                        if (addSwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) {
1587                            replySucceeded(msg);
1588                        } else {
1589                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
1590                            transitionTo(mStartedState);
1591                        }
1592                        break;
1593                    case WifiScanner.CMD_STOP_PNO_SCAN:
1594                        removeSwPnoScanRequest(ci, msg.arg2);
1595                        transitionTo(mStartedState);
1596                        break;
1597                    case WifiScanner.CMD_FULL_SCAN_RESULT:
1598                        // Aggregate full scan results until we get the |CMD_SCAN_RESULT| message
1599                        mSwPnoFullScanResults.add((ScanResult) msg.obj);
1600                        break;
1601                    case WifiScanner.CMD_SCAN_RESULT:
1602                        ScanResult[] scanResults = mSwPnoFullScanResults.toArray(
1603                                new ScanResult[mSwPnoFullScanResults.size()]);
1604                        reportPnoNetworkFound(scanResults);
1605                        mSwPnoFullScanResults.clear();
1606                        break;
1607                    case WifiScanner.CMD_OP_FAILED:
1608                        sendPnoScanFailedToAllAndClear(
1609                                WifiScanner.REASON_UNSPECIFIED, "background scan failed");
1610                        transitionTo(mStartedState);
1611                        break;
1612                    default:
1613                        return NOT_HANDLED;
1614                }
1615                return HANDLED;
1616            }
1617        }
1618
1619        private WifiNative.PnoSettings convertPnoSettingsToNative(PnoSettings pnoSettings) {
1620            WifiNative.PnoSettings nativePnoSetting = new WifiNative.PnoSettings();
1621            nativePnoSetting.min5GHzRssi = pnoSettings.min5GHzRssi;
1622            nativePnoSetting.min24GHzRssi = pnoSettings.min24GHzRssi;
1623            nativePnoSetting.initialScoreMax = pnoSettings.initialScoreMax;
1624            nativePnoSetting.currentConnectionBonus = pnoSettings.currentConnectionBonus;
1625            nativePnoSetting.sameNetworkBonus = pnoSettings.sameNetworkBonus;
1626            nativePnoSetting.secureBonus = pnoSettings.secureBonus;
1627            nativePnoSetting.band5GHzBonus = pnoSettings.band5GHzBonus;
1628            nativePnoSetting.isConnected = pnoSettings.isConnected;
1629            nativePnoSetting.networkList =
1630                    new WifiNative.PnoNetwork[pnoSettings.networkList.length];
1631            for (int i = 0; i < pnoSettings.networkList.length; i++) {
1632                nativePnoSetting.networkList[i] = new WifiNative.PnoNetwork();
1633                nativePnoSetting.networkList[i].ssid = pnoSettings.networkList[i].ssid;
1634                nativePnoSetting.networkList[i].networkId = pnoSettings.networkList[i].networkId;
1635                nativePnoSetting.networkList[i].priority = pnoSettings.networkList[i].priority;
1636                nativePnoSetting.networkList[i].flags = pnoSettings.networkList[i].flags;
1637                nativePnoSetting.networkList[i].auth_bit_field =
1638                        pnoSettings.networkList[i].authBitField;
1639            }
1640            return nativePnoSetting;
1641        }
1642
1643        // Retrieve the only active scan settings.
1644        private ScanSettings getScanSettings() {
1645            for (Pair<PnoSettings, ScanSettings> settingsPair : mActivePnoScans.getAllSettings()) {
1646                return settingsPair.second;
1647            }
1648            return null;
1649        }
1650
1651        private void removeInternalClient() {
1652            if (mInternalClientInfo != null) {
1653                mInternalClientInfo.cleanup();
1654                mInternalClientInfo = null;
1655            } else {
1656                Log.w(TAG, "No Internal client for PNO");
1657            }
1658        }
1659
1660        private void addInternalClient(ClientInfo ci) {
1661            if (mInternalClientInfo == null) {
1662                mInternalClientInfo =
1663                        new InternalClientInfo(ci.getUid(), new Messenger(this.getHandler()));
1664                mInternalClientInfo.register();
1665            } else {
1666                Log.w(TAG, "Internal client for PNO already exists");
1667            }
1668        }
1669
1670        private void addPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings,
1671                PnoSettings pnoSettings) {
1672            mActivePnoScans.addRequest(ci, handler, WifiStateMachine.WIFI_WORK_SOURCE,
1673                    Pair.create(pnoSettings, scanSettings));
1674            addInternalClient(ci);
1675        }
1676
1677        private Pair<PnoSettings, ScanSettings> removePnoScanRequest(ClientInfo ci, int handler) {
1678            Pair<PnoSettings, ScanSettings> settings = mActivePnoScans.removeRequest(ci, handler);
1679            return settings;
1680        }
1681
1682        private boolean addHwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings,
1683                PnoSettings pnoSettings) {
1684            if (ci == null) {
1685                Log.d(TAG, "Failing scan request ClientInfo not found " + handler);
1686                return false;
1687            }
1688            if (!mActivePnoScans.isEmpty()) {
1689                loge("Failing scan request because there is already an active scan");
1690                return false;
1691            }
1692            WifiNative.PnoSettings nativePnoSettings = convertPnoSettingsToNative(pnoSettings);
1693            if (!mScannerImpl.setHwPnoList(nativePnoSettings, mPnoScanStateMachine)) {
1694                return false;
1695            }
1696            logScanRequest("addHwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings);
1697            addPnoScanRequest(ci, handler, scanSettings, pnoSettings);
1698            // HW PNO is supported, check if we need a background scan running for this.
1699            if (mScannerImpl.shouldScheduleBackgroundScanForHwPno()) {
1700                addBackgroundScanRequest(scanSettings);
1701            }
1702            return true;
1703        }
1704
1705        private void removeHwPnoScanRequest(ClientInfo ci, int handler) {
1706            if (ci != null) {
1707                Pair<PnoSettings, ScanSettings> settings = removePnoScanRequest(ci, handler);
1708                logScanRequest("removeHwPnoScanRequest", ci, handler, null,
1709                        settings.second, settings.first);
1710            }
1711        }
1712
1713        private boolean addSwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings,
1714                PnoSettings pnoSettings) {
1715            if (ci == null) {
1716                Log.d(TAG, "Failing scan request ClientInfo not found " + handler);
1717                return false;
1718            }
1719            if (!mActivePnoScans.isEmpty()) {
1720                loge("Failing scan request because there is already an active scan");
1721                return false;
1722            }
1723            logScanRequest("addSwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings);
1724            addPnoScanRequest(ci, handler, scanSettings, pnoSettings);
1725            // HW PNO is not supported, we need to revert to normal background scans and
1726            // report events after each scan and we need full scan results to get the IE information
1727            scanSettings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
1728                    | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
1729            addBackgroundScanRequest(scanSettings);
1730            return true;
1731        }
1732
1733        private void removeSwPnoScanRequest(ClientInfo ci, int handler) {
1734            if (ci != null) {
1735                Pair<PnoSettings, ScanSettings> settings = removePnoScanRequest(ci, handler);
1736                logScanRequest("removeSwPnoScanRequest", ci, handler, null,
1737                        settings.second, settings.first);
1738            }
1739        }
1740
1741        private void reportPnoNetworkFound(ScanResult[] results) {
1742            WifiScanner.ParcelableScanResults parcelableScanResults =
1743                    new WifiScanner.ParcelableScanResults(results);
1744            for (RequestInfo<Pair<PnoSettings, ScanSettings>> entry : mActivePnoScans) {
1745                ClientInfo ci = entry.clientInfo;
1746                int handler = entry.handlerId;
1747                logCallback("pnoNetworkFound", ci, handler, describeForLog(results));
1748                ci.reportEvent(
1749                        WifiScanner.CMD_PNO_NETWORK_FOUND, 0, handler, parcelableScanResults);
1750            }
1751        }
1752
1753        private void sendPnoScanFailedToAllAndClear(int reason, String description) {
1754            for (RequestInfo<Pair<PnoSettings, ScanSettings>> entry : mActivePnoScans) {
1755                ClientInfo ci = entry.clientInfo;
1756                int handler = entry.handlerId;
1757                ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler,
1758                        new WifiScanner.OperationResult(reason, description));
1759            }
1760            mActivePnoScans.clear();
1761        }
1762
1763        private void addBackgroundScanRequest(ScanSettings settings) {
1764            if (DBG) localLog("Starting background scan");
1765            if (mInternalClientInfo != null) {
1766                mInternalClientInfo.sendRequestToClientHandler(
1767                        WifiScanner.CMD_START_BACKGROUND_SCAN, settings,
1768                        WifiStateMachine.WIFI_WORK_SOURCE);
1769            }
1770        }
1771
1772        private void addSingleScanRequest(ScanSettings settings) {
1773            if (DBG) localLog("Starting single scan");
1774            if (mInternalClientInfo != null) {
1775                mInternalClientInfo.sendRequestToClientHandler(
1776                        WifiScanner.CMD_START_SINGLE_SCAN, settings,
1777                        WifiStateMachine.WIFI_WORK_SOURCE);
1778            }
1779        }
1780
1781        /**
1782         * Checks if IE are present in scan data, if no single scan is needed to report event to
1783         * client
1784         */
1785        private boolean isSingleScanNeeded(ScanResult[] scanResults) {
1786            for (ScanResult scanResult : scanResults) {
1787                if (scanResult.informationElements != null
1788                        && scanResult.informationElements.length > 0) {
1789                    return false;
1790                }
1791            }
1792            return true;
1793        }
1794    }
1795
1796    private abstract class ClientInfo {
1797        private final int mUid;
1798        private final WorkSource mWorkSource;
1799        private boolean mScanWorkReported = false;
1800        protected final Messenger mMessenger;
1801
1802        ClientInfo(int uid, Messenger messenger) {
1803            mUid = uid;
1804            mMessenger = messenger;
1805            mWorkSource = new WorkSource(uid);
1806        }
1807
1808        /**
1809         * Register this client to main client map.
1810         */
1811        public void register() {
1812            mClients.put(mMessenger, this);
1813        }
1814
1815        /**
1816         * Unregister this client from main client map.
1817         */
1818        private void unregister() {
1819            mClients.remove(mMessenger);
1820        }
1821
1822        public void cleanup() {
1823            mSingleScanStateMachine.removeSingleScanRequests(this);
1824            mBackgroundScanStateMachine.removeBackgroundScanSettings(this);
1825            mBackgroundScanStateMachine.removeHotlistSettings(this);
1826            unregister();
1827            localLog("Successfully stopped all requests for client " + this);
1828        }
1829
1830        public int getUid() {
1831            return mUid;
1832        }
1833
1834        public void reportEvent(int what, int arg1, int arg2) {
1835            reportEvent(what, arg1, arg2, null);
1836        }
1837
1838        // This has to be implemented by subclasses to report events back to clients.
1839        public abstract void reportEvent(int what, int arg1, int arg2, Object obj);
1840
1841        // TODO(b/27903217): Blame scan on provided work source
1842        private void reportBatchedScanStart() {
1843            if (mUid == 0)
1844                return;
1845
1846            int csph = getCsph();
1847
1848            try {
1849                mBatteryStats.noteWifiBatchedScanStartedFromSource(mWorkSource, csph);
1850            } catch (RemoteException e) {
1851                logw("failed to report scan work: " + e.toString());
1852            }
1853        }
1854
1855        private void reportBatchedScanStop() {
1856            if (mUid == 0)
1857                return;
1858
1859            try {
1860                mBatteryStats.noteWifiBatchedScanStoppedFromSource(mWorkSource);
1861            } catch (RemoteException e) {
1862                logw("failed to cleanup scan work: " + e.toString());
1863            }
1864        }
1865
1866        // TODO migrate batterystats to accept scan duration per hour instead of csph
1867        private int getCsph() {
1868            int totalScanDurationPerHour = 0;
1869            Collection<ScanSettings> settingsList =
1870                    mBackgroundScanStateMachine.getBackgroundScanSettings(this);
1871            for (ScanSettings settings : settingsList) {
1872                int scanDurationMs = mChannelHelper.estimateScanDuration(settings);
1873                int scans_per_Hour = settings.periodInMs == 0 ? 1 : (3600 * 1000) /
1874                        settings.periodInMs;
1875                totalScanDurationPerHour += scanDurationMs * scans_per_Hour;
1876            }
1877
1878            return totalScanDurationPerHour / ChannelHelper.SCAN_PERIOD_PER_CHANNEL_MS;
1879        }
1880
1881        public void reportScanWorkUpdate() {
1882            if (mScanWorkReported) {
1883                reportBatchedScanStop();
1884                mScanWorkReported = false;
1885            }
1886            if (mBackgroundScanStateMachine.getBackgroundScanSettings(this).isEmpty()) {
1887                reportBatchedScanStart();
1888                mScanWorkReported = true;
1889            }
1890        }
1891
1892        @Override
1893        public String toString() {
1894            return "ClientInfo[uid=" + mUid + "]";
1895        }
1896    }
1897
1898    /**
1899     * This class is used to represent external clients to the WifiScanning Service.
1900     */
1901    private class ExternalClientInfo extends ClientInfo {
1902        private final AsyncChannel mChannel;
1903        /**
1904         * Indicates if the client is still connected
1905         * If the client is no longer connected then messages to it will be silently dropped
1906         */
1907        private boolean mDisconnected = false;
1908
1909        ExternalClientInfo(int uid, Messenger messenger, AsyncChannel c) {
1910            super(uid, messenger);
1911            mChannel = c;
1912            if (DBG) localLog("New client, channel: " + c);
1913        }
1914
1915        @Override
1916        public void reportEvent(int what, int arg1, int arg2, Object obj) {
1917            if (!mDisconnected) {
1918                mChannel.sendMessage(what, arg1, arg2, obj);
1919            }
1920        }
1921
1922        @Override
1923        public void cleanup() {
1924            mDisconnected = true;
1925            // Internal clients should not have any wifi change requests. So, keeping this cleanup
1926            // only for external client because this will otherwise cause an infinite recursion
1927            // when the internal client in WifiChangeStateMachine is cleaned up.
1928            mWifiChangeStateMachine.removeWifiChangeHandler(this);
1929            mPnoScanStateMachine.removePnoSettings(this);
1930            super.cleanup();
1931        }
1932    }
1933
1934    /**
1935     * This class is used to represent internal clients to the WifiScanning Service. This is needed
1936     * for communicating between State Machines.
1937     * This leaves the onReportEvent method unimplemented, so that the clients have the freedom
1938     * to handle the events as they need.
1939     */
1940    private class InternalClientInfo extends ClientInfo {
1941        private static final int INTERNAL_CLIENT_HANDLER = 0;
1942
1943        /**
1944         * The UID here is used to proxy the original external requester UID.
1945         */
1946        InternalClientInfo(int requesterUid, Messenger messenger) {
1947            super(requesterUid, messenger);
1948        }
1949
1950        @Override
1951        public void reportEvent(int what, int arg1, int arg2, Object obj) {
1952            Message message = Message.obtain();
1953            message.what = what;
1954            message.arg1 = arg1;
1955            message.arg2 = arg2;
1956            message.obj = obj;
1957            try {
1958                mMessenger.send(message);
1959            } catch (RemoteException e) {
1960                loge("Failed to send message: " + what);
1961            }
1962        }
1963
1964        /**
1965         * Send a message to the client handler which should reroute the message to the appropriate
1966         * state machine.
1967         */
1968        public void sendRequestToClientHandler(int what, ScanSettings settings,
1969                WorkSource workSource) {
1970            Message msg = Message.obtain();
1971            msg.what = what;
1972            msg.arg2 = INTERNAL_CLIENT_HANDLER;
1973            if (settings != null) {
1974                Bundle bundle = new Bundle();
1975                bundle.putParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
1976                bundle.putParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
1977                msg.obj = bundle;
1978            }
1979            msg.replyTo = mMessenger;
1980            msg.sendingUid = getUid();
1981            mClientHandler.sendMessage(msg);
1982        }
1983
1984        /**
1985         * Send a message to the client handler which should reroute the message to the appropriate
1986         * state machine.
1987         */
1988        public void sendRequestToClientHandler(int what) {
1989            sendRequestToClientHandler(what, null, null);
1990        }
1991    }
1992
1993    void replySucceeded(Message msg) {
1994        if (msg.replyTo != null) {
1995            Message reply = Message.obtain();
1996            reply.what = WifiScanner.CMD_OP_SUCCEEDED;
1997            reply.arg2 = msg.arg2;
1998            try {
1999                msg.replyTo.send(reply);
2000            } catch (RemoteException e) {
2001                // There's not much we can do if reply can't be sent!
2002            }
2003        } else {
2004            // locally generated message; doesn't need a reply!
2005        }
2006    }
2007
2008    void replyFailed(Message msg, int reason, String description) {
2009        if (msg.replyTo != null) {
2010            Message reply = Message.obtain();
2011            reply.what = WifiScanner.CMD_OP_FAILED;
2012            reply.arg2 = msg.arg2;
2013            reply.obj = new WifiScanner.OperationResult(reason, description);
2014            try {
2015                msg.replyTo.send(reply);
2016            } catch (RemoteException e) {
2017                // There's not much we can do if reply can't be sent!
2018            }
2019        } else {
2020            // locally generated message; doesn't need a reply!
2021        }
2022    }
2023
2024    /**
2025     * Wifi Change state machine is used to handle any wifi change tracking requests.
2026     * TODO: This state machine doesn't handle driver loading/unloading yet.
2027     */
2028    class WifiChangeStateMachine extends StateMachine
2029            implements WifiNative.SignificantWifiChangeEventHandler {
2030
2031        private static final int MAX_APS_TO_TRACK = 3;
2032        private static final int MOVING_SCAN_PERIOD_MS      = 10000;
2033        private static final int STATIONARY_SCAN_PERIOD_MS  =  5000;
2034        private static final int MOVING_STATE_TIMEOUT_MS    = 30000;
2035
2036        State mDefaultState = new DefaultState();
2037        State mStationaryState = new StationaryState();
2038        State mMovingState = new MovingState();
2039
2040        private static final String ACTION_TIMEOUT =
2041                "com.android.server.WifiScanningServiceImpl.action.TIMEOUT";
2042        private PendingIntent mTimeoutIntent;
2043        private ScanResult[] mCurrentBssids;
2044        private InternalClientInfo mInternalClientInfo;
2045
2046        private final Set<Pair<ClientInfo, Integer>> mActiveWifiChangeHandlers = new HashSet<>();
2047
2048        WifiChangeStateMachine(Looper looper) {
2049            super("SignificantChangeStateMachine", looper);
2050
2051            // CHECKSTYLE:OFF IndentationCheck
2052            addState(mDefaultState);
2053                addState(mStationaryState, mDefaultState);
2054                addState(mMovingState, mDefaultState);
2055            // CHECKSTYLE:ON IndentationCheck
2056
2057            setInitialState(mDefaultState);
2058        }
2059
2060        public void removeWifiChangeHandler(ClientInfo ci) {
2061            Iterator<Pair<ClientInfo, Integer>> iter = mActiveWifiChangeHandlers.iterator();
2062            while (iter.hasNext()) {
2063                Pair<ClientInfo, Integer> entry = iter.next();
2064                if (entry.first == ci) {
2065                    iter.remove();
2066                }
2067            }
2068            untrackSignificantWifiChangeOnEmpty();
2069        }
2070
2071        class DefaultState extends State {
2072            @Override
2073            public void enter() {
2074                if (DBG) localLog("Entering IdleState");
2075            }
2076
2077            @Override
2078            public boolean processMessage(Message msg) {
2079                if (DBG) localLog("DefaultState state got " + msg);
2080                ClientInfo ci = mClients.get(msg.replyTo);
2081                switch (msg.what) {
2082                    case WifiScanner.CMD_START_TRACKING_CHANGE:
2083                        addWifiChangeHandler(ci, msg.arg2);
2084                        replySucceeded(msg);
2085                        transitionTo(mMovingState);
2086                        break;
2087                    case WifiScanner.CMD_STOP_TRACKING_CHANGE:
2088                        // nothing to do
2089                        break;
2090                    case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
2091                        /* save configuration till we transition to moving state */
2092                        deferMessage(msg);
2093                        break;
2094                    case WifiScanner.CMD_SCAN_RESULT:
2095                        // nothing to do
2096                        break;
2097                    default:
2098                        return NOT_HANDLED;
2099                }
2100                return HANDLED;
2101            }
2102        }
2103
2104        class StationaryState extends State {
2105            @Override
2106            public void enter() {
2107                if (DBG) localLog("Entering StationaryState");
2108                reportWifiStabilized(mCurrentBssids);
2109            }
2110
2111            @Override
2112            public boolean processMessage(Message msg) {
2113                if (DBG) localLog("Stationary state got " + msg);
2114                ClientInfo ci = mClients.get(msg.replyTo);
2115                switch (msg.what) {
2116                    case WifiScanner.CMD_START_TRACKING_CHANGE:
2117                        addWifiChangeHandler(ci, msg.arg2);
2118                        replySucceeded(msg);
2119                        break;
2120                    case WifiScanner.CMD_STOP_TRACKING_CHANGE:
2121                        removeWifiChangeHandler(ci, msg.arg2);
2122                        break;
2123                    case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
2124                        /* save configuration till we transition to moving state */
2125                        deferMessage(msg);
2126                        break;
2127                    case CMD_WIFI_CHANGE_DETECTED:
2128                        if (DBG) localLog("Got wifi change detected");
2129                        reportWifiChanged((ScanResult[]) msg.obj);
2130                        transitionTo(mMovingState);
2131                        break;
2132                    case WifiScanner.CMD_SCAN_RESULT:
2133                        // nothing to do
2134                        break;
2135                    default:
2136                        return NOT_HANDLED;
2137                }
2138                return HANDLED;
2139            }
2140        }
2141
2142        class MovingState extends State {
2143            boolean mWifiChangeDetected = false;
2144            boolean mScanResultsPending = false;
2145
2146            @Override
2147            public void enter() {
2148                if (DBG) localLog("Entering MovingState");
2149                if (mTimeoutIntent == null) {
2150                    Intent intent = new Intent(ACTION_TIMEOUT, null);
2151                    mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
2152
2153                    mContext.registerReceiver(
2154                            new BroadcastReceiver() {
2155                                @Override
2156                                public void onReceive(Context context, Intent intent) {
2157                                    sendMessage(CMD_WIFI_CHANGE_TIMEOUT);
2158                                }
2159                            }, new IntentFilter(ACTION_TIMEOUT));
2160                }
2161                issueFullScan();
2162            }
2163
2164            @Override
2165            public boolean processMessage(Message msg) {
2166                if (DBG) localLog("MovingState state got " + msg);
2167                ClientInfo ci = mClients.get(msg.replyTo);
2168                switch (msg.what) {
2169                    case WifiScanner.CMD_START_TRACKING_CHANGE:
2170                        addWifiChangeHandler(ci, msg.arg2);
2171                        replySucceeded(msg);
2172                        break;
2173                    case WifiScanner.CMD_STOP_TRACKING_CHANGE:
2174                        removeWifiChangeHandler(ci, msg.arg2);
2175                        break;
2176                    case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
2177                        if (DBG) localLog("Got configuration from app");
2178                        WifiScanner.WifiChangeSettings settings =
2179                                (WifiScanner.WifiChangeSettings) msg.obj;
2180                        reconfigureScan(settings);
2181                        mWifiChangeDetected = false;
2182                        long unchangedDelay = settings.unchangedSampleSize * settings.periodInMs;
2183                        mAlarmManager.cancel(mTimeoutIntent);
2184                        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
2185                                mClock.elapsedRealtime() + unchangedDelay,
2186                                mTimeoutIntent);
2187                        break;
2188                    case WifiScanner.CMD_SCAN_RESULT:
2189                        if (DBG) localLog("Got scan results");
2190                        if (mScanResultsPending) {
2191                            if (DBG) localLog("reconfiguring scan");
2192                            reconfigureScan((ScanData[])msg.obj,
2193                                    STATIONARY_SCAN_PERIOD_MS);
2194                            mWifiChangeDetected = false;
2195                            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
2196                                    mClock.elapsedRealtime() + MOVING_STATE_TIMEOUT_MS,
2197                                    mTimeoutIntent);
2198                            mScanResultsPending = false;
2199                        }
2200                        break;
2201                    case CMD_WIFI_CHANGE_DETECTED:
2202                        if (DBG) localLog("Change detected");
2203                        mAlarmManager.cancel(mTimeoutIntent);
2204                        reportWifiChanged((ScanResult[])msg.obj);
2205                        mWifiChangeDetected = true;
2206                        issueFullScan();
2207                        break;
2208                    case CMD_WIFI_CHANGE_TIMEOUT:
2209                        if (DBG) localLog("Got timeout event");
2210                        if (mWifiChangeDetected == false) {
2211                            transitionTo(mStationaryState);
2212                        }
2213                        break;
2214                    default:
2215                        return NOT_HANDLED;
2216                }
2217                return HANDLED;
2218            }
2219
2220            @Override
2221            public void exit() {
2222                mAlarmManager.cancel(mTimeoutIntent);
2223            }
2224
2225            void issueFullScan() {
2226                if (DBG) localLog("Issuing full scan");
2227                ScanSettings settings = new ScanSettings();
2228                settings.band = WifiScanner.WIFI_BAND_BOTH;
2229                settings.periodInMs = MOVING_SCAN_PERIOD_MS;
2230                settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
2231                addScanRequest(settings);
2232                mScanResultsPending = true;
2233            }
2234
2235        }
2236
2237        private void reconfigureScan(ScanData[] results, int period) {
2238            // find brightest APs and set them as sentinels
2239            if (results.length < MAX_APS_TO_TRACK) {
2240                localLog("too few APs (" + results.length + ") available to track wifi change");
2241                return;
2242            }
2243
2244            removeScanRequest();
2245
2246            // remove duplicate BSSIDs
2247            HashMap<String, ScanResult> bssidToScanResult = new HashMap<String, ScanResult>();
2248            for (ScanResult result : results[0].getResults()) {
2249                ScanResult saved = bssidToScanResult.get(result.BSSID);
2250                if (saved == null) {
2251                    bssidToScanResult.put(result.BSSID, result);
2252                } else if (saved.level > result.level) {
2253                    bssidToScanResult.put(result.BSSID, result);
2254                }
2255            }
2256
2257            // find brightest BSSIDs
2258            ScanResult brightest[] = new ScanResult[MAX_APS_TO_TRACK];
2259            Collection<ScanResult> results2 = bssidToScanResult.values();
2260            for (ScanResult result : results2) {
2261                for (int j = 0; j < brightest.length; j++) {
2262                    if (brightest[j] == null
2263                            || (brightest[j].level < result.level)) {
2264                        for (int k = brightest.length; k > (j + 1); k--) {
2265                            brightest[k - 1] = brightest[k - 2];
2266                        }
2267                        brightest[j] = result;
2268                        break;
2269                    }
2270                }
2271            }
2272
2273            // Get channels to scan for
2274            ArrayList<Integer> channels = new ArrayList<Integer>();
2275            for (int i = 0; i < brightest.length; i++) {
2276                boolean found = false;
2277                for (int j = i + 1; j < brightest.length; j++) {
2278                    if (brightest[j].frequency == brightest[i].frequency) {
2279                        found = true;
2280                    }
2281                }
2282                if (!found) {
2283                    channels.add(brightest[i].frequency);
2284                }
2285            }
2286
2287            if (DBG) localLog("Found " + channels.size() + " channels");
2288
2289            // set scanning schedule
2290            ScanSettings settings = new ScanSettings();
2291            settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
2292            settings.channels = new ChannelSpec[channels.size()];
2293            for (int i = 0; i < channels.size(); i++) {
2294                settings.channels[i] = new ChannelSpec(channels.get(i));
2295            }
2296
2297            settings.periodInMs = period;
2298            addScanRequest(settings);
2299
2300            WifiScanner.WifiChangeSettings settings2 = new WifiScanner.WifiChangeSettings();
2301            settings2.rssiSampleSize = 3;
2302            settings2.lostApSampleSize = 3;
2303            settings2.unchangedSampleSize = 3;
2304            settings2.minApsBreachingThreshold = 2;
2305            settings2.bssidInfos = new BssidInfo[brightest.length];
2306
2307            for (int i = 0; i < brightest.length; i++) {
2308                BssidInfo BssidInfo = new BssidInfo();
2309                BssidInfo.bssid = brightest[i].BSSID;
2310                int threshold = (100 + brightest[i].level) / 32 + 2;
2311                BssidInfo.low = brightest[i].level - threshold;
2312                BssidInfo.high = brightest[i].level + threshold;
2313                settings2.bssidInfos[i] = BssidInfo;
2314
2315                if (DBG) localLog("Setting bssid=" + BssidInfo.bssid + ", " +
2316                        "low=" + BssidInfo.low + ", high=" + BssidInfo.high);
2317            }
2318
2319            trackSignificantWifiChange(settings2);
2320            mCurrentBssids = brightest;
2321        }
2322
2323        private void reconfigureScan(WifiScanner.WifiChangeSettings settings) {
2324
2325            if (settings.bssidInfos.length < MAX_APS_TO_TRACK) {
2326                localLog("too few APs (" + settings.bssidInfos.length
2327                        + ") available to track wifi change");
2328                return;
2329            }
2330
2331            if (DBG) localLog("Setting configuration specified by app");
2332
2333            mCurrentBssids = new ScanResult[settings.bssidInfos.length];
2334            HashSet<Integer> channels = new HashSet<Integer>();
2335
2336            for (int i = 0; i < settings.bssidInfos.length; i++) {
2337                ScanResult result = new ScanResult();
2338                result.BSSID = settings.bssidInfos[i].bssid;
2339                mCurrentBssids[i] = result;
2340                channels.add(settings.bssidInfos[i].frequencyHint);
2341            }
2342
2343            // cancel previous scan
2344            removeScanRequest();
2345
2346            // set new scanning schedule
2347            ScanSettings settings2 = new ScanSettings();
2348            settings2.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
2349            settings2.channels = new ChannelSpec[channels.size()];
2350            int i = 0;
2351            for (Integer channel : channels) {
2352                settings2.channels[i++] = new ChannelSpec(channel);
2353            }
2354
2355            settings2.periodInMs = settings.periodInMs;
2356            addScanRequest(settings2);
2357
2358            // start tracking new APs
2359            trackSignificantWifiChange(settings);
2360        }
2361
2362
2363        @Override
2364        public void onChangesFound(ScanResult results[]) {
2365            sendMessage(CMD_WIFI_CHANGE_DETECTED, 0, 0, results);
2366        }
2367
2368        private void addScanRequest(ScanSettings settings) {
2369            if (DBG) localLog("Starting scans");
2370            if (mInternalClientInfo != null) {
2371                mInternalClientInfo.sendRequestToClientHandler(
2372                        WifiScanner.CMD_START_BACKGROUND_SCAN, settings, null);
2373            }
2374        }
2375
2376        private void removeScanRequest() {
2377            if (DBG) localLog("Stopping scans");
2378            if (mInternalClientInfo != null) {
2379                mInternalClientInfo.sendRequestToClientHandler(
2380                        WifiScanner.CMD_STOP_BACKGROUND_SCAN);
2381            }
2382        }
2383
2384        private void trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings) {
2385            if (mScannerImpl != null) {
2386                mScannerImpl.untrackSignificantWifiChange();
2387                mScannerImpl.trackSignificantWifiChange(settings, this);
2388            }
2389        }
2390
2391        private void untrackSignificantWifiChange() {
2392            if (mScannerImpl != null) {
2393                mScannerImpl.untrackSignificantWifiChange();
2394            }
2395        }
2396
2397        private void addWifiChangeHandler(ClientInfo ci, int handler) {
2398            mActiveWifiChangeHandlers.add(Pair.create(ci, handler));
2399            // Add an internal client to make background scan requests.
2400            if (mInternalClientInfo == null) {
2401                mInternalClientInfo =
2402                        new InternalClientInfo(ci.getUid(), new Messenger(this.getHandler()));
2403                mInternalClientInfo.register();
2404            }
2405        }
2406
2407        private void removeWifiChangeHandler(ClientInfo ci, int handler) {
2408            mActiveWifiChangeHandlers.remove(Pair.create(ci, handler));
2409            untrackSignificantWifiChangeOnEmpty();
2410        }
2411
2412        private void untrackSignificantWifiChangeOnEmpty() {
2413            if (mActiveWifiChangeHandlers.isEmpty()) {
2414                if (DBG) localLog("Got Disable Wifi Change");
2415                mCurrentBssids = null;
2416                untrackSignificantWifiChange();
2417                // Remove the internal client when there are no more external clients.
2418                if (mInternalClientInfo != null) {
2419                    mInternalClientInfo.cleanup();
2420                    mInternalClientInfo = null;
2421                }
2422                transitionTo(mDefaultState);
2423            }
2424        }
2425
2426        private void reportWifiChanged(ScanResult[] results) {
2427            WifiScanner.ParcelableScanResults parcelableScanResults =
2428                    new WifiScanner.ParcelableScanResults(results);
2429            Iterator<Pair<ClientInfo, Integer>> it = mActiveWifiChangeHandlers.iterator();
2430            while (it.hasNext()) {
2431                Pair<ClientInfo, Integer> entry = it.next();
2432                ClientInfo ci = entry.first;
2433                int handler = entry.second;
2434                ci.reportEvent(WifiScanner.CMD_WIFI_CHANGE_DETECTED, 0, handler,
2435                        parcelableScanResults);
2436            }
2437        }
2438
2439        private void reportWifiStabilized(ScanResult[] results) {
2440            WifiScanner.ParcelableScanResults parcelableScanResults =
2441                    new WifiScanner.ParcelableScanResults(results);
2442            Iterator<Pair<ClientInfo, Integer>> it = mActiveWifiChangeHandlers.iterator();
2443            while (it.hasNext()) {
2444                Pair<ClientInfo, Integer> entry = it.next();
2445                ClientInfo ci = entry.first;
2446                int handler = entry.second;
2447                ci.reportEvent(WifiScanner.CMD_WIFI_CHANGES_STABILIZED, 0, handler,
2448                        parcelableScanResults);
2449            }
2450        }
2451    }
2452
2453    private static String toString(int uid, ScanSettings settings) {
2454        StringBuilder sb = new StringBuilder();
2455        sb.append("ScanSettings[uid=").append(uid);
2456        sb.append(", period=").append(settings.periodInMs);
2457        sb.append(", report=").append(settings.reportEvents);
2458        if (settings.reportEvents == WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL
2459                && settings.numBssidsPerScan > 0
2460                && settings.maxScansToCache > 1) {
2461            sb.append(", batch=").append(settings.maxScansToCache);
2462            sb.append(", numAP=").append(settings.numBssidsPerScan);
2463        }
2464        sb.append(", ").append(ChannelHelper.toString(settings));
2465        sb.append("]");
2466
2467        return sb.toString();
2468    }
2469
2470    @Override
2471    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2472        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2473                != PackageManager.PERMISSION_GRANTED) {
2474            pw.println("Permission Denial: can't dump WifiScanner from from pid="
2475                    + Binder.getCallingPid()
2476                    + ", uid=" + Binder.getCallingUid()
2477                    + " without permission "
2478                    + android.Manifest.permission.DUMP);
2479            return;
2480        }
2481        pw.println("WifiScanningService - Log Begin ----");
2482        mLocalLog.dump(fd, pw, args);
2483        pw.println("WifiScanningService - Log End ----");
2484        pw.println();
2485        pw.println("clients:");
2486        for (ClientInfo client : mClients.values()) {
2487            pw.println("  " + client);
2488        }
2489        pw.println("listeners:");
2490        for (ClientInfo client : mClients.values()) {
2491            Collection<ScanSettings> settingsList =
2492                    mBackgroundScanStateMachine.getBackgroundScanSettings(client);
2493            for (ScanSettings settings : settingsList) {
2494                pw.println("  " + toString(client.mUid, settings));
2495            }
2496        }
2497        if (mBackgroundScheduler != null) {
2498            WifiNative.ScanSettings schedule = mBackgroundScheduler.getSchedule();
2499            if (schedule != null) {
2500                pw.println("schedule:");
2501                pw.println("  base period: " + schedule.base_period_ms);
2502                pw.println("  max ap per scan: " + schedule.max_ap_per_scan);
2503                pw.println("  batched scans: " + schedule.report_threshold_num_scans);
2504                pw.println("  buckets:");
2505                for (int b = 0; b < schedule.num_buckets; b++) {
2506                    WifiNative.BucketSettings bucket = schedule.buckets[b];
2507                    pw.println("    bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)["
2508                            + bucket.report_events + "]: "
2509                            + ChannelHelper.toString(bucket));
2510                }
2511            }
2512        }
2513        if (mPnoScanStateMachine != null) {
2514            mPnoScanStateMachine.dump(fd, pw, args);
2515        }
2516    }
2517
2518    void logScanRequest(String request, ClientInfo ci, int id, WorkSource workSource,
2519            ScanSettings settings, PnoSettings pnoSettings) {
2520        StringBuilder sb = new StringBuilder();
2521        sb.append(request)
2522                .append(": ")
2523                .append(ci.toString())
2524                .append(",Id=")
2525                .append(id);
2526        if (workSource != null) {
2527            sb.append(",").append(workSource);
2528        }
2529        if (settings != null) {
2530            sb.append(", ");
2531            describeTo(sb, settings);
2532        }
2533        if (pnoSettings != null) {
2534            sb.append(", ");
2535            describeTo(sb, pnoSettings);
2536        }
2537        localLog(sb.toString());
2538    }
2539
2540    void logCallback(String callback, ClientInfo ci, int id, String extra) {
2541        StringBuilder sb = new StringBuilder();
2542        sb.append(callback)
2543                .append(": ")
2544                .append(ci.toString())
2545                .append(",Id=")
2546                .append(id);
2547        if (extra != null) {
2548            sb.append(",").append(extra);
2549        }
2550        localLog(sb.toString());
2551    }
2552
2553    static String describeForLog(ScanData[] results) {
2554        StringBuilder sb = new StringBuilder();
2555        sb.append("results=");
2556        for (int i = 0; i < results.length; ++i) {
2557            if (i > 0) sb.append(";");
2558            sb.append(results[i].getResults().length);
2559        }
2560        return sb.toString();
2561    }
2562
2563    static String describeForLog(ScanResult[] results) {
2564        return "results=" + results.length;
2565    }
2566
2567    static String describeTo(StringBuilder sb, ScanSettings scanSettings) {
2568        sb.append("ScanSettings { ")
2569          .append(" band:").append(scanSettings.band)
2570          .append(" period:").append(scanSettings.periodInMs)
2571          .append(" reportEvents:").append(scanSettings.reportEvents)
2572          .append(" numBssidsPerScan:").append(scanSettings.numBssidsPerScan)
2573          .append(" maxScansToCache:").append(scanSettings.maxScansToCache)
2574          .append(" channels:[ ");
2575        if (scanSettings.channels != null) {
2576            for (int i = 0; i < scanSettings.channels.length; i++) {
2577                sb.append(scanSettings.channels[i].frequency)
2578                  .append(" ");
2579            }
2580        }
2581        sb.append(" ] ")
2582          .append(" } ");
2583        return sb.toString();
2584    }
2585
2586    static String describeTo(StringBuilder sb, PnoSettings pnoSettings) {
2587        sb.append("PnoSettings { ")
2588          .append(" min5GhzRssi:").append(pnoSettings.min5GHzRssi)
2589          .append(" min24GhzRssi:").append(pnoSettings.min24GHzRssi)
2590          .append(" initialScoreMax:").append(pnoSettings.initialScoreMax)
2591          .append(" currentConnectionBonus:").append(pnoSettings.currentConnectionBonus)
2592          .append(" sameNetworkBonus:").append(pnoSettings.sameNetworkBonus)
2593          .append(" secureBonus:").append(pnoSettings.secureBonus)
2594          .append(" band5GhzBonus:").append(pnoSettings.band5GHzBonus)
2595          .append(" isConnected:").append(pnoSettings.isConnected)
2596          .append(" networks:[ ");
2597        if (pnoSettings.networkList != null) {
2598            for (int i = 0; i < pnoSettings.networkList.length; i++) {
2599                sb.append(pnoSettings.networkList[i].ssid)
2600                  .append(",")
2601                  .append(pnoSettings.networkList[i].networkId)
2602                  .append(" ");
2603            }
2604        }
2605        sb.append(" ] ")
2606          .append(" } ");
2607        return sb.toString();
2608    }
2609}
2610