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