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