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