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