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