• 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_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 
CarBluetoothService(Context context, CarPropertyService carPropertyService, PerUserCarServiceHelper userSwitchService, CarUxRestrictionsManagerService uxrService)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
init()70     public void init() {
71         mBluetoothDeviceConnectionPolicy.init();
72     }
73 
74     @Override
release()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      */
setBluetoothDeviceConnectionPriority(BluetoothDevice deviceToSet, int profileToSet, int priorityToSet)90     public void setBluetoothDeviceConnectionPriority(BluetoothDevice deviceToSet, int profileToSet,
91             int priorityToSet) {
92         setBluetoothDeviceConnectionPriority(deviceToSet.getAddress(), profileToSet, priorityToSet);
93     }
94 
setBluetoothDeviceConnectionPriority(String deviceAddress, int profileToSet, int priorityToSet)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      */
clearBluetoothDeviceConnectionPriority(int profileToClear, int priorityToClear)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      */
isPriorityDevicePresent(int profile, int priorityToCheck)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      */
getDeviceNameWithPriority(int profile, int priorityToCheck)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 
enforceBluetoothAdminPermission()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 
getKeyForProfile(int profile, int priority)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
dump(PrintWriter writer)261     public synchronized void dump(PrintWriter writer) {
262         mBluetoothDeviceConnectionPolicy.dump(writer);
263     }
264 
265 }
266