/*
*Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
*Redistribution and use in source and binary forms, with or without
*modification, are permitted provided that the following conditions are
*met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
*THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
*WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
*MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
*ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
*BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
*CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
*SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
*BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
*WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
*OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
*IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package android.bluetooth;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
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.List;
/**
* This class provides the APIs to control the Bluetooth Dun
* Profile.
*
*<p>BluetoothDun is a proxy object for controlling the Bluetooth DUN
* Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
* the BluetoothDun proxy object.
*
*<p>Each method is protected with its appropriate permission.
*@hide
*/
public final class BluetoothDun implements BluetoothProfile {
private static final String TAG = "BluetoothDun";
private static final boolean DBG = false;
private static final boolean VDBG = false;
/**
* Intent used to broadcast the change in connection state of the Dun
* profile.
*
* <p>This intent will have 3 extras:
* <ul>
* <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
* <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
* <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
* </ul>
*
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTED}.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
* receive.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"codeaurora.bluetooth.dun.profile.action.CONNECTION_STATE_CHANGED";
private Context mContext;
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
private IBluetoothDun mDunService;
/**
* Create a BluetoothDun proxy object for interacting with the local
* Bluetooth Service which handles the Dun profile
*
*/
/*package*/ BluetoothDun(Context context, ServiceListener l) {
mContext = context;
mServiceListener = l;
mAdapter = BluetoothAdapter.getDefaultAdapter();
try {
mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback);
} catch (RemoteException re) {
Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re);
}
Log.d(TAG, "BluetoothDun() call bindService");
doBind();
}
boolean doBind() {
Intent intent = new Intent(IBluetoothDun.class.getName());
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
android.os.Process.myUserHandle())) {
Log.e(TAG, "Could not bind to Bluetooth Dun Service with " + intent);
return false;
}
return true;
}
/*package*/ void close() {
if (VDBG) log("close()");
mServiceListener = null;
IBluetoothManager mgr = mAdapter.getBluetoothManager();
if (mgr != null) {
try {
mgr.unregisterStateChangeCallback(mStateChangeCallback);
} catch (RemoteException re) {
Log.w(TAG,"Unable to unregister BluetoothStateChangeCallback",re);
}
}
synchronized (mConnection) {
if ( mDunService != null) {
try {
mDunService = null;
mContext.unbindService(mConnection);
} catch (Exception re) {
Log.e(TAG,"",re);
}
}
}
}
protected void finalize() {
close();
}
private IBluetoothStateChangeCallback mStateChangeCallback =
new IBluetoothStateChangeCallback.Stub() {
@Override
public void onBluetoothStateChange(boolean on) {
//Handle enable request to bind again.
Log.d(TAG, "onBluetoothStateChange on: " + on);
if (on) {
try {
if (mDunService == null) {
Log.d(TAG, "onBluetoothStateChange call bindService");
doBind();
}
} catch (IllegalStateException e) {
Log.e(TAG,"onBluetoothStateChange: could not bind to DUN service: ", e);
} catch (SecurityException e) {
Log.e(TAG,"onBluetoothStateChange: could not bind to DUN service: ", e);
}
} else {
if (VDBG) Log.d(TAG,"Unbinding service...");
synchronized (mConnection) {
if ( mDunService != null) {
try {
mDunService = null;
mContext.unbindService(mConnection);
} catch (Exception re) {
Log.e(TAG,"",re);
}
}
}
}
}
};
/**
* Initiate disconnection from DUN server.
*
* <p> Once the disconnection is initiated by any device either local host
* or remote device, the state will transition from {@link #STATE_CONNECTED}
* to {@link #STATE_DISCONNECTED}.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
* permission.
*
* @param device Remote Bluetooth Device
* @return false on immediate error,
* true otherwise
* @hide
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
if (mDunService != null && isEnabled() &&
isValidDevice(device)) {
try {
return mDunService.disconnect(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
if (mDunService == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
/**
* {@inheritDoc}
*/
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
if (mDunService != null && isEnabled()) {
try {
return mDunService.getConnectedDevices();
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
if (mDunService == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
/**
* {@inheritDoc}
*/
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
if (mDunService != null && isEnabled()) {
try {
return mDunService.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
if (mDunService == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
/**
* {@inheritDoc}
*/
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
if (mDunService != null && isEnabled()
&& isValidDevice(device)) {
try {
return mDunService.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
}
}
if (mDunService == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "BluetoothDUN Proxy object connected");
mDunService = IBluetoothDun.Stub.asInterface(service);
if (mServiceListener != null) {
mServiceListener.onServiceConnected(BluetoothProfile.DUN,
BluetoothDun.this);
}
}
public void onServiceDisconnected(ComponentName className) {
if (DBG) Log.d(TAG, "BluetoothDUN Proxy object disconnected");
mDunService = null;
if (mServiceListener != null) {
mServiceListener.onServiceDisconnected(BluetoothProfile.DUN);
}
}
};
private boolean isEnabled() {
if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
return false;
}
private boolean isValidDevice(BluetoothDevice device) {
if (device == null) return false;
if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
return false;
}
private static void log(String msg) {
Log.d(TAG, msg);
}
}