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