Home | History | Annotate | Download | only in car
      1 /*
      2  * Copyright (C) 2017 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 package com.android.car;
     17 
     18 import static android.car.settings.CarSettings.Secure
     19         .KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0;
     20 import static android.car.settings.CarSettings.Secure
     21         .KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1;
     22 import static android.car.settings.CarSettings.Secure
     23         .KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0;
     24 import static android.car.settings.CarSettings.Secure
     25         .KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1;
     26 import static android.car.settings.CarSettings.Secure
     27         .KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0;
     28 import static android.car.settings.CarSettings.Secure
     29         .KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1;
     30 
     31 import android.app.ActivityManager;
     32 import android.bluetooth.BluetoothDevice;
     33 import android.bluetooth.BluetoothProfile;
     34 import android.car.CarBluetoothManager;
     35 import android.car.ICarBluetooth;
     36 import android.content.Context;
     37 import android.content.pm.PackageManager;
     38 import android.provider.Settings;
     39 import android.util.Log;
     40 
     41 import java.io.PrintWriter;
     42 
     43 /**
     44  * CarBluetoothService - deals with the automatically connecting to a known device via bluetooth.
     45  * Interacts with a policy -{@link BluetoothDeviceConnectionPolicy} -to initiate connections and
     46  * update status.
     47  * The {@link BluetoothDeviceConnectionPolicy} is responsible for finding the appropriate device to
     48  * connect for a specific profile.
     49  */
     50 
     51 public class CarBluetoothService extends ICarBluetooth.Stub implements CarServiceBase {
     52 
     53     private static final String TAG = "CarBluetoothService";
     54     private final Context mContext;
     55     private final BluetoothDeviceConnectionPolicy mBluetoothDeviceConnectionPolicy;
     56     private static final boolean DBG = false;
     57 
     58     public CarBluetoothService(Context context, CarCabinService carCabinService,
     59             CarSensorService carSensorService, PerUserCarServiceHelper userSwitchService) {
     60         mContext = context;
     61         mBluetoothDeviceConnectionPolicy = BluetoothDeviceConnectionPolicy.create(mContext,
     62                 carCabinService, carSensorService, userSwitchService, this);
     63     }
     64 
     65     @Override
     66     public void init() {
     67         mBluetoothDeviceConnectionPolicy.init();
     68     }
     69 
     70     @Override
     71     public synchronized void release() {
     72         mBluetoothDeviceConnectionPolicy.release();
     73     }
     74 
     75     /**
     76      * Set the Auto connect priority for a paired Bluetooth Device.
     77      * For example, if a device is tagged as a Primary device for a supported Bluetooth Profile,
     78      * every new Auto Connect attempt would start with trying to connect to *that* device.
     79      * This priority is set at a Bluetooth profile granularity
     80      *
     81      * @param deviceToSet   - Device to set priority (Tag)
     82      * @param profileToSet  - BluetoothProfile to set priority for.
     83      * @param priorityToSet - What priority level to set to
     84      * @hide
     85      */
     86     public void setBluetoothDeviceConnectionPriority(BluetoothDevice deviceToSet, int profileToSet,
     87             int priorityToSet) {
     88         setBluetoothDeviceConnectionPriority(deviceToSet.getAddress(), profileToSet, priorityToSet);
     89     }
     90 
     91     public void setBluetoothDeviceConnectionPriority(String deviceAddress, int profileToSet,
     92             int priorityToSet) {
     93         // Check if the caller has Bluetooth Admin Permissions
     94         enforceBluetoothAdminPermission();
     95         if (priorityToSet == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1) {
     96             if (!isPriorityDevicePresent(profileToSet,
     97                     CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)) {
     98                 Log.e(TAG, "Secondary Device not allowed without a primary device");
     99                 return;
    100             }
    101         }
    102         // Write the priority preference to Secure settings.  The Bluetooth device connection policy
    103         // will look up the Settings when it initiates a connection
    104         Settings.Secure.putStringForUser(mContext.getContentResolver(),
    105                 getKeyForProfile(profileToSet, priorityToSet), deviceAddress,
    106                 ActivityManager.getCurrentUser());
    107 
    108     }
    109 
    110     /**
    111      * Unset the Auto connect priority for the given profile
    112      *
    113      * @param profileToClear  - Profile to unset priority
    114      * @param priorityToClear - Which priority to clear (Primary or Secondary)
    115      * @hide
    116      */
    117     public void clearBluetoothDeviceConnectionPriority(int profileToClear, int priorityToClear) {
    118         // Check if the caller has Bluetooth Admin F@Permissions
    119         enforceBluetoothAdminPermission();
    120         if (priorityToClear == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) {
    121             if (isPriorityDevicePresent(profileToClear,
    122                     CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1)) {
    123                 Log.e(TAG, "Please remove Secondary device before removing Primary Device");
    124                 return;
    125             }
    126         }
    127         Settings.Secure.putStringForUser(mContext.getContentResolver(),
    128                 getKeyForProfile(profileToClear, priorityToClear),
    129                 CarBluetoothManager.BLUETOOTH_NO_PRIORITY_DEVICE,
    130                 ActivityManager.getCurrentUser());
    131     }
    132 
    133     /**
    134      * Returns if there is a device that has been tagged with the given priority for the given
    135      * profile.
    136      *
    137      * @param profile         - BluetoothProfile
    138      * @param priorityToCheck - Priority to check
    139      * @return true if there is a device present with the given priority, false if not
    140      */
    141     public boolean isPriorityDevicePresent(int profile, int priorityToCheck) {
    142         String deviceName = getDeviceNameWithPriority(profile, priorityToCheck);
    143         if (deviceName != null && !deviceName.equalsIgnoreCase(
    144                 CarBluetoothManager.BLUETOOTH_NO_PRIORITY_DEVICE)) {
    145             return true;
    146         } else {
    147             if (DBG) {
    148                 Log.d(TAG,
    149                         "No device present for priority: " + priorityToCheck + " profile: "
    150                                 + profile);
    151             }
    152             return false;
    153         }
    154     }
    155 
    156     /**
    157      * Returns the Bluetooth device address as a String that has been tagged with the given priority
    158      * for the given profile.
    159      *
    160      * @param profile         - BluetoothProfile
    161      * @param priorityToCheck - Priority to check
    162      * @return BluetoothDevice address if present, null if absent
    163      */
    164     public String getDeviceNameWithPriority(int profile, int priorityToCheck) {
    165         String keyToQuery = null;
    166         String deviceName = null;
    167         enforceBluetoothAdminPermission();
    168         switch (profile) {
    169             case BluetoothProfile.A2DP_SINK:
    170                 keyToQuery = (priorityToCheck
    171                         == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
    172                         ? KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0
    173                         : KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1;
    174                 break;
    175             case BluetoothProfile.HEADSET_CLIENT:
    176             case BluetoothProfile.PBAP_CLIENT:
    177                 keyToQuery = (priorityToCheck
    178                         == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
    179                         ? KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0
    180                         : KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1;
    181                 break;
    182             case BluetoothProfile.MAP_CLIENT:
    183                 keyToQuery = (priorityToCheck
    184                         == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
    185                         ? KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0
    186                         : KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1;
    187                 break;
    188             default:
    189                 if (DBG) {
    190                     Log.d(TAG, "Unknown Bluetooth profile");
    191                 }
    192         }
    193         if (keyToQuery != null) {
    194             deviceName = Settings.Secure.getStringForUser(mContext.getContentResolver(),
    195                     keyToQuery, (int) ActivityManager.getCurrentUser());
    196         }
    197         return deviceName;
    198     }
    199 
    200     private void enforceBluetoothAdminPermission() {
    201         if (mContext != null
    202                 && PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
    203                 android.Manifest.permission.BLUETOOTH_ADMIN)) {
    204             return;
    205         }
    206         if (mContext == null) {
    207             Log.e(TAG, "CarBluetoothPrioritySettings does not have a Context");
    208         }
    209         throw new SecurityException("requires permission " + android.Manifest.permission.BLUETOOTH_ADMIN);
    210     }
    211 
    212     private String getKeyForProfile(int profile, int priority) {
    213         String keyToLookup = null;
    214         switch (profile) {
    215             case BluetoothProfile.A2DP_SINK:
    216                 keyToLookup = (priority
    217                         == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
    218                         ? KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0
    219                         : KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1;
    220                 break;
    221             case BluetoothProfile.MAP_CLIENT:
    222                 keyToLookup = (priority
    223                         == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
    224                         ? KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0
    225                         : KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1;
    226                 break;
    227             case BluetoothProfile.PBAP_CLIENT:
    228                 // fall through
    229             case BluetoothProfile.HEADSET_CLIENT:
    230                 keyToLookup = (priority
    231                         == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
    232                         ? KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0
    233                         : KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1;
    234                 break;
    235             default:
    236                 Log.e(TAG, "Unsupported Bluetooth profile to set priority to");
    237                 break;
    238         }
    239         return keyToLookup;
    240     }
    241 
    242     @Override
    243     public synchronized void dump(PrintWriter writer) {
    244         mBluetoothDeviceConnectionPolicy.dump(writer);
    245     }
    246 
    247 }
    248