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