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