/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.bluetooth; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.util.Log; import com.android.settings.R; import com.android.settings.bluetooth.LocalBluetoothManager.Callback; import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * CachedBluetoothDeviceManager manages the set of remote Bluetooth devices. */ public class CachedBluetoothDeviceManager { private static final String TAG = "CachedBluetoothDeviceManager"; final LocalBluetoothManager mLocalManager; final List<Callback> mCallbacks; final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>(); public CachedBluetoothDeviceManager(LocalBluetoothManager localManager) { mLocalManager = localManager; mCallbacks = localManager.getCallbacks(); } private synchronized boolean readPairedDevices() { BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter(); Set<BluetoothDevice> bondedDevices = adapter.getBondedDevices(); if (bondedDevices == null) return false; boolean deviceAdded = false; for (BluetoothDevice device : bondedDevices) { CachedBluetoothDevice cachedDevice = findDevice(device); if (cachedDevice == null) { cachedDevice = new CachedBluetoothDevice(mLocalManager.getContext(), device); mCachedDevices.add(cachedDevice); dispatchDeviceAdded(cachedDevice); deviceAdded = true; } } return deviceAdded; } public synchronized List<CachedBluetoothDevice> getCachedDevicesCopy() { return new ArrayList<CachedBluetoothDevice>(mCachedDevices); } void onBluetoothStateChanged(boolean enabled) { if (enabled) { readPairedDevices(); } } public synchronized void onDeviceAppeared(BluetoothDevice device, short rssi, BluetoothClass btClass, String name) { boolean deviceAdded = false; CachedBluetoothDevice cachedDevice = findDevice(device); if (cachedDevice == null) { cachedDevice = new CachedBluetoothDevice(mLocalManager.getContext(), device); mCachedDevices.add(cachedDevice); deviceAdded = true; } cachedDevice.setRssi(rssi); cachedDevice.setBtClass(btClass); cachedDevice.setName(name); cachedDevice.setVisible(true); if (deviceAdded) { dispatchDeviceAdded(cachedDevice); } } public synchronized void onDeviceDisappeared(BluetoothDevice device) { CachedBluetoothDevice cachedDevice = findDevice(device); if (cachedDevice == null) return; cachedDevice.setVisible(false); checkForDeviceRemoval(cachedDevice); } private void checkForDeviceRemoval(CachedBluetoothDevice cachedDevice) { if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE && !cachedDevice.isVisible()) { // If device isn't paired, remove it altogether mCachedDevices.remove(cachedDevice); dispatchDeviceDeleted(cachedDevice); } } public synchronized void onDeviceNameUpdated(BluetoothDevice device) { CachedBluetoothDevice cachedDevice = findDevice(device); if (cachedDevice != null) { cachedDevice.refreshName(); } } public synchronized CachedBluetoothDevice findDevice(BluetoothDevice device) { for (int i = mCachedDevices.size() - 1; i >= 0; i--) { CachedBluetoothDevice cachedDevice = mCachedDevices.get(i); if (cachedDevice.getDevice().equals(device)) { return cachedDevice; } } return null; } /** * Attempts to get the name of a remote device, otherwise returns the address. * * @param device The remote device. * @return The name, or if unavailable, the address. */ public String getName(BluetoothDevice device) { CachedBluetoothDevice cachedDevice = findDevice(device); if (cachedDevice != null) return cachedDevice.getName(); String name = device.getName(); if (name != null) return name; return device.getAddress(); } private void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) { synchronized (mCallbacks) { for (Callback callback : mCallbacks) { callback.onDeviceAdded(cachedDevice); } } // TODO: divider between prev paired/connected and scanned } private void dispatchDeviceDeleted(CachedBluetoothDevice cachedDevice) { synchronized (mCallbacks) { for (Callback callback : mCallbacks) { callback.onDeviceDeleted(cachedDevice); } } } public synchronized void onBondingStateChanged(BluetoothDevice device, int bondState) { CachedBluetoothDevice cachedDevice = findDevice(device); if (cachedDevice == null) { if (!readPairedDevices()) { Log.e(TAG, "Got bonding state changed for " + device + ", but we have no record of that device."); return; } cachedDevice = findDevice(device); if (cachedDevice == null) { Log.e(TAG, "Got bonding state changed for " + device + "but device not added in cache"); return; } } cachedDevice.onBondingStateChanged(bondState); if (bondState == BluetoothDevice.BOND_BONDED) { // Auto-connect after pairing if (!device.isBluetoothDock()) { cachedDevice.connect(); } else { cachedDevice.onBondingDockConnect(); } } } /** * Called when we have reached the un-bond state. * * @param device The remote device. * @param reason The reason, one of the error reasons from * BluetoothDevice.UNBOND_REASON_* */ public synchronized void showUnbondMessage(BluetoothDevice device, int reason) { int errorMsg; switch(reason) { case BluetoothDevice.UNBOND_REASON_AUTH_FAILED: errorMsg = R.string.bluetooth_pairing_pin_error_message; mLocalManager.showError(device, R.string.bluetooth_error_title, errorMsg); break; case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED: errorMsg = R.string.bluetooth_pairing_rejected_error_message; mLocalManager.showError(device, R.string.bluetooth_error_title, errorMsg); break; case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN: errorMsg = R.string.bluetooth_pairing_device_down_error_message; mLocalManager.showError(device, R.string.bluetooth_error_title, errorMsg); break; case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS: case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT: case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS: case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED: errorMsg = R.string.bluetooth_pairing_error_message; mLocalManager.showError(device, R.string.bluetooth_error_title, errorMsg); break; default: Log.w(TAG, "showUnbondMessage: Not displaying any message for reason:" + reason); break; } } public synchronized void onProfileStateChanged(BluetoothDevice device, Profile profile, int newProfileState) { CachedBluetoothDevice cachedDevice = findDevice(device); if (cachedDevice == null) return; cachedDevice.onProfileStateChanged(profile, newProfileState); cachedDevice.refresh(); } public synchronized void onConnectingError(BluetoothDevice device) { CachedBluetoothDevice cachedDevice = findDevice(device); if (cachedDevice == null) return; /* * Go through the device's delegate so we don't spam the user with * errors connecting to different profiles, and instead make sure the * user sees a single error for his single 'connect' action. */ cachedDevice.showConnectingError(); } public synchronized void onScanningStateChanged(boolean started) { if (!started) return; // If starting a new scan, clear old visibility for (int i = mCachedDevices.size() - 1; i >= 0; i--) { CachedBluetoothDevice cachedDevice = mCachedDevices.get(i); cachedDevice.setVisible(false); checkForDeviceRemoval(cachedDevice); } } public synchronized void onBtClassChanged(BluetoothDevice device) { CachedBluetoothDevice cachedDevice = findDevice(device); if (cachedDevice != null) { cachedDevice.refreshBtClass(); } } public synchronized void onUuidChanged(BluetoothDevice device) { CachedBluetoothDevice cachedDevice = findDevice(device); if (cachedDevice != null) { cachedDevice.onUuidChanged(); } } }