1 /*
2  * Copyright (c) 2016 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.bluetooth.pbapclient;
18 
19 import android.accounts.Account;
20 import android.accounts.AccountManager;
21 import android.bluetooth.BluetoothDevice;
22 import android.bluetooth.BluetoothProfile;
23 import android.bluetooth.IBluetoothPbapClient;
24 import android.bluetooth.IBluetoothHeadsetClient;
25 import android.content.BroadcastReceiver;
26 import android.content.ContentProviderOperation;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.OperationApplicationException;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.HandlerThread;
34 import android.os.Message;
35 import android.os.RemoteException;
36 import android.provider.Settings;
37 import android.util.Log;
38 import android.provider.ContactsContract;
39 
40 import com.android.bluetooth.btservice.ProfileService;
41 import com.android.bluetooth.Utils;
42 import com.android.vcard.VCardEntry;
43 
44 
45 import java.lang.ref.WeakReference;
46 import java.util.Arrays;
47 import java.util.ArrayList;
48 import java.util.List;
49 import java.util.HashMap;
50 
51 /**
52  * Provides Bluetooth Phone Book Access Profile Client profile.
53  *
54  * @hide
55  */
56 public class PbapClientService extends ProfileService {
57     private static final boolean DBG = false;
58     private static final String TAG = "PbapClientService";
59     private PbapPCEClient mClient;
60     private HandlerThread mHandlerThread;
61     private AccountManager mAccountManager;
62     private static PbapClientService sPbapClientService;
63     private PbapBroadcastReceiver mPbapBroadcastReceiver = new PbapBroadcastReceiver();
64 
65     @Override
getName()66     protected String getName() {
67         return TAG;
68     }
69 
70     @Override
initBinder()71     public IProfileServiceBinder initBinder() {
72         return new BluetoothPbapClientBinder(this);
73     }
74 
75     @Override
start()76     protected boolean start() {
77         IntentFilter filter = new IntentFilter();
78         filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
79         filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
80         try {
81             registerReceiver(mPbapBroadcastReceiver, filter);
82         } catch (Exception e) {
83             Log.w(TAG,"Unable to register pbapclient receiver",e);
84         }
85         mClient = new PbapPCEClient(this);
86         mAccountManager = AccountManager.get(this);
87         setPbapClientService(this);
88         mClient.start();
89         return true;
90     }
91 
92     @Override
stop()93     protected boolean stop() {
94         try {
95             unregisterReceiver(mPbapBroadcastReceiver);
96         } catch (Exception e) {
97             Log.w(TAG,"Unable to unregister sap receiver",e);
98         }
99         mClient.disconnect(null);
100         return true;
101     }
102 
103     @Override
cleanup()104     protected boolean cleanup() {
105         clearPbapClientService();
106         return true;
107     }
108 
109     private class PbapBroadcastReceiver extends BroadcastReceiver {
110         @Override
onReceive(Context context, Intent intent)111         public void onReceive(Context context, Intent intent) {
112             Log.v(TAG, "onReceive");
113             String action = intent.getAction();
114             if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
115                   BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
116                   if(getPriority(device) >= BluetoothProfile.PRIORITY_ON) {
117                       connect(device);
118                   }
119             } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
120                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
121                 disconnect(device);
122             }
123         }
124     }
125 
126     /**
127      * Handler for incoming service calls
128      */
129     private static class BluetoothPbapClientBinder extends IBluetoothPbapClient.Stub
130             implements IProfileServiceBinder {
131         private PbapClientService mService;
132 
BluetoothPbapClientBinder(PbapClientService svc)133         public BluetoothPbapClientBinder(PbapClientService svc) {
134             mService = svc;
135         }
136 
137         @Override
cleanup()138         public boolean cleanup() {
139             mService = null;
140             return true;
141         }
142 
getService()143         private PbapClientService getService() {
144             if (!Utils.checkCaller()) {
145                 Log.w(TAG, "PbapClient call not allowed for non-active user");
146                 return null;
147             }
148 
149             if (mService != null && mService.isAvailable()) {
150                 return mService;
151             }
152             return null;
153         }
154 
155         @Override
connect(BluetoothDevice device)156         public boolean connect(BluetoothDevice device) {
157             PbapClientService service = getService();
158             if (service == null) {
159                 return false;
160             }
161             return service.connect(device);
162         }
163 
164         @Override
disconnect(BluetoothDevice device)165         public boolean disconnect(BluetoothDevice device) {
166             PbapClientService service = getService();
167             if (service == null) {
168                 return false;
169             }
170             return service.disconnect(device);
171         }
172 
173         @Override
getConnectedDevices()174         public List<BluetoothDevice> getConnectedDevices() {
175             PbapClientService service = getService();
176             if (service == null) {
177                 return new ArrayList<BluetoothDevice>(0);
178             }
179             return service.getConnectedDevices();
180         }
181         @Override
getDevicesMatchingConnectionStates(int[] states)182         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
183             PbapClientService service = getService();
184             if (service == null) {
185                 return new ArrayList<BluetoothDevice>(0);
186             }
187             return service.getDevicesMatchingConnectionStates(states);
188         }
189 
190         @Override
getConnectionState(BluetoothDevice device)191         public int getConnectionState(BluetoothDevice device) {
192             PbapClientService service = getService();
193             if (service == null) {
194                 return BluetoothProfile.STATE_DISCONNECTED;
195             }
196             return service.getConnectionState(device);
197         }
198 
setPriority(BluetoothDevice device, int priority)199         public boolean setPriority(BluetoothDevice device, int priority) {
200             PbapClientService service = getService();
201             if (service == null) {
202                 return false;
203             }
204             return service.setPriority(device, priority);
205         }
206 
getPriority(BluetoothDevice device)207         public int getPriority(BluetoothDevice device) {
208             PbapClientService service = getService();
209             if (service == null) {
210                 return BluetoothProfile.PRIORITY_UNDEFINED;
211             }
212             return service.getPriority(device);
213         }
214 
215 
216     }
217 
218 
219     // API methods
getPbapClientService()220     public static synchronized PbapClientService getPbapClientService() {
221         if (sPbapClientService != null && sPbapClientService.isAvailable()) {
222             if (DBG) {
223                 Log.d(TAG, "getPbapClientService(): returning " + sPbapClientService);
224             }
225             return sPbapClientService;
226         }
227         if (DBG) {
228             if (sPbapClientService == null) {
229                 Log.d(TAG, "getPbapClientService(): service is NULL");
230             } else if (!(sPbapClientService.isAvailable())) {
231                 Log.d(TAG, "getPbapClientService(): service is not available");
232             }
233         }
234         return null;
235     }
236 
setPbapClientService(PbapClientService instance)237     private static synchronized void setPbapClientService(PbapClientService instance) {
238         if (instance != null && instance.isAvailable()) {
239             if (DBG) {
240                 Log.d(TAG, "setPbapClientService(): set to: " + sPbapClientService);
241             }
242             sPbapClientService = instance;
243         } else {
244             if (DBG) {
245                 if (sPbapClientService == null) {
246                     Log.d(TAG, "setPbapClientService(): service not available");
247                 } else if (!sPbapClientService.isAvailable()) {
248                     Log.d(TAG, "setPbapClientService(): service is cleaning up");
249                 }
250             }
251         }
252     }
253 
clearPbapClientService()254     private static synchronized void clearPbapClientService() {
255         sPbapClientService = null;
256     }
257 
connect(BluetoothDevice device)258     public boolean connect(BluetoothDevice device) {
259         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
260                 "Need BLUETOOTH ADMIN permission");
261         Log.d(TAG,"Received request to ConnectPBAPPhonebook " + device.getAddress());
262         int connectionState = mClient.getConnectionState();
263         if (connectionState == BluetoothProfile.STATE_CONNECTED ||
264                 connectionState == BluetoothProfile.STATE_CONNECTING) {
265             return false;
266         }
267         if (getPriority(device)>BluetoothProfile.PRIORITY_OFF) {
268             mClient.connect(device);
269             return true;
270         }
271         return false;
272     }
273 
disconnect(BluetoothDevice device)274     boolean disconnect(BluetoothDevice device) {
275         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
276                 "Need BLUETOOTH ADMIN permission");
277         mClient.disconnect(device);
278         return true;
279     }
getConnectedDevices()280     public List<BluetoothDevice> getConnectedDevices() {
281         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
282         int[] desiredStates = {BluetoothProfile.STATE_CONNECTED};
283         return getDevicesMatchingConnectionStates(desiredStates);
284     }
285 
getDevicesMatchingConnectionStates(int[] states)286     private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
287         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
288         int clientState = mClient.getConnectionState();
289         Log.d(TAG,"getDevicesMatchingConnectionStates " + Arrays.toString(states) + " == " + clientState);
290         List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
291         for (int state : states) {
292             if (clientState == state) {
293                 BluetoothDevice currentDevice = mClient.getDevice();
294                 if (currentDevice != null) {
295                     deviceList.add(currentDevice);
296                 }
297             }
298         }
299         return deviceList;
300     }
301 
getConnectionState(BluetoothDevice device)302     int getConnectionState(BluetoothDevice device) {
303         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
304         if (device == mClient.getDevice()) {
305             return mClient.getConnectionState();
306         }
307         return BluetoothProfile.STATE_DISCONNECTED;
308     }
309 
setPriority(BluetoothDevice device, int priority)310     public boolean setPriority(BluetoothDevice device, int priority) {
311         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
312                 "Need BLUETOOTH_ADMIN permission");
313         Settings.Global.putInt(getContentResolver(),
314                 Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()),
315                 priority);
316         if (DBG) {
317             Log.d(TAG,"Saved priority " + device + " = " + priority);
318         }
319         return true;
320     }
321 
getPriority(BluetoothDevice device)322     public int getPriority(BluetoothDevice device) {
323         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
324                 "Need BLUETOOTH_ADMIN permission");
325         int priority = Settings.Global.getInt(getContentResolver(),
326                 Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()),
327                 BluetoothProfile.PRIORITY_UNDEFINED);
328         return priority;
329     }
330 
331 }
332