16ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla/* 26ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * Copyright (C) 2013 The Android Open Source Project 36ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * 46ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * Licensed under the Apache License, Version 2.0 (the "License"); 56ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * you may not use this file except in compliance with the License. 66ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * You may obtain a copy of the License at 76ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * 86ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * http://www.apache.org/licenses/LICENSE-2.0 96ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * 106ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * Unless required by applicable law or agreed to in writing, software 116ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * distributed under the License is distributed on an "AS IS" BASIS, 126ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * See the License for the specific language governing permissions and 146ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla * limitations under the License. 156ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla */ 166ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 176ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslapackage com.android.companiondevicemanager; 186ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1936e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Suslaimport static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayNameInternal; 2036e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Suslaimport static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress; 2136e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla 2236e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Suslaimport static com.android.internal.util.ArrayUtils.isEmpty; 230c4a9266264d37e724f7372ef7ef932cf60c505cEugene Suslaimport static com.android.internal.util.CollectionUtils.emptyIfNull; 240c4a9266264d37e724f7372ef7ef932cf60c505cEugene Suslaimport static com.android.internal.util.CollectionUtils.size; 256ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 266ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.annotation.NonNull; 276ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.annotation.Nullable; 286ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.app.PendingIntent; 296ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.app.Service; 306ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.BluetoothAdapter; 316ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.BluetoothDevice; 326ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.BluetoothManager; 336ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.le.BluetoothLeScanner; 346ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.le.ScanCallback; 356ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.le.ScanFilter; 366ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.le.ScanResult; 376ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.bluetooth.le.ScanSettings; 386ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.companion.AssociationRequest; 3936e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Suslaimport android.companion.BluetoothDeviceFilter; 40722463ff953168ae27bedf61a586f296813fc9feEugene Suslaimport android.companion.BluetoothLeDeviceFilter; 4136e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Suslaimport android.companion.DeviceFilter; 42da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganovimport android.companion.ICompanionDeviceDiscoveryService; 43da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganovimport android.companion.ICompanionDeviceDiscoveryServiceCallback; 44da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganovimport android.companion.IFindDeviceCallback; 4536e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Suslaimport android.companion.WifiDeviceFilter; 466ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.content.BroadcastReceiver; 476ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.content.Context; 486ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.content.Intent; 496ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.content.IntentFilter; 506ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.graphics.Color; 516ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.graphics.drawable.Drawable; 5236e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Suslaimport android.net.wifi.WifiManager; 536ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.os.IBinder; 5436e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Suslaimport android.os.Parcelable; 556ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.os.RemoteException; 5636e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Suslaimport android.text.TextUtils; 576ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.util.Log; 586ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.view.View; 596ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.view.ViewGroup; 606ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.widget.ArrayAdapter; 616ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport android.widget.TextView; 626ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 63a38fbf63fd2a29884637a59387643c801ed4f663Eugene Suslaimport com.android.internal.util.ArrayUtils; 646a7006a9683ba5a79ca338050c7c50b346b04de0Eugene Suslaimport com.android.internal.util.CollectionUtils; 6536e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Suslaimport com.android.internal.util.Preconditions; 6636e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla 676ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport java.util.ArrayList; 686ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslaimport java.util.List; 6936e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Suslaimport java.util.Objects; 706ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 716ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Suslapublic class DeviceDiscoveryService extends Service { 726ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 736ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private static final boolean DEBUG = false; 746ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private static final String LOG_TAG = "DeviceDiscoveryService"; 756ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 766ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla static DeviceDiscoveryService sInstance; 776ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 786ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private BluetoothAdapter mBluetoothAdapter; 7936e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla private WifiManager mWifiManager; 807a090a11ed00aa492684800de890f4df633409d8Eugene Susla @Nullable private BluetoothLeScanner mBLEScanner; 816ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private ScanSettings mDefaultScanSettings = new ScanSettings.Builder().build(); 82a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla 8336e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla private List<DeviceFilter<?>> mFilters; 84722463ff953168ae27bedf61a586f296813fc9feEugene Susla private List<BluetoothLeDeviceFilter> mBLEFilters; 8536e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla private List<BluetoothDeviceFilter> mBluetoothFilters; 8636e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla private List<WifiDeviceFilter> mWifiFilters; 8736e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla private List<ScanFilter> mBLEScanFilters; 88a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla 8936e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla AssociationRequest mRequest; 9036e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla List<DeviceFilterPair> mDevicesFound; 9136e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla DeviceFilterPair mSelectedDevice; 926ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla DevicesAdapter mDevicesAdapter; 93da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganov IFindDeviceCallback mFindCallback; 94a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla 95da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganov ICompanionDeviceDiscoveryServiceCallback mServiceCallback; 966ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 97da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganov private final ICompanionDeviceDiscoveryService mBinder = 98da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganov new ICompanionDeviceDiscoveryService.Stub() { 996ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Override 1006ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public void startDiscovery(AssociationRequest request, 101da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganov String callingPackage, 102da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganov IFindDeviceCallback findCallback, 103da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganov ICompanionDeviceDiscoveryServiceCallback serviceCallback) { 1046ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (DEBUG) { 1056ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla Log.i(LOG_TAG, 106da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganov "startDiscovery() called with: filter = [" + request 107da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganov + "], findCallback = [" + findCallback + "]" 108da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganov + "], serviceCallback = [" + serviceCallback + "]"); 1096ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 110da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganov mFindCallback = findCallback; 111da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganov mServiceCallback = serviceCallback; 1126ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla DeviceDiscoveryService.this.startDiscovery(request); 1136ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1146ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla }; 1156ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 116a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla private ScanCallback mBLEScanCallback; 117a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla private BluetoothBroadcastReceiver mBluetoothBroadcastReceiver; 118a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla private WifiBroadcastReceiver mWifiBroadcastReceiver; 1196ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1206ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Override 1216ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public IBinder onBind(Intent intent) { 1226ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (DEBUG) Log.i(LOG_TAG, "onBind(" + intent + ")"); 1236ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla return mBinder.asBinder(); 1246ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1256ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1266ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Override 1276ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public void onCreate() { 1286ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla super.onCreate(); 1296ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1306ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (DEBUG) Log.i(LOG_TAG, "onCreate()"); 1316ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1326ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mBluetoothAdapter = getSystemService(BluetoothManager.class).getAdapter(); 1336ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mBLEScanner = mBluetoothAdapter.getBluetoothLeScanner(); 13436e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla mWifiManager = getSystemService(WifiManager.class); 1356ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1366ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesFound = new ArrayList<>(); 1376ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesAdapter = new DevicesAdapter(); 1386ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1396ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla sInstance = this; 1406ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 1416ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 14236e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla private void startDiscovery(AssociationRequest request) { 143a38fbf63fd2a29884637a59387643c801ed4f663Eugene Susla if (!request.equals(mRequest)) { 144a38fbf63fd2a29884637a59387643c801ed4f663Eugene Susla mRequest = request; 14536e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla 146a38fbf63fd2a29884637a59387643c801ed4f663Eugene Susla mFilters = request.getDeviceFilters(); 147a38fbf63fd2a29884637a59387643c801ed4f663Eugene Susla mWifiFilters = CollectionUtils.filter(mFilters, WifiDeviceFilter.class); 148a38fbf63fd2a29884637a59387643c801ed4f663Eugene Susla mBluetoothFilters = CollectionUtils.filter(mFilters, BluetoothDeviceFilter.class); 149722463ff953168ae27bedf61a586f296813fc9feEugene Susla mBLEFilters = CollectionUtils.filter(mFilters, BluetoothLeDeviceFilter.class); 150722463ff953168ae27bedf61a586f296813fc9feEugene Susla mBLEScanFilters = CollectionUtils.map(mBLEFilters, BluetoothLeDeviceFilter::getScanFilter); 1516ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 152a38fbf63fd2a29884637a59387643c801ed4f663Eugene Susla reset(); 1533c9aa1767ca0ddc881f53c2ce7eb3135670efef3Eugene Susla } else if (DEBUG) Log.i(LOG_TAG, "startDiscovery: duplicate request: " + request); 1543c9aa1767ca0ddc881f53c2ce7eb3135670efef3Eugene Susla 155a38fbf63fd2a29884637a59387643c801ed4f663Eugene Susla if (!ArrayUtils.isEmpty(mDevicesFound)) { 156a38fbf63fd2a29884637a59387643c801ed4f663Eugene Susla onReadyToShowUI(); 157a38fbf63fd2a29884637a59387643c801ed4f663Eugene Susla } 1586ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1590c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla // If filtering to get single device by mac address, also search in the set of already 1600c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla // bonded devices to allow linking those directly 1610c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla String singleMacAddressFilter = null; 1620c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla if (mRequest.isSingleDevice()) { 1630c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla int numFilters = size(mBluetoothFilters); 1640c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla for (int i = 0; i < numFilters; i++) { 1650c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla BluetoothDeviceFilter filter = mBluetoothFilters.get(i); 1660c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla if (!TextUtils.isEmpty(filter.getAddress())) { 1670c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla singleMacAddressFilter = filter.getAddress(); 1680c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla break; 1690c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla } 1700c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla } 1710c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla } 1720c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla if (singleMacAddressFilter != null) { 1730c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla for (BluetoothDevice dev : emptyIfNull(mBluetoothAdapter.getBondedDevices())) { 1740c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla onDeviceFound(DeviceFilterPair.findMatch(dev, mBluetoothFilters)); 1750c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla } 1760c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla } 1770c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla 17836e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla if (shouldScan(mBluetoothFilters)) { 17936e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla final IntentFilter intentFilter = new IntentFilter(); 18036e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla intentFilter.addAction(BluetoothDevice.ACTION_FOUND); 18136e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla intentFilter.addAction(BluetoothDevice.ACTION_DISAPPEARED); 1826ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 183a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla mBluetoothBroadcastReceiver = new BluetoothBroadcastReceiver(); 184a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla registerReceiver(mBluetoothBroadcastReceiver, intentFilter); 18536e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla mBluetoothAdapter.startDiscovery(); 18636e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } 1876ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 1887a090a11ed00aa492684800de890f4df633409d8Eugene Susla if (shouldScan(mBLEFilters) && mBLEScanner != null) { 189a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla mBLEScanCallback = new BLEScanCallback(); 19036e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla mBLEScanner.startScan(mBLEScanFilters, mDefaultScanSettings, mBLEScanCallback); 19136e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } 19236e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla 19336e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla if (shouldScan(mWifiFilters)) { 194a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla mWifiBroadcastReceiver = new WifiBroadcastReceiver(); 195a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla registerReceiver(mWifiBroadcastReceiver, 19636e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); 19736e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla mWifiManager.startScan(); 19836e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } 19936e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } 20036e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla 20136e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla private boolean shouldScan(List<? extends DeviceFilter> mediumSpecificFilters) { 20236e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla return !isEmpty(mediumSpecificFilters) || isEmpty(mFilters); 2036ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2046ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2056ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private void reset() { 2063c9aa1767ca0ddc881f53c2ce7eb3135670efef3Eugene Susla if (DEBUG) Log.i(LOG_TAG, "reset()"); 207a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla stopScan(); 2086ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesFound.clear(); 2096ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mSelectedDevice = null; 2106ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesAdapter.notifyDataSetChanged(); 2116ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2126ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2136ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Override 2146ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public boolean onUnbind(Intent intent) { 2156ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla stopScan(); 2166ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla return super.onUnbind(intent); 2176ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2186ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2196ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private void stopScan() { 220a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (DEBUG) Log.i(LOG_TAG, "stopScan()"); 221a38fbf63fd2a29884637a59387643c801ed4f663Eugene Susla 222a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla mBluetoothAdapter.cancelDiscovery(); 223a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (mBluetoothBroadcastReceiver != null) { 224a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla unregisterReceiver(mBluetoothBroadcastReceiver); 225a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla mBluetoothBroadcastReceiver = null; 226a38fbf63fd2a29884637a59387643c801ed4f663Eugene Susla } 2277a090a11ed00aa492684800de890f4df633409d8Eugene Susla if (mBLEScanner != null) mBLEScanner.stopScan(mBLEScanCallback); 228a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (mWifiBroadcastReceiver != null) { 229a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla unregisterReceiver(mWifiBroadcastReceiver); 230a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla mWifiBroadcastReceiver = null; 231a38fbf63fd2a29884637a59387643c801ed4f663Eugene Susla } 2326ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2336ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 23436e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla private void onDeviceFound(@Nullable DeviceFilterPair device) { 2350c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla if (device == null) return; 2360c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla 2376ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (mDevicesFound.contains(device)) { 2386ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla return; 2396ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2406ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 241a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (DEBUG) Log.i(LOG_TAG, "Found device " + device); 2426ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2436ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla if (mDevicesFound.isEmpty()) { 2446ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla onReadyToShowUI(); 2456ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2466ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesFound.add(device); 2476ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesAdapter.notifyDataSetChanged(); 2486ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2496ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2506ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla //TODO also, on timeout -> call onFailure 2516ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private void onReadyToShowUI() { 2526ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla try { 253da0acdf938f1d6e7a978e143d5d80d8dd5af52adSvet Ganov mFindCallback.onSuccess(PendingIntent.getActivity( 2546ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla this, 0, 2556ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla new Intent(this, DeviceChooserActivity.class), 2566ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT 2576ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla | PendingIntent.FLAG_IMMUTABLE)); 2586ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } catch (RemoteException e) { 2596ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla throw new RuntimeException(e); 2606ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2616ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2626ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 26336e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla private void onDeviceLost(@Nullable DeviceFilterPair device) { 2646ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesFound.remove(device); 2656ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mDevicesAdapter.notifyDataSetChanged(); 26636e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla if (DEBUG) Log.i(LOG_TAG, "Lost device " + device.getDisplayName()); 2676ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2686ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 26947aafbe033202ccc2c0ea9af2b0f1596ebed4373Eugene Susla void onDeviceSelected(String callingPackage, String deviceAddress) { 27047aafbe033202ccc2c0ea9af2b0f1596ebed4373Eugene Susla try { 27147aafbe033202ccc2c0ea9af2b0f1596ebed4373Eugene Susla mServiceCallback.onDeviceSelected( 27247aafbe033202ccc2c0ea9af2b0f1596ebed4373Eugene Susla //TODO is this the right userId? 27347aafbe033202ccc2c0ea9af2b0f1596ebed4373Eugene Susla callingPackage, getUserId(), deviceAddress); 27447aafbe033202ccc2c0ea9af2b0f1596ebed4373Eugene Susla } catch (RemoteException e) { 27547aafbe033202ccc2c0ea9af2b0f1596ebed4373Eugene Susla Log.e(LOG_TAG, "Failed to record association: " 27647aafbe033202ccc2c0ea9af2b0f1596ebed4373Eugene Susla + callingPackage + " <-> " + deviceAddress); 27747aafbe033202ccc2c0ea9af2b0f1596ebed4373Eugene Susla } 27847aafbe033202ccc2c0ea9af2b0f1596ebed4373Eugene Susla } 27947aafbe033202ccc2c0ea9af2b0f1596ebed4373Eugene Susla 280200c37f4136e0d49fd853122900d2209e424701cEugene Susla void onCancel() { 281a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (DEBUG) Log.i(LOG_TAG, "onCancel()"); 282200c37f4136e0d49fd853122900d2209e424701cEugene Susla try { 283200c37f4136e0d49fd853122900d2209e424701cEugene Susla mServiceCallback.onDeviceSelectionCancel(); 284200c37f4136e0d49fd853122900d2209e424701cEugene Susla } catch (RemoteException e) { 285200c37f4136e0d49fd853122900d2209e424701cEugene Susla throw new RuntimeException(e); 286200c37f4136e0d49fd853122900d2209e424701cEugene Susla } 287200c37f4136e0d49fd853122900d2209e424701cEugene Susla } 288200c37f4136e0d49fd853122900d2209e424701cEugene Susla 28936e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla class DevicesAdapter extends ArrayAdapter<DeviceFilterPair> { 2906ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth); 291254b3730df8daf7d7cc1017a473dedc3ca4cec5bEugene Susla private Drawable WIFI_ICON = icon(com.android.internal.R.drawable.ic_wifi_signal_3); 2926ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2936ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private Drawable icon(int drawableRes) { 2946ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla Drawable icon = getResources().getDrawable(drawableRes, null); 2956ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla icon.setTint(Color.DKGRAY); 2966ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla return icon; 2976ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 2986ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 2996ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public DevicesAdapter() { 3006ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla super(DeviceDiscoveryService.this, 0, mDevicesFound); 3016ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 3026ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 3036ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Override 3046ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla public View getView( 3056ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla int position, 3066ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @Nullable View convertView, 3076ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla @NonNull ViewGroup parent) { 3086ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla TextView view = convertView instanceof TextView 3096ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla ? (TextView) convertView 3106ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla : newView(); 3116ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla bind(view, getItem(position)); 3126ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla return view; 3136ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 3146ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 31536e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla private void bind(TextView textView, DeviceFilterPair device) { 31636e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla textView.setText(device.getDisplayName()); 3176ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla textView.setBackgroundColor( 3186ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla device.equals(mSelectedDevice) 3196ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla ? Color.GRAY 3206ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla : Color.TRANSPARENT); 321254b3730df8daf7d7cc1017a473dedc3ca4cec5bEugene Susla textView.setCompoundDrawablesWithIntrinsicBounds( 322254b3730df8daf7d7cc1017a473dedc3ca4cec5bEugene Susla device.device instanceof android.net.wifi.ScanResult 323254b3730df8daf7d7cc1017a473dedc3ca4cec5bEugene Susla ? WIFI_ICON 324254b3730df8daf7d7cc1017a473dedc3ca4cec5bEugene Susla : BLUETOOTH_ICON, 325254b3730df8daf7d7cc1017a473dedc3ca4cec5bEugene Susla null, null, null); 3266ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla textView.setOnClickListener((view) -> { 3276ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla mSelectedDevice = device; 3286ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla notifyDataSetChanged(); 3296ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla }); 3306ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 3316ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla 3326ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla //TODO move to a layout file 3336ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla private TextView newView() { 3346ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla final TextView textView = new TextView(DeviceDiscoveryService.this); 3356ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla textView.setTextColor(Color.BLACK); 3366ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla final int padding = DeviceChooserActivity.getPadding(getResources()); 3376ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla textView.setPadding(padding, padding, padding, padding); 3386ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla textView.setCompoundDrawablePadding(padding); 3396ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla return textView; 3406ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 3416ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla } 34236e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla 34336e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla /** 34436e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla * A pair of device and a filter that matched this device if any. 34536e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla * 34636e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla * @param <T> device type 34736e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla */ 34836e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla static class DeviceFilterPair<T extends Parcelable> { 34936e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla public final T device; 35036e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla @Nullable 35136e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla public final DeviceFilter<T> filter; 35236e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla 35336e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla private DeviceFilterPair(T device, @Nullable DeviceFilter<T> filter) { 35436e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla this.device = device; 35536e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla this.filter = filter; 35636e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } 35736e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla 35836e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla /** 35936e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla * {@code (device, null)} if the filters list is empty or null 36036e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla * {@code null} if none of the provided filters match the device 36136e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla * {@code (device, filter)} where filter is among the list of filters and matches the device 36236e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla */ 36336e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla @Nullable 36436e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla public static <T extends Parcelable> DeviceFilterPair<T> findMatch( 36536e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla T dev, @Nullable List<? extends DeviceFilter<T>> filters) { 36636e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla if (isEmpty(filters)) return new DeviceFilterPair<>(dev, null); 3673c9aa1767ca0ddc881f53c2ce7eb3135670efef3Eugene Susla final DeviceFilter<T> matchingFilter 3683c9aa1767ca0ddc881f53c2ce7eb3135670efef3Eugene Susla = CollectionUtils.find(filters, f -> f.matches(dev)); 3693c9aa1767ca0ddc881f53c2ce7eb3135670efef3Eugene Susla 3703c9aa1767ca0ddc881f53c2ce7eb3135670efef3Eugene Susla DeviceFilterPair<T> result = matchingFilter != null 3713c9aa1767ca0ddc881f53c2ce7eb3135670efef3Eugene Susla ? new DeviceFilterPair<>(dev, matchingFilter) 3723c9aa1767ca0ddc881f53c2ce7eb3135670efef3Eugene Susla : null; 3733c9aa1767ca0ddc881f53c2ce7eb3135670efef3Eugene Susla if (DEBUG) Log.i(LOG_TAG, "findMatch(dev = " + dev + ", filters = " + filters + 3743c9aa1767ca0ddc881f53c2ce7eb3135670efef3Eugene Susla ") -> " + result); 3753c9aa1767ca0ddc881f53c2ce7eb3135670efef3Eugene Susla return result; 37636e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } 37736e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla 37836e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla public String getDisplayName() { 37936e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla if (filter == null) { 38036e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla Preconditions.checkNotNull(device); 38136e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla if (device instanceof BluetoothDevice) { 38236e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla return getDeviceDisplayNameInternal((BluetoothDevice) device); 38336e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } else if (device instanceof android.net.wifi.ScanResult) { 38436e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla return getDeviceDisplayNameInternal((android.net.wifi.ScanResult) device); 38536e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } else if (device instanceof ScanResult) { 38636e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla return getDeviceDisplayNameInternal(((ScanResult) device).getDevice()); 38736e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } else { 38836e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla throw new IllegalArgumentException("Unknown device type: " + device.getClass()); 38936e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } 39036e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } 39136e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla return filter.getDeviceDisplayName(device); 39236e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } 39336e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla 39436e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla @Override 39536e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla public boolean equals(Object o) { 39636e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla if (this == o) return true; 39736e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla if (o == null || getClass() != o.getClass()) return false; 39836e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla DeviceFilterPair<?> that = (DeviceFilterPair<?>) o; 39936e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla return Objects.equals(getDeviceMacAddress(device), getDeviceMacAddress(that.device)); 40036e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } 40136e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla 40236e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla @Override 40336e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla public int hashCode() { 40436e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla return Objects.hash(getDeviceMacAddress(device)); 40536e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } 406a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla 407a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla @Override 408a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla public String toString() { 409a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla return "DeviceFilterPair{" + 410a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla "device=" + device + 411a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla ", filter=" + filter + 412a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla '}'; 413a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 414a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 415a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla 416a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla private class BLEScanCallback extends ScanCallback { 417a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla 418a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla public BLEScanCallback() { 419a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (DEBUG) Log.i(LOG_TAG, "new BLEScanCallback() -> " + this); 420a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 421a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla 422a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla @Override 423a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla public void onScanResult(int callbackType, ScanResult result) { 424a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (DEBUG) { 425a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla Log.i(LOG_TAG, 426a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla "BLE.onScanResult(callbackType = " + callbackType + ", result = " + result 427a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla + ")"); 428a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 429a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla final DeviceFilterPair<ScanResult> deviceFilterPair 430a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla = DeviceFilterPair.findMatch(result, mBLEFilters); 431a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (deviceFilterPair == null) return; 432a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST) { 433a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla onDeviceLost(deviceFilterPair); 434a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } else { 435a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla onDeviceFound(deviceFilterPair); 436a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 437a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 438a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 439a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla 440a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla private class BluetoothBroadcastReceiver extends BroadcastReceiver { 441a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla @Override 442a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla public void onReceive(Context context, Intent intent) { 443a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (DEBUG) { 444a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla Log.i(LOG_TAG, 445a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla "BL.onReceive(context = " + context + ", intent = " + intent + ")"); 446a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 447a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 448a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla final DeviceFilterPair<BluetoothDevice> deviceFilterPair 449a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla = DeviceFilterPair.findMatch(device, mBluetoothFilters); 450a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (deviceFilterPair == null) return; 451a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) { 452a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla onDeviceFound(deviceFilterPair); 453a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } else { 454a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla onDeviceLost(deviceFilterPair); 455a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 456a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 457a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 458a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla 459a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla private class WifiBroadcastReceiver extends BroadcastReceiver { 460a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla @Override 461a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla public void onReceive(Context context, Intent intent) { 462a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { 463a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla List<android.net.wifi.ScanResult> scanResults = mWifiManager.getScanResults(); 464a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla 465a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla if (DEBUG) { 466a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla Log.i(LOG_TAG, "Wifi scan results: " + TextUtils.join("\n", scanResults)); 467a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 468a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla 469a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla for (int i = 0; i < scanResults.size(); i++) { 4700c4a9266264d37e724f7372ef7ef932cf60c505cEugene Susla onDeviceFound(DeviceFilterPair.findMatch(scanResults.get(i), mWifiFilters)); 471a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 472a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 473a7717e3072acad6cd6256ce3fdbb2bb94ecb06caEugene Susla } 47436e866b8e0ec08e45b5e7fbc65aeeb3a9bb7b11eEugene Susla } 4756ed45d8cd33c297e608aba94fc1f61dace7a7ccaEugene Susla} 476