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