WifiScanningServiceImpl.java revision e2e66be742947c65f882dd1fe6a267b1acf97001
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.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.pm.PackageManager;
26import android.net.wifi.IWifiScanner;
27import android.net.wifi.ScanResult;
28import android.net.wifi.WifiManager;
29import android.net.wifi.WifiScanner;
30import android.net.wifi.WifiScanner.ChannelSpec;
31import android.net.wifi.WifiScanner.PnoSettings;
32import android.net.wifi.WifiScanner.ScanData;
33import android.net.wifi.WifiScanner.ScanSettings;
34import android.os.Binder;
35import android.os.Bundle;
36import android.os.Looper;
37import android.os.Message;
38import android.os.Messenger;
39import android.os.RemoteException;
40import android.os.UserHandle;
41import android.os.WorkSource;
42import android.util.ArrayMap;
43import android.util.LocalLog;
44import android.util.Log;
45import android.util.Pair;
46
47import com.android.internal.annotations.VisibleForTesting;
48import com.android.internal.app.IBatteryStats;
49import com.android.internal.util.ArrayUtils;
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.FrameworkFacade;
56import com.android.server.wifi.WifiInjector;
57import com.android.server.wifi.WifiLog;
58import com.android.server.wifi.WifiMetrics;
59import com.android.server.wifi.WifiNative;
60import com.android.server.wifi.WifiStateMachine;
61import com.android.server.wifi.nano.WifiMetricsProto;
62import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection;
63import com.android.server.wifi.util.WifiHandler;
64
65import java.io.FileDescriptor;
66import java.io.PrintWriter;
67import java.util.ArrayList;
68import java.util.Arrays;
69import java.util.Collection;
70import java.util.Iterator;
71import java.util.List;
72
73public class WifiScanningServiceImpl extends IWifiScanner.Stub {
74
75    private static final String TAG = WifiScanningService.TAG;
76    private static final boolean DBG = false;
77
78    private static final int MIN_PERIOD_PER_CHANNEL_MS = 200;               // DFS needs 120 ms
79    private static final int UNKNOWN_PID = -1;
80
81    private final LocalLog mLocalLog = new LocalLog(512);
82
83    private WifiLog mLog;
84
85    private void localLog(String message) {
86        mLocalLog.log(message);
87    }
88
89    private void logw(String message) {
90        Log.w(TAG, message);
91        mLocalLog.log(message);
92    }
93
94    private void loge(String message) {
95        Log.e(TAG, message);
96        mLocalLog.log(message);
97    }
98
99    private WifiScannerImpl mScannerImpl;
100
101    @Override
102    public Messenger getMessenger() {
103        if (mClientHandler != null) {
104            mLog.trace("getMessenger() uid=%").c(Binder.getCallingUid()).flush();
105            return new Messenger(mClientHandler);
106        }
107        loge("WifiScanningServiceImpl trying to get messenger w/o initialization");
108        return null;
109    }
110
111    @Override
112    public Bundle getAvailableChannels(int band) {
113        mChannelHelper.updateChannels();
114        ChannelSpec[] channelSpecs = mChannelHelper.getAvailableScanChannels(band);
115        ArrayList<Integer> list = new ArrayList<Integer>(channelSpecs.length);
116        for (ChannelSpec channelSpec : channelSpecs) {
117            list.add(channelSpec.frequency);
118        }
119        Bundle b = new Bundle();
120        b.putIntegerArrayList(WifiScanner.GET_AVAILABLE_CHANNELS_EXTRA, list);
121        mLog.trace("getAvailableChannels uid=%").c(Binder.getCallingUid()).flush();
122        return b;
123    }
124
125    private void enforceLocationHardwarePermission(int uid) {
126        mContext.enforcePermission(
127                Manifest.permission.LOCATION_HARDWARE,
128                UNKNOWN_PID, uid,
129                "LocationHardware");
130    }
131
132    private class ClientHandler extends WifiHandler {
133
134        ClientHandler(String tag, Looper looper) {
135            super(tag, looper);
136        }
137
138        @Override
139        public void handleMessage(Message msg) {
140            super.handleMessage(msg);
141            switch (msg.what) {
142                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
143                    ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo);
144                    if (client != null) {
145                        logw("duplicate client connection: " + msg.sendingUid + ", messenger="
146                                + msg.replyTo);
147                        client.mChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
148                                AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
149                        return;
150                    }
151
152                    AsyncChannel ac = mFrameworkFacade.makeWifiAsyncChannel(TAG);
153                    ac.connected(mContext, this, msg.replyTo);
154
155                    client = new ExternalClientInfo(msg.sendingUid, msg.replyTo, ac);
156                    client.register();
157
158                    ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
159                            AsyncChannel.STATUS_SUCCESSFUL);
160                    localLog("client connected: " + client);
161                    return;
162                }
163                case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
164                    ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo);
165                    if (client != null) {
166                        client.mChannel.disconnect();
167                    }
168                    return;
169                }
170                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
171                    ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo);
172                    if (client != null && msg.arg1 != AsyncChannel.STATUS_SEND_UNSUCCESSFUL
173                            && msg.arg1
174                            != AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED) {
175                        localLog("client disconnected: " + client + ", reason: " + msg.arg1);
176                        client.cleanup();
177                    }
178                    return;
179                }
180            }
181
182            try {
183                enforceLocationHardwarePermission(msg.sendingUid);
184            } catch (SecurityException e) {
185                localLog("failed to authorize app: " + e);
186                replyFailed(msg, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
187                return;
188            }
189
190            // Since the CMD_GET_SCAN_RESULTS and CMD_GET_SINGLE_SCAN_RESULTS messages are
191            // sent from WifiScanner using |sendMessageSynchronously|, handle separately since
192            // the |msg.replyTo| field does not actually correspond to the Messenger that is
193            // registered for that client.
194            if (msg.what == WifiScanner.CMD_GET_SCAN_RESULTS) {
195                mBackgroundScanStateMachine.sendMessage(Message.obtain(msg));
196                return;
197            }
198            if (msg.what == WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS) {
199                mSingleScanStateMachine.sendMessage(Message.obtain(msg));
200                return;
201            }
202
203            ClientInfo ci = mClients.get(msg.replyTo);
204            if (ci == null) {
205                loge("Could not find client info for message " + msg.replyTo + ", msg=" + msg);
206                replyFailed(msg, WifiScanner.REASON_INVALID_LISTENER, "Could not find listener");
207                return;
208            }
209
210            switch (msg.what) {
211                case WifiScanner.CMD_START_BACKGROUND_SCAN:
212                case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
213                    mBackgroundScanStateMachine.sendMessage(Message.obtain(msg));
214                    break;
215                case WifiScanner.CMD_START_PNO_SCAN:
216                case WifiScanner.CMD_STOP_PNO_SCAN:
217                    mPnoScanStateMachine.sendMessage(Message.obtain(msg));
218                    break;
219                case WifiScanner.CMD_START_SINGLE_SCAN:
220                case WifiScanner.CMD_STOP_SINGLE_SCAN:
221                    mSingleScanStateMachine.sendMessage(Message.obtain(msg));
222                    break;
223                case WifiScanner.CMD_REGISTER_SCAN_LISTENER:
224                    logScanRequest("registerScanListener", ci, msg.arg2, null, null, null);
225                    mSingleScanListeners.addRequest(ci, msg.arg2, null, null);
226                    replySucceeded(msg);
227                    break;
228                case WifiScanner.CMD_DEREGISTER_SCAN_LISTENER:
229                    logScanRequest("deregisterScanListener", ci, msg.arg2, null, null, null);
230                    mSingleScanListeners.removeRequest(ci, msg.arg2);
231                    break;
232                default:
233                    replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "Invalid request");
234                    break;
235            }
236        }
237    }
238
239    private static final int BASE = Protocol.BASE_WIFI_SCANNER_SERVICE;
240
241    private static final int CMD_SCAN_RESULTS_AVAILABLE              = BASE + 0;
242    private static final int CMD_FULL_SCAN_RESULTS                   = BASE + 1;
243    private static final int CMD_HOTLIST_AP_FOUND                    = BASE + 2;
244    private static final int CMD_HOTLIST_AP_LOST                     = BASE + 3;
245    private static final int CMD_WIFI_CHANGE_DETECTED                = BASE + 4;
246    private static final int CMD_WIFI_CHANGE_TIMEOUT                 = BASE + 5;
247    private static final int CMD_DRIVER_LOADED                       = BASE + 6;
248    private static final int CMD_DRIVER_UNLOADED                     = BASE + 7;
249    private static final int CMD_SCAN_PAUSED                         = BASE + 8;
250    private static final int CMD_SCAN_RESTARTED                      = BASE + 9;
251    private static final int CMD_SCAN_FAILED                         = BASE + 10;
252    private static final int CMD_PNO_NETWORK_FOUND                   = BASE + 11;
253    private static final int CMD_PNO_SCAN_FAILED                     = BASE + 12;
254
255    private final Context mContext;
256    private final Looper mLooper;
257    private final WifiScannerImpl.WifiScannerImplFactory mScannerImplFactory;
258    private final ArrayMap<Messenger, ClientInfo> mClients;
259
260    private final RequestList<Void> mSingleScanListeners = new RequestList<>();
261
262    private ChannelHelper mChannelHelper;
263    private BackgroundScanScheduler mBackgroundScheduler;
264    private WifiNative.ScanSettings mPreviousSchedule;
265
266    private WifiBackgroundScanStateMachine mBackgroundScanStateMachine;
267    private WifiSingleScanStateMachine mSingleScanStateMachine;
268    private WifiPnoScanStateMachine mPnoScanStateMachine;
269    private ClientHandler mClientHandler;
270    private final IBatteryStats mBatteryStats;
271    private final AlarmManager mAlarmManager;
272    private final WifiMetrics mWifiMetrics;
273    private final Clock mClock;
274    private final FrameworkFacade mFrameworkFacade;
275
276    WifiScanningServiceImpl(Context context, Looper looper,
277            WifiScannerImpl.WifiScannerImplFactory scannerImplFactory, IBatteryStats batteryStats,
278            WifiInjector wifiInjector) {
279        mContext = context;
280        mLooper = looper;
281        mScannerImplFactory = scannerImplFactory;
282        mBatteryStats = batteryStats;
283        mClients = new ArrayMap<>();
284        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
285        mWifiMetrics = wifiInjector.getWifiMetrics();
286        mClock = wifiInjector.getClock();
287        mLog = wifiInjector.makeLog(TAG);
288        mFrameworkFacade = wifiInjector.getFrameworkFacade();
289        mPreviousSchedule = null;
290    }
291
292    public void startService() {
293        mClientHandler = new ClientHandler(TAG, mLooper);
294        mBackgroundScanStateMachine = new WifiBackgroundScanStateMachine(mLooper);
295        mSingleScanStateMachine = new WifiSingleScanStateMachine(mLooper);
296        mPnoScanStateMachine = new WifiPnoScanStateMachine(mLooper);
297
298        mContext.registerReceiver(
299                new BroadcastReceiver() {
300                    @Override
301                    public void onReceive(Context context, Intent intent) {
302                        int state = intent.getIntExtra(
303                                WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED);
304                        if (DBG) localLog("SCAN_AVAILABLE : " + state);
305                        if (state == WifiManager.WIFI_STATE_ENABLED) {
306                            mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_LOADED);
307                            mSingleScanStateMachine.sendMessage(CMD_DRIVER_LOADED);
308                            mPnoScanStateMachine.sendMessage(CMD_DRIVER_LOADED);
309                        } else if (state == WifiManager.WIFI_STATE_DISABLED) {
310                            mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
311                            mSingleScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
312                            mPnoScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
313                        }
314                    }
315                }, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE));
316
317        mBackgroundScanStateMachine.start();
318        mSingleScanStateMachine.start();
319        mPnoScanStateMachine.start();
320    }
321
322    /**
323     * Provide a way for unit tests to set valid log object in the WifiHandler
324     * @param log WifiLog object to assign to the clientHandler
325     */
326    @VisibleForTesting
327    public void setWifiHandlerLogForTest(WifiLog log) {
328        mClientHandler.setWifiLog(log);
329    }
330
331    private static boolean isWorkSourceValid(WorkSource workSource) {
332        return workSource != null && workSource.size() > 0 && workSource.get(0) >= 0;
333    }
334
335    private WorkSource computeWorkSource(ClientInfo ci, WorkSource requestedWorkSource) {
336        if (requestedWorkSource != null) {
337            if (isWorkSourceValid(requestedWorkSource)) {
338                // Wifi currently doesn't use names, so need to clear names out of the
339                // supplied WorkSource to allow future WorkSource combining.
340                requestedWorkSource.clearNames();
341                return requestedWorkSource;
342            } else {
343                loge("Got invalid work source request: " + requestedWorkSource.toString() +
344                        " from " + ci);
345            }
346        }
347        WorkSource callingWorkSource = new WorkSource(ci.getUid());
348        if (isWorkSourceValid(callingWorkSource)) {
349            return callingWorkSource;
350        } else {
351            loge("Client has invalid work source: " + callingWorkSource);
352            return new WorkSource();
353        }
354    }
355
356    private class RequestInfo<T> {
357        final ClientInfo clientInfo;
358        final int handlerId;
359        final WorkSource workSource;
360        final T settings;
361
362        RequestInfo(ClientInfo clientInfo, int handlerId, WorkSource requestedWorkSource,
363                T settings) {
364            this.clientInfo = clientInfo;
365            this.handlerId = handlerId;
366            this.settings = settings;
367            this.workSource = computeWorkSource(clientInfo, requestedWorkSource);
368        }
369
370        void reportEvent(int what, int arg1, Object obj) {
371            clientInfo.reportEvent(what, arg1, handlerId, obj);
372        }
373    }
374
375    private class RequestList<T> extends ArrayList<RequestInfo<T>> {
376        void addRequest(ClientInfo ci, int handler, WorkSource reqworkSource, T settings) {
377            add(new RequestInfo<T>(ci, handler, reqworkSource, settings));
378        }
379
380        T removeRequest(ClientInfo ci, int handlerId) {
381            T removed = null;
382            Iterator<RequestInfo<T>> iter = iterator();
383            while (iter.hasNext()) {
384                RequestInfo<T> entry = iter.next();
385                if (entry.clientInfo == ci && entry.handlerId == handlerId) {
386                    removed = entry.settings;
387                    iter.remove();
388                }
389            }
390            return removed;
391        }
392
393        Collection<T> getAllSettings() {
394            ArrayList<T> settingsList = new ArrayList<>();
395            Iterator<RequestInfo<T>> iter = iterator();
396            while (iter.hasNext()) {
397                RequestInfo<T> entry = iter.next();
398                settingsList.add(entry.settings);
399            }
400            return settingsList;
401        }
402
403        Collection<T> getAllSettingsForClient(ClientInfo ci) {
404            ArrayList<T> settingsList = new ArrayList<>();
405            Iterator<RequestInfo<T>> iter = iterator();
406            while (iter.hasNext()) {
407                RequestInfo<T> entry = iter.next();
408                if (entry.clientInfo == ci) {
409                    settingsList.add(entry.settings);
410                }
411            }
412            return settingsList;
413        }
414
415        void removeAllForClient(ClientInfo ci) {
416            Iterator<RequestInfo<T>> iter = iterator();
417            while (iter.hasNext()) {
418                RequestInfo<T> entry = iter.next();
419                if (entry.clientInfo == ci) {
420                    iter.remove();
421                }
422            }
423        }
424
425        WorkSource createMergedWorkSource() {
426            WorkSource mergedSource = new WorkSource();
427            for (RequestInfo<T> entry : this) {
428                mergedSource.add(entry.workSource);
429            }
430            return mergedSource;
431        }
432    }
433
434    /**
435     * State machine that holds the state of single scans. Scans should only be active in the
436     * ScanningState. The pending scans and active scans maps are swaped when entering
437     * ScanningState. Any requests queued while scanning will be placed in the pending queue and
438     * executed after transitioning back to IdleState.
439     */
440    class WifiSingleScanStateMachine extends StateMachine implements WifiNative.ScanEventHandler {
441        private final DefaultState mDefaultState = new DefaultState();
442        private final DriverStartedState mDriverStartedState = new DriverStartedState();
443        private final IdleState  mIdleState  = new IdleState();
444        private final ScanningState  mScanningState  = new ScanningState();
445
446        private WifiNative.ScanSettings mActiveScanSettings = null;
447        private RequestList<ScanSettings> mActiveScans = new RequestList<>();
448        private RequestList<ScanSettings> mPendingScans = new RequestList<>();
449
450        // Scan results cached from the last full single scan request.
451        private ScanResult[] mCachedScanResults = new ScanResult[0];
452
453        WifiSingleScanStateMachine(Looper looper) {
454            super("WifiSingleScanStateMachine", looper);
455
456            setLogRecSize(128);
457            setLogOnlyTransitions(false);
458
459            // CHECKSTYLE:OFF IndentationCheck
460            addState(mDefaultState);
461                addState(mDriverStartedState, mDefaultState);
462                    addState(mIdleState, mDriverStartedState);
463                    addState(mScanningState, mDriverStartedState);
464            // CHECKSTYLE:ON IndentationCheck
465
466            setInitialState(mDefaultState);
467        }
468
469        /**
470         * Called to indicate a change in state for the current scan.
471         * Will dispatch a coresponding event to the state machine
472         */
473        @Override
474        public void onScanStatus(int event) {
475            if (DBG) localLog("onScanStatus event received, event=" + event);
476            switch(event) {
477                case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE:
478                case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS:
479                case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT:
480                    sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
481                    break;
482                case WifiNative.WIFI_SCAN_FAILED:
483                    sendMessage(CMD_SCAN_FAILED);
484                    break;
485                default:
486                    Log.e(TAG, "Unknown scan status event: " + event);
487                    break;
488            }
489        }
490
491        /**
492         * Called for each full scan result if requested
493         */
494        @Override
495        public void onFullScanResult(ScanResult fullScanResult, int bucketsScanned) {
496            if (DBG) localLog("onFullScanResult received");
497            sendMessage(CMD_FULL_SCAN_RESULTS, 0, bucketsScanned, fullScanResult);
498        }
499
500        @Override
501        public void onScanPaused(ScanData[] scanData) {
502            // should not happen for single scan
503            Log.e(TAG, "Got scan paused for single scan");
504        }
505
506        @Override
507        public void onScanRestarted() {
508            // should not happen for single scan
509            Log.e(TAG, "Got scan restarted for single scan");
510        }
511
512        class DefaultState extends State {
513            @Override
514            public void enter() {
515                mActiveScans.clear();
516                mPendingScans.clear();
517            }
518            @Override
519            public boolean processMessage(Message msg) {
520                switch (msg.what) {
521                    case CMD_DRIVER_LOADED:
522                        transitionTo(mIdleState);
523                        return HANDLED;
524                    case CMD_DRIVER_UNLOADED:
525                        transitionTo(mDefaultState);
526                        return HANDLED;
527                    case WifiScanner.CMD_START_SINGLE_SCAN:
528                    case WifiScanner.CMD_STOP_SINGLE_SCAN:
529                        replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
530                        return HANDLED;
531                    case CMD_SCAN_RESULTS_AVAILABLE:
532                        if (DBG) localLog("ignored scan results available event");
533                        return HANDLED;
534                    case CMD_FULL_SCAN_RESULTS:
535                        if (DBG) localLog("ignored full scan result event");
536                        return HANDLED;
537                    case WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS:
538                        msg.obj = new WifiScanner.ParcelableScanResults(mCachedScanResults.clone());
539                        replySucceeded(msg);
540                        return HANDLED;
541                    default:
542                        return NOT_HANDLED;
543                }
544
545            }
546        }
547
548        /**
549         * State representing when the driver is running. This state is not meant to be transitioned
550         * directly, but is instead indented as a parent state of ScanningState and IdleState
551         * to hold common functionality and handle cleaning up scans when the driver is shut down.
552         */
553        class DriverStartedState extends State {
554            @Override
555            public void exit() {
556                // clear scan results when scan mode is not active
557                mCachedScanResults = new ScanResult[0];
558
559                mWifiMetrics.incrementScanReturnEntry(
560                        WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED,
561                        mPendingScans.size());
562                sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED,
563                        "Scan was interrupted");
564            }
565
566            @Override
567            public boolean processMessage(Message msg) {
568                ClientInfo ci = mClients.get(msg.replyTo);
569
570                switch (msg.what) {
571                    case WifiScanner.CMD_START_SINGLE_SCAN:
572                        mWifiMetrics.incrementOneshotScanCount();
573                        int handler = msg.arg2;
574                        Bundle scanParams = (Bundle) msg.obj;
575                        if (scanParams == null) {
576                            logCallback("singleScanInvalidRequest",  ci, handler, "null params");
577                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
578                            return HANDLED;
579                        }
580                        scanParams.setDefusable(true);
581                        ScanSettings scanSettings =
582                                scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY);
583                        WorkSource workSource =
584                                scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY);
585                        if (validateScanRequest(ci, handler, scanSettings, workSource)) {
586                            logScanRequest("addSingleScanRequest", ci, handler, workSource,
587                                    scanSettings, null);
588                            replySucceeded(msg);
589
590                            // If there is an active scan that will fulfill the scan request then
591                            // mark this request as an active scan, otherwise mark it pending.
592                            // If were not currently scanning then try to start a scan. Otherwise
593                            // this scan will be scheduled when transitioning back to IdleState
594                            // after finishing the current scan.
595                            if (getCurrentState() == mScanningState) {
596                                if (activeScanSatisfies(scanSettings)) {
597                                    mActiveScans.addRequest(ci, handler, workSource, scanSettings);
598                                } else {
599                                    mPendingScans.addRequest(ci, handler, workSource, scanSettings);
600                                }
601                            } else {
602                                mPendingScans.addRequest(ci, handler, workSource, scanSettings);
603                                tryToStartNewScan();
604                            }
605                        } else {
606                            logCallback("singleScanInvalidRequest",  ci, handler, "bad request");
607                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
608                            mWifiMetrics.incrementScanReturnEntry(
609                                    WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION, 1);
610                        }
611                        return HANDLED;
612                    case WifiScanner.CMD_STOP_SINGLE_SCAN:
613                        removeSingleScanRequest(ci, msg.arg2);
614                        return HANDLED;
615                    default:
616                        return NOT_HANDLED;
617                }
618            }
619        }
620
621        class IdleState extends State {
622            @Override
623            public void enter() {
624                tryToStartNewScan();
625            }
626
627            @Override
628            public boolean processMessage(Message msg) {
629                return NOT_HANDLED;
630            }
631        }
632
633        class ScanningState extends State {
634            private WorkSource mScanWorkSource;
635
636            @Override
637            public void enter() {
638                mScanWorkSource = mActiveScans.createMergedWorkSource();
639                try {
640                    mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource);
641                } catch (RemoteException e) {
642                    loge(e.toString());
643                }
644            }
645
646            @Override
647            public void exit() {
648                mActiveScanSettings = null;
649                try {
650                    mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource);
651                } catch (RemoteException e) {
652                    loge(e.toString());
653                }
654
655                // if any scans are still active (never got results available then indicate failure)
656                mWifiMetrics.incrementScanReturnEntry(
657                                WifiMetricsProto.WifiLog.SCAN_UNKNOWN,
658                                mActiveScans.size());
659                sendOpFailedToAllAndClear(mActiveScans, WifiScanner.REASON_UNSPECIFIED,
660                        "Scan was interrupted");
661            }
662
663            @Override
664            public boolean processMessage(Message msg) {
665                switch (msg.what) {
666                    case CMD_SCAN_RESULTS_AVAILABLE:
667                        mWifiMetrics.incrementScanReturnEntry(
668                                WifiMetricsProto.WifiLog.SCAN_SUCCESS,
669                                mActiveScans.size());
670                        reportScanResults(mScannerImpl.getLatestSingleScanResults());
671                        mActiveScans.clear();
672                        transitionTo(mIdleState);
673                        return HANDLED;
674                    case CMD_FULL_SCAN_RESULTS:
675                        reportFullScanResult((ScanResult) msg.obj, /* bucketsScanned */ msg.arg2);
676                        return HANDLED;
677                    case CMD_SCAN_FAILED:
678                        mWifiMetrics.incrementScanReturnEntry(
679                                WifiMetricsProto.WifiLog.SCAN_UNKNOWN, mActiveScans.size());
680                        sendScanResultBroadcast(false);
681                        sendOpFailedToAllAndClear(mActiveScans, WifiScanner.REASON_UNSPECIFIED,
682                                "Scan failed");
683                        transitionTo(mIdleState);
684                        return HANDLED;
685                    default:
686                        return NOT_HANDLED;
687                }
688            }
689        }
690
691        boolean validateScanRequest(ClientInfo ci, int handler, ScanSettings settings,
692                WorkSource workSource) {
693            if (ci == null) {
694                Log.d(TAG, "Failing single scan request ClientInfo not found " + handler);
695                return false;
696            }
697            if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) {
698                if (settings.channels == null || settings.channels.length == 0) {
699                    Log.d(TAG, "Failing single scan because channel list was empty");
700                    return false;
701                }
702            }
703            return true;
704        }
705
706        boolean activeScanSatisfies(ScanSettings settings) {
707            if (mActiveScanSettings == null) {
708                return false;
709            }
710
711            // there is always one bucket for a single scan
712            WifiNative.BucketSettings activeBucket = mActiveScanSettings.buckets[0];
713
714            // validate that all requested channels are being scanned
715            ChannelCollection activeChannels = mChannelHelper.createChannelCollection();
716            activeChannels.addChannels(activeBucket);
717            if (!activeChannels.containsSettings(settings)) {
718                return false;
719            }
720
721            // if the request is for a full scan, but there is no ongoing full scan
722            if ((settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0
723                    && (activeBucket.report_events & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)
724                    == 0) {
725                return false;
726            }
727
728            if (!ArrayUtils.isEmpty(settings.hiddenNetworks)) {
729                if (ArrayUtils.isEmpty(mActiveScanSettings.hiddenNetworks)) {
730                    return false;
731                }
732                List<WifiNative.HiddenNetwork> activeHiddenNetworks = new ArrayList<>();
733                for (WifiNative.HiddenNetwork hiddenNetwork : mActiveScanSettings.hiddenNetworks) {
734                    activeHiddenNetworks.add(hiddenNetwork);
735                }
736                for (ScanSettings.HiddenNetwork hiddenNetwork : settings.hiddenNetworks) {
737                    WifiNative.HiddenNetwork nativeHiddenNetwork = new WifiNative.HiddenNetwork();
738                    nativeHiddenNetwork.ssid = hiddenNetwork.ssid;
739                    if (!activeHiddenNetworks.contains(nativeHiddenNetwork)) {
740                        return false;
741                    }
742                }
743            }
744
745            return true;
746        }
747
748        void removeSingleScanRequest(ClientInfo ci, int handler) {
749            if (ci != null) {
750                logScanRequest("removeSingleScanRequest", ci, handler, null, null, null);
751                mPendingScans.removeRequest(ci, handler);
752                mActiveScans.removeRequest(ci, handler);
753            }
754        }
755
756        void removeSingleScanRequests(ClientInfo ci) {
757            if (ci != null) {
758                logScanRequest("removeSingleScanRequests", ci, -1, null, null, null);
759                mPendingScans.removeAllForClient(ci);
760                mActiveScans.removeAllForClient(ci);
761            }
762        }
763
764        void tryToStartNewScan() {
765            if (mPendingScans.size() == 0) { // no pending requests
766                return;
767            }
768            mChannelHelper.updateChannels();
769            // TODO move merging logic to a scheduler
770            WifiNative.ScanSettings settings = new WifiNative.ScanSettings();
771            settings.num_buckets = 1;
772            WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings();
773            bucketSettings.bucket = 0;
774            bucketSettings.period_ms = 0;
775            bucketSettings.report_events = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
776
777            ChannelCollection channels = mChannelHelper.createChannelCollection();
778            List<WifiNative.HiddenNetwork> hiddenNetworkList = new ArrayList<>();
779            for (RequestInfo<ScanSettings> entry : mPendingScans) {
780                channels.addChannels(entry.settings);
781                if (entry.settings.hiddenNetworks != null) {
782                    for (int i = 0; i < entry.settings.hiddenNetworks.length; i++) {
783                        WifiNative.HiddenNetwork hiddenNetwork = new WifiNative.HiddenNetwork();
784                        hiddenNetwork.ssid = entry.settings.hiddenNetworks[i].ssid;
785                        hiddenNetworkList.add(hiddenNetwork);
786                    }
787                }
788                if ((entry.settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)
789                        != 0) {
790                    bucketSettings.report_events |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
791                }
792            }
793            if (hiddenNetworkList.size() > 0) {
794                settings.hiddenNetworks = new WifiNative.HiddenNetwork[hiddenNetworkList.size()];
795                int numHiddenNetworks = 0;
796                for (WifiNative.HiddenNetwork hiddenNetwork : hiddenNetworkList) {
797                    settings.hiddenNetworks[numHiddenNetworks++] = hiddenNetwork;
798                }
799            }
800
801            channels.fillBucketSettings(bucketSettings, Integer.MAX_VALUE);
802
803            settings.buckets = new WifiNative.BucketSettings[] {bucketSettings};
804            if (mScannerImpl.startSingleScan(settings, this)) {
805                // store the active scan settings
806                mActiveScanSettings = settings;
807                // swap pending and active scan requests
808                RequestList<ScanSettings> tmp = mActiveScans;
809                mActiveScans = mPendingScans;
810                mPendingScans = tmp;
811                // make sure that the pending list is clear
812                mPendingScans.clear();
813                transitionTo(mScanningState);
814            } else {
815                mWifiMetrics.incrementScanReturnEntry(
816                        WifiMetricsProto.WifiLog.SCAN_UNKNOWN, mPendingScans.size());
817                // notify and cancel failed scans
818                sendOpFailedToAllAndClear(mPendingScans, WifiScanner.REASON_UNSPECIFIED,
819                        "Failed to start single scan");
820            }
821        }
822
823        void sendOpFailedToAllAndClear(RequestList<?> clientHandlers, int reason,
824                String description) {
825            for (RequestInfo<?> entry : clientHandlers) {
826                logCallback("singleScanFailed",  entry.clientInfo, entry.handlerId,
827                        "reason=" + reason + ", " + description);
828                entry.reportEvent(WifiScanner.CMD_OP_FAILED, 0,
829                        new WifiScanner.OperationResult(reason, description));
830            }
831            clientHandlers.clear();
832        }
833
834        void reportFullScanResult(ScanResult result, int bucketsScanned) {
835            for (RequestInfo<ScanSettings> entry : mActiveScans) {
836                if (ScanScheduleUtil.shouldReportFullScanResultForSettings(mChannelHelper,
837                                result, bucketsScanned, entry.settings, -1)) {
838                    entry.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, result);
839                }
840            }
841
842            for (RequestInfo<Void> entry : mSingleScanListeners) {
843                entry.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, result);
844            }
845        }
846
847        private void sendScanResultBroadcast(boolean scanSucceeded) {
848            Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
849            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
850            intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, scanSucceeded);
851            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
852        }
853
854        void reportScanResults(ScanData results) {
855            if (results != null && results.getResults() != null) {
856                if (results.getResults().length > 0) {
857                    mWifiMetrics.incrementNonEmptyScanResultCount();
858                } else {
859                    mWifiMetrics.incrementEmptyScanResultCount();
860                }
861            }
862            ScanData[] allResults = new ScanData[] {results};
863            for (RequestInfo<ScanSettings> entry : mActiveScans) {
864                ScanData[] resultsToDeliver = ScanScheduleUtil.filterResultsForSettings(
865                        mChannelHelper, allResults, entry.settings, -1);
866                WifiScanner.ParcelableScanData parcelableResultsToDeliver =
867                        new WifiScanner.ParcelableScanData(resultsToDeliver);
868                logCallback("singleScanResults",  entry.clientInfo, entry.handlerId,
869                        describeForLog(resultsToDeliver));
870                entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableResultsToDeliver);
871                // make sure the handler is removed
872                entry.reportEvent(WifiScanner.CMD_SINGLE_SCAN_COMPLETED, 0, null);
873            }
874
875            WifiScanner.ParcelableScanData parcelableAllResults =
876                    new WifiScanner.ParcelableScanData(allResults);
877            for (RequestInfo<Void> entry : mSingleScanListeners) {
878                logCallback("singleScanResults",  entry.clientInfo, entry.handlerId,
879                        describeForLog(allResults));
880                entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableAllResults);
881            }
882
883            // Since we use NoBandChannelHelper, as long as a specific band is mentioned, the scan
884            // request is treated as full band (WifiScanner.WIFI_BAND_*).
885            if (results.isAllChannelsScanned()) {
886                mCachedScanResults = results.getResults();
887            }
888            sendScanResultBroadcast(true);
889        }
890
891        List<ScanResult> getCachedScanResultsAsList() {
892            return Arrays.asList(mCachedScanResults);
893        }
894    }
895
896    class WifiBackgroundScanStateMachine extends StateMachine
897            implements WifiNative.ScanEventHandler {
898
899        private final DefaultState mDefaultState = new DefaultState();
900        private final StartedState mStartedState = new StartedState();
901        private final PausedState  mPausedState  = new PausedState();
902
903        private final RequestList<ScanSettings> mActiveBackgroundScans = new RequestList<>();
904
905        WifiBackgroundScanStateMachine(Looper looper) {
906            super("WifiBackgroundScanStateMachine", looper);
907
908            setLogRecSize(512);
909            setLogOnlyTransitions(false);
910
911            // CHECKSTYLE:OFF IndentationCheck
912            addState(mDefaultState);
913                addState(mStartedState, mDefaultState);
914                addState(mPausedState, mDefaultState);
915            // CHECKSTYLE:ON IndentationCheck
916
917            setInitialState(mDefaultState);
918        }
919
920        public Collection<ScanSettings> getBackgroundScanSettings(ClientInfo ci) {
921            return mActiveBackgroundScans.getAllSettingsForClient(ci);
922        }
923
924        public void removeBackgroundScanSettings(ClientInfo ci) {
925            mActiveBackgroundScans.removeAllForClient(ci);
926            updateSchedule();
927        }
928
929        @Override
930        public void onScanStatus(int event) {
931            if (DBG) localLog("onScanStatus event received, event=" + event);
932            switch(event) {
933                case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE:
934                case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS:
935                case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT:
936                    sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
937                    break;
938                case WifiNative.WIFI_SCAN_FAILED:
939                    sendMessage(CMD_SCAN_FAILED);
940                    break;
941                default:
942                    Log.e(TAG, "Unknown scan status event: " + event);
943                    break;
944            }
945        }
946
947        @Override
948        public void onFullScanResult(ScanResult fullScanResult, int bucketsScanned) {
949            if (DBG) localLog("onFullScanResult received");
950            sendMessage(CMD_FULL_SCAN_RESULTS, 0, bucketsScanned, fullScanResult);
951        }
952
953        @Override
954        public void onScanPaused(ScanData scanData[]) {
955            if (DBG) localLog("onScanPaused received");
956            sendMessage(CMD_SCAN_PAUSED, scanData);
957        }
958
959        @Override
960        public void onScanRestarted() {
961            if (DBG) localLog("onScanRestarted received");
962            sendMessage(CMD_SCAN_RESTARTED);
963        }
964
965        class DefaultState extends State {
966            @Override
967            public void enter() {
968                if (DBG) localLog("DefaultState");
969                mActiveBackgroundScans.clear();
970            }
971
972            @Override
973            public boolean processMessage(Message msg) {
974                switch (msg.what) {
975                    case CMD_DRIVER_LOADED:
976                        // TODO this should be moved to a common location since it is used outside
977                        // of this state machine. It is ok right now because the driver loaded event
978                        // is sent to this state machine first.
979                        if (mScannerImpl == null) {
980                            mScannerImpl = mScannerImplFactory.create(mContext, mLooper, mClock);
981                            mChannelHelper = mScannerImpl.getChannelHelper();
982                        }
983
984                        mBackgroundScheduler = new BackgroundScanScheduler(mChannelHelper);
985
986                        WifiNative.ScanCapabilities capabilities =
987                                new WifiNative.ScanCapabilities();
988                        if (!mScannerImpl.getScanCapabilities(capabilities)) {
989                            loge("could not get scan capabilities");
990                            return HANDLED;
991                        }
992                        if (capabilities.max_scan_buckets <= 0) {
993                            loge("invalid max buckets in scan capabilities "
994                                    + capabilities.max_scan_buckets);
995                            return HANDLED;
996                        }
997                        mBackgroundScheduler.setMaxBuckets(capabilities.max_scan_buckets);
998                        mBackgroundScheduler.setMaxApPerScan(capabilities.max_ap_cache_per_scan);
999
1000                        Log.i(TAG, "wifi driver loaded with scan capabilities: "
1001                                + "max buckets=" + capabilities.max_scan_buckets);
1002
1003                        transitionTo(mStartedState);
1004                        return HANDLED;
1005                    case CMD_DRIVER_UNLOADED:
1006                        Log.i(TAG, "wifi driver unloaded");
1007                        transitionTo(mDefaultState);
1008                        break;
1009                    case WifiScanner.CMD_START_BACKGROUND_SCAN:
1010                    case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
1011                    case WifiScanner.CMD_START_SINGLE_SCAN:
1012                    case WifiScanner.CMD_STOP_SINGLE_SCAN:
1013                    case WifiScanner.CMD_GET_SCAN_RESULTS:
1014                        replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
1015                        break;
1016
1017                    case CMD_SCAN_RESULTS_AVAILABLE:
1018                        if (DBG) localLog("ignored scan results available event");
1019                        break;
1020
1021                    case CMD_FULL_SCAN_RESULTS:
1022                        if (DBG) localLog("ignored full scan result event");
1023                        break;
1024
1025                    default:
1026                        break;
1027                }
1028
1029                return HANDLED;
1030            }
1031        }
1032
1033        class StartedState extends State {
1034
1035            @Override
1036            public void enter() {
1037                if (DBG) localLog("StartedState");
1038            }
1039
1040            @Override
1041            public void exit() {
1042                sendBackgroundScanFailedToAllAndClear(
1043                        WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted");
1044                mScannerImpl.cleanup();
1045            }
1046
1047            @Override
1048            public boolean processMessage(Message msg) {
1049                ClientInfo ci = mClients.get(msg.replyTo);
1050
1051                switch (msg.what) {
1052                    case CMD_DRIVER_LOADED:
1053                        return NOT_HANDLED;
1054                    case CMD_DRIVER_UNLOADED:
1055                        return NOT_HANDLED;
1056                    case WifiScanner.CMD_START_BACKGROUND_SCAN: {
1057                        mWifiMetrics.incrementBackgroundScanCount();
1058                        Bundle scanParams = (Bundle) msg.obj;
1059                        if (scanParams == null) {
1060                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
1061                            return HANDLED;
1062                        }
1063                        scanParams.setDefusable(true);
1064                        ScanSettings scanSettings =
1065                                scanParams.getParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY);
1066                        WorkSource workSource =
1067                                scanParams.getParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY);
1068                        if (addBackgroundScanRequest(ci, msg.arg2, scanSettings, workSource)) {
1069                            replySucceeded(msg);
1070                        } else {
1071                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
1072                        }
1073                        break;
1074                    }
1075                    case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
1076                        removeBackgroundScanRequest(ci, msg.arg2);
1077                        break;
1078                    case WifiScanner.CMD_GET_SCAN_RESULTS:
1079                        reportScanResults(mScannerImpl.getLatestBatchedScanResults(true));
1080                        replySucceeded(msg);
1081                        break;
1082                    case CMD_SCAN_RESULTS_AVAILABLE:
1083                        reportScanResults(mScannerImpl.getLatestBatchedScanResults(true));
1084                        break;
1085                    case CMD_FULL_SCAN_RESULTS:
1086                        reportFullScanResult((ScanResult) msg.obj, /* bucketsScanned */ msg.arg2);
1087                        break;
1088                    case CMD_SCAN_PAUSED:
1089                        reportScanResults((ScanData[]) msg.obj);
1090                        transitionTo(mPausedState);
1091                        break;
1092                    case CMD_SCAN_FAILED:
1093                        Log.e(TAG, "WifiScanner background scan gave CMD_SCAN_FAILED");
1094                        sendBackgroundScanFailedToAllAndClear(
1095                                WifiScanner.REASON_UNSPECIFIED, "Background Scan failed");
1096                        break;
1097                    default:
1098                        return NOT_HANDLED;
1099                }
1100
1101                return HANDLED;
1102            }
1103        }
1104
1105        class PausedState extends State {
1106            @Override
1107            public void enter() {
1108                if (DBG) localLog("PausedState");
1109            }
1110
1111            @Override
1112            public boolean processMessage(Message msg) {
1113                switch (msg.what) {
1114                    case CMD_SCAN_RESTARTED:
1115                        transitionTo(mStartedState);
1116                        break;
1117                    default:
1118                        deferMessage(msg);
1119                        break;
1120                }
1121                return HANDLED;
1122            }
1123        }
1124
1125        private boolean addBackgroundScanRequest(ClientInfo ci, int handler,
1126                ScanSettings settings, WorkSource workSource) {
1127            // sanity check the input
1128            if (ci == null) {
1129                Log.d(TAG, "Failing scan request ClientInfo not found " + handler);
1130                return false;
1131            }
1132            if (settings.periodInMs < WifiScanner.MIN_SCAN_PERIOD_MS) {
1133                loge("Failing scan request because periodInMs is " + settings.periodInMs
1134                        + ", min scan period is: " + WifiScanner.MIN_SCAN_PERIOD_MS);
1135                return false;
1136            }
1137
1138            if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED && settings.channels == null) {
1139                loge("Channels was null with unspecified band");
1140                return false;
1141            }
1142
1143            if (settings.band == WifiScanner.WIFI_BAND_UNSPECIFIED
1144                    && settings.channels.length == 0) {
1145                loge("No channels specified");
1146                return false;
1147            }
1148
1149            int minSupportedPeriodMs = mChannelHelper.estimateScanDuration(settings);
1150            if (settings.periodInMs < minSupportedPeriodMs) {
1151                loge("Failing scan request because minSupportedPeriodMs is "
1152                        + minSupportedPeriodMs + " but the request wants " + settings.periodInMs);
1153                return false;
1154            }
1155
1156            // check truncated binary exponential back off scan settings
1157            if (settings.maxPeriodInMs != 0 && settings.maxPeriodInMs != settings.periodInMs) {
1158                if (settings.maxPeriodInMs < settings.periodInMs) {
1159                    loge("Failing scan request because maxPeriodInMs is " + settings.maxPeriodInMs
1160                            + " but less than periodInMs " + settings.periodInMs);
1161                    return false;
1162                }
1163                if (settings.maxPeriodInMs > WifiScanner.MAX_SCAN_PERIOD_MS) {
1164                    loge("Failing scan request because maxSupportedPeriodMs is "
1165                            + WifiScanner.MAX_SCAN_PERIOD_MS + " but the request wants "
1166                            + settings.maxPeriodInMs);
1167                    return false;
1168                }
1169                if (settings.stepCount < 1) {
1170                    loge("Failing scan request because stepCount is " + settings.stepCount
1171                            + " which is less than 1");
1172                    return false;
1173                }
1174            }
1175
1176            logScanRequest("addBackgroundScanRequest", ci, handler, null, settings, null);
1177            mActiveBackgroundScans.addRequest(ci, handler, workSource, settings);
1178
1179            if (updateSchedule()) {
1180                return true;
1181            } else {
1182                mActiveBackgroundScans.removeRequest(ci, handler);
1183                localLog("Failing scan request because failed to reset scan");
1184                return false;
1185            }
1186        }
1187
1188        private boolean updateSchedule() {
1189            if (mChannelHelper == null || mBackgroundScheduler == null || mScannerImpl == null) {
1190                loge("Failed to update schedule because WifiScanningService is not initialized");
1191                return false;
1192            }
1193            mChannelHelper.updateChannels();
1194            Collection<ScanSettings> settings = mActiveBackgroundScans.getAllSettings();
1195
1196            mBackgroundScheduler.updateSchedule(settings);
1197            WifiNative.ScanSettings schedule = mBackgroundScheduler.getSchedule();
1198
1199            if (ScanScheduleUtil.scheduleEquals(mPreviousSchedule, schedule)) {
1200                if (DBG) Log.d(TAG, "schedule updated with no change");
1201                return true;
1202            }
1203
1204            mPreviousSchedule = schedule;
1205
1206            if (schedule.num_buckets == 0) {
1207                mScannerImpl.stopBatchedScan();
1208                if (DBG) Log.d(TAG, "scan stopped");
1209                return true;
1210            } else {
1211                localLog("starting scan: "
1212                        + "base period=" + schedule.base_period_ms
1213                        + ", max ap per scan=" + schedule.max_ap_per_scan
1214                        + ", batched scans=" + schedule.report_threshold_num_scans);
1215                for (int b = 0; b < schedule.num_buckets; b++) {
1216                    WifiNative.BucketSettings bucket = schedule.buckets[b];
1217                    localLog("bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)"
1218                            + "[" + bucket.report_events + "]: "
1219                            + ChannelHelper.toString(bucket));
1220                }
1221
1222                if (mScannerImpl.startBatchedScan(schedule, this)) {
1223                    if (DBG) {
1224                        Log.d(TAG, "scan restarted with " + schedule.num_buckets
1225                                + " bucket(s) and base period: " + schedule.base_period_ms);
1226                    }
1227                    return true;
1228                } else {
1229                    mPreviousSchedule = null;
1230                    loge("error starting scan: "
1231                            + "base period=" + schedule.base_period_ms
1232                            + ", max ap per scan=" + schedule.max_ap_per_scan
1233                            + ", batched scans=" + schedule.report_threshold_num_scans);
1234                    for (int b = 0; b < schedule.num_buckets; b++) {
1235                        WifiNative.BucketSettings bucket = schedule.buckets[b];
1236                        loge("bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)"
1237                                + "[" + bucket.report_events + "]: "
1238                                + ChannelHelper.toString(bucket));
1239                    }
1240                    return false;
1241                }
1242            }
1243        }
1244
1245        private void removeBackgroundScanRequest(ClientInfo ci, int handler) {
1246            if (ci != null) {
1247                ScanSettings settings = mActiveBackgroundScans.removeRequest(ci, handler);
1248                logScanRequest("removeBackgroundScanRequest", ci, handler, null, settings, null);
1249                updateSchedule();
1250            }
1251        }
1252
1253        private void reportFullScanResult(ScanResult result, int bucketsScanned) {
1254            for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) {
1255                ClientInfo ci = entry.clientInfo;
1256                int handler = entry.handlerId;
1257                ScanSettings settings = entry.settings;
1258                if (mBackgroundScheduler.shouldReportFullScanResultForSettings(
1259                                result, bucketsScanned, settings)) {
1260                    ScanResult newResult = new ScanResult(result);
1261                    if (result.informationElements != null) {
1262                        newResult.informationElements = result.informationElements.clone();
1263                    }
1264                    else {
1265                        newResult.informationElements = null;
1266                    }
1267                    ci.reportEvent(WifiScanner.CMD_FULL_SCAN_RESULT, 0, handler, newResult);
1268                }
1269            }
1270        }
1271
1272        private void reportScanResults(ScanData[] results) {
1273            if (results == null) {
1274                Log.d(TAG,"The results is null, nothing to report.");
1275                return;
1276            }
1277            for (ScanData result : results) {
1278                if (result != null && result.getResults() != null) {
1279                    if (result.getResults().length > 0) {
1280                        mWifiMetrics.incrementNonEmptyScanResultCount();
1281                    } else {
1282                        mWifiMetrics.incrementEmptyScanResultCount();
1283                    }
1284                }
1285            }
1286            for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) {
1287                ClientInfo ci = entry.clientInfo;
1288                int handler = entry.handlerId;
1289                ScanSettings settings = entry.settings;
1290                ScanData[] resultsToDeliver =
1291                        mBackgroundScheduler.filterResultsForSettings(results, settings);
1292                if (resultsToDeliver != null) {
1293                    logCallback("backgroundScanResults", ci, handler,
1294                            describeForLog(resultsToDeliver));
1295                    WifiScanner.ParcelableScanData parcelableScanData =
1296                            new WifiScanner.ParcelableScanData(resultsToDeliver);
1297                    ci.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, handler, parcelableScanData);
1298                }
1299            }
1300        }
1301
1302        private void sendBackgroundScanFailedToAllAndClear(int reason, String description) {
1303            for (RequestInfo<ScanSettings> entry : mActiveBackgroundScans) {
1304                ClientInfo ci = entry.clientInfo;
1305                int handler = entry.handlerId;
1306                ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler,
1307                        new WifiScanner.OperationResult(reason, description));
1308            }
1309            mActiveBackgroundScans.clear();
1310        }
1311    }
1312
1313    /**
1314     * PNO scan state machine has 5 states:
1315     * -Default State
1316     *   -Started State
1317     *     -Hw Pno Scan state
1318     *       -Single Scan state
1319     *     -Sw Pno Scan state
1320     *
1321     * These are the main state transitions:
1322     * 1. Start at |Default State|
1323     * 2. Move to |Started State| when we get the |WIFI_SCAN_AVAILABLE| broadcast from WifiManager.
1324     * 3. When a new PNO scan request comes in:
1325     *   a.1. Switch to |Hw Pno Scan state| when the device supports HW PNO
1326     *        (This could either be HAL based ePNO or wificond based PNO).
1327     *   a.2. In |Hw Pno Scan state| when PNO scan results are received, check if the result
1328     *        contains IE (information elements). If yes, send the results to the client, else
1329     *        switch to |Single Scan state| and send the result to the client when the scan result
1330     *        is obtained.
1331     *   b.1. Switch to |Sw Pno Scan state| when the device does not supports HW PNO
1332     *        (This is for older devices which do not support HW PNO and for connected PNO on
1333     *         devices which support wificond based PNO)
1334     *   b.2. In |Sw Pno Scan state| send the result to the client when the background scan result
1335     *        is obtained
1336     *
1337     * Note: PNO scans only work for a single client today. We don't have support in HW to support
1338     * multiple requests at the same time, so will need non-trivial changes to support (if at all
1339     * possible) in WifiScanningService.
1340     */
1341    class WifiPnoScanStateMachine extends StateMachine implements WifiNative.PnoEventHandler {
1342
1343        private final DefaultState mDefaultState = new DefaultState();
1344        private final StartedState mStartedState = new StartedState();
1345        private final HwPnoScanState mHwPnoScanState = new HwPnoScanState();
1346        private final SwPnoScanState mSwPnoScanState = new SwPnoScanState();
1347        private final SingleScanState mSingleScanState = new SingleScanState();
1348        private InternalClientInfo mInternalClientInfo;
1349
1350        private final RequestList<Pair<PnoSettings, ScanSettings>> mActivePnoScans =
1351                new RequestList<>();
1352
1353        WifiPnoScanStateMachine(Looper looper) {
1354            super("WifiPnoScanStateMachine", looper);
1355
1356            setLogRecSize(256);
1357            setLogOnlyTransitions(false);
1358
1359            // CHECKSTYLE:OFF IndentationCheck
1360            addState(mDefaultState);
1361                addState(mStartedState, mDefaultState);
1362                    addState(mHwPnoScanState, mStartedState);
1363                        addState(mSingleScanState, mHwPnoScanState);
1364                    addState(mSwPnoScanState, mStartedState);
1365            // CHECKSTYLE:ON IndentationCheck
1366
1367            setInitialState(mDefaultState);
1368        }
1369
1370        public void removePnoSettings(ClientInfo ci) {
1371            mActivePnoScans.removeAllForClient(ci);
1372            transitionTo(mStartedState);
1373        }
1374
1375        @Override
1376        public void onPnoNetworkFound(ScanResult[] results) {
1377            if (DBG) localLog("onWifiPnoNetworkFound event received");
1378            sendMessage(CMD_PNO_NETWORK_FOUND, 0, 0, results);
1379        }
1380
1381        @Override
1382        public void onPnoScanFailed() {
1383            if (DBG) localLog("onWifiPnoScanFailed event received");
1384            sendMessage(CMD_PNO_SCAN_FAILED, 0, 0, null);
1385        }
1386
1387        class DefaultState extends State {
1388            @Override
1389            public void enter() {
1390                if (DBG) localLog("DefaultState");
1391            }
1392
1393            @Override
1394            public boolean processMessage(Message msg) {
1395                switch (msg.what) {
1396                    case CMD_DRIVER_LOADED:
1397                        transitionTo(mStartedState);
1398                        break;
1399                    case CMD_DRIVER_UNLOADED:
1400                        transitionTo(mDefaultState);
1401                        break;
1402                    case WifiScanner.CMD_START_PNO_SCAN:
1403                    case WifiScanner.CMD_STOP_PNO_SCAN:
1404                        replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
1405                        break;
1406                    case CMD_PNO_NETWORK_FOUND:
1407                    case CMD_PNO_SCAN_FAILED:
1408                    case WifiScanner.CMD_SCAN_RESULT:
1409                    case WifiScanner.CMD_OP_FAILED:
1410                        loge("Unexpected message " + msg.what);
1411                        break;
1412                    default:
1413                        return NOT_HANDLED;
1414                }
1415                return HANDLED;
1416            }
1417        }
1418
1419        class StartedState extends State {
1420            @Override
1421            public void enter() {
1422                if (DBG) localLog("StartedState");
1423            }
1424
1425            @Override
1426            public void exit() {
1427                sendPnoScanFailedToAllAndClear(
1428                        WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted");
1429            }
1430
1431            @Override
1432            public boolean processMessage(Message msg) {
1433                ClientInfo ci = mClients.get(msg.replyTo);
1434                switch (msg.what) {
1435                    case WifiScanner.CMD_START_PNO_SCAN:
1436                        Bundle pnoParams = (Bundle) msg.obj;
1437                        if (pnoParams == null) {
1438                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
1439                            return HANDLED;
1440                        }
1441                        pnoParams.setDefusable(true);
1442                        PnoSettings pnoSettings =
1443                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
1444                        // This message is handled after the transition to SwPnoScan/HwPnoScan state
1445                        deferMessage(msg);
1446                        if (mScannerImpl.isHwPnoSupported(pnoSettings.isConnected)) {
1447                            transitionTo(mHwPnoScanState);
1448                        } else {
1449                            transitionTo(mSwPnoScanState);
1450                        }
1451                        break;
1452                    case WifiScanner.CMD_STOP_PNO_SCAN:
1453                        replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "no scan running");
1454                        break;
1455                    default:
1456                        return NOT_HANDLED;
1457                }
1458                return HANDLED;
1459            }
1460        }
1461
1462        class HwPnoScanState extends State {
1463            @Override
1464            public void enter() {
1465                if (DBG) localLog("HwPnoScanState");
1466            }
1467
1468            @Override
1469            public void exit() {
1470                // Reset PNO scan in ScannerImpl before we exit.
1471                mScannerImpl.resetHwPnoList();
1472                removeInternalClient();
1473            }
1474
1475            @Override
1476            public boolean processMessage(Message msg) {
1477                ClientInfo ci = mClients.get(msg.replyTo);
1478                switch (msg.what) {
1479                    case WifiScanner.CMD_START_PNO_SCAN:
1480                        Bundle pnoParams = (Bundle) msg.obj;
1481                        if (pnoParams == null) {
1482                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
1483                            return HANDLED;
1484                        }
1485                        pnoParams.setDefusable(true);
1486                        PnoSettings pnoSettings =
1487                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
1488                        ScanSettings scanSettings =
1489                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY);
1490                        if (addHwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) {
1491                            replySucceeded(msg);
1492                        } else {
1493                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
1494                            transitionTo(mStartedState);
1495                        }
1496                        break;
1497                    case WifiScanner.CMD_STOP_PNO_SCAN:
1498                        removeHwPnoScanRequest(ci, msg.arg2);
1499                        transitionTo(mStartedState);
1500                        break;
1501                    case CMD_PNO_NETWORK_FOUND:
1502                        ScanResult[] scanResults = ((ScanResult[]) msg.obj);
1503                        if (isSingleScanNeeded(scanResults)) {
1504                            ScanSettings activeScanSettings = getScanSettings();
1505                            if (activeScanSettings == null) {
1506                                sendPnoScanFailedToAllAndClear(
1507                                        WifiScanner.REASON_UNSPECIFIED,
1508                                        "couldn't retrieve setting");
1509                                transitionTo(mStartedState);
1510                            } else {
1511                                addSingleScanRequest(activeScanSettings);
1512                                transitionTo(mSingleScanState);
1513                            }
1514                        } else {
1515                            reportPnoNetworkFound((ScanResult[]) msg.obj);
1516                        }
1517                        break;
1518                    case CMD_PNO_SCAN_FAILED:
1519                        sendPnoScanFailedToAllAndClear(
1520                                WifiScanner.REASON_UNSPECIFIED, "pno scan failed");
1521                        transitionTo(mStartedState);
1522                        break;
1523                    default:
1524                        return NOT_HANDLED;
1525                }
1526                return HANDLED;
1527            }
1528        }
1529
1530        class SingleScanState extends State {
1531            @Override
1532            public void enter() {
1533                if (DBG) localLog("SingleScanState");
1534            }
1535
1536            @Override
1537            public boolean processMessage(Message msg) {
1538                ClientInfo ci = mClients.get(msg.replyTo);
1539                switch (msg.what) {
1540                    case WifiScanner.CMD_SCAN_RESULT:
1541                        WifiScanner.ParcelableScanData parcelableScanData =
1542                                (WifiScanner.ParcelableScanData) msg.obj;
1543                        ScanData[] scanDatas = parcelableScanData.getResults();
1544                        ScanData lastScanData = scanDatas[scanDatas.length - 1];
1545                        reportPnoNetworkFound(lastScanData.getResults());
1546                        transitionTo(mHwPnoScanState);
1547                        break;
1548                    case WifiScanner.CMD_OP_FAILED:
1549                        sendPnoScanFailedToAllAndClear(
1550                                WifiScanner.REASON_UNSPECIFIED, "single scan failed");
1551                        transitionTo(mStartedState);
1552                        break;
1553                    default:
1554                        return NOT_HANDLED;
1555                }
1556                return HANDLED;
1557            }
1558        }
1559
1560        class SwPnoScanState extends State {
1561            private final ArrayList<ScanResult> mSwPnoFullScanResults = new ArrayList<>();
1562
1563            @Override
1564            public void enter() {
1565                if (DBG) localLog("SwPnoScanState");
1566                mSwPnoFullScanResults.clear();
1567            }
1568
1569            @Override
1570            public void exit() {
1571                removeInternalClient();
1572            }
1573
1574            @Override
1575            public boolean processMessage(Message msg) {
1576                ClientInfo ci = mClients.get(msg.replyTo);
1577                switch (msg.what) {
1578                    case WifiScanner.CMD_START_PNO_SCAN:
1579                        Bundle pnoParams = (Bundle) msg.obj;
1580                        if (pnoParams == null) {
1581                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "params null");
1582                            return HANDLED;
1583                        }
1584                        pnoParams.setDefusable(true);
1585                        PnoSettings pnoSettings =
1586                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_PNO_SETTINGS_KEY);
1587                        ScanSettings scanSettings =
1588                                pnoParams.getParcelable(WifiScanner.PNO_PARAMS_SCAN_SETTINGS_KEY);
1589                        if (addSwPnoScanRequest(ci, msg.arg2, scanSettings, pnoSettings)) {
1590                            replySucceeded(msg);
1591                        } else {
1592                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
1593                            transitionTo(mStartedState);
1594                        }
1595                        break;
1596                    case WifiScanner.CMD_STOP_PNO_SCAN:
1597                        removeSwPnoScanRequest(ci, msg.arg2);
1598                        transitionTo(mStartedState);
1599                        break;
1600                    case WifiScanner.CMD_FULL_SCAN_RESULT:
1601                        // Aggregate full scan results until we get the |CMD_SCAN_RESULT| message
1602                        mSwPnoFullScanResults.add((ScanResult) msg.obj);
1603                        break;
1604                    case WifiScanner.CMD_SCAN_RESULT:
1605                        ScanResult[] scanResults = mSwPnoFullScanResults.toArray(
1606                                new ScanResult[mSwPnoFullScanResults.size()]);
1607                        reportPnoNetworkFound(scanResults);
1608                        mSwPnoFullScanResults.clear();
1609                        break;
1610                    case WifiScanner.CMD_OP_FAILED:
1611                        sendPnoScanFailedToAllAndClear(
1612                                WifiScanner.REASON_UNSPECIFIED, "background scan failed");
1613                        transitionTo(mStartedState);
1614                        break;
1615                    default:
1616                        return NOT_HANDLED;
1617                }
1618                return HANDLED;
1619            }
1620        }
1621
1622        private WifiNative.PnoSettings convertSettingsToPnoNative(ScanSettings scanSettings,
1623                                                                  PnoSettings pnoSettings) {
1624            WifiNative.PnoSettings nativePnoSetting = new WifiNative.PnoSettings();
1625            nativePnoSetting.periodInMs = scanSettings.periodInMs;
1626            nativePnoSetting.min5GHzRssi = pnoSettings.min5GHzRssi;
1627            nativePnoSetting.min24GHzRssi = pnoSettings.min24GHzRssi;
1628            nativePnoSetting.initialScoreMax = pnoSettings.initialScoreMax;
1629            nativePnoSetting.currentConnectionBonus = pnoSettings.currentConnectionBonus;
1630            nativePnoSetting.sameNetworkBonus = pnoSettings.sameNetworkBonus;
1631            nativePnoSetting.secureBonus = pnoSettings.secureBonus;
1632            nativePnoSetting.band5GHzBonus = pnoSettings.band5GHzBonus;
1633            nativePnoSetting.isConnected = pnoSettings.isConnected;
1634            nativePnoSetting.networkList =
1635                    new WifiNative.PnoNetwork[pnoSettings.networkList.length];
1636            for (int i = 0; i < pnoSettings.networkList.length; i++) {
1637                nativePnoSetting.networkList[i] = new WifiNative.PnoNetwork();
1638                nativePnoSetting.networkList[i].ssid = pnoSettings.networkList[i].ssid;
1639                nativePnoSetting.networkList[i].flags = pnoSettings.networkList[i].flags;
1640                nativePnoSetting.networkList[i].auth_bit_field =
1641                        pnoSettings.networkList[i].authBitField;
1642            }
1643            return nativePnoSetting;
1644        }
1645
1646        // Retrieve the only active scan settings.
1647        private ScanSettings getScanSettings() {
1648            for (Pair<PnoSettings, ScanSettings> settingsPair : mActivePnoScans.getAllSettings()) {
1649                return settingsPair.second;
1650            }
1651            return null;
1652        }
1653
1654        private void removeInternalClient() {
1655            if (mInternalClientInfo != null) {
1656                mInternalClientInfo.cleanup();
1657                mInternalClientInfo = null;
1658            } else {
1659                Log.w(TAG, "No Internal client for PNO");
1660            }
1661        }
1662
1663        private void addInternalClient(ClientInfo ci) {
1664            if (mInternalClientInfo == null) {
1665                mInternalClientInfo =
1666                        new InternalClientInfo(ci.getUid(), new Messenger(this.getHandler()));
1667                mInternalClientInfo.register();
1668            } else {
1669                Log.w(TAG, "Internal client for PNO already exists");
1670            }
1671        }
1672
1673        private void addPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings,
1674                PnoSettings pnoSettings) {
1675            mActivePnoScans.addRequest(ci, handler, WifiStateMachine.WIFI_WORK_SOURCE,
1676                    Pair.create(pnoSettings, scanSettings));
1677            addInternalClient(ci);
1678        }
1679
1680        private Pair<PnoSettings, ScanSettings> removePnoScanRequest(ClientInfo ci, int handler) {
1681            Pair<PnoSettings, ScanSettings> settings = mActivePnoScans.removeRequest(ci, handler);
1682            return settings;
1683        }
1684
1685        private boolean addHwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings,
1686                PnoSettings pnoSettings) {
1687            if (ci == null) {
1688                Log.d(TAG, "Failing scan request ClientInfo not found " + handler);
1689                return false;
1690            }
1691            if (!mActivePnoScans.isEmpty()) {
1692                loge("Failing scan request because there is already an active scan");
1693                return false;
1694            }
1695            WifiNative.PnoSettings nativePnoSettings =
1696                    convertSettingsToPnoNative(scanSettings, pnoSettings);
1697            if (!mScannerImpl.setHwPnoList(nativePnoSettings, mPnoScanStateMachine)) {
1698                return false;
1699            }
1700            logScanRequest("addHwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings);
1701            addPnoScanRequest(ci, handler, scanSettings, pnoSettings);
1702            // HW PNO is supported, check if we need a background scan running for this.
1703            if (mScannerImpl.shouldScheduleBackgroundScanForHwPno()) {
1704                addBackgroundScanRequest(scanSettings);
1705            }
1706            return true;
1707        }
1708
1709        private void removeHwPnoScanRequest(ClientInfo ci, int handler) {
1710            if (ci != null) {
1711                Pair<PnoSettings, ScanSettings> settings = removePnoScanRequest(ci, handler);
1712                logScanRequest("removeHwPnoScanRequest", ci, handler, null,
1713                        settings.second, settings.first);
1714            }
1715        }
1716
1717        private boolean addSwPnoScanRequest(ClientInfo ci, int handler, ScanSettings scanSettings,
1718                PnoSettings pnoSettings) {
1719            if (ci == null) {
1720                Log.d(TAG, "Failing scan request ClientInfo not found " + handler);
1721                return false;
1722            }
1723            if (!mActivePnoScans.isEmpty()) {
1724                loge("Failing scan request because there is already an active scan");
1725                return false;
1726            }
1727            logScanRequest("addSwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings);
1728            addPnoScanRequest(ci, handler, scanSettings, pnoSettings);
1729            // HW PNO is not supported, we need to revert to normal background scans and
1730            // report events after each scan and we need full scan results to get the IE information
1731            scanSettings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
1732                    | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
1733            addBackgroundScanRequest(scanSettings);
1734            return true;
1735        }
1736
1737        private void removeSwPnoScanRequest(ClientInfo ci, int handler) {
1738            if (ci != null) {
1739                Pair<PnoSettings, ScanSettings> settings = removePnoScanRequest(ci, handler);
1740                logScanRequest("removeSwPnoScanRequest", ci, handler, null,
1741                        settings.second, settings.first);
1742            }
1743        }
1744
1745        private void reportPnoNetworkFound(ScanResult[] results) {
1746            WifiScanner.ParcelableScanResults parcelableScanResults =
1747                    new WifiScanner.ParcelableScanResults(results);
1748            for (RequestInfo<Pair<PnoSettings, ScanSettings>> entry : mActivePnoScans) {
1749                ClientInfo ci = entry.clientInfo;
1750                int handler = entry.handlerId;
1751                logCallback("pnoNetworkFound", ci, handler, describeForLog(results));
1752                ci.reportEvent(
1753                        WifiScanner.CMD_PNO_NETWORK_FOUND, 0, handler, parcelableScanResults);
1754            }
1755        }
1756
1757        private void sendPnoScanFailedToAllAndClear(int reason, String description) {
1758            for (RequestInfo<Pair<PnoSettings, ScanSettings>> entry : mActivePnoScans) {
1759                ClientInfo ci = entry.clientInfo;
1760                int handler = entry.handlerId;
1761                ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler,
1762                        new WifiScanner.OperationResult(reason, description));
1763            }
1764            mActivePnoScans.clear();
1765        }
1766
1767        private void addBackgroundScanRequest(ScanSettings settings) {
1768            if (DBG) localLog("Starting background scan");
1769            if (mInternalClientInfo != null) {
1770                mInternalClientInfo.sendRequestToClientHandler(
1771                        WifiScanner.CMD_START_BACKGROUND_SCAN, settings,
1772                        WifiStateMachine.WIFI_WORK_SOURCE);
1773            }
1774        }
1775
1776        private void addSingleScanRequest(ScanSettings settings) {
1777            if (DBG) localLog("Starting single scan");
1778            if (mInternalClientInfo != null) {
1779                mInternalClientInfo.sendRequestToClientHandler(
1780                        WifiScanner.CMD_START_SINGLE_SCAN, settings,
1781                        WifiStateMachine.WIFI_WORK_SOURCE);
1782            }
1783        }
1784
1785        /**
1786         * Checks if IE are present in scan data, if no single scan is needed to report event to
1787         * client
1788         */
1789        private boolean isSingleScanNeeded(ScanResult[] scanResults) {
1790            for (ScanResult scanResult : scanResults) {
1791                if (scanResult.informationElements != null
1792                        && scanResult.informationElements.length > 0) {
1793                    return false;
1794                }
1795            }
1796            return true;
1797        }
1798    }
1799
1800    private abstract class ClientInfo {
1801        private final int mUid;
1802        private final WorkSource mWorkSource;
1803        private boolean mScanWorkReported = false;
1804        protected final Messenger mMessenger;
1805
1806        ClientInfo(int uid, Messenger messenger) {
1807            mUid = uid;
1808            mMessenger = messenger;
1809            mWorkSource = new WorkSource(uid);
1810        }
1811
1812        /**
1813         * Register this client to main client map.
1814         */
1815        public void register() {
1816            mClients.put(mMessenger, this);
1817        }
1818
1819        /**
1820         * Unregister this client from main client map.
1821         */
1822        private void unregister() {
1823            mClients.remove(mMessenger);
1824        }
1825
1826        public void cleanup() {
1827            mSingleScanListeners.removeAllForClient(this);
1828            mSingleScanStateMachine.removeSingleScanRequests(this);
1829            mBackgroundScanStateMachine.removeBackgroundScanSettings(this);
1830            unregister();
1831            localLog("Successfully stopped all requests for client " + this);
1832        }
1833
1834        public int getUid() {
1835            return mUid;
1836        }
1837
1838        public void reportEvent(int what, int arg1, int arg2) {
1839            reportEvent(what, arg1, arg2, null);
1840        }
1841
1842        // This has to be implemented by subclasses to report events back to clients.
1843        public abstract void reportEvent(int what, int arg1, int arg2, Object obj);
1844
1845        // TODO(b/27903217): Blame scan on provided work source
1846        private void reportBatchedScanStart() {
1847            if (mUid == 0)
1848                return;
1849
1850            int csph = getCsph();
1851
1852            try {
1853                mBatteryStats.noteWifiBatchedScanStartedFromSource(mWorkSource, csph);
1854            } catch (RemoteException e) {
1855                logw("failed to report scan work: " + e.toString());
1856            }
1857        }
1858
1859        private void reportBatchedScanStop() {
1860            if (mUid == 0)
1861                return;
1862
1863            try {
1864                mBatteryStats.noteWifiBatchedScanStoppedFromSource(mWorkSource);
1865            } catch (RemoteException e) {
1866                logw("failed to cleanup scan work: " + e.toString());
1867            }
1868        }
1869
1870        // TODO migrate batterystats to accept scan duration per hour instead of csph
1871        private int getCsph() {
1872            int totalScanDurationPerHour = 0;
1873            Collection<ScanSettings> settingsList =
1874                    mBackgroundScanStateMachine.getBackgroundScanSettings(this);
1875            for (ScanSettings settings : settingsList) {
1876                int scanDurationMs = mChannelHelper.estimateScanDuration(settings);
1877                int scans_per_Hour = settings.periodInMs == 0 ? 1 : (3600 * 1000) /
1878                        settings.periodInMs;
1879                totalScanDurationPerHour += scanDurationMs * scans_per_Hour;
1880            }
1881
1882            return totalScanDurationPerHour / ChannelHelper.SCAN_PERIOD_PER_CHANNEL_MS;
1883        }
1884
1885        public void reportScanWorkUpdate() {
1886            if (mScanWorkReported) {
1887                reportBatchedScanStop();
1888                mScanWorkReported = false;
1889            }
1890            if (mBackgroundScanStateMachine.getBackgroundScanSettings(this).isEmpty()) {
1891                reportBatchedScanStart();
1892                mScanWorkReported = true;
1893            }
1894        }
1895
1896        @Override
1897        public String toString() {
1898            return "ClientInfo[uid=" + mUid + "," + mMessenger + "]";
1899        }
1900    }
1901
1902    /**
1903     * This class is used to represent external clients to the WifiScanning Service.
1904     */
1905    private class ExternalClientInfo extends ClientInfo {
1906        private final AsyncChannel mChannel;
1907        /**
1908         * Indicates if the client is still connected
1909         * If the client is no longer connected then messages to it will be silently dropped
1910         */
1911        private boolean mDisconnected = false;
1912
1913        ExternalClientInfo(int uid, Messenger messenger, AsyncChannel c) {
1914            super(uid, messenger);
1915            mChannel = c;
1916            if (DBG) localLog("New client, channel: " + c);
1917        }
1918
1919        @Override
1920        public void reportEvent(int what, int arg1, int arg2, Object obj) {
1921            if (!mDisconnected) {
1922                mChannel.sendMessage(what, arg1, arg2, obj);
1923            }
1924        }
1925
1926        @Override
1927        public void cleanup() {
1928            mDisconnected = true;
1929            mPnoScanStateMachine.removePnoSettings(this);
1930            super.cleanup();
1931        }
1932    }
1933
1934    /**
1935     * This class is used to represent internal clients to the WifiScanning Service. This is needed
1936     * for communicating between State Machines.
1937     * This leaves the onReportEvent method unimplemented, so that the clients have the freedom
1938     * to handle the events as they need.
1939     */
1940    private class InternalClientInfo extends ClientInfo {
1941        private static final int INTERNAL_CLIENT_HANDLER = 0;
1942
1943        /**
1944         * The UID here is used to proxy the original external requester UID.
1945         */
1946        InternalClientInfo(int requesterUid, Messenger messenger) {
1947            super(requesterUid, messenger);
1948        }
1949
1950        @Override
1951        public void reportEvent(int what, int arg1, int arg2, Object obj) {
1952            Message message = Message.obtain();
1953            message.what = what;
1954            message.arg1 = arg1;
1955            message.arg2 = arg2;
1956            message.obj = obj;
1957            try {
1958                mMessenger.send(message);
1959            } catch (RemoteException e) {
1960                loge("Failed to send message: " + what);
1961            }
1962        }
1963
1964        /**
1965         * Send a message to the client handler which should reroute the message to the appropriate
1966         * state machine.
1967         */
1968        public void sendRequestToClientHandler(int what, ScanSettings settings,
1969                WorkSource workSource) {
1970            Message msg = Message.obtain();
1971            msg.what = what;
1972            msg.arg2 = INTERNAL_CLIENT_HANDLER;
1973            if (settings != null) {
1974                Bundle bundle = new Bundle();
1975                bundle.putParcelable(WifiScanner.SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
1976                bundle.putParcelable(WifiScanner.SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
1977                msg.obj = bundle;
1978            }
1979            msg.replyTo = mMessenger;
1980            msg.sendingUid = getUid();
1981            mClientHandler.sendMessage(msg);
1982        }
1983
1984        /**
1985         * Send a message to the client handler which should reroute the message to the appropriate
1986         * state machine.
1987         */
1988        public void sendRequestToClientHandler(int what) {
1989            sendRequestToClientHandler(what, null, null);
1990        }
1991
1992        @Override
1993        public String toString() {
1994            return "InternalClientInfo[]";
1995        }
1996    }
1997
1998    void replySucceeded(Message msg) {
1999        if (msg.replyTo != null) {
2000            Message reply = Message.obtain();
2001            reply.what = WifiScanner.CMD_OP_SUCCEEDED;
2002            reply.arg2 = msg.arg2;
2003            if (msg.obj != null) {
2004                reply.obj = msg.obj;
2005            }
2006            try {
2007                msg.replyTo.send(reply);
2008                mLog.trace("replySucceeded recvdMessage=%").c(msg.what).flush();
2009            } catch (RemoteException e) {
2010                // There's not much we can do if reply can't be sent!
2011            }
2012        } else {
2013            // locally generated message; doesn't need a reply!
2014        }
2015    }
2016
2017    void replyFailed(Message msg, int reason, String description) {
2018        if (msg.replyTo != null) {
2019            Message reply = Message.obtain();
2020            reply.what = WifiScanner.CMD_OP_FAILED;
2021            reply.arg2 = msg.arg2;
2022            reply.obj = new WifiScanner.OperationResult(reason, description);
2023            try {
2024                msg.replyTo.send(reply);
2025                mLog.trace("replyFailed recvdMessage=% reason=%")
2026                            .c(msg.what)
2027                            .c(reason)
2028                            .flush();
2029            } catch (RemoteException e) {
2030                // There's not much we can do if reply can't be sent!
2031            }
2032        } else {
2033            // locally generated message; doesn't need a reply!
2034        }
2035    }
2036
2037    private static String toString(int uid, ScanSettings settings) {
2038        StringBuilder sb = new StringBuilder();
2039        sb.append("ScanSettings[uid=").append(uid);
2040        sb.append(", period=").append(settings.periodInMs);
2041        sb.append(", report=").append(settings.reportEvents);
2042        if (settings.reportEvents == WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL
2043                && settings.numBssidsPerScan > 0
2044                && settings.maxScansToCache > 1) {
2045            sb.append(", batch=").append(settings.maxScansToCache);
2046            sb.append(", numAP=").append(settings.numBssidsPerScan);
2047        }
2048        sb.append(", ").append(ChannelHelper.toString(settings));
2049        sb.append("]");
2050
2051        return sb.toString();
2052    }
2053
2054    @Override
2055    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2056        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2057                != PackageManager.PERMISSION_GRANTED) {
2058            pw.println("Permission Denial: can't dump WifiScanner from from pid="
2059                    + Binder.getCallingPid()
2060                    + ", uid=" + Binder.getCallingUid()
2061                    + " without permission "
2062                    + android.Manifest.permission.DUMP);
2063            return;
2064        }
2065        pw.println("WifiScanningService - Log Begin ----");
2066        mLocalLog.dump(fd, pw, args);
2067        pw.println("WifiScanningService - Log End ----");
2068        pw.println();
2069        pw.println("clients:");
2070        for (ClientInfo client : mClients.values()) {
2071            pw.println("  " + client);
2072        }
2073        pw.println("listeners:");
2074        for (ClientInfo client : mClients.values()) {
2075            Collection<ScanSettings> settingsList =
2076                    mBackgroundScanStateMachine.getBackgroundScanSettings(client);
2077            for (ScanSettings settings : settingsList) {
2078                pw.println("  " + toString(client.mUid, settings));
2079            }
2080        }
2081        if (mBackgroundScheduler != null) {
2082            WifiNative.ScanSettings schedule = mBackgroundScheduler.getSchedule();
2083            if (schedule != null) {
2084                pw.println("schedule:");
2085                pw.println("  base period: " + schedule.base_period_ms);
2086                pw.println("  max ap per scan: " + schedule.max_ap_per_scan);
2087                pw.println("  batched scans: " + schedule.report_threshold_num_scans);
2088                pw.println("  buckets:");
2089                for (int b = 0; b < schedule.num_buckets; b++) {
2090                    WifiNative.BucketSettings bucket = schedule.buckets[b];
2091                    pw.println("    bucket " + bucket.bucket + " (" + bucket.period_ms + "ms)["
2092                            + bucket.report_events + "]: "
2093                            + ChannelHelper.toString(bucket));
2094                }
2095            }
2096        }
2097        if (mPnoScanStateMachine != null) {
2098            mPnoScanStateMachine.dump(fd, pw, args);
2099        }
2100        pw.println();
2101
2102        if (mSingleScanStateMachine != null) {
2103            mSingleScanStateMachine.dump(fd, pw, args);
2104            pw.println();
2105            pw.println("Latest scan results:");
2106            List<ScanResult> scanResults = mSingleScanStateMachine.getCachedScanResultsAsList();
2107            long nowMs = System.currentTimeMillis();
2108            if (scanResults != null && scanResults.size() != 0) {
2109                pw.println("    BSSID              Frequency  RSSI  Age(sec)   SSID "
2110                        + "                                Flags");
2111                for (ScanResult r : scanResults) {
2112                    String age;
2113                    if (r.seen <= 0) {
2114                        age = "___?___";
2115                    } else if (nowMs < r.seen) {
2116                        age = "  0.000";
2117                    } else if (r.seen < nowMs - 1000000) {
2118                        age = ">1000.0";
2119                    } else {
2120                        age = String.format("%3.3f", (nowMs - r.seen) / 1000.0);
2121                    }
2122                    String ssid = r.SSID == null ? "" : r.SSID;
2123                    pw.printf("  %17s  %9d  %5d   %7s    %-32s  %s\n",
2124                              r.BSSID,
2125                              r.frequency,
2126                              r.level,
2127                              age,
2128                              String.format("%1.32s", ssid),
2129                              r.capabilities);
2130                }
2131            }
2132            pw.println();
2133        }
2134    }
2135
2136    void logScanRequest(String request, ClientInfo ci, int id, WorkSource workSource,
2137            ScanSettings settings, PnoSettings pnoSettings) {
2138        StringBuilder sb = new StringBuilder();
2139        sb.append(request)
2140                .append(": ")
2141                .append((ci == null) ? "ClientInfo[unknown]" : ci.toString())
2142                .append(",Id=")
2143                .append(id);
2144        if (workSource != null) {
2145            sb.append(",").append(workSource);
2146        }
2147        if (settings != null) {
2148            sb.append(", ");
2149            describeTo(sb, settings);
2150        }
2151        if (pnoSettings != null) {
2152            sb.append(", ");
2153            describeTo(sb, pnoSettings);
2154        }
2155        localLog(sb.toString());
2156    }
2157
2158    void logCallback(String callback, ClientInfo ci, int id, String extra) {
2159        StringBuilder sb = new StringBuilder();
2160        sb.append(callback)
2161                .append(": ")
2162                .append((ci == null) ? "ClientInfo[unknown]" : ci.toString())
2163                .append(",Id=")
2164                .append(id);
2165        if (extra != null) {
2166            sb.append(",").append(extra);
2167        }
2168        localLog(sb.toString());
2169    }
2170
2171    static String describeForLog(ScanData[] results) {
2172        StringBuilder sb = new StringBuilder();
2173        sb.append("results=");
2174        for (int i = 0; i < results.length; ++i) {
2175            if (i > 0) sb.append(";");
2176            sb.append(results[i].getResults().length);
2177        }
2178        return sb.toString();
2179    }
2180
2181    static String describeForLog(ScanResult[] results) {
2182        return "results=" + results.length;
2183    }
2184
2185    static String describeTo(StringBuilder sb, ScanSettings scanSettings) {
2186        sb.append("ScanSettings { ")
2187          .append(" band:").append(scanSettings.band)
2188          .append(" period:").append(scanSettings.periodInMs)
2189          .append(" reportEvents:").append(scanSettings.reportEvents)
2190          .append(" numBssidsPerScan:").append(scanSettings.numBssidsPerScan)
2191          .append(" maxScansToCache:").append(scanSettings.maxScansToCache)
2192          .append(" channels:[ ");
2193        if (scanSettings.channels != null) {
2194            for (int i = 0; i < scanSettings.channels.length; i++) {
2195                sb.append(scanSettings.channels[i].frequency)
2196                  .append(" ");
2197            }
2198        }
2199        sb.append(" ] ")
2200          .append(" } ");
2201        return sb.toString();
2202    }
2203
2204    static String describeTo(StringBuilder sb, PnoSettings pnoSettings) {
2205        sb.append("PnoSettings { ")
2206          .append(" min5GhzRssi:").append(pnoSettings.min5GHzRssi)
2207          .append(" min24GhzRssi:").append(pnoSettings.min24GHzRssi)
2208          .append(" initialScoreMax:").append(pnoSettings.initialScoreMax)
2209          .append(" currentConnectionBonus:").append(pnoSettings.currentConnectionBonus)
2210          .append(" sameNetworkBonus:").append(pnoSettings.sameNetworkBonus)
2211          .append(" secureBonus:").append(pnoSettings.secureBonus)
2212          .append(" band5GhzBonus:").append(pnoSettings.band5GHzBonus)
2213          .append(" isConnected:").append(pnoSettings.isConnected)
2214          .append(" networks:[ ");
2215        if (pnoSettings.networkList != null) {
2216            for (int i = 0; i < pnoSettings.networkList.length; i++) {
2217                sb.append(pnoSettings.networkList[i].ssid).append(",");
2218            }
2219        }
2220        sb.append(" ] ")
2221          .append(" } ");
2222        return sb.toString();
2223    }
2224}
2225