• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CarBluetoothService(Context context, CarCabinService carCabinService, CarSensorService carSensorService, PerUserCarServiceHelper userSwitchService)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
init()66     public void init() {
67         mBluetoothDeviceConnectionPolicy.init();
68     }
69 
70     @Override
release()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      */
setBluetoothDeviceConnectionPriority(BluetoothDevice deviceToSet, int profileToSet, int priorityToSet)86     public void setBluetoothDeviceConnectionPriority(BluetoothDevice deviceToSet, int profileToSet,
87             int priorityToSet) {
88         setBluetoothDeviceConnectionPriority(deviceToSet.getAddress(), profileToSet, priorityToSet);
89     }
90 
setBluetoothDeviceConnectionPriority(String deviceAddress, int profileToSet, int priorityToSet)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      */
clearBluetoothDeviceConnectionPriority(int profileToClear, int priorityToClear)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      */
isPriorityDevicePresent(int profile, int priorityToCheck)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      */
getDeviceNameWithPriority(int profile, int priorityToCheck)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 
enforceBluetoothAdminPermission()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 
getKeyForProfile(int profile, int priority)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
dump(PrintWriter writer)243     public synchronized void dump(PrintWriter writer) {
244         mBluetoothDeviceConnectionPolicy.dump(writer);
245     }
246 
247 }
248