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