Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings.bluetooth;
     18 
     19 import android.bluetooth.BluetoothAdapter;
     20 import android.bluetooth.BluetoothClass;
     21 import android.bluetooth.BluetoothDevice;
     22 import android.util.Log;
     23 
     24 import com.android.settings.R;
     25 import com.android.settings.bluetooth.LocalBluetoothManager.Callback;
     26 import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
     27 
     28 import java.util.ArrayList;
     29 import java.util.List;
     30 import java.util.Set;
     31 
     32 /**
     33  * CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
     34  */
     35 public class CachedBluetoothDeviceManager {
     36     private static final String TAG = "CachedBluetoothDeviceManager";
     37 
     38     final LocalBluetoothManager mLocalManager;
     39     final List<Callback> mCallbacks;
     40 
     41     final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>();
     42 
     43     public CachedBluetoothDeviceManager(LocalBluetoothManager localManager) {
     44         mLocalManager = localManager;
     45         mCallbacks = localManager.getCallbacks();
     46     }
     47 
     48     private synchronized boolean readPairedDevices() {
     49         BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter();
     50         Set<BluetoothDevice> bondedDevices = adapter.getBondedDevices();
     51         if (bondedDevices == null) return false;
     52 
     53         boolean deviceAdded = false;
     54         for (BluetoothDevice device : bondedDevices) {
     55             CachedBluetoothDevice cachedDevice = findDevice(device);
     56             if (cachedDevice == null) {
     57                 cachedDevice = new CachedBluetoothDevice(mLocalManager.getContext(), device);
     58                 mCachedDevices.add(cachedDevice);
     59                 dispatchDeviceAdded(cachedDevice);
     60                 deviceAdded = true;
     61             }
     62         }
     63 
     64         return deviceAdded;
     65     }
     66 
     67     public synchronized List<CachedBluetoothDevice> getCachedDevicesCopy() {
     68         return new ArrayList<CachedBluetoothDevice>(mCachedDevices);
     69     }
     70 
     71     void onBluetoothStateChanged(boolean enabled) {
     72         if (enabled) {
     73             readPairedDevices();
     74         }
     75     }
     76 
     77     public synchronized void onDeviceAppeared(BluetoothDevice device, short rssi,
     78             BluetoothClass btClass, String name) {
     79         boolean deviceAdded = false;
     80 
     81         CachedBluetoothDevice cachedDevice = findDevice(device);
     82         if (cachedDevice == null) {
     83             cachedDevice = new CachedBluetoothDevice(mLocalManager.getContext(), device);
     84             mCachedDevices.add(cachedDevice);
     85             deviceAdded = true;
     86         }
     87         cachedDevice.setRssi(rssi);
     88         cachedDevice.setBtClass(btClass);
     89         cachedDevice.setName(name);
     90         cachedDevice.setVisible(true);
     91 
     92         if (deviceAdded) {
     93             dispatchDeviceAdded(cachedDevice);
     94         }
     95     }
     96 
     97     public synchronized void onDeviceDisappeared(BluetoothDevice device) {
     98         CachedBluetoothDevice cachedDevice = findDevice(device);
     99         if (cachedDevice == null) return;
    100 
    101         cachedDevice.setVisible(false);
    102         checkForDeviceRemoval(cachedDevice);
    103     }
    104 
    105     private void checkForDeviceRemoval(CachedBluetoothDevice cachedDevice) {
    106         if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&
    107                 !cachedDevice.isVisible()) {
    108             // If device isn't paired, remove it altogether
    109             mCachedDevices.remove(cachedDevice);
    110             dispatchDeviceDeleted(cachedDevice);
    111         }
    112     }
    113 
    114     public synchronized void onDeviceNameUpdated(BluetoothDevice device) {
    115         CachedBluetoothDevice cachedDevice = findDevice(device);
    116         if (cachedDevice != null) {
    117             cachedDevice.refreshName();
    118         }
    119     }
    120 
    121     public synchronized CachedBluetoothDevice findDevice(BluetoothDevice device) {
    122 
    123         for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
    124             CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
    125 
    126             if (cachedDevice.getDevice().equals(device)) {
    127                 return cachedDevice;
    128             }
    129         }
    130 
    131         return null;
    132     }
    133 
    134     /**
    135      * Attempts to get the name of a remote device, otherwise returns the address.
    136      *
    137      * @param device The remote device.
    138      * @return The name, or if unavailable, the address.
    139      */
    140     public String getName(BluetoothDevice device) {
    141         CachedBluetoothDevice cachedDevice = findDevice(device);
    142         if (cachedDevice != null) return cachedDevice.getName();
    143 
    144         String name = device.getName();
    145         if (name != null) return name;
    146 
    147         return device.getAddress();
    148     }
    149 
    150     private void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
    151         synchronized (mCallbacks) {
    152             for (Callback callback : mCallbacks) {
    153                 callback.onDeviceAdded(cachedDevice);
    154             }
    155         }
    156 
    157         // TODO: divider between prev paired/connected and scanned
    158     }
    159 
    160     private void dispatchDeviceDeleted(CachedBluetoothDevice cachedDevice) {
    161         synchronized (mCallbacks) {
    162             for (Callback callback : mCallbacks) {
    163                 callback.onDeviceDeleted(cachedDevice);
    164             }
    165         }
    166     }
    167 
    168     public synchronized void onBondingStateChanged(BluetoothDevice device, int bondState) {
    169         CachedBluetoothDevice cachedDevice = findDevice(device);
    170         if (cachedDevice == null) {
    171             if (!readPairedDevices()) {
    172                 Log.e(TAG, "Got bonding state changed for " + device +
    173                         ", but we have no record of that device.");
    174                 return;
    175             }
    176             cachedDevice = findDevice(device);
    177             if (cachedDevice == null) {
    178                 Log.e(TAG, "Got bonding state changed for " + device +
    179                         "but device not added in cache");
    180                 return;
    181             }
    182         }
    183 
    184         cachedDevice.onBondingStateChanged(bondState);
    185     }
    186 
    187     /**
    188      * Called when we have reached the un-bond state.
    189      *
    190      * @param device The remote device.
    191      * @param reason The reason, one of the error reasons from
    192      *            BluetoothDevice.UNBOND_REASON_*
    193      */
    194     public synchronized void showUnbondMessage(BluetoothDevice device, int reason) {
    195         int errorMsg;
    196 
    197         switch(reason) {
    198         case BluetoothDevice.UNBOND_REASON_AUTH_FAILED:
    199             errorMsg = R.string.bluetooth_pairing_pin_error_message;
    200             mLocalManager.showError(device, R.string.bluetooth_error_title, errorMsg);
    201             break;
    202         case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED:
    203             errorMsg = R.string.bluetooth_pairing_rejected_error_message;
    204             mLocalManager.showError(device, R.string.bluetooth_error_title, errorMsg);
    205             break;
    206         case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN:
    207             errorMsg = R.string.bluetooth_pairing_device_down_error_message;
    208             mLocalManager.showError(device, R.string.bluetooth_error_title, errorMsg);
    209             break;
    210         case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS:
    211         case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
    212         case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
    213         case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
    214             errorMsg = R.string.bluetooth_pairing_error_message;
    215             mLocalManager.showError(device, R.string.bluetooth_error_title, errorMsg);
    216             break;
    217         default:
    218             Log.w(TAG, "showUnbondMessage: Not displaying any message for reason:" + reason);
    219             break;
    220         }
    221     }
    222 
    223     public synchronized void onProfileStateChanged(BluetoothDevice device, Profile profile,
    224             int newProfileState) {
    225         CachedBluetoothDevice cachedDevice = findDevice(device);
    226         if (cachedDevice == null) return;
    227 
    228         cachedDevice.onProfileStateChanged(profile, newProfileState);
    229         cachedDevice.refresh();
    230     }
    231 
    232     public synchronized void onConnectingError(BluetoothDevice device) {
    233         CachedBluetoothDevice cachedDevice = findDevice(device);
    234         if (cachedDevice == null) return;
    235 
    236         /*
    237          * Go through the device's delegate so we don't spam the user with
    238          * errors connecting to different profiles, and instead make sure the
    239          * user sees a single error for his single 'connect' action.
    240          */
    241         cachedDevice.showConnectingError();
    242     }
    243 
    244     public synchronized void onScanningStateChanged(boolean started) {
    245         if (!started) return;
    246 
    247         // If starting a new scan, clear old visibility
    248         for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
    249             CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
    250             cachedDevice.setVisible(false);
    251             checkForDeviceRemoval(cachedDevice);
    252         }
    253     }
    254 
    255     public synchronized void onBtClassChanged(BluetoothDevice device) {
    256         CachedBluetoothDevice cachedDevice = findDevice(device);
    257         if (cachedDevice != null) {
    258             cachedDevice.refreshBtClass();
    259         }
    260     }
    261 
    262     public synchronized void onUuidChanged(BluetoothDevice device) {
    263         CachedBluetoothDevice cachedDevice = findDevice(device);
    264         if (cachedDevice != null) {
    265             cachedDevice.onUuidChanged();
    266         }
    267     }
    268 }
    269