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