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