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