/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.bluetooth;

import android.annotation.SdkConstant;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

The Android Bluetooth API is not finalized, and *will* change. Use at your own risk. Public API for controlling the Bluetooth Pbap Service. This includes Bluetooth Phone book Access profile. BluetoothPbap is a proxy object for controlling the Bluetooth Pbap Service via IPC. Creating a BluetoothPbap object will create a binding with the BluetoothPbap service. Users of this object should call close() when they are finished with the BluetoothPbap, so that this proxy object can unbind from the service. This BluetoothPbap object is not immediately bound to the BluetoothPbap service. Use the ServiceListener interface to obtain a notification when it is bound, this is especially important if you wish to immediately call methods on BluetoothPbap after construction. Android only supports one connected Bluetooth Pce at a time.
@hide
/** * The Android Bluetooth API is not finalized, and *will* change. Use at your * own risk. * * Public API for controlling the Bluetooth Pbap Service. This includes * Bluetooth Phone book Access profile. * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap * Service via IPC. * * Creating a BluetoothPbap object will create a binding with the * BluetoothPbap service. Users of this object should call close() when they * are finished with the BluetoothPbap, so that this proxy object can unbind * from the service. * * This BluetoothPbap object is not immediately bound to the * BluetoothPbap service. Use the ServiceListener interface to obtain a * notification when it is bound, this is especially important if you wish to * immediately call methods on BluetoothPbap after construction. * * Android only supports one connected Bluetooth Pce at a time. * * @hide */
public class BluetoothPbap implements BluetoothProfile { private static final String TAG = "BluetoothPbap"; private static final boolean DBG = false;
Intent used to broadcast the change in connection state of the PBAP profile.

This intent will have 3 extras:

BluetoothProfile.EXTRA_STATE or BluetoothProfile.EXTRA_PREVIOUS_STATE can be any of BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTING.

Requires BLUETOOTH.BLUETOOTH permission to receive.

/** * Intent used to broadcast the change in connection state of the PBAP * profile. * * <p>This intent will have 3 extras: * <ul> * <li> {@link BluetoothProfile#EXTRA_STATE} - The current state of the profile. </li> * <li> {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li> * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> * </ul> * <p>{@link BluetoothProfile#EXTRA_STATE} or {@link BluetoothProfile#EXTRA_PREVIOUS_STATE} * can be any of {@link BluetoothProfile#STATE_DISCONNECTED}, * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, * {@link BluetoothProfile#STATE_DISCONNECTING}. * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to * receive. */
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; private volatile IBluetoothPbap mService; private final Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1;
Connection canceled before completion.
/** Connection canceled before completion. */
public static final int RESULT_CANCELED = 2;
An interface for notifying Bluetooth PCE IPC clients when they have been connected to the BluetoothPbap service.
/** * An interface for notifying Bluetooth PCE IPC clients when they have * been connected to the BluetoothPbap service. */
public interface ServiceListener {
Called to notify the client when this proxy object has been connected to the BluetoothPbap service. Clients must wait for this callback before making IPC calls on the BluetoothPbap service.
/** * Called to notify the client when this proxy object has been * connected to the BluetoothPbap service. Clients must wait for * this callback before making IPC calls on the BluetoothPbap * service. */
public void onServiceConnected(BluetoothPbap proxy);
Called to notify the client that this proxy object has been disconnected from the BluetoothPbap service. Clients must not make IPC calls on the BluetoothPbap service after this callback. This callback will currently only occur if the application hosting the BluetoothPbap service, but may be called more often in future.
/** * Called to notify the client that this proxy object has been * disconnected from the BluetoothPbap service. Clients must not * make IPC calls on the BluetoothPbap service after this callback. * This callback will currently only occur if the application hosting * the BluetoothPbap service, but may be called more often in future. */
public void onServiceDisconnected(); } private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { log("onBluetoothStateChange: up=" + up); if (!up) { log("Unbinding service..."); synchronized (mConnection) { try { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { Log.e(TAG, "", re); } } } else { synchronized (mConnection) { try { if (mService == null) { log("Binding service..."); doBind(); } } catch (Exception re) { Log.e(TAG, "", re); } } } } };
Create a BluetoothPbap proxy object.
/** * Create a BluetoothPbap proxy object. */
public BluetoothPbap(Context context, ServiceListener l) { mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); } catch (RemoteException e) { Log.e(TAG, "", e); } } doBind(); } boolean doBind() { Intent intent = new Intent(IBluetoothPbap.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, mContext.getUser())) { Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent); return false; } return true; } protected void finalize() throws Throwable { try { close(); } finally { super.finalize(); } }
Close the connection to the backing service. Other public functions of BluetoothPbap will return default error results once close() has been called. Multiple invocations of close() are ok.
/** * Close the connection to the backing service. * Other public functions of BluetoothPbap will return default error * results once close() has been called. Multiple invocations of close() * are ok. */
public synchronized void close() { IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); } catch (Exception e) { Log.e(TAG, "", e); } } synchronized (mConnection) { if (mService != null) { try { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { Log.e(TAG, "", re); } } } mServiceListener = null; }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public List<BluetoothDevice> getConnectedDevices() { log("getConnectedDevices()"); final IBluetoothPbap service = mService; if (service == null) { Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); } try { return service.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } return new ArrayList<BluetoothDevice>(); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public int getConnectionState(BluetoothDevice device) { log("getConnectionState: device=" + device); final IBluetoothPbap service = mService; if (service == null) { Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; } try { return service.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } return BluetoothProfile.STATE_DISCONNECTED; }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states)); final IBluetoothPbap service = mService; if (service == null) { Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); } try { return service.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, e.toString()); } return new ArrayList<BluetoothDevice>(); }
Returns true if the specified Bluetooth device is connected (does not include connecting). Returns false if not connected, or if this proxy object is not currently connected to the Pbap service.
/** * Returns true if the specified Bluetooth device is connected (does not * include connecting). Returns false if not connected, or if this proxy * object is not currently connected to the Pbap service. */
// TODO: This is currently being used by SettingsLib and internal app. public boolean isConnected(BluetoothDevice device) { return getConnectionState(device) == BluetoothAdapter.STATE_CONNECTED; }
Disconnects the current Pbap client (PCE). Currently this call blocks, it may soon be made asynchronous. Returns false if this proxy object is not currently connected to the Pbap service.
/** * Disconnects the current Pbap client (PCE). Currently this call blocks, * it may soon be made asynchronous. Returns false if this proxy object is * not currently connected to the Pbap service. */
// TODO: This is currently being used by SettingsLib and will be used in the future. // TODO: Must specify target device. Implement this in the service. public boolean disconnect(BluetoothDevice device) { log("disconnect()"); final IBluetoothPbap service = mService; if (service == null) { Log.w(TAG, "Proxy not attached to service"); return false; } try { service.disconnect(device); return true; } catch (RemoteException e) { Log.e(TAG, e.toString()); } return false; } private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { log("Proxy object connected"); mService = IBluetoothPbap.Stub.asInterface(service); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothPbap.this); } } public void onServiceDisconnected(ComponentName className) { log("Proxy object disconnected"); mService = null; if (mServiceListener != null) { mServiceListener.onServiceDisconnected(); } } }; private static void log(String msg) { if (DBG) { Log.d(TAG, msg); } } }