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