/* *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); } }