165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/* 265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Copyright (C) 2014 The Android Open Source Project 365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Licensed under the Apache License, Version 2.0 (the "License"); 565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * you may not use this file except in compliance with the License. 665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * You may obtain a copy of the License at 765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * http://www.apache.org/licenses/LICENSE-2.0 965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 1065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Unless required by applicable law or agreed to in writing, software 1165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * distributed under the License is distributed on an "AS IS" BASIS, 1265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * See the License for the specific language governing permissions and 1465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * limitations under the License. 1565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 1665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 1765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepackage com.android.tv.settings.util.bluetooth; 1865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 1965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.bluetooth.BluetoothAdapter; 2065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.bluetooth.BluetoothDevice; 2165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.BroadcastReceiver; 2265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.Context; 2365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.Intent; 2465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.IntentFilter; 2565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.Handler; 2665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.Log; 2765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 286e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantlerimport java.util.ArrayList; 29510203ee5e3350758dae4bf9af05b1585029af7dWally Yauimport java.util.List; 306e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler 3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/** 3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Listens for unconfigured or problematic devices to show up on 3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * bluetooth and returns lists of them. Also manages their colors. 3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic class BluetoothScanner { 3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final String TAG = "BluetoothScanner"; 3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final boolean DEBUG = false; 3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int FOUND_ON_SCAN = -1; 4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int CONSECUTIVE_MISS_THRESHOLD = 4; 4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int FAILED_SETTING_NAME = CONSECUTIVE_MISS_THRESHOLD + 1; 4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int SCAN_DELAY = 4000; 4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static Receiver sReceiver; 4565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 4665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static class Device { 4765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public BluetoothDevice btDevice; 4865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public String address; 4965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public String btName; 5065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public String name = ""; 5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public LedConfiguration leds; 5265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public int consecutiveMisses; 5365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // the type of configuration this device needs, or -1 if the device does not 5465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // specify a configuration type 5565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public int configurationType = 0; 5665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public String toString() { 5965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane StringBuilder str = new StringBuilder(); 6065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane str.append("Device(addr="); 6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane str.append(address); 6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane str.append(" name=\""); 6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane str.append(name); 6465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane str.append("\" leds="); 6565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane str.append(leds); 6665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane str.append("\" configuration_type="); 6765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane str.append(configurationType); 6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane str.append(")"); 6965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return str.toString(); 7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 7165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public String getNameString() { 7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return String.format("\"%s\" (%s)", this.name, 7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this.leds == null ? "" : this.leds.getNameString()); 7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public boolean setNameString(String str) { 7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this.btName = str; 7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (str == null || !BluetoothNameUtils.isValidName(str)) { 8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this.name = ""; 8165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this.leds = null; 8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return false; 8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this.leds = BluetoothNameUtils.getColorConfiguration(str); 8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this.configurationType = BluetoothNameUtils.getSetupType(str); 8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return true; 8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public boolean hasConfigurationType() { 9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return configurationType != 0; 9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static class Listener { 9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void onScanningStarted() { 9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void onScanningStopped(ArrayList<Device> devices) { 9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void onDeviceAdded(Device device) { 10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void onDeviceChanged(Device device) { 10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void onDeviceRemoved(Device device) { 10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private BluetoothScanner() { 10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane throw new RuntimeException("do not instantiate"); 11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Starts listening. Will call onto listener with any devices we have 11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * cached before this call returns. 11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static void startListening(Context context, Listener listener, 117510203ee5e3350758dae4bf9af05b1585029af7dWally Yau List<BluetoothDeviceCriteria> criteria) { 11865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (sReceiver == null) { 11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sReceiver = new Receiver(context.getApplicationContext()); 12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sReceiver.startListening(listener, criteria); 12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Log.d(TAG, "startListening"); 12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Removes the listener now, so there will be no more callbacks, but 12765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * leaves the scan running for 20 seconds to keep the cache warm just 12865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * in case it's needed again. 12965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 1300678a03fc59624074a7d739d6e934f1a45c63d2cTony Mantler public static boolean stopListening(Listener listener) { 13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Log.d(TAG, "stopListening sReceiver=" + sReceiver); 13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (sReceiver != null) { 1330678a03fc59624074a7d739d6e934f1a45c63d2cTony Mantler return sReceiver.stopListening(listener); 13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 1350678a03fc59624074a7d739d6e934f1a45c63d2cTony Mantler return false; 13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 13865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Initiates a scan right now. 14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static void scanNow() { 14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (sReceiver != null) { 14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sReceiver.scanNow(); 14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static void stopNow() { 14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (sReceiver != null) { 14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sReceiver.stopNow(); 15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static void removeDevice(Device device) { 15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane removeDevice(device.address); 15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 15765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static void removeDevice(String btAddress) { 15865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (sReceiver != null) { 15965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sReceiver.removeDevice(btAddress); 16065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 16165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 16265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 16365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static class ClientRecord { 1646e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler public final Listener listener; 1656e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler public final ArrayList<Device> devices; 166510203ee5e3350758dae4bf9af05b1585029af7dWally Yau public final List<BluetoothDeviceCriteria> matchers; 16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 168510203ee5e3350758dae4bf9af05b1585029af7dWally Yau public ClientRecord(Listener listener, List<BluetoothDeviceCriteria> matchers) { 16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this.listener = listener; 1706e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler devices = new ArrayList<>(); 171510203ee5e3350758dae4bf9af05b1585029af7dWally Yau this.matchers = matchers; 17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 17465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static class Receiver extends BroadcastReceiver { 17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final Handler mHandler = new Handler(); 17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // TODO mListenerLock should probably now protect mClients 1786e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler private final ArrayList<ClientRecord> mClients = new ArrayList<>(); 1796e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler private final ArrayList<Device> mPresentDevices = new ArrayList<>(); 1806e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler private final Context mContext; 1816e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler private final BluetoothAdapter mBtAdapter; 18265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static boolean mKeepScanning; 18365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private boolean mRegistered = false; 1846e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler private final Object mListenerLock = new Object(); 18565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 18665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public Receiver(Context context) { 18765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mContext = context; 18865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 18965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Bluetooth 19065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mBtAdapter = BluetoothAdapter.getDefaultAdapter(); 19165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 19265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 19365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 19465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @param listener 195510203ee5e3350758dae4bf9af05b1585029af7dWally Yau * @param matchers Pattern matchers to determine whether this listener 19665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * will be notified about changes in status of a discovered device. Note 19765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * that the matcher is only run against the device when the device is 19865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * first discovered, not each time it appears in scan results. Device 19965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * properties are assumed to be stable. 20065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 201510203ee5e3350758dae4bf9af05b1585029af7dWally Yau public void startListening(Listener listener, List<BluetoothDeviceCriteria> matchers) { 20265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int size = 0; 203510203ee5e3350758dae4bf9af05b1585029af7dWally Yau ClientRecord newClient = new ClientRecord(listener, matchers); 20465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane synchronized (mListenerLock) { 20565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int ptr = mClients.size() - 1; ptr > -1; ptr--) { 20665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mClients.get(ptr).listener == listener) { 20765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane throw new RuntimeException("Listener already registered: " + listener); 20865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 20965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 21065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 21165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Save this listener in the list 21265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mClients.add(newClient); 21365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane size = mClients.size(); 21465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 21565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 21665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Register for broadcasts when a device is discovered 21765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // and broadcasts when discovery has finished 21865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (size == 1) { 21965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPresentDevices.clear(); 22065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); 22165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); 22265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mContext.registerReceiver(this, filter); 22365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mRegistered = true; 22465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 22565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 22665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Keep retrying until we say stop 22765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mKeepScanning = true; 22865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 22965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Call back with the ones we have already 23065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final int N = mPresentDevices.size(); 23165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i=0; i<N; i++) { 23265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Device target = mPresentDevices.get(i); 233510203ee5e3350758dae4bf9af05b1585029af7dWally Yau for (BluetoothDeviceCriteria matcher : newClient.matchers) { 234510203ee5e3350758dae4bf9af05b1585029af7dWally Yau if (matcher.isMatchingDevice(target.btDevice)) { 235510203ee5e3350758dae4bf9af05b1585029af7dWally Yau newClient.devices.add(target); 236510203ee5e3350758dae4bf9af05b1585029af7dWally Yau newClient.listener.onDeviceAdded(target); 237510203ee5e3350758dae4bf9af05b1585029af7dWally Yau break; 238510203ee5e3350758dae4bf9af05b1585029af7dWally Yau } 23965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 24065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 24165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 24265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // If we have a pending stop, cancel that. 24365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHandler.removeCallbacks(mStopTask); 24465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 24565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // If there is a pending scan, we'll do one now, so we can scan any 24665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // pending ones. 24765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHandler.removeCallbacks(mScanTask); 24865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 24965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scanNow(); 25065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 25165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 2520678a03fc59624074a7d739d6e934f1a45c63d2cTony Mantler public boolean stopListening(Listener listener) { 2536e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler final int size; 2540678a03fc59624074a7d739d6e934f1a45c63d2cTony Mantler boolean stopped = false; 25565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane synchronized (mListenerLock) { 25665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int ptr = mClients.size() - 1; ptr > -1; ptr--) { 25765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ClientRecord client = mClients.get(ptr); 25865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (client.listener == listener) { 25965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mClients.remove(ptr); 2600678a03fc59624074a7d739d6e934f1a45c63d2cTony Mantler stopped = true; 26165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 26265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 26365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 26465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane size = mClients.size(); 26565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 26665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (size == 0) { 26765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHandler.removeCallbacks(mStopTask); 26865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHandler.postDelayed(mStopTask, 20 * 1000 /* ms */); 26965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 2700678a03fc59624074a7d739d6e934f1a45c63d2cTony Mantler return stopped; 27165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 27265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 27365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void scanNow() { 27465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // If we're already discovering, stop it. 27565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mBtAdapter.isDiscovering()) { 27665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mBtAdapter.cancelDiscovery(); 27765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 27865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 27965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sendScanningStarted(); 28065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 28165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Request discover from BluetoothAdapter 28265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mBtAdapter.startDiscovery(); 28365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 28465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 28565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void stopNow() { 2866e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler final int size; 28765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane synchronized (mListenerLock) { 28865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane size = mClients.size(); 28965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 29065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (size == 0) { 29165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Log.d(TAG, "mStopTask.run()"); 29265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 29365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // cancel any pending scans 29465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHandler.removeCallbacks(mScanTask); 29565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 29665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // If there is a pending stop, cancel it 29765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHandler.removeCallbacks(mStopTask); 29865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 29965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Make sure we're not doing discovery anymore 30065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mBtAdapter != null) { 30165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mBtAdapter.cancelDiscovery(); 30265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 30365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 30465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // shut down discovery and prevent it from restarting 30565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mKeepScanning = false; 30665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 30765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // if the Bluetooth adapter is enabled, we're listening for discovery events and 30865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // should stop 30965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (BluetoothAdapter.getDefaultAdapter().isEnabled() && mRegistered) { 31065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mContext.unregisterReceiver(Receiver.this); 31165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mRegistered = false; 31265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 31365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 31465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 31565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 31665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void removeDevice(String btAddress) { 31765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int count = mPresentDevices.size(); 31865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i = 0; i < count; i++) { 31965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Device d = mPresentDevices.get(i); 32065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (btAddress.equals(d.address)) { 32165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPresentDevices.remove(d); 32265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 32365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 32465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 32565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 32665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int ptr = mClients.size() - 1; ptr > -1; ptr--) { 32765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ClientRecord client = mClients.get(ptr); 32865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int devPtr = client.devices.size() - 1; devPtr > -1; devPtr--) { 32965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Device d = client.devices.get(devPtr); 33065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (btAddress.equals(d.address)) { 33165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane client.devices.remove(devPtr); 33265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 33365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 33465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 33565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 33665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 33765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 33865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final Runnable mStopTask = new Runnable() { 33965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 34065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void run() { 34165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane synchronized (mListenerLock) { 34265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mClients.size() != 0) { 34365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane throw new RuntimeException("mStopTask running with mListeners.size=" 34465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane + mClients.size()); 34565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 34665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 34765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane stopNow(); 34865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 34965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane }; 35065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 35165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final Runnable mScanTask = new Runnable() { 35265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 35365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void run() { 35465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // If there is a pending scan request, cancel it 35565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHandler.removeCallbacks(mScanTask); 35665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 35765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane scanNow(); 35865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 35965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane }; 36065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 36165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 36265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void onReceive(Context context, Intent intent) { 36365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final String action = intent.getAction(); 36465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 36565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (BluetoothDevice.ACTION_FOUND.equals(action)) { 36665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 36765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // When discovery finds a device 36865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 36965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Get the BluetoothDevice object from the Intent 37065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 37165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final String address = btDevice.getAddress(); 37265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane String name = btDevice.getName(); 37365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 37465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (DEBUG) { 37565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Log.d(TAG, "Device found, address: " + address + " name: \"" + name + "\""); 37665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 37765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 37865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (address == null || name == null) { 37965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return; 38065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 38165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 38265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Older Bluetooth stacks may append a null character to a device name 38365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (name.endsWith("\0")) { 38465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane name = name.substring(0, name.length() - 1); 38565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 38665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 38765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // See if this is a device we already know about 38865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Device device = null; 38965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final int N = mPresentDevices.size(); 39065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i=0; i<N; i++) { 39165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final Device d = mPresentDevices.get(i); 39265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (address.equals(d.address)) { 39365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane device = d; 39465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 39565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 39665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 39765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 39865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (device == null) { 39965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (DEBUG) { 40065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Log.d(TAG, "Device is a new device."); 40165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 40265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // New device. 40365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane device = new Device(); 40465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane device.btDevice = btDevice; 40565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane device.address = address; 40665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane device.consecutiveMisses = -1; 40765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 40865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane device.setNameString(name); 409510203ee5e3350758dae4bf9af05b1585029af7dWally Yau // Save it 410510203ee5e3350758dae4bf9af05b1585029af7dWally Yau mPresentDevices.add(device); 41165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 412510203ee5e3350758dae4bf9af05b1585029af7dWally Yau // Tell the listeners 413510203ee5e3350758dae4bf9af05b1585029af7dWally Yau sendDeviceAdded(device); 41465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 41565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (DEBUG) { 41665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Log.d(TAG, "Device is an existing device."); 41765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 41865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Existing device: update miss count. 41965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane device.consecutiveMisses = FOUND_ON_SCAN; 42065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (device.btName == name 42165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane || (device.btName != null && device.btName.equals(name))) { 42265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Name hasn't changed 42365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return; 42465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 42565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane device.setNameString(name); 42665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sendDeviceChanged(device); 42765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // If we can't parse it properly, treat it as a delete 42865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // when we iterate through them again. 42965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 43065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 43165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { 43265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Clear any devices that have disappeared since the last scan completed 43365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final int N = mPresentDevices.size(); 43465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i=N-1; i>=0; i--) { 43565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Device device = mPresentDevices.get(i); 43665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (device.consecutiveMisses < 0) { 43765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // -1 means found on this scan, raise to 0 for next time 43865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (DEBUG) Log.d(TAG, device.address + " -- Found"); 43965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane device.consecutiveMisses = 0; 44065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 44165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (device.consecutiveMisses >= CONSECUTIVE_MISS_THRESHOLD) { 44265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Too many failures 44365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (DEBUG) Log.d(TAG, device.address + " -- Removing"); 44465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPresentDevices.remove(i); 44565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sendDeviceRemoved(device); 44665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 44765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 44865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Didn't see it this time, but not ready to delete it yet 44965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane device.consecutiveMisses++; 45065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (DEBUG) { 45165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Log.d(TAG, device.address + " -- Missed consecutiveMisses=" 45265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane + device.consecutiveMisses); 45365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 45465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 45565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 45665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 45765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Show status when scanning is completed. 45865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sendScanningStopped(); 45965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 46065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mKeepScanning) { 46165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Try again in SCAN_DELAY ms. 46265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHandler.postDelayed(mScanTask, SCAN_DELAY); 46365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 46465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 46565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 46665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 46765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void sendScanningStarted() { 46865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane synchronized (mListenerLock) { 46965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final int N = mClients.size(); 47065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i = 0; i < N; i++) { 47165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mClients.get(i).listener.onScanningStarted(); 47265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 47365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 47465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 47565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 47665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void sendScanningStopped() { 47765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane synchronized (mListenerLock) { 47865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final int N = mClients.size(); 47965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Loop backwards through the list in case a client wants to 48065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // remove its listener in this callback. 48165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i = N - 1; i >= 0; --i) { 48265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ClientRecord client = mClients.get(i); 48365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane client.listener.onScanningStopped(client.devices); 48465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 48565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 48665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 48765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 48865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void sendDeviceAdded(Device device) { 48965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane synchronized (mListenerLock) { 49065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int ptr = mClients.size() - 1; ptr > -1; ptr--) { 49165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ClientRecord client = mClients.get(ptr); 492510203ee5e3350758dae4bf9af05b1585029af7dWally Yau for (BluetoothDeviceCriteria matcher : client.matchers) { 493510203ee5e3350758dae4bf9af05b1585029af7dWally Yau if (matcher.isMatchingDevice(device.btDevice)) { 494510203ee5e3350758dae4bf9af05b1585029af7dWally Yau client.devices.add(device); 495510203ee5e3350758dae4bf9af05b1585029af7dWally Yau client.listener.onDeviceAdded(device); 496510203ee5e3350758dae4bf9af05b1585029af7dWally Yau break; 497510203ee5e3350758dae4bf9af05b1585029af7dWally Yau } 49865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 49965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 50065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 50165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 50265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 50365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void sendDeviceChanged(Device device) { 50465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane synchronized (mListenerLock) { 50565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final int N = mClients.size(); 50665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i = 0; i < N; i++) { 50765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ClientRecord client = mClients.get(i); 50865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int ptr = client.devices.size() - 1; ptr > -1; ptr--) { 50965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Device d = client.devices.get(ptr); 51065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (d.btDevice.getAddress().equals(device.btDevice.getAddress())) { 51165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane client.listener.onDeviceChanged(device); 51265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 51365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 51465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 51565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 51665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 51765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 51865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 51965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void sendDeviceRemoved(Device device) { 52065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane synchronized (mListenerLock) { 52165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int ptr = mClients.size() - 1; ptr > -1; ptr--) { 52265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ClientRecord client = mClients.get(ptr); 52365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int devPtr = client.devices.size() - 1; devPtr > -1; devPtr--) { 52465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Device d = client.devices.get(devPtr); 52565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (d.btDevice.getAddress().equals(device.btDevice.getAddress())) { 52665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane client.devices.remove(devPtr); 52765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane client.listener.onDeviceRemoved(device); 52865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 52965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 53065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 53165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 53265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 53365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 53465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 53565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane} 536