WifiScanningServiceImpl.java revision 00ebd8f3776aa8e6f91e9a1ab541f53f389b00b6
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;
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.net.wifi.IWifiScanner;
27import android.net.wifi.ScanResult;
28import android.net.wifi.WifiManager;
29import android.net.wifi.WifiScanner;
30import android.net.wifi.WifiScanner.BssidInfo;
31import android.net.wifi.WifiScanner.ChannelSpec;
32import android.net.wifi.WifiScanner.ScanData;
33import android.net.wifi.WifiScanner.ScanSettings;
34import android.net.wifi.WifiSsid;
35import android.os.Bundle;
36import android.os.Handler;
37import android.os.HandlerThread;
38import android.os.Looper;
39import android.os.Message;
40import android.os.Messenger;
41import android.os.RemoteException;
42import android.os.SystemClock;
43import android.os.WorkSource;
44import android.util.Log;
45import android.util.Slog;
46
47import com.android.internal.app.IBatteryStats;
48import com.android.internal.util.AsyncChannel;
49import com.android.internal.util.Protocol;
50import com.android.internal.util.StateMachine;
51import com.android.internal.util.State;
52import com.android.server.am.BatteryStatsService;
53
54
55import java.io.FileDescriptor;
56import java.io.PrintWriter;
57import java.util.ArrayList;
58import java.util.Collection;
59import java.util.HashMap;
60import java.util.HashSet;
61import java.util.Iterator;
62import java.util.Map;
63
64public class WifiScanningServiceImpl extends IWifiScanner.Stub {
65
66    private static final String TAG = "WifiScanningService";
67    private static final boolean DBG = true;
68    private static final boolean VDBG = false;
69
70    private static final int INVALID_KEY = 0;                               // same as WifiScanner
71    private static final int MIN_PERIOD_PER_CHANNEL_MS = 200;               // DFS needs 120 ms
72    private static final int UNKNOWN_PID = -1;
73
74    @Override
75    public Messenger getMessenger() {
76        if (mClientHandler != null) {
77            return new Messenger(mClientHandler);
78        } else {
79            Log.e(TAG, "WifiScanningServiceImpl trying to get messenger w/o initialization");
80            return null;
81        }
82    }
83
84    @Override
85    public Bundle getAvailableChannels(int band) {
86        ChannelSpec channelSpecs[] = getChannelsForBand(band);
87        ArrayList<Integer> list = new ArrayList<Integer>(channelSpecs.length);
88        for (ChannelSpec channelSpec : channelSpecs) {
89            list.add(channelSpec.frequency);
90        }
91        Bundle b = new Bundle();
92        b.putIntegerArrayList(WifiScanner.GET_AVAILABLE_CHANNELS_EXTRA, list);
93        return b;
94    }
95
96    private void enforceLocationHardwarePermission(int uid) {
97        mContext.enforcePermission(
98                Manifest.permission.LOCATION_HARDWARE,
99                UNKNOWN_PID, uid,
100                "LocationHardware");
101    }
102
103    private class ClientHandler extends Handler {
104
105        ClientHandler(android.os.Looper looper) {
106            super(looper);
107        }
108
109        @Override
110        public void handleMessage(Message msg) {
111
112            if (DBG) Log.d(TAG, "ClientHandler got" + msg);
113
114            switch (msg.what) {
115
116                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
117                    if (msg.arg1 != AsyncChannel.STATUS_SUCCESSFUL) {
118                        Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
119                    }
120                    return;
121                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
122                    AsyncChannel ac = new AsyncChannel();
123                    ac.connect(mContext, this, msg.replyTo);
124                    if (DBG) Slog.d(TAG, "New client connected : " + msg.sendingUid + msg.replyTo);
125                    ClientInfo cInfo = new ClientInfo(msg.sendingUid, ac, msg.replyTo);
126                    mClients.put(msg.replyTo, cInfo);
127                    return;
128                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
129                    if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
130                        Slog.e(TAG, "Send failed, client connection lost");
131                    } else {
132                        if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
133                    }
134                    if (DBG) Slog.d(TAG, "closing client " + msg.replyTo);
135                    ClientInfo ci = mClients.remove(msg.replyTo);
136                    if (ci != null) {                       /* can be null if send failed above */
137                        if (DBG) Slog.d(TAG, "closing client " + ci.mUid);
138                        ci.cleanup();
139                    }
140                    return;
141            }
142
143            try {
144                enforceLocationHardwarePermission(msg.sendingUid);
145            } catch (SecurityException e) {
146                Log.d(TAG, "failed to authorize app: " + e);
147                replyFailed(msg, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
148                return;
149            }
150
151            if (msg.what == WifiScanner.CMD_GET_SCAN_RESULTS) {
152                mStateMachine.sendMessage(Message.obtain(msg));
153                return;
154            }
155            ClientInfo ci = mClients.get(msg.replyTo);
156            if (ci == null) {
157                Slog.e(TAG, "Could not find client info for message " + msg.replyTo);
158                replyFailed(msg, WifiScanner.REASON_INVALID_LISTENER, "Could not find listener");
159                return;
160            }
161
162            int validCommands[] = {
163                    WifiScanner.CMD_SCAN,
164                    WifiScanner.CMD_START_BACKGROUND_SCAN,
165                    WifiScanner.CMD_STOP_BACKGROUND_SCAN,
166                    WifiScanner.CMD_START_SINGLE_SCAN,
167                    WifiScanner.CMD_STOP_SINGLE_SCAN,
168                    WifiScanner.CMD_SET_HOTLIST,
169                    WifiScanner.CMD_RESET_HOTLIST,
170                    WifiScanner.CMD_CONFIGURE_WIFI_CHANGE,
171                    WifiScanner.CMD_START_TRACKING_CHANGE,
172                    WifiScanner.CMD_STOP_TRACKING_CHANGE };
173
174            for (int cmd : validCommands) {
175                if (cmd == msg.what) {
176                    mStateMachine.sendMessage(Message.obtain(msg));
177                    return;
178                }
179            }
180
181            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "Invalid request");
182        }
183    }
184
185    private static final int BASE = Protocol.BASE_WIFI_SCANNER_SERVICE;
186
187    private static final int CMD_SCAN_RESULTS_AVAILABLE              = BASE + 0;
188    private static final int CMD_FULL_SCAN_RESULTS                   = BASE + 1;
189    private static final int CMD_HOTLIST_AP_FOUND                    = BASE + 2;
190    private static final int CMD_HOTLIST_AP_LOST                     = BASE + 3;
191    private static final int CMD_WIFI_CHANGE_DETECTED                = BASE + 4;
192    private static final int CMD_WIFI_CHANGES_STABILIZED             = BASE + 5;
193    private static final int CMD_DRIVER_LOADED                       = BASE + 6;
194    private static final int CMD_DRIVER_UNLOADED                     = BASE + 7;
195    private static final int CMD_SCAN_PAUSED                         = BASE + 8;
196    private static final int CMD_SCAN_RESTARTED                      = BASE + 9;
197    private static final int CMD_STOP_SCAN_INTERNAL                  = BASE + 10;
198
199    private Context mContext;
200    private WifiScanningStateMachine mStateMachine;
201    private ClientHandler mClientHandler;
202    private IBatteryStats mBatteryStats;
203
204    WifiScanningServiceImpl() { }
205
206    WifiScanningServiceImpl(Context context) {
207        mContext = context;
208    }
209
210    public void startService(Context context) {
211        mContext = context;
212
213        HandlerThread thread = new HandlerThread("WifiScanningService");
214        thread.start();
215
216        mClientHandler = new ClientHandler(thread.getLooper());
217        mStateMachine = new WifiScanningStateMachine(thread.getLooper());
218        mWifiChangeStateMachine = new WifiChangeStateMachine(thread.getLooper());
219        mBatteryStats = BatteryStatsService.getService();
220
221        mContext.registerReceiver(
222                new BroadcastReceiver() {
223                    @Override
224                    public void onReceive(Context context, Intent intent) {
225                        int state = intent.getIntExtra(
226                                WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED);
227                        if (DBG) Log.d(TAG, "SCAN_AVAILABLE : " + state);
228                        if (state == WifiManager.WIFI_STATE_ENABLED) {
229                            mStateMachine.sendMessage(CMD_DRIVER_LOADED);
230                        } else if (state == WifiManager.WIFI_STATE_DISABLED) {
231                            mStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
232                        }
233                    }
234                }, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE));
235
236        mStateMachine.start();
237        mWifiChangeStateMachine.start();
238    }
239
240    class WifiScanningStateMachine extends StateMachine implements WifiNative.ScanEventHandler,
241            WifiNative.HotlistEventHandler, WifiNative.SignificantWifiChangeEventHandler {
242
243        private final DefaultState mDefaultState = new DefaultState();
244        private final StartedState mStartedState = new StartedState();
245        private final PausedState  mPausedState  = new PausedState();
246
247        public WifiScanningStateMachine(Looper looper) {
248            super(TAG, looper);
249
250            setLogRecSize(512);
251            setLogOnlyTransitions(false);
252            // setDbg(DBG);
253
254            addState(mDefaultState);
255                addState(mStartedState, mDefaultState);
256                addState(mPausedState, mDefaultState);
257
258            setInitialState(mDefaultState);
259        }
260
261        @Override
262        public void onScanResultsAvailable() {
263            if (DBG) Log.d(TAG, "onScanResultAvailable event received");
264            sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
265        }
266
267        @Override
268        public void onScanStatus() {
269            if (DBG) Log.d(TAG, "onScanStatus event received");
270            sendMessage(CMD_SCAN_RESULTS_AVAILABLE);
271        }
272
273        @Override
274        public void onFullScanResult(ScanResult fullScanResult) {
275            if (DBG) Log.d(TAG, "Full scanresult received");
276            sendMessage(CMD_FULL_SCAN_RESULTS, 0, 0, fullScanResult);
277        }
278
279        @Override
280        public void onScanPaused(ScanData scanData[]) {
281            sendMessage(CMD_SCAN_PAUSED, scanData);
282        }
283
284        @Override
285        public void onScanRestarted() {
286            sendMessage(CMD_SCAN_RESTARTED);
287        }
288
289        @Override
290        public void onHotlistApFound(ScanResult[] results) {
291            if (DBG) Log.d(TAG, "HotlistApFound event received");
292            sendMessage(CMD_HOTLIST_AP_FOUND, 0, 0, results);
293        }
294
295        @Override
296        public void onHotlistApLost(ScanResult[] results) {
297            if (DBG) Log.d(TAG, "HotlistApLost event received");
298            sendMessage(CMD_HOTLIST_AP_LOST, 0, 0, results);
299        }
300
301        @Override
302        public void onChangesFound(ScanResult[] results) {
303            if (DBG) Log.d(TAG, "onWifiChangesFound event received");
304            sendMessage(CMD_WIFI_CHANGE_DETECTED, 0, 0, results);
305        }
306
307        class DefaultState extends State {
308            @Override
309            public void enter() {
310                if (DBG) Log.d(TAG, "DefaultState");
311            }
312            @Override
313            public boolean processMessage(Message msg) {
314
315                if (DBG) Log.d(TAG, "DefaultState got" + msg);
316
317                ClientInfo ci = mClients.get(msg.replyTo);
318
319                switch (msg.what) {
320                    case CMD_DRIVER_LOADED:
321                        if (WifiNative.startHal() && WifiNative.getInterfaces() != 0) {
322                            WifiNative.ScanCapabilities capabilities =
323                                    new WifiNative.ScanCapabilities();
324                            if (WifiNative.getScanCapabilities(capabilities)) {
325                                transitionTo(mStartedState);
326                            } else {
327                                loge("could not get scan capabilities");
328                            }
329                        } else {
330                            loge("could not start HAL");
331                        }
332                        break;
333                    case WifiScanner.CMD_SCAN:
334                    case WifiScanner.CMD_START_BACKGROUND_SCAN:
335                    case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
336                    case WifiScanner.CMD_START_SINGLE_SCAN:
337                    case WifiScanner.CMD_STOP_SINGLE_SCAN:
338                    case WifiScanner.CMD_SET_HOTLIST:
339                    case WifiScanner.CMD_RESET_HOTLIST:
340                    case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
341                    case WifiScanner.CMD_START_TRACKING_CHANGE:
342                    case WifiScanner.CMD_STOP_TRACKING_CHANGE:
343                    case WifiScanner.CMD_GET_SCAN_RESULTS:
344                        replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
345                        break;
346
347                    case CMD_SCAN_RESULTS_AVAILABLE:
348                        if (DBG) log("ignored scan results available event");
349                        break;
350
351                    case CMD_FULL_SCAN_RESULTS:
352                        if (DBG) log("ignored full scan result event");
353                        break;
354
355                    default:
356                        break;
357                }
358
359                return HANDLED;
360            }
361        }
362
363        class StartedState extends State {
364
365            @Override
366            public void enter() {
367                if (DBG) Log.d(TAG, "StartedState");
368            }
369
370            @Override
371            public boolean processMessage(Message msg) {
372
373                if (DBG) Log.d(TAG, "StartedState got" + msg);
374
375                ClientInfo ci = mClients.get(msg.replyTo);
376
377                switch (msg.what) {
378                    case CMD_DRIVER_UNLOADED:
379                        transitionTo(mDefaultState);
380                        break;
381                    case WifiScanner.CMD_SCAN:
382                        replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not implemented");
383                        break;
384                    case WifiScanner.CMD_START_BACKGROUND_SCAN:
385                        if (addScanRequest(ci, msg.arg2, (ScanSettings) msg.obj)) {
386                            replySucceeded(msg);
387                        } else {
388                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
389                        }
390                        break;
391                    case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
392                        removeScanRequest(ci, msg.arg2);
393                        break;
394                    case WifiScanner.CMD_GET_SCAN_RESULTS:
395                        reportScanResults();
396                        replySucceeded(msg);
397                        break;
398                    case WifiScanner.CMD_START_SINGLE_SCAN:
399                        if (addSingleScanRequest(ci, msg.arg2, (ScanSettings) msg.obj)) {
400                            replySucceeded(msg);
401                        } else {
402                            replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
403                        }
404                        break;
405                    case WifiScanner.CMD_STOP_SINGLE_SCAN:
406                        removeScanRequest(ci, msg.arg2);
407                        break;
408                    case CMD_STOP_SCAN_INTERNAL:
409                        Log.d(TAG, "Removing single shot scan");
410                        removeScanRequest((ClientInfo) msg.obj, msg.arg2);
411                        break;
412                    case WifiScanner.CMD_SET_HOTLIST:
413                        setHotlist(ci, msg.arg2, (WifiScanner.HotlistSettings) msg.obj);
414                        replySucceeded(msg);
415                        break;
416                    case WifiScanner.CMD_RESET_HOTLIST:
417                        resetHotlist(ci, msg.arg2);
418                        break;
419                    case WifiScanner.CMD_START_TRACKING_CHANGE:
420                        trackWifiChanges(ci, msg.arg2);
421                        replySucceeded(msg);
422                        break;
423                    case WifiScanner.CMD_STOP_TRACKING_CHANGE:
424                        untrackWifiChanges(ci, msg.arg2);
425                        break;
426                    case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
427                        configureWifiChange((WifiScanner.WifiChangeSettings) msg.obj);
428                        break;
429                    case CMD_SCAN_RESULTS_AVAILABLE: {
430                            ScanData[] results = WifiNative.getScanResults(/* flush = */ true);
431                            Collection<ClientInfo> clients = mClients.values();
432                            for (ClientInfo ci2 : clients) {
433                                ci2.reportScanResults(results);
434                            }
435                        }
436                        break;
437                    case CMD_FULL_SCAN_RESULTS: {
438                            ScanResult result = (ScanResult) msg.obj;
439                            if (DBG) Log.d(TAG, "reporting fullscan result for " + result.SSID);
440                            Collection<ClientInfo> clients = mClients.values();
441                            for (ClientInfo ci2 : clients) {
442                                ci2.reportFullScanResult(result);
443                            }
444                        }
445                        break;
446
447                    case CMD_HOTLIST_AP_FOUND: {
448                            ScanResult[] results = (ScanResult[])msg.obj;
449                            if (DBG) Log.d(TAG, "Found " + results.length + " results");
450                            Collection<ClientInfo> clients = mClients.values();
451                            for (ClientInfo ci2 : clients) {
452                                ci2.reportHotlistResults(WifiScanner.CMD_AP_FOUND, results);
453                            }
454                        }
455                        break;
456                    case CMD_HOTLIST_AP_LOST: {
457                            ScanResult[] results = (ScanResult[])msg.obj;
458                            if (DBG) Log.d(TAG, "Lost " + results.length + " results");
459                            Collection<ClientInfo> clients = mClients.values();
460                            for (ClientInfo ci2 : clients) {
461                                ci2.reportHotlistResults(WifiScanner.CMD_AP_LOST, results);
462                            }
463                        }
464                        break;
465                    case CMD_WIFI_CHANGE_DETECTED: {
466                            ScanResult[] results = (ScanResult[])msg.obj;
467                            reportWifiChanged(results);
468                        }
469                        break;
470                    case CMD_WIFI_CHANGES_STABILIZED: {
471                            ScanResult[] results = (ScanResult[])msg.obj;
472                            reportWifiStabilized(results);
473                        }
474                        break;
475                    case CMD_SCAN_PAUSED: {
476                            ScanData results[] = (ScanData[]) msg.obj;
477                            Collection<ClientInfo> clients = mClients.values();
478                            for (ClientInfo ci2 : clients) {
479                                ci2.reportScanResults(results);
480                            }
481                            transitionTo(mPausedState);
482                        }
483                        break;
484                    default:
485                        return NOT_HANDLED;
486                }
487
488                return HANDLED;
489            }
490        }
491
492        class PausedState extends State {
493            @Override
494            public void enter() {
495                if (DBG) Log.d(TAG, "PausedState");
496            }
497
498            @Override
499            public boolean processMessage(Message msg) {
500
501                if (DBG) Log.d(TAG, "PausedState got" + msg);
502
503                switch (msg.what) {
504                    case CMD_SCAN_RESTARTED:
505                        transitionTo(mStartedState);
506                        break;
507                    default:
508                        deferMessage(msg);
509                        break;
510                }
511                return HANDLED;
512            }
513
514        }
515
516        @Override
517        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
518            super.dump(fd, pw, args);
519            pw.println("number of clients : " + mClients.size());
520            for (ClientInfo client : mClients.values()) {
521                pw.append(client.toString());
522                pw.append("------\n");
523            }
524            pw.println();
525        }
526    }
527
528    /* client management */
529    HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>();
530
531    private class ClientInfo {
532        private static final int MAX_LIMIT = 16;
533        private final AsyncChannel mChannel;
534        private final Messenger mMessenger;
535        private final int mUid;
536        private final WorkSource mWorkSource;
537        private boolean mScanWorkReported = false;
538
539        ClientInfo(int uid, AsyncChannel c, Messenger m) {
540            mChannel = c;
541            mMessenger = m;
542            mUid = uid;
543            mWorkSource = new WorkSource(uid, TAG);
544            if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
545        }
546
547        void reportBatchedScanStart() {
548            if (mUid == 0)
549                return;
550
551            int csph = getCsph();
552
553            try {
554                mBatteryStats.noteWifiBatchedScanStartedFromSource(mWorkSource, csph);
555                Log.d(TAG, "started scanning for UID " + mUid + ", csph = " + csph);
556            } catch (RemoteException e) {
557                Log.w(TAG, "failed to report scan work: " + e.toString());
558            }
559        }
560
561        void reportBatchedScanStop() {
562            if (mUid == 0)
563                return;
564
565            try {
566                mBatteryStats.noteWifiBatchedScanStoppedFromSource(mWorkSource);
567                Log.d(TAG, "stopped scanning for UID " + mUid);
568            } catch (RemoteException e) {
569                Log.w(TAG, "failed to cleanup scan work: " + e.toString());
570            }
571        }
572
573        int getCsph() {
574            int csph = 0;
575            for (ScanSettings settings : getScanSettings()) {
576                int num_channels = settings.channels == null ? 0 : settings.channels.length;
577                if (num_channels == 0 && settings.band != 0) {
578                    num_channels = getChannelsForBand(settings.band).length;
579                }
580
581                int scans_per_Hour = settings.periodInMs == 0 ? 1 : (3600 * 1000) / settings.periodInMs;
582                csph += num_channels * scans_per_Hour;
583            }
584
585            return csph;
586        }
587
588        void reportScanWorkUpdate() {
589            if (mScanWorkReported) {
590                reportBatchedScanStop();
591                mScanWorkReported = false;
592            }
593            if (mScanSettings.isEmpty() == false) {
594                reportBatchedScanStart();
595                mScanWorkReported = true;
596            }
597        }
598
599        @Override
600        public String toString() {
601            StringBuffer sb = new StringBuffer();
602            sb.append("mChannel ").append(mChannel).append("\n");
603            sb.append("mMessenger ").append(mMessenger).append("\n\n");
604
605            Iterator<Map.Entry<Integer, ScanSettings>> it = mScanSettings.entrySet().iterator();
606            for (; it.hasNext(); ) {
607                Map.Entry<Integer, ScanSettings> entry = it.next();
608                sb.append("ScanId ").append(entry.getKey()).append("\n");
609
610                ScanSettings scanSettings = entry.getValue();
611                sb.append("  band:").append(scanSettings.band);
612                sb.append("  period:").append(scanSettings.periodInMs);
613                sb.append("  reportEvents:").append(scanSettings.reportEvents);
614                sb.append("  numBssidsPerScan:").append(scanSettings.numBssidsPerScan).append("\n");
615                sb.append("  maxScansToCache:").append(scanSettings.maxScansToCache);
616
617                sb.append("  channels: ");
618
619                if (scanSettings.channels != null) {
620                    for (int i = 0; i < scanSettings.channels.length; i++) {
621                        sb.append(scanSettings.channels[i].frequency);
622                        sb.append(" ");
623                    }
624                }
625
626                sb.append("\n\n");
627            }
628
629            return sb.toString();
630        }
631
632        HashMap<Integer, ScanSettings> mScanSettings = new HashMap<Integer, ScanSettings>(4);
633        HashMap<Integer, Integer> mScanPeriods = new HashMap<Integer, Integer>(4);
634
635        void addScanRequest(ScanSettings settings, int id) {
636            mScanSettings.put(id, settings);
637            reportScanWorkUpdate();
638        }
639
640        void removeScanRequest(int id) {
641            ScanSettings settings = mScanSettings.remove(id);
642            if (settings != null && settings.periodInMs == 0) {
643                /* this was a single shot scan */
644                mChannel.sendMessage(WifiScanner.CMD_SINGLE_SCAN_COMPLETED, 0, id);
645            }
646            reportScanWorkUpdate();
647        }
648
649        Iterator<Map.Entry<Integer, ScanSettings>> getScans() {
650            return mScanSettings.entrySet().iterator();
651        }
652
653        Collection<ScanSettings> getScanSettings() {
654            return mScanSettings.values();
655        }
656
657        void reportScanResults(ScanData[] results) {
658            Iterator<Integer> it = mScanSettings.keySet().iterator();
659            while (it.hasNext()) {
660                int handler = it.next();
661                reportScanResults(results, handler);
662            }
663        }
664
665        void reportScanResults(ScanData[] results, int handler) {
666            ScanSettings settings = mScanSettings.get(handler);
667            ChannelSpec desiredChannels[] = settings.channels;
668            if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED
669                    || desiredChannels == null || desiredChannels.length == 0)  {
670                desiredChannels = getChannelsForBand(settings.band);
671            }
672
673            // check the channels this client asked for ..
674            int num_results = 0;
675            for (ScanData result : results) {
676                boolean copyScanData = false;
677                for (ScanResult scanResult : result.getResults()) {
678                    for (ChannelSpec channelSpec : desiredChannels) {
679                        if (channelSpec.frequency == scanResult.frequency) {
680                            copyScanData = true;
681                            break;
682                        }
683                    }
684                    if (copyScanData) {
685                        num_results++;
686                        break;
687                    }
688                }
689            }
690
691            Log.d(TAG, "results = " + results.length + ", num_results = " + num_results);
692
693            ScanData results2[] = new ScanData[num_results];
694            int index = 0;
695            for (ScanData result : results) {
696                boolean copyScanData = false;
697                for (ScanResult scanResult : result.getResults()) {
698                    for (ChannelSpec channelSpec : desiredChannels) {
699                        if (channelSpec.frequency == scanResult.frequency) {
700                            copyScanData = true;
701                            break;
702                        }
703                    }
704                    if (copyScanData) {
705                        break;
706                    }
707                }
708
709                if (copyScanData) {
710                    if (VDBG) {
711                        Log.d(TAG, "adding at " + index);
712                    }
713                    results2[index] = new WifiScanner.ScanData(result);
714                    index++;
715                }
716            }
717            Log.d(TAG, "delivering results, num = " + results2.length);
718
719            deliverScanResults(handler, results2);
720            if (settings.periodInMs == 0) {
721                /* this is a single shot scan; stop the scan now */
722                mStateMachine.sendMessage(CMD_STOP_SCAN_INTERNAL, 0, handler, this);
723            }
724        }
725
726        void deliverScanResults(int handler, ScanData results[]) {
727            WifiScanner.ParcelableScanData parcelableScanData =
728                    new WifiScanner.ParcelableScanData(results);
729            mChannel.sendMessage(WifiScanner.CMD_SCAN_RESULT, 0, handler, parcelableScanData);
730        }
731
732        void reportFullScanResult(ScanResult result) {
733            Iterator<Integer> it = mScanSettings.keySet().iterator();
734            while (it.hasNext()) {
735                int handler = it.next();
736                ScanSettings settings = mScanSettings.get(handler);
737                ChannelSpec desiredChannels[] = settings.channels;
738                if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED
739                        || desiredChannels == null || desiredChannels.length == 0)  {
740                    desiredChannels = getChannelsForBand(settings.band);
741                }
742                for (ChannelSpec channelSpec : desiredChannels) {
743                    if (channelSpec.frequency == result.frequency) {
744                        WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(result.SSID);
745                        ScanResult newResult = new ScanResult(wifiSsid, result.BSSID, "",
746                                result.level, result.frequency, result.timestamp,
747                                ScanResult.UNSPECIFIED, ScanResult.UNSPECIFIED,result.channelWidth,
748                                result.centerFreq0, result.centerFreq1,
749                                result.is80211McRTTResponder);
750                        if (DBG) Log.d(TAG, "sending it to " + handler);
751                        newResult.informationElements = result.informationElements.clone();
752                        mChannel.sendMessage(
753                                WifiScanner.CMD_FULL_SCAN_RESULT, 0, handler, newResult);
754                    }
755                }
756            }
757        }
758
759        void reportPeriodChanged(int handler, ScanSettings settings, int newPeriodInMs) {
760            Integer prevPeriodObject = mScanPeriods.get(handler);
761            int prevPeriodInMs = settings.periodInMs;
762            if (prevPeriodObject != null) {
763                prevPeriodInMs = prevPeriodObject;
764            }
765
766            if (prevPeriodInMs != newPeriodInMs) {
767                mChannel.sendMessage(WifiScanner.CMD_PERIOD_CHANGED, newPeriodInMs, handler);
768            }
769        }
770
771        HashMap<Integer, WifiScanner.HotlistSettings> mHotlistSettings =
772                new HashMap<Integer, WifiScanner.HotlistSettings>();
773
774        void addHostlistSettings(WifiScanner.HotlistSettings settings, int handler) {
775            mHotlistSettings.put(handler, settings);
776        }
777
778        void removeHostlistSettings(int handler) {
779            mHotlistSettings.remove(handler);
780        }
781
782        Collection<WifiScanner.HotlistSettings> getHotlistSettings() {
783            return mHotlistSettings.values();
784        }
785
786        void reportHotlistResults(int what, ScanResult[] results) {
787            Iterator<Map.Entry<Integer, WifiScanner.HotlistSettings>> it =
788                    mHotlistSettings.entrySet().iterator();
789            while (it.hasNext()) {
790                Map.Entry<Integer, WifiScanner.HotlistSettings> entry = it.next();
791                int handler = entry.getKey();
792                WifiScanner.HotlistSettings settings = entry.getValue();
793                int num_results = 0;
794                for (ScanResult result : results) {
795                    for (BssidInfo BssidInfo : settings.bssidInfos) {
796                        if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) {
797                            num_results++;
798                            break;
799                        }
800                    }
801                }
802
803                if (num_results == 0) {
804                    // nothing to report
805                    return;
806                }
807
808                ScanResult results2[] = new ScanResult[num_results];
809                int index = 0;
810                for (ScanResult result : results) {
811                    for (BssidInfo BssidInfo : settings.bssidInfos) {
812                        if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) {
813                            results2[index] = result;
814                            index++;
815                        }
816                    }
817                }
818
819                WifiScanner.ParcelableScanResults parcelableScanResults =
820                        new WifiScanner.ParcelableScanResults(results2);
821
822                mChannel.sendMessage(what, 0, handler, parcelableScanResults);
823            }
824        }
825
826        HashSet<Integer> mSignificantWifiHandlers = new HashSet<Integer>();
827        void addSignificantWifiChange(int handler) {
828            mSignificantWifiHandlers.add(handler);
829        }
830
831        void removeSignificantWifiChange(int handler) {
832            mSignificantWifiHandlers.remove(handler);
833        }
834
835        Collection<Integer> getWifiChangeHandlers() {
836            return mSignificantWifiHandlers;
837        }
838
839        void reportWifiChanged(ScanResult[] results) {
840            WifiScanner.ParcelableScanResults parcelableScanResults =
841                    new WifiScanner.ParcelableScanResults(results);
842            Iterator<Integer> it = mSignificantWifiHandlers.iterator();
843            while (it.hasNext()) {
844                int handler = it.next();
845                mChannel.sendMessage(WifiScanner.CMD_WIFI_CHANGE_DETECTED,
846                        0, handler, parcelableScanResults);
847            }
848        }
849
850        void reportWifiStabilized(ScanResult[] results) {
851            WifiScanner.ParcelableScanResults parcelableScanResults =
852                    new WifiScanner.ParcelableScanResults(results);
853            Iterator<Integer> it = mSignificantWifiHandlers.iterator();
854            while (it.hasNext()) {
855                int handler = it.next();
856                mChannel.sendMessage(WifiScanner.CMD_WIFI_CHANGES_STABILIZED,
857                        0, handler, parcelableScanResults);
858            }
859        }
860
861        void cleanup() {
862            mScanSettings.clear();
863            resetBuckets();
864
865            mHotlistSettings.clear();
866            resetHotlist();
867
868            for (Integer handler :  mSignificantWifiHandlers) {
869                untrackWifiChanges(this, handler);
870            }
871
872            mSignificantWifiHandlers.clear();
873            Log.d(TAG, "Successfully stopped all requests for client " + this);
874        }
875    }
876
877    void replySucceeded(Message msg) {
878        if (msg.replyTo != null) {
879            Message reply = Message.obtain();
880            reply.what = WifiScanner.CMD_OP_SUCCEEDED;
881            reply.arg2 = msg.arg2;
882            try {
883                msg.replyTo.send(reply);
884            } catch (RemoteException e) {
885                // There's not much we can do if reply can't be sent!
886            }
887        } else {
888            // locally generated message; doesn't need a reply!
889        }
890    }
891
892    void replyFailed(Message msg, int reason, String description) {
893        if (msg.replyTo != null) {
894            Message reply = Message.obtain();
895            reply.what = WifiScanner.CMD_OP_FAILED;
896            reply.arg2 = msg.arg2;
897            reply.obj = new WifiScanner.OperationResult(reason, description);
898            try {
899                msg.replyTo.send(reply);
900            } catch (RemoteException e) {
901                // There's not much we can do if reply can't be sent!
902            }
903        } else {
904            // locally generated message; doesn't need a reply!
905        }
906    }
907
908    private class SettingsComputer {
909
910        private class TimeBucket {
911            int periodInSecond;
912            int periodMinInSecond;
913            int periodMaxInSecond;
914
915            TimeBucket(int p, int min, int max) {
916                periodInSecond = p;
917                periodMinInSecond = min;
918                periodMaxInSecond = max;
919            }
920        }
921
922        private final TimeBucket[] mTimeBuckets = new TimeBucket[] {
923                new TimeBucket( 1, 0, 5 ),
924                new TimeBucket( 5, 5, 10 ),
925                new TimeBucket( 10, 10, 25 ),
926                new TimeBucket( 30, 25, 55 ),
927                new TimeBucket( 60, 55, 100),
928                new TimeBucket( 300, 240, 500),
929                new TimeBucket( 600, 500, 1500),
930                new TimeBucket( 1800, 1500, WifiScanner.MAX_SCAN_PERIOD_MS) };
931
932        private static final int MAX_BUCKETS = 8;
933        private static final int MAX_CHANNELS = 8;
934        private static final int DEFAULT_MAX_AP_PER_SCAN = 10;
935        private static final int DEFAULT_REPORT_THRESHOLD_PERCENT = 100;
936        private static final int DEFAULT_BASE_PERIOD_MS = 5000;
937        private static final int DEFAULT_REPORT_THRESHOLD_NUM_SCANS = 10;
938
939        private WifiNative.ScanSettings mSettings;
940        {
941            mSettings = new WifiNative.ScanSettings();
942            mSettings.max_ap_per_scan = DEFAULT_MAX_AP_PER_SCAN;
943            mSettings.base_period_ms = DEFAULT_BASE_PERIOD_MS;
944            mSettings.report_threshold_percent = DEFAULT_REPORT_THRESHOLD_PERCENT;
945            mSettings.report_threshold_num_scans = DEFAULT_REPORT_THRESHOLD_NUM_SCANS;
946
947            mSettings.buckets = new WifiNative.BucketSettings[MAX_BUCKETS];
948            for (int i = 0; i < mSettings.buckets.length; i++) {
949                WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings();
950                bucketSettings.bucket = i;
951                bucketSettings.report_events = 0;
952                bucketSettings.channels = new WifiNative.ChannelSettings[MAX_CHANNELS];
953                bucketSettings.num_channels = 0;
954                for (int j = 0; j < bucketSettings.channels.length; j++) {
955                    WifiNative.ChannelSettings channelSettings = new WifiNative.ChannelSettings();
956                    bucketSettings.channels[j] = channelSettings;
957                }
958                mSettings.buckets[i] = bucketSettings;
959            }
960        }
961
962        HashMap<Integer, Integer> mChannelToBucketMap = new HashMap<Integer, Integer>();
963
964        private int getBestBucket(ScanSettings settings) {
965
966            // check to see if any of the channels are being scanned already
967            // and find the smallest bucket index (it represents the quickest
968            // period of scan)
969
970            ChannelSpec channels[] = settings.channels;
971            if (channels == null) {
972                // set channels based on band
973                channels = getChannelsForBand(settings.band);
974            }
975
976            if (channels == null) {
977                // still no channels; then there's nothing to scan
978                Log.e(TAG, "No channels to scan!!");
979                return -1;
980            }
981
982            int mostFrequentBucketIndex = mTimeBuckets.length;
983
984            for (ChannelSpec desiredChannelSpec : channels) {
985                if (mChannelToBucketMap.containsKey(desiredChannelSpec.frequency)) {
986                    int bucket = mChannelToBucketMap.get(desiredChannelSpec.frequency);
987                    if (bucket < mostFrequentBucketIndex) {
988                        mostFrequentBucketIndex = bucket;
989                    }
990                }
991            }
992
993            int bestBucketIndex = -1;                                   // best by period
994            for (int i = 0; i < mTimeBuckets.length; i++) {
995                TimeBucket bucket = mTimeBuckets[i];
996                if (bucket.periodMinInSecond * 1000 <= settings.periodInMs
997                        && settings.periodInMs < bucket.periodMaxInSecond * 1000) {
998                    // we set the time period to this
999                    bestBucketIndex = i;
1000                    break;
1001                }
1002            }
1003
1004            if (mostFrequentBucketIndex < bestBucketIndex) {
1005                for (ChannelSpec desiredChannelSpec : channels) {
1006                    mChannelToBucketMap.put(desiredChannelSpec.frequency, mostFrequentBucketIndex);
1007                }
1008                Log.d(TAG, "returning mf bucket number " + mostFrequentBucketIndex);
1009                return mostFrequentBucketIndex;
1010            } else if (bestBucketIndex != -1) {
1011                for (ChannelSpec desiredChannelSpec : channels) {
1012                    mChannelToBucketMap.put(desiredChannelSpec.frequency, bestBucketIndex);
1013                }
1014                Log.d(TAG, "returning best bucket number " + bestBucketIndex);
1015                return bestBucketIndex;
1016            }
1017
1018            Log.e(TAG, "Could not find suitable bucket for period " + settings.periodInMs);
1019            return -1;
1020        }
1021
1022        void prepChannelMap(ScanSettings settings) {
1023            getBestBucket(settings);
1024        }
1025
1026        int addScanRequestToBucket(ScanSettings settings) {
1027
1028            int bucketIndex = getBestBucket(settings);
1029            if (bucketIndex == -1) {
1030                Log.e(TAG, "Ignoring invalid settings");
1031                return -1;
1032            }
1033
1034            ChannelSpec desiredChannels[] = settings.channels;
1035            if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED
1036                    || desiredChannels == null
1037                    || desiredChannels.length == 0) {
1038                // set channels based on band
1039                desiredChannels = getChannelsForBand(settings.band);
1040                if (desiredChannels == null) {
1041                    // still no channels; then there's nothing to scan
1042                    Log.e(TAG, "No channels to scan!!");
1043                    return -1;
1044                }
1045            }
1046
1047            // merge the channel lists for these buckets
1048            Log.d(TAG, "merging " + desiredChannels.length + " channels "
1049                    + " for period " + settings.periodInMs
1050                    + " maxScans " + settings.maxScansToCache);
1051
1052            WifiNative.BucketSettings bucket = mSettings.buckets[bucketIndex];
1053            boolean added = (bucket.num_channels == 0)
1054                    && (bucket.band == WifiScanner.WIFI_BAND_UNSPECIFIED);
1055            Log.d(TAG, "existing " + bucket.num_channels + " channels ");
1056
1057            HashSet<ChannelSpec> newChannels = new HashSet<ChannelSpec>();
1058            for (ChannelSpec desiredChannelSpec : desiredChannels) {
1059
1060                if (VDBG) Log.d(TAG, "desired channel " + desiredChannelSpec.frequency);
1061
1062                boolean found = false;
1063                for (WifiNative.ChannelSettings existingChannelSpec : bucket.channels) {
1064                    if (desiredChannelSpec.frequency == existingChannelSpec.frequency) {
1065                        found = true;
1066                        break;
1067                    }
1068                }
1069
1070                if (!found) {
1071                    newChannels.add(desiredChannelSpec);
1072                } else {
1073                    if (DBG) Log.d(TAG, "Already scanning channel " + desiredChannelSpec.frequency);
1074                }
1075            }
1076
1077            if (settings.band != WifiScanner.WIFI_BAND_UNSPECIFIED
1078                    || (bucket.num_channels + newChannels.size()) > bucket.channels.length) {
1079                // can't accommodate all channels; switch to specifying band
1080                bucket.num_channels = 0;
1081                bucket.band = getBandFromChannels(bucket.channels)
1082                        | getBandFromChannels(desiredChannels);
1083                bucket.channels = new WifiNative.ChannelSettings[0];
1084                Log.d(TAG, "switching to using band " + bucket.band);
1085            } else {
1086                for (ChannelSpec desiredChannelSpec : newChannels) {
1087
1088                    if (VDBG) Log.d(TAG, "adding new channel spec " + desiredChannelSpec.frequency);
1089
1090                    WifiNative.ChannelSettings channelSettings = bucket.channels[bucket.num_channels];
1091                    channelSettings.frequency = desiredChannelSpec.frequency;
1092                    bucket.num_channels++;
1093                    mChannelToBucketMap.put(bucketIndex, channelSettings.frequency);
1094                }
1095            }
1096
1097            if (bucket.report_events < settings.reportEvents) {
1098                if (DBG) Log.d(TAG, "setting report_events to " + settings.reportEvents);
1099                bucket.report_events = settings.reportEvents;
1100            } else {
1101                if (DBG) Log.d(TAG, "report_events is " + settings.reportEvents);
1102            }
1103
1104            if (added) {
1105                bucket.period_ms = mTimeBuckets[bucketIndex].periodInSecond * 1000;
1106                mSettings.num_buckets++;
1107            }
1108
1109            if (mSettings.max_ap_per_scan < settings.numBssidsPerScan) {
1110                mSettings.max_ap_per_scan = settings.numBssidsPerScan;
1111            }
1112
1113            if (settings.maxScansToCache != 0) {
1114                if (mSettings.report_threshold_num_scans > settings.maxScansToCache) {
1115                    mSettings.report_threshold_num_scans = settings.maxScansToCache;
1116                }
1117            }
1118
1119            return bucket.period_ms;
1120        }
1121
1122        public WifiNative.ScanSettings getComputedSettings() {
1123            return mSettings;
1124        }
1125
1126        public void compressBuckets() {
1127            int num_buckets = 0;
1128            for (int i = 0; i < mSettings.buckets.length; i++) {
1129                if (mSettings.buckets[i].num_channels != 0
1130                        || mSettings.buckets[i].band != WifiScanner.WIFI_BAND_UNSPECIFIED) {
1131                    mSettings.buckets[num_buckets] = mSettings.buckets[i];
1132                    num_buckets++;
1133                }
1134            }
1135            // remove unused buckets
1136            for (int i = num_buckets; i < mSettings.buckets.length; i++) {
1137                mSettings.buckets[i] = null;
1138            }
1139
1140            mSettings.num_buckets = num_buckets;
1141            if (num_buckets != 0) {
1142                mSettings.base_period_ms = mSettings.buckets[0].period_ms;
1143            }
1144        }
1145    }
1146
1147    boolean resetBuckets() {
1148        if (DBG) Log.d(TAG, "resetBuckets num clients: " + mClients.size());
1149        SettingsComputer c = new SettingsComputer();
1150        Collection<ClientInfo> clients = mClients.values();
1151        for (ClientInfo ci : clients) {
1152            Collection<ScanSettings> settings = ci.getScanSettings();
1153            for (ScanSettings s : settings) {
1154                c.prepChannelMap(s);
1155            }
1156        }
1157
1158        for (ClientInfo ci : clients) {
1159            Iterator it = ci.getScans();
1160            while (it.hasNext()) {
1161                Map.Entry<Integer, ScanSettings> entry =
1162                        (Map.Entry<Integer,ScanSettings>)it.next();
1163                int id = entry.getKey();
1164                ScanSettings s = entry.getValue();
1165                int newPeriodInMs = c.addScanRequestToBucket(s);
1166                if (newPeriodInMs  == -1) {
1167                    if (DBG) Log.d(TAG, "could not find a good bucket");
1168                    return false;
1169                }
1170                if (newPeriodInMs != s.periodInMs) {
1171                    ci.reportPeriodChanged(id, s, newPeriodInMs);
1172                }
1173            }
1174        }
1175
1176        c.compressBuckets();
1177
1178        WifiNative.ScanSettings s = c.getComputedSettings();
1179        if (s.num_buckets == 0) {
1180            if (DBG) Log.d(TAG, "Stopping scan because there are no buckets");
1181            WifiNative.stopScan();
1182            return true;
1183        } else {
1184            if (WifiNative.startScan(s, mStateMachine)) {
1185                if (DBG) Log.d(TAG, "Successfully started scan of " + s.num_buckets + " buckets at"
1186                        + "time = " + SystemClock.elapsedRealtimeNanos() / 1000 + " period "
1187                        + s.base_period_ms);
1188                return true;
1189            } else {
1190                if (DBG) Log.e(TAG, "Failed to start scan of " + s.num_buckets + " buckets");
1191                return false;
1192            }
1193        }
1194    }
1195
1196    boolean addScanRequest(ClientInfo ci, int handler, ScanSettings settings) {
1197        // sanity check the input
1198        if (settings.periodInMs < WifiScanner.MIN_SCAN_PERIOD_MS) {
1199            Log.d(TAG, "Failing scan request because periodInMs is " + settings.periodInMs);
1200            return false;
1201        }
1202
1203        int minSupportedPeriodMs = 0;
1204        if (settings.channels != null) {
1205            minSupportedPeriodMs = settings.channels.length * MIN_PERIOD_PER_CHANNEL_MS;
1206        } else {
1207            if ((settings.band & WifiScanner.WIFI_BAND_24_GHZ) == 0) {
1208                /* 2.4 GHz band has 11 to 13 channels */
1209                minSupportedPeriodMs += 1000;
1210            }
1211            if ((settings.band & WifiScanner.WIFI_BAND_5_GHZ) == 0) {
1212                /* 5 GHz band has another 10 channels */
1213                minSupportedPeriodMs += 1000;
1214            }
1215            if ((settings.band & WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY) == 0) {
1216                /* DFS requires passive scan which takes longer time */
1217                minSupportedPeriodMs += 2000;
1218            }
1219        }
1220
1221        if (settings.periodInMs < minSupportedPeriodMs) {
1222            Log.d(TAG, "Failing scan request because minSupportedPeriodMs is "
1223                    + minSupportedPeriodMs + " but the request wants " + settings.periodInMs);
1224            return false;
1225        }
1226
1227        ci.addScanRequest(settings, handler);
1228        if (resetBuckets()) {
1229            return true;
1230        } else {
1231            ci.removeScanRequest(handler);
1232            Log.d(TAG, "Failing scan request because failed to reset scan");
1233            return false;
1234        }
1235    }
1236
1237    boolean addSingleScanRequest(ClientInfo ci, int handler, ScanSettings settings) {
1238        if (settings.reportEvents == 0) {
1239            settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
1240        }
1241        if (settings.periodInMs == 0) {
1242            settings.periodInMs = 10000;        // 10s - although second scan should never happen
1243        }
1244        ci.addScanRequest(settings, handler);
1245        if (resetBuckets()) {
1246            /* reset periodInMs to 0 to indicate single shot scan */
1247            settings.periodInMs = 0;
1248            return true;
1249        } else {
1250            ci.removeScanRequest(handler);
1251            Log.d(TAG, "Failing scan request because failed to reset scan");
1252            return false;
1253        }
1254    }
1255
1256    void removeScanRequest(ClientInfo ci, int handler) {
1257        ci.removeScanRequest(handler);
1258        resetBuckets();
1259    }
1260
1261    boolean reportScanResults() {
1262        ScanData results[] = WifiNative.getScanResults(/* flush = */ true);
1263        Collection<ClientInfo> clients = mClients.values();
1264        for (ClientInfo ci2 : clients) {
1265            ci2.reportScanResults(results);
1266        }
1267
1268        return true;
1269    }
1270
1271    void resetHotlist() {
1272        Collection<ClientInfo> clients = mClients.values();
1273        int num_hotlist_ap = 0;
1274
1275        for (ClientInfo ci : clients) {
1276            Collection<WifiScanner.HotlistSettings> c = ci.getHotlistSettings();
1277            for (WifiScanner.HotlistSettings s : c) {
1278                num_hotlist_ap +=  s.bssidInfos.length;
1279            }
1280        }
1281
1282        if (num_hotlist_ap == 0) {
1283            WifiNative.resetHotlist();
1284        } else {
1285            BssidInfo bssidInfos[] = new BssidInfo[num_hotlist_ap];
1286            int index = 0;
1287            for (ClientInfo ci : clients) {
1288                Collection<WifiScanner.HotlistSettings> settings = ci.getHotlistSettings();
1289                for (WifiScanner.HotlistSettings s : settings) {
1290                    for (int i = 0; i < s.bssidInfos.length; i++, index++) {
1291                        bssidInfos[index] = s.bssidInfos[i];
1292                    }
1293                }
1294            }
1295
1296            WifiScanner.HotlistSettings settings = new WifiScanner.HotlistSettings();
1297            settings.bssidInfos = bssidInfos;
1298            settings.apLostThreshold = 3;
1299            WifiNative.setHotlist(settings, mStateMachine);
1300        }
1301    }
1302
1303    void setHotlist(ClientInfo ci, int handler, WifiScanner.HotlistSettings settings) {
1304        ci.addHostlistSettings(settings, handler);
1305        resetHotlist();
1306    }
1307
1308    void resetHotlist(ClientInfo ci, int handler) {
1309        ci.removeHostlistSettings(handler);
1310        resetHotlist();
1311    }
1312
1313    WifiChangeStateMachine mWifiChangeStateMachine;
1314
1315    void trackWifiChanges(ClientInfo ci, int handler) {
1316        mWifiChangeStateMachine.enable();
1317        ci.addSignificantWifiChange(handler);
1318    }
1319
1320    void untrackWifiChanges(ClientInfo ci, int handler) {
1321        ci.removeSignificantWifiChange(handler);
1322        Collection<ClientInfo> clients = mClients.values();
1323        for (ClientInfo ci2 : clients) {
1324            if (ci2.getWifiChangeHandlers().size() != 0) {
1325                // there is at least one client watching for
1326                // significant changes; so nothing more to do
1327                return;
1328            }
1329        }
1330
1331        // no more clients looking for significant wifi changes
1332        // no need to keep the state machine running; disable it
1333        mWifiChangeStateMachine.disable();
1334    }
1335
1336    void configureWifiChange(WifiScanner.WifiChangeSettings settings) {
1337        mWifiChangeStateMachine.configure(settings);
1338    }
1339
1340    void reportWifiChanged(ScanResult results[]) {
1341        Collection<ClientInfo> clients = mClients.values();
1342        for (ClientInfo ci : clients) {
1343            ci.reportWifiChanged(results);
1344        }
1345    }
1346
1347    void reportWifiStabilized(ScanResult results[]) {
1348        Collection<ClientInfo> clients = mClients.values();
1349        for (ClientInfo ci : clients) {
1350            ci.reportWifiStabilized(results);
1351        }
1352    }
1353
1354    class WifiChangeStateMachine extends StateMachine
1355            implements WifiNative.SignificantWifiChangeEventHandler {
1356
1357        private static final String TAG = "WifiChangeStateMachine";
1358
1359        private static final int WIFI_CHANGE_CMD_NEW_SCAN_RESULTS           = 0;
1360        private static final int WIFI_CHANGE_CMD_CHANGE_DETECTED            = 1;
1361        private static final int WIFI_CHANGE_CMD_CHANGE_TIMEOUT             = 2;
1362        private static final int WIFI_CHANGE_CMD_ENABLE                     = 3;
1363        private static final int WIFI_CHANGE_CMD_DISABLE                    = 4;
1364        private static final int WIFI_CHANGE_CMD_CONFIGURE                  = 5;
1365
1366        private static final int MAX_APS_TO_TRACK = 3;
1367        private static final int MOVING_SCAN_PERIOD_MS      = 10000;
1368        private static final int STATIONARY_SCAN_PERIOD_MS  =  5000;
1369        private static final int MOVING_STATE_TIMEOUT_MS    = 30000;
1370
1371        State mDefaultState = new DefaultState();
1372        State mStationaryState = new StationaryState();
1373        State mMovingState = new MovingState();
1374
1375        private static final String ACTION_TIMEOUT =
1376                "com.android.server.WifiScanningServiceImpl.action.TIMEOUT";
1377        AlarmManager  mAlarmManager;
1378        PendingIntent mTimeoutIntent;
1379        ScanResult    mCurrentBssids[];
1380
1381        WifiChangeStateMachine(Looper looper) {
1382            super("SignificantChangeStateMachine", looper);
1383
1384            mClients.put(null, mClientInfo);
1385
1386            addState(mDefaultState);
1387            addState(mStationaryState, mDefaultState);
1388            addState(mMovingState, mDefaultState);
1389
1390            setInitialState(mDefaultState);
1391        }
1392
1393        public void enable() {
1394            if (mAlarmManager == null) {
1395                mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
1396            }
1397
1398            if (mTimeoutIntent == null) {
1399                Intent intent = new Intent(ACTION_TIMEOUT, null);
1400                mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
1401
1402                mContext.registerReceiver(
1403                        new BroadcastReceiver() {
1404                            @Override
1405                            public void onReceive(Context context, Intent intent) {
1406                                sendMessage(WIFI_CHANGE_CMD_CHANGE_TIMEOUT);
1407                            }
1408                        }, new IntentFilter(ACTION_TIMEOUT));
1409            }
1410
1411            sendMessage(WIFI_CHANGE_CMD_ENABLE);
1412        }
1413
1414        public void disable() {
1415            sendMessage(WIFI_CHANGE_CMD_DISABLE);
1416        }
1417
1418        public void configure(WifiScanner.WifiChangeSettings settings) {
1419            sendMessage(WIFI_CHANGE_CMD_CONFIGURE, settings);
1420        }
1421
1422        class DefaultState extends State {
1423            @Override
1424            public void enter() {
1425                if (DBG) Log.d(TAG, "Entering IdleState");
1426            }
1427
1428            @Override
1429            public boolean processMessage(Message msg) {
1430                if (DBG) Log.d(TAG, "DefaultState state got " + msg);
1431                switch (msg.what) {
1432                    case WIFI_CHANGE_CMD_ENABLE :
1433                        transitionTo(mMovingState);
1434                        break;
1435                    case WIFI_CHANGE_CMD_DISABLE:
1436                        // nothing to do
1437                        break;
1438                    case WIFI_CHANGE_CMD_NEW_SCAN_RESULTS:
1439                        // nothing to do
1440                        break;
1441                    case WIFI_CHANGE_CMD_CONFIGURE:
1442                        /* save configuration till we transition to moving state */
1443                        deferMessage(msg);
1444                        break;
1445                    default:
1446                        return NOT_HANDLED;
1447                }
1448                return HANDLED;
1449            }
1450        }
1451
1452        class StationaryState extends State {
1453            @Override
1454            public void enter() {
1455                if (DBG) Log.d(TAG, "Entering StationaryState");
1456                reportWifiStabilized(mCurrentBssids);
1457            }
1458
1459            @Override
1460            public boolean processMessage(Message msg) {
1461                if (DBG) Log.d(TAG, "Stationary state got " + msg);
1462                switch (msg.what) {
1463                    case WIFI_CHANGE_CMD_ENABLE :
1464                        // do nothing
1465                        break;
1466                    case WIFI_CHANGE_CMD_CHANGE_DETECTED:
1467                        if (DBG) Log.d(TAG, "Got wifi change detected");
1468                        reportWifiChanged((ScanResult[])msg.obj);
1469                        transitionTo(mMovingState);
1470                        break;
1471                    case WIFI_CHANGE_CMD_DISABLE:
1472                        if (DBG) Log.d(TAG, "Got Disable Wifi Change");
1473                        mCurrentBssids = null;
1474                        removeScanRequest();
1475                        untrackSignificantWifiChange();
1476                        transitionTo(mDefaultState);
1477                        break;
1478                    case WIFI_CHANGE_CMD_CONFIGURE:
1479                        /* save configuration till we transition to moving state */
1480                        deferMessage(msg);
1481                        break;
1482                    default:
1483                        return NOT_HANDLED;
1484                }
1485                return HANDLED;
1486            }
1487        }
1488
1489        class MovingState extends State {
1490            boolean mWifiChangeDetected = false;
1491            boolean mScanResultsPending = false;
1492
1493            @Override
1494            public void enter() {
1495                if (DBG) Log.d(TAG, "Entering MovingState");
1496                issueFullScan();
1497            }
1498
1499            @Override
1500            public boolean processMessage(Message msg) {
1501                if (DBG) Log.d(TAG, "MovingState state got " + msg);
1502                switch (msg.what) {
1503                    case WIFI_CHANGE_CMD_ENABLE :
1504                        // do nothing
1505                        break;
1506                    case WIFI_CHANGE_CMD_DISABLE:
1507                        if (DBG) Log.d(TAG, "Got Disable Wifi Change");
1508                        mCurrentBssids = null;
1509                        removeScanRequest();
1510                        untrackSignificantWifiChange();
1511                        transitionTo(mDefaultState);
1512                        break;
1513                    case WIFI_CHANGE_CMD_NEW_SCAN_RESULTS:
1514                        if (DBG) Log.d(TAG, "Got scan results");
1515                        if (mScanResultsPending) {
1516                            if (DBG) Log.d(TAG, "reconfiguring scan");
1517                            reconfigureScan((ScanData[])msg.obj,
1518                                    STATIONARY_SCAN_PERIOD_MS);
1519                            mWifiChangeDetected = false;
1520                            mAlarmManager.setExact(AlarmManager.RTC_WAKEUP,
1521                                    System.currentTimeMillis() + MOVING_STATE_TIMEOUT_MS,
1522                                    mTimeoutIntent);
1523                            mScanResultsPending = false;
1524                        }
1525                        break;
1526                    case WIFI_CHANGE_CMD_CONFIGURE:
1527                        if (DBG) Log.d(TAG, "Got configuration from app");
1528                        WifiScanner.WifiChangeSettings settings =
1529                                (WifiScanner.WifiChangeSettings) msg.obj;
1530                        reconfigureScan(settings);
1531                        mWifiChangeDetected = false;
1532                        long unchangedDelay = settings.unchangedSampleSize * settings.periodInMs;
1533                        mAlarmManager.cancel(mTimeoutIntent);
1534                        mAlarmManager.setExact(AlarmManager.RTC_WAKEUP,
1535                                System.currentTimeMillis() + unchangedDelay,
1536                                mTimeoutIntent);
1537                        break;
1538                    case WIFI_CHANGE_CMD_CHANGE_DETECTED:
1539                        if (DBG) Log.d(TAG, "Change detected");
1540                        mAlarmManager.cancel(mTimeoutIntent);
1541                        reportWifiChanged((ScanResult[])msg.obj);
1542                        mWifiChangeDetected = true;
1543                        issueFullScan();
1544                        break;
1545                    case WIFI_CHANGE_CMD_CHANGE_TIMEOUT:
1546                        if (DBG) Log.d(TAG, "Got timeout event");
1547                        if (mWifiChangeDetected == false) {
1548                            transitionTo(mStationaryState);
1549                        }
1550                        break;
1551                    default:
1552                        return NOT_HANDLED;
1553                }
1554                return HANDLED;
1555            }
1556
1557            @Override
1558            public void exit() {
1559                mAlarmManager.cancel(mTimeoutIntent);
1560            }
1561
1562            void issueFullScan() {
1563                if (DBG) Log.d(TAG, "Issuing full scan");
1564                ScanSettings settings = new ScanSettings();
1565                settings.band = WifiScanner.WIFI_BAND_BOTH;
1566                settings.periodInMs = MOVING_SCAN_PERIOD_MS;
1567                settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
1568                addScanRequest(settings);
1569                mScanResultsPending = true;
1570            }
1571
1572        }
1573
1574        void reconfigureScan(ScanData[] results, int period) {
1575            // find brightest APs and set them as sentinels
1576            if (results.length < MAX_APS_TO_TRACK) {
1577                Log.d(TAG, "too few APs (" + results.length + ") available to track wifi change");
1578                return;
1579            }
1580
1581            removeScanRequest();
1582
1583            // remove duplicate BSSIDs
1584            HashMap<String, ScanResult> bssidToScanResult = new HashMap<String, ScanResult>();
1585            for (ScanResult result : results[0].getResults()) {
1586                ScanResult saved = bssidToScanResult.get(result.BSSID);
1587                if (saved == null) {
1588                    bssidToScanResult.put(result.BSSID, result);
1589                } else if (saved.level > result.level) {
1590                    bssidToScanResult.put(result.BSSID, result);
1591                }
1592            }
1593
1594            // find brightest BSSIDs
1595            ScanResult brightest[] = new ScanResult[MAX_APS_TO_TRACK];
1596            Collection<ScanResult> results2 = bssidToScanResult.values();
1597            for (ScanResult result : results2) {
1598                for (int j = 0; j < brightest.length; j++) {
1599                    if (brightest[j] == null
1600                            || (brightest[j].level < result.level)) {
1601                        for (int k = brightest.length; k > (j + 1); k--) {
1602                            brightest[k - 1] = brightest[k - 2];
1603                        }
1604                        brightest[j] = result;
1605                        break;
1606                    }
1607                }
1608            }
1609
1610            // Get channels to scan for
1611            ArrayList<Integer> channels = new ArrayList<Integer>();
1612            for (int i = 0; i < brightest.length; i++) {
1613                boolean found = false;
1614                for (int j = i + 1; j < brightest.length; j++) {
1615                    if (brightest[j].frequency == brightest[i].frequency) {
1616                        found = true;
1617                    }
1618                }
1619                if (!found) {
1620                    channels.add(brightest[i].frequency);
1621                }
1622            }
1623
1624            if (DBG) Log.d(TAG, "Found " + channels.size() + " channels");
1625
1626            // set scanning schedule
1627            ScanSettings settings = new ScanSettings();
1628            settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
1629            settings.channels = new ChannelSpec[channels.size()];
1630            for (int i = 0; i < channels.size(); i++) {
1631                settings.channels[i] = new ChannelSpec(channels.get(i));
1632            }
1633
1634            settings.periodInMs = period;
1635            addScanRequest(settings);
1636
1637            WifiScanner.WifiChangeSettings settings2 = new WifiScanner.WifiChangeSettings();
1638            settings2.rssiSampleSize = 3;
1639            settings2.lostApSampleSize = 3;
1640            settings2.unchangedSampleSize = 3;
1641            settings2.minApsBreachingThreshold = 2;
1642            settings2.bssidInfos = new BssidInfo[brightest.length];
1643
1644            for (int i = 0; i < brightest.length; i++) {
1645                BssidInfo BssidInfo = new BssidInfo();
1646                BssidInfo.bssid = brightest[i].BSSID;
1647                int threshold = (100 + brightest[i].level) / 32 + 2;
1648                BssidInfo.low = brightest[i].level - threshold;
1649                BssidInfo.high = brightest[i].level + threshold;
1650                settings2.bssidInfos[i] = BssidInfo;
1651
1652                if (DBG) Log.d(TAG, "Setting bssid=" + BssidInfo.bssid + ", " +
1653                        "low=" + BssidInfo.low + ", high=" + BssidInfo.high);
1654            }
1655
1656            trackSignificantWifiChange(settings2);
1657            mCurrentBssids = brightest;
1658        }
1659
1660        void reconfigureScan(WifiScanner.WifiChangeSettings settings) {
1661
1662            if (settings.bssidInfos.length < MAX_APS_TO_TRACK) {
1663                Log.d(TAG, "too few APs (" + settings.bssidInfos.length
1664                        + ") available to track wifi change");
1665                return;
1666            }
1667
1668            if (DBG) Log.d(TAG, "Setting configuration specified by app");
1669
1670            mCurrentBssids = new ScanResult[settings.bssidInfos.length];
1671            HashSet<Integer> channels = new HashSet<Integer>();
1672
1673            for (int i = 0; i < settings.bssidInfos.length; i++) {
1674                ScanResult result = new ScanResult();
1675                result.BSSID = settings.bssidInfos[i].bssid;
1676                mCurrentBssids[i] = result;
1677                channels.add(settings.bssidInfos[i].frequencyHint);
1678            }
1679
1680            // cancel previous scan
1681            removeScanRequest();
1682
1683            // set new scanning schedule
1684            ScanSettings settings2 = new ScanSettings();
1685            settings2.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
1686            settings2.channels = new ChannelSpec[channels.size()];
1687            int i = 0;
1688            for (Integer channel : channels) {
1689                settings2.channels[i++] = new ChannelSpec(channel);
1690            }
1691
1692            settings2.periodInMs = settings.periodInMs;
1693            addScanRequest(settings2);
1694
1695            // start tracking new APs
1696            trackSignificantWifiChange(settings);
1697        }
1698
1699        class ClientInfoLocal extends ClientInfo {
1700            ClientInfoLocal() {
1701                super(0, null, null);
1702            }
1703            @Override
1704            void deliverScanResults(int handler, ScanData results[]) {
1705                if (DBG) Log.d(TAG, "Delivering messages directly");
1706                sendMessage(WIFI_CHANGE_CMD_NEW_SCAN_RESULTS, 0, 0, results);
1707            }
1708            @Override
1709            void reportPeriodChanged(int handler, ScanSettings settings, int newPeriodInMs) {
1710                // nothing to do; no one is listening for this
1711            }
1712        }
1713
1714        @Override
1715        public void onChangesFound(ScanResult results[]) {
1716            sendMessage(WIFI_CHANGE_CMD_CHANGE_DETECTED, 0, 0, results);
1717        }
1718
1719        ClientInfo mClientInfo = new ClientInfoLocal();
1720        private static final int SCAN_COMMAND_ID = 1;
1721
1722        void addScanRequest(ScanSettings settings) {
1723            if (DBG) Log.d(TAG, "Starting scans");
1724            Message msg = Message.obtain();
1725            msg.what = WifiScanner.CMD_START_BACKGROUND_SCAN;
1726            msg.arg2 = SCAN_COMMAND_ID;
1727            msg.obj = settings;
1728            mClientHandler.sendMessage(msg);
1729        }
1730
1731        void removeScanRequest() {
1732            if (DBG) Log.d(TAG, "Stopping scans");
1733            Message msg = Message.obtain();
1734            msg.what = WifiScanner.CMD_STOP_BACKGROUND_SCAN;
1735            msg.arg2 = SCAN_COMMAND_ID;
1736            mClientHandler.sendMessage(msg);
1737        }
1738
1739        void trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings) {
1740            WifiNative.untrackSignificantWifiChange();
1741            WifiNative.trackSignificantWifiChange(settings, this);
1742        }
1743
1744        void untrackSignificantWifiChange() {
1745            WifiNative.untrackSignificantWifiChange();
1746        }
1747
1748    }
1749
1750    private static ChannelSpec mChannels[][];
1751
1752    private static void copyChannels(
1753            ChannelSpec channelSpec[], int offset, int channels[]) {
1754        for (int i = 0; i < channels.length; i++) {
1755            channelSpec[offset +i] = new ChannelSpec(channels[i]);
1756        }
1757    }
1758
1759    private static boolean initChannels() {
1760        if (mChannels != null) {
1761            /* already initialized */
1762            return true;
1763        }
1764
1765        int channels24[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ);
1766        if (channels24 == null) {
1767            Log.e(TAG, "Could not get channels for 2.4 GHz");
1768            return false;
1769        }
1770
1771        int channels5[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ);
1772        if (channels5 == null) {
1773            Log.e(TAG, "Could not get channels for 5 GHz");
1774            return false;
1775        }
1776
1777        int channelsDfs[] = WifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY);
1778        if (channelsDfs == null) {
1779            Log.e(TAG, "Could not get channels for DFS");
1780            return false;
1781        }
1782
1783        mChannels = new ChannelSpec[8][];
1784
1785        mChannels[0] = new ChannelSpec[0];
1786
1787        mChannels[1] = new ChannelSpec[channels24.length];
1788        copyChannels(mChannels[1], 0, channels24);
1789
1790        mChannels[2] = new ChannelSpec[channels5.length];
1791        copyChannels(mChannels[2], 0, channels5);
1792
1793        mChannels[3] = new ChannelSpec[channels24.length + channels5.length];
1794        copyChannels(mChannels[3], 0, channels24);
1795        copyChannels(mChannels[3], channels24.length, channels5);
1796
1797        mChannels[4] = new ChannelSpec[channelsDfs.length];
1798        copyChannels(mChannels[4], 0, channelsDfs);
1799
1800        mChannels[5] = new ChannelSpec[channels24.length + channelsDfs.length];
1801        copyChannels(mChannels[5], 0, channels24);
1802        copyChannels(mChannels[5], channels24.length, channelsDfs);
1803
1804        mChannels[6] = new ChannelSpec[channels5.length + channelsDfs.length];
1805        copyChannels(mChannels[6], 0, channels5);
1806        copyChannels(mChannels[6], channels5.length, channelsDfs);
1807
1808        mChannels[7] = new ChannelSpec[
1809                channels24.length + channels5.length + channelsDfs.length];
1810        copyChannels(mChannels[7], 0, channels24);
1811        copyChannels(mChannels[7], channels24.length, channels5);
1812        copyChannels(mChannels[7], channels24.length + channels5.length, channelsDfs);
1813
1814        return true;
1815    }
1816
1817    private static ChannelSpec[] getChannelsForBand(int band) {
1818        initChannels();
1819
1820        if (band < WifiScanner.WIFI_BAND_24_GHZ || band > WifiScanner.WIFI_BAND_BOTH_WITH_DFS)
1821            /* invalid value for band */
1822            return mChannels[0];
1823        else
1824            return mChannels[band];
1825    }
1826
1827    private static boolean isDfs(int channel) {
1828        ChannelSpec[] dfsChannels = getChannelsForBand(WifiScanner
1829                .WIFI_BAND_5_GHZ_DFS_ONLY);
1830        for (int i = 0; i < dfsChannels.length; i++) {
1831            if (channel == dfsChannels[i].frequency) {
1832                return true;
1833            }
1834        }
1835        return false;
1836    }
1837
1838    private static int getBandFromChannels(ChannelSpec[] channels) {
1839        int band = WifiScanner.WIFI_BAND_UNSPECIFIED;
1840        for (ChannelSpec channel : channels) {
1841            if (2400 <= channel.frequency && channel.frequency < 2500) {
1842                band |= WifiScanner.WIFI_BAND_24_GHZ;
1843            } else if ( isDfs(channel.frequency)) {
1844                band |= WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY;
1845            } else if (5100 <= channel.frequency && channel.frequency < 6000) {
1846                band |= WifiScanner.WIFI_BAND_5_GHZ;
1847            }
1848        }
1849        return band;
1850    }
1851
1852    private static int getBandFromChannels(WifiNative.ChannelSettings[] channels) {
1853        int band = WifiScanner.WIFI_BAND_UNSPECIFIED;
1854        for (WifiNative.ChannelSettings channel : channels) {
1855            if (2400 <= channel.frequency && channel.frequency < 2500) {
1856                band |= WifiScanner.WIFI_BAND_24_GHZ;
1857            } else if ( isDfs(channel.frequency)) {
1858                band |= WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY;
1859            } else if (5100 <= channel.frequency && channel.frequency < 6000) {
1860                band |= WifiScanner.WIFI_BAND_5_GHZ;
1861            }
1862        }
1863        return band;
1864    }
1865
1866    @Override
1867    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1868        mStateMachine.dump(fd, pw, args);
1869    }
1870}
1871