blob: a80f5b7f36d4eefb01f13209e0c14dda7dde8852 [file] [log] [blame]
Alan Viverette3da604b2020-06-10 18:34:39 +00001/*
2 * Copyright (C) 2008 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 android.bluetooth;
18
19import android.Manifest;
20import android.annotation.IntDef;
21import android.annotation.NonNull;
22import android.annotation.RequiresPermission;
23import android.annotation.SdkConstant;
24import android.annotation.SdkConstant.SdkConstantType;
25import android.annotation.SuppressLint;
26import android.annotation.SystemApi;
27import android.compat.annotation.UnsupportedAppUsage;
28import android.content.Context;
29import android.os.Binder;
30import android.os.IBinder;
31import android.os.RemoteException;
32import android.util.Log;
33
34import java.lang.annotation.Retention;
35import java.lang.annotation.RetentionPolicy;
36import java.util.ArrayList;
37import java.util.List;
38
39/**
40 * This class provides the APIs to control the Bluetooth Pan
41 * Profile.
42 *
43 * <p>BluetoothPan is a proxy object for controlling the Bluetooth
44 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
45 * the BluetoothPan proxy object.
46 *
47 * <p>Each method is protected with its appropriate permission.
48 *
49 * @hide
50 */
51@SystemApi
52public final class BluetoothPan implements BluetoothProfile {
53 private static final String TAG = "BluetoothPan";
54 private static final boolean DBG = true;
55 private static final boolean VDBG = false;
56
57 /**
58 * Intent used to broadcast the change in connection state of the Pan
59 * profile.
60 *
61 * <p>This intent will have 4 extras:
62 * <ul>
63 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
64 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
65 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
66 * <li> {@link #EXTRA_LOCAL_ROLE} - Which local role the remote device is
67 * bound to. </li>
68 * </ul>
69 *
70 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
71 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
72 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
73 *
74 * <p> {@link #EXTRA_LOCAL_ROLE} can be one of {@link #LOCAL_NAP_ROLE} or
75 * {@link #LOCAL_PANU_ROLE}
76 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
77 * receive.
78 */
79 @SuppressLint("ActionValue")
80 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
81 public static final String ACTION_CONNECTION_STATE_CHANGED =
82 "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
83
84 /**
85 * Extra for {@link #ACTION_CONNECTION_STATE_CHANGED} intent
86 * The local role of the PAN profile that the remote device is bound to.
87 * It can be one of {@link #LOCAL_NAP_ROLE} or {@link #LOCAL_PANU_ROLE}.
88 */
89 @SuppressLint("ActionValue")
90 public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
91
92 /** @hide */
93 @IntDef({PAN_ROLE_NONE, LOCAL_NAP_ROLE, LOCAL_PANU_ROLE})
94 @Retention(RetentionPolicy.SOURCE)
95 public @interface LocalPanRole {}
96
97 public static final int PAN_ROLE_NONE = 0;
98 /**
99 * The local device is acting as a Network Access Point.
100 */
101 public static final int LOCAL_NAP_ROLE = 1;
102
103 /**
104 * The local device is acting as a PAN User.
105 */
106 public static final int LOCAL_PANU_ROLE = 2;
107
108 /** @hide */
109 @IntDef({PAN_ROLE_NONE, REMOTE_NAP_ROLE, REMOTE_PANU_ROLE})
110 @Retention(RetentionPolicy.SOURCE)
111 public @interface RemotePanRole {}
112
113 public static final int REMOTE_NAP_ROLE = 1;
114
115 public static final int REMOTE_PANU_ROLE = 2;
116
117 /**
118 * Return codes for the connect and disconnect Bluez / Dbus calls.
119 *
120 * @hide
121 */
122 public static final int PAN_DISCONNECT_FAILED_NOT_CONNECTED = 1000;
123
124 /**
125 * @hide
126 */
127 public static final int PAN_CONNECT_FAILED_ALREADY_CONNECTED = 1001;
128
129 /**
130 * @hide
131 */
132 public static final int PAN_CONNECT_FAILED_ATTEMPT_FAILED = 1002;
133
134 /**
135 * @hide
136 */
137 public static final int PAN_OPERATION_GENERIC_FAILURE = 1003;
138
139 /**
140 * @hide
141 */
142 public static final int PAN_OPERATION_SUCCESS = 1004;
143
144 private final Context mContext;
145
146 private BluetoothAdapter mAdapter;
147 private final BluetoothProfileConnector<IBluetoothPan> mProfileConnector =
148 new BluetoothProfileConnector(this, BluetoothProfile.PAN,
149 "BluetoothPan", IBluetoothPan.class.getName()) {
150 @Override
151 public IBluetoothPan getServiceInterface(IBinder service) {
152 return IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service));
153 }
154 };
155
156
157 /**
158 * Create a BluetoothPan proxy object for interacting with the local
159 * Bluetooth Service which handles the Pan profile
160 *
161 * @hide
162 */
163 @UnsupportedAppUsage
164 /*package*/ BluetoothPan(Context context, ServiceListener listener) {
165 mAdapter = BluetoothAdapter.getDefaultAdapter();
166 mContext = context;
167 mProfileConnector.connect(context, listener);
168 }
169
170 /**
171 * Closes the connection to the service and unregisters callbacks
172 */
173 @UnsupportedAppUsage
174 void close() {
175 if (VDBG) log("close()");
176 mProfileConnector.disconnect();
177 }
178
179 private IBluetoothPan getService() {
180 return mProfileConnector.getService();
181 }
182
183 /** @hide */
184 protected void finalize() {
185 close();
186 }
187
188 /**
189 * Initiate connection to a profile of the remote bluetooth device.
190 *
191 * <p> This API returns false in scenarios like the profile on the
192 * device is already connected or Bluetooth is not turned on.
193 * When this API returns true, it is guaranteed that
194 * connection state intent for the profile will be broadcasted with
195 * the state. Users can get the connection state of the profile
196 * from this intent.
197 *
198 * @param device Remote Bluetooth Device
199 * @return false on immediate error, true otherwise
200 * @hide
201 */
202 @UnsupportedAppUsage
203 public boolean connect(BluetoothDevice device) {
204 if (DBG) log("connect(" + device + ")");
205 final IBluetoothPan service = getService();
206 if (service != null && isEnabled() && isValidDevice(device)) {
207 try {
208 return service.connect(device);
209 } catch (RemoteException e) {
210 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
211 return false;
212 }
213 }
214 if (service == null) Log.w(TAG, "Proxy not attached to service");
215 return false;
216 }
217
218 /**
219 * Initiate disconnection from a profile
220 *
221 * <p> This API will return false in scenarios like the profile on the
222 * Bluetooth device is not in connected state etc. When this API returns,
223 * true, it is guaranteed that the connection state change
224 * intent will be broadcasted with the state. Users can get the
225 * disconnection state of the profile from this intent.
226 *
227 * <p> If the disconnection is initiated by a remote device, the state
228 * will transition from {@link #STATE_CONNECTED} to
229 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
230 * host (local) device the state will transition from
231 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
232 * state {@link #STATE_DISCONNECTED}. The transition to
233 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
234 * two scenarios.
235 *
236 * @param device Remote Bluetooth Device
237 * @return false on immediate error, true otherwise
238 * @hide
239 */
240 @UnsupportedAppUsage
241 public boolean disconnect(BluetoothDevice device) {
242 if (DBG) log("disconnect(" + device + ")");
243 final IBluetoothPan service = getService();
244 if (service != null && isEnabled() && isValidDevice(device)) {
245 try {
246 return service.disconnect(device);
247 } catch (RemoteException e) {
248 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
249 return false;
250 }
251 }
252 if (service == null) Log.w(TAG, "Proxy not attached to service");
253 return false;
254 }
255
256 /**
257 * Set connection policy of the profile
258 *
259 * <p> The device should already be paired.
260 * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
261 * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
262 *
263 * @param device Paired bluetooth device
264 * @param connectionPolicy is the connection policy to set to for this profile
265 * @return true if connectionPolicy is set, false on error
266 * @hide
267 */
268 @SystemApi
269 @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
270 public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
271 @ConnectionPolicy int connectionPolicy) {
272 if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
273 try {
274 final IBluetoothPan service = getService();
275 if (service != null && isEnabled()
276 && isValidDevice(device)) {
277 if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
278 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
279 return false;
280 }
281 return service.setConnectionPolicy(device, connectionPolicy);
282 }
283 if (service == null) Log.w(TAG, "Proxy not attached to service");
284 return false;
285 } catch (RemoteException e) {
286 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
287 return false;
288 }
289 }
290
291 /**
292 * {@inheritDoc}
293 * @hide
294 */
295 @SystemApi
296 @Override
297 @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
298 public @NonNull List<BluetoothDevice> getConnectedDevices() {
299 if (VDBG) log("getConnectedDevices()");
300 final IBluetoothPan service = getService();
301 if (service != null && isEnabled()) {
302 try {
303 return service.getConnectedDevices();
304 } catch (RemoteException e) {
305 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
306 return new ArrayList<BluetoothDevice>();
307 }
308 }
309 if (service == null) Log.w(TAG, "Proxy not attached to service");
310 return new ArrayList<BluetoothDevice>();
311 }
312
313 /**
314 * {@inheritDoc}
315 * @hide
316 */
317 @Override
318 @RequiresPermission(Manifest.permission.BLUETOOTH)
319 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
320 if (VDBG) log("getDevicesMatchingStates()");
321 final IBluetoothPan service = getService();
322 if (service != null && isEnabled()) {
323 try {
324 return service.getDevicesMatchingConnectionStates(states);
325 } catch (RemoteException e) {
326 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
327 return new ArrayList<BluetoothDevice>();
328 }
329 }
330 if (service == null) Log.w(TAG, "Proxy not attached to service");
331 return new ArrayList<BluetoothDevice>();
332 }
333
334 /**
335 * {@inheritDoc}
336 * @hide
337 */
338 @SystemApi
339 @Override
340 @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
341 public int getConnectionState(@NonNull BluetoothDevice device) {
342 if (VDBG) log("getState(" + device + ")");
343 final IBluetoothPan service = getService();
344 if (service != null && isEnabled() && isValidDevice(device)) {
345 try {
346 return service.getConnectionState(device);
347 } catch (RemoteException e) {
348 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
349 return BluetoothProfile.STATE_DISCONNECTED;
350 }
351 }
352 if (service == null) Log.w(TAG, "Proxy not attached to service");
353 return BluetoothProfile.STATE_DISCONNECTED;
354 }
355
356 /**
357 * Turns on/off bluetooth tethering
358 *
359 * @param value is whether to enable or disable bluetooth tethering
360 * @hide
361 */
362 @SystemApi
363 @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
364 public void setBluetoothTethering(boolean value) {
365 String pkgName = mContext.getOpPackageName();
366 if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
367 final IBluetoothPan service = getService();
368 if (service != null && isEnabled()) {
369 try {
370 service.setBluetoothTethering(value, pkgName);
371 } catch (RemoteException e) {
372 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
373 }
374 }
375 }
376
377 /**
378 * Determines whether tethering is enabled
379 *
380 * @return true if tethering is on, false if not or some error occurred
381 * @hide
382 */
383 @SystemApi
384 @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
385 public boolean isTetheringOn() {
386 if (VDBG) log("isTetheringOn()");
387 final IBluetoothPan service = getService();
388 if (service != null && isEnabled()) {
389 try {
390 return service.isTetheringOn();
391 } catch (RemoteException e) {
392 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
393 }
394 }
395 return false;
396 }
397
398 @UnsupportedAppUsage
399 private boolean isEnabled() {
400 return mAdapter.getState() == BluetoothAdapter.STATE_ON;
401 }
402
403 @UnsupportedAppUsage
404 private static boolean isValidDevice(BluetoothDevice device) {
405 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
406 }
407
408 @UnsupportedAppUsage
409 private static void log(String msg) {
410 Log.d(TAG, msg);
411 }
412}