/** * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, * copy, modify, and distribute this software in source code or binary form for use * in connection with the web services and APIs provided by Facebook. * * As with any software that integrates with the Facebook platform, your use of * this software is subject to the Facebook Developer Principles and Policies * [http://developers.facebook.com/policy/]. This copyright notice shall be * included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.facebook.places.internal; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.net.wifi.ScanResult; import android.net.wifi.SupplicantState; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Build; import android.os.Bundle; import android.os.SystemClock; import android.text.TextUtils; import com.facebook.internal.Validate; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; /** * com.facebook.places.internal is solely for the use of other packages within the * Facebook SDK for Android. Use of any of the classes in this package is * unsupported, and they may be modified or removed without warning at any time. */ @SuppressWarnings("MissingPermission") public class WifiScannerImpl implements WifiScanner { private Context context; private WifiManager wifiManager; private ScanResultBroadcastReceiver broadcastReceiver; private final Object scanLock = new Object(); private final LocationPackageRequestParams params; WifiScannerImpl(Context context, LocationPackageRequestParams params) { this.context = context; this.params = params; } @Override public void initAndCheckEligibility() throws ScannerException { if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) { throw new ScannerException(ScannerException.Type.NOT_SUPPORTED); } if (!Validate.hasWiFiPermission(context)) { throw new ScannerException(ScannerException.Type.PERMISSION_DENIED); } if (wifiManager == null) { wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); } boolean isWifiScanningAlwaysOn = isWifiScanningAlwaysOn(); if (!isWifiScanningAlwaysOn && !wifiManager.isWifiEnabled()) { throw new ScannerException(ScannerException.Type.DISABLED); } } @Override public WifiScanResult getConnectedWifi() throws ScannerException { try { WifiInfo wifiInfo = wifiManager.getConnectionInfo(); if (wifiInfo == null || TextUtils.isEmpty(wifiInfo.getBSSID()) || wifiInfo.getSupplicantState() != SupplicantState.COMPLETED) { return null; } WifiScanResult wifiScanResult = new WifiScanResult(); wifiScanResult.bssid = wifiInfo.getBSSID(); wifiScanResult.ssid = wifiInfo.getSSID(); wifiScanResult.rssi = wifiInfo.getRssi(); if (Build.VERSION.SDK_INT >= ScannerFactory.OS_VERSION_LOLLIPOP) { wifiScanResult.frequency = wifiInfo.getFrequency(); } return wifiScanResult; } catch (Exception e) { throw new ScannerException(ScannerException.Type.UNKNOWN_ERROR, e); } } @Override public boolean isWifiScanningEnabled() { try { initAndCheckEligibility(); if (Validate.hasLocationPermission(context)) { return true; } } catch (ScannerException e) { // ignore } return false; } private boolean isWifiScanningAlwaysOn() { if (Build.VERSION.SDK_INT >= ScannerFactory.OS_VERSION_JELLY_BEAN_MR2) { return wifiManager.isScanAlwaysAvailable(); } return false; } private List<WifiScanResult> getCachedScanResults() throws ScannerException { try { List<ScanResult> scanResults = wifiManager.getScanResults(); scanResults = filterWifiScanResultsByMaxAge(scanResults, params.getWifiScanMaxAgeMs()); filterResults(scanResults, params.getWifiMaxScanResults()); List<WifiScanResult> wifiScanResults = new ArrayList<>(scanResults.size()); for (ScanResult scanResult : scanResults) { WifiScanResult wifiScanResult = new WifiScanResult(); wifiScanResult.bssid = scanResult.BSSID; wifiScanResult.ssid = scanResult.SSID; wifiScanResult.rssi = scanResult.level; wifiScanResult.frequency = scanResult.frequency; wifiScanResults.add(wifiScanResult); } return wifiScanResults; } catch (Exception e) { throw new ScannerException(ScannerException.Type.UNKNOWN_ERROR, e); } } private static void filterResults(List<ScanResult> scanResults, int maxResults) { if (scanResults.size() > maxResults) { Comparator<ScanResult> comparator = new Comparator<ScanResult>() { @Override public int compare(ScanResult lhs, ScanResult rhs) { return rhs.level - lhs.level; } }; Collections.sort(scanResults, comparator); scanResults.subList(maxResults, scanResults.size()).clear(); } } private static List<ScanResult> filterWifiScanResultsByMaxAge( List<ScanResult> scanResults, long maxAgeMs) { List<ScanResult> filtered = new ArrayList<>(); if (scanResults != null) { if (Build.VERSION.SDK_INT < ScannerFactory.OS_VERSION_JELLY_BEAN_MR1) { filtered.addAll(scanResults); } else { long nowSinceBootMs = SystemClock.elapsedRealtime(); for (ScanResult result : scanResults) { long ageMs = nowSinceBootMs - (result.timestamp / 1000); if (ageMs < 0) { // Some platform use unix timestmap ageMs = System.currentTimeMillis() - result.timestamp; } if (ageMs < maxAgeMs) { filtered.add(result); } } } } return filtered; } @Override public synchronized List<WifiScanResult> getWifiScans() throws ScannerException{ List<WifiScanResult> wifiScanResults = null; if (!params.isWifiActiveScanForced()) { wifiScanResults = getCachedScanResults(); } boolean isListEmpty = wifiScanResults == null || wifiScanResults.isEmpty(); if (params.isWifiActiveScanForced() || (params.isWifiActiveScanAllowed() && isListEmpty)) { wifiScanResults = getActiveScanResults(); } return wifiScanResults; } private List<WifiScanResult> getActiveScanResults() throws ScannerException{ List<WifiScanResult> wifiScanResults = null; try { if (Validate.hasChangeWifiStatePermission(context)) { registerBroadcastReceiver(); boolean isScanStarted = wifiManager.startScan(); if (isScanStarted) { try { synchronized (scanLock) { scanLock.wait(params.getWifiScanTimeoutMs()); } } catch (InterruptedException e) { // ignore } wifiScanResults = getCachedScanResults(); } } } catch (Exception e) { // ignore } finally { unregisterBroadcastReceiver(); } return wifiScanResults; } private void registerBroadcastReceiver() { if (broadcastReceiver != null) { unregisterBroadcastReceiver(); } broadcastReceiver = new ScanResultBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); context.registerReceiver(broadcastReceiver, intentFilter); } private void unregisterBroadcastReceiver() { if (broadcastReceiver != null) { try { context.unregisterReceiver(broadcastReceiver); } catch (Exception e) { // ignore } broadcastReceiver = null; } } private class ScanResultBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent != null) { if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) { synchronized (scanLock) { scanLock.notify(); } unregisterBroadcastReceiver(); } } } } }