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.settingslib.bluetooth;
     18 
     19 import android.bluetooth.BluetoothAdapter;
     20 import android.bluetooth.BluetoothDevice;
     21 import android.content.Context;
     22 import android.util.Log;
     23 
     24 import com.android.internal.annotations.VisibleForTesting;
     25 
     26 import java.util.ArrayList;
     27 import java.util.Collection;
     28 import java.util.List;
     29 import java.util.Objects;
     30 
     31 /**
     32  * CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
     33  */
     34 public class CachedBluetoothDeviceManager {
     35     private static final String TAG = "CachedBluetoothDeviceManager";
     36     private static final boolean DEBUG = BluetoothUtils.D;
     37 
     38     private Context mContext;
     39     private final LocalBluetoothManager mBtManager;
     40 
     41     @VisibleForTesting
     42     final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>();
     43     @VisibleForTesting
     44     HearingAidDeviceManager mHearingAidDeviceManager;
     45 
     46     CachedBluetoothDeviceManager(Context context, LocalBluetoothManager localBtManager) {
     47         mContext = context;
     48         mBtManager = localBtManager;
     49         mHearingAidDeviceManager = new HearingAidDeviceManager(localBtManager, mCachedDevices);
     50     }
     51 
     52     public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() {
     53         return new ArrayList<>(mCachedDevices);
     54     }
     55 
     56     public static boolean onDeviceDisappeared(CachedBluetoothDevice cachedDevice) {
     57         cachedDevice.setJustDiscovered(false);
     58         return cachedDevice.getBondState() == BluetoothDevice.BOND_NONE;
     59     }
     60 
     61     public void onDeviceNameUpdated(BluetoothDevice device) {
     62         CachedBluetoothDevice cachedDevice = findDevice(device);
     63         if (cachedDevice != null) {
     64             cachedDevice.refreshName();
     65         }
     66     }
     67 
     68     /**
     69      * Search for existing {@link CachedBluetoothDevice} or return null
     70      * if this device isn't in the cache. Use {@link #addDevice}
     71      * to create and return a new {@link CachedBluetoothDevice} for
     72      * a newly discovered {@link BluetoothDevice}.
     73      *
     74      * @param device the address of the Bluetooth device
     75      * @return the cached device object for this device, or null if it has
     76      *   not been previously seen
     77      */
     78     public synchronized CachedBluetoothDevice findDevice(BluetoothDevice device) {
     79         for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
     80             if (cachedDevice.getDevice().equals(device)) {
     81                 return cachedDevice;
     82             }
     83             // Check sub devices if it exists
     84             CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
     85             if (subDevice != null && subDevice.getDevice().equals(device)) {
     86                 return subDevice;
     87             }
     88         }
     89 
     90         return null;
     91     }
     92 
     93     /**
     94      * Create and return a new {@link CachedBluetoothDevice}. This assumes
     95      * that {@link #findDevice} has already been called and returned null.
     96      * @param device the address of the new Bluetooth device
     97      * @return the newly created CachedBluetoothDevice object
     98      */
     99     public CachedBluetoothDevice addDevice(BluetoothDevice device) {
    100         LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
    101         CachedBluetoothDevice newDevice = new CachedBluetoothDevice(mContext, profileManager,
    102                 device);
    103         mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice);
    104         synchronized (this) {
    105             if (!mHearingAidDeviceManager.setSubDeviceIfNeeded(newDevice)) {
    106                 mCachedDevices.add(newDevice);
    107                 mBtManager.getEventManager().dispatchDeviceAdded(newDevice);
    108             }
    109         }
    110 
    111         return newDevice;
    112     }
    113 
    114     /**
    115      * Returns device summary of the pair of the hearing aid passed as the parameter.
    116      *
    117      * @param CachedBluetoothDevice device
    118      * @return Device summary, or if the pair does not exist or if it is not a hearing aid,
    119      * then {@code null}.
    120      */
    121     public synchronized String getSubDeviceSummary(CachedBluetoothDevice device) {
    122         CachedBluetoothDevice subDevice = device.getSubDevice();
    123         if (subDevice != null && subDevice.isConnected()) {
    124             return subDevice.getConnectionSummary();
    125         }
    126         return null;
    127     }
    128 
    129     /**
    130      * Search for existing sub device {@link CachedBluetoothDevice}.
    131      *
    132      * @param device the address of the Bluetooth device
    133      * @return true for found sub device or false.
    134      */
    135     public synchronized boolean isSubDevice(BluetoothDevice device) {
    136         for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
    137             if (!cachedDevice.getDevice().equals(device)) {
    138                 // Check sub devices if it exists
    139                 CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
    140                 if (subDevice != null && subDevice.getDevice().equals(device)) {
    141                     return true;
    142                 }
    143             }
    144         }
    145         return false;
    146     }
    147 
    148     /**
    149      * Updates the Hearing Aid devices; specifically the HiSyncId's. This routine is called when the
    150      * Hearing Aid Service is connected and the HiSyncId's are now available.
    151      * @param LocalBluetoothProfileManager profileManager
    152      */
    153     public synchronized void updateHearingAidsDevices() {
    154         mHearingAidDeviceManager.updateHearingAidsDevices();
    155     }
    156 
    157     /**
    158      * Attempts to get the name of a remote device, otherwise returns the address.
    159      *
    160      * @param device The remote device.
    161      * @return The name, or if unavailable, the address.
    162      */
    163     public String getName(BluetoothDevice device) {
    164         CachedBluetoothDevice cachedDevice = findDevice(device);
    165         if (cachedDevice != null && cachedDevice.getName() != null) {
    166             return cachedDevice.getName();
    167         }
    168 
    169         String name = device.getAliasName();
    170         if (name != null) {
    171             return name;
    172         }
    173 
    174         return device.getAddress();
    175     }
    176 
    177     public synchronized void clearNonBondedDevices() {
    178         clearNonBondedSubDevices();
    179         mCachedDevices.removeIf(cachedDevice
    180             -> cachedDevice.getBondState() == BluetoothDevice.BOND_NONE);
    181     }
    182 
    183     private void clearNonBondedSubDevices() {
    184         for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
    185             CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
    186             CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
    187             if (subDevice != null
    188                     && subDevice.getDevice().getBondState() == BluetoothDevice.BOND_NONE) {
    189                 // Sub device exists and it is not bonded
    190                 cachedDevice.setSubDevice(null);
    191             }
    192         }
    193     }
    194 
    195     public synchronized void onScanningStateChanged(boolean started) {
    196         if (!started) return;
    197         // If starting a new scan, clear old visibility
    198         // Iterate in reverse order since devices may be removed.
    199         for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
    200             CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
    201             cachedDevice.setJustDiscovered(false);
    202             final CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
    203             if (subDevice != null) {
    204                 subDevice.setJustDiscovered(false);
    205             }
    206         }
    207     }
    208 
    209     public synchronized void onBluetoothStateChanged(int bluetoothState) {
    210         // When Bluetooth is turning off, we need to clear the non-bonded devices
    211         // Otherwise, they end up showing up on the next BT enable
    212         if (bluetoothState == BluetoothAdapter.STATE_TURNING_OFF) {
    213             for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
    214                 CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
    215                 CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
    216                 if (subDevice != null) {
    217                     if (subDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
    218                         cachedDevice.setSubDevice(null);
    219                     }
    220                 }
    221                 if (cachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
    222                     cachedDevice.setJustDiscovered(false);
    223                     mCachedDevices.remove(i);
    224                 }
    225             }
    226         }
    227     }
    228 
    229     public synchronized void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
    230             int bluetoothProfile) {
    231         for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
    232             boolean isActive = Objects.equals(cachedDevice, activeDevice);
    233             cachedDevice.onActiveDeviceChanged(isActive, bluetoothProfile);
    234         }
    235     }
    236 
    237     public synchronized boolean onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice
    238             cachedDevice, int state) {
    239         return mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
    240                 state);
    241     }
    242 
    243     public synchronized void onDeviceUnpaired(CachedBluetoothDevice device) {
    244         CachedBluetoothDevice mainDevice = mHearingAidDeviceManager.findMainDevice(device);
    245         CachedBluetoothDevice subDevice = device.getSubDevice();
    246         if (subDevice != null) {
    247             // Main device is unpaired, to unpair sub device
    248             subDevice.unpair();
    249             device.setSubDevice(null);
    250         } else if (mainDevice != null) {
    251             // Sub device unpaired, to unpair main device
    252             mainDevice.unpair();
    253             mainDevice.setSubDevice(null);
    254         }
    255     }
    256 
    257     public synchronized void dispatchAudioModeChanged() {
    258         for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
    259             cachedDevice.onAudioModeChanged();
    260         }
    261     }
    262 
    263     private void log(String msg) {
    264         if (DEBUG) {
    265             Log.d(TAG, msg);
    266         }
    267     }
    268 }
    269