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