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