1 /* 2 * Copyright (C) 2014 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 17 package android.bluetooth; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.os.IBinder; 24 import android.os.RemoteException; 25 import android.util.Log; 26 27 import java.util.ArrayList; 28 import java.util.List; 29 30 /** 31 * This class provides the public APIs to control the Bluetooth AVRCP Controller 32 * profile. 33 * 34 *<p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP 35 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get 36 * the BluetoothAvrcpController proxy object. 37 * 38 * {@hide} 39 */ 40 public final class BluetoothAvrcpController implements BluetoothProfile { 41 private static final String TAG = "BluetoothAvrcpController"; 42 private static final boolean DBG = true; 43 private static final boolean VDBG = false; 44 45 /** 46 * Intent used to broadcast the change in connection state of the AVRCP Controller 47 * profile. 48 * 49 * <p>This intent will have 3 extras: 50 * <ul> 51 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> 52 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> 53 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> 54 * </ul> 55 * 56 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of 57 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, 58 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. 59 * 60 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to 61 * receive. 62 */ 63 public static final String ACTION_CONNECTION_STATE_CHANGED = 64 "android.bluetooth.acrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; 65 66 private Context mContext; 67 private ServiceListener mServiceListener; 68 private IBluetoothAvrcpController mService; 69 private BluetoothAdapter mAdapter; 70 71 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = 72 new IBluetoothStateChangeCallback.Stub() { 73 public void onBluetoothStateChange(boolean up) { 74 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); 75 if (!up) { 76 if (VDBG) Log.d(TAG,"Unbinding service..."); 77 synchronized (mConnection) { 78 try { 79 mService = null; 80 mContext.unbindService(mConnection); 81 } catch (Exception re) { 82 Log.e(TAG,"",re); 83 } 84 } 85 } else { 86 synchronized (mConnection) { 87 try { 88 if (mService == null) { 89 if (VDBG) Log.d(TAG,"Binding service..."); 90 doBind(); 91 } 92 } catch (Exception re) { 93 Log.e(TAG,"",re); 94 } 95 } 96 } 97 } 98 }; 99 100 /** 101 * Create a BluetoothAvrcpController proxy object for interacting with the local 102 * Bluetooth AVRCP service. 103 * 104 */ 105 /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) { 106 mContext = context; 107 mServiceListener = l; 108 mAdapter = BluetoothAdapter.getDefaultAdapter(); 109 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 110 if (mgr != null) { 111 try { 112 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); 113 } catch (RemoteException e) { 114 Log.e(TAG,"",e); 115 } 116 } 117 118 doBind(); 119 } 120 121 boolean doBind() { 122 Intent intent = new Intent(IBluetoothAvrcpController.class.getName()); 123 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); 124 intent.setComponent(comp); 125 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, 126 android.os.Process.myUserHandle())) { 127 Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent); 128 return false; 129 } 130 return true; 131 } 132 133 /*package*/ void close() { 134 mServiceListener = null; 135 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 136 if (mgr != null) { 137 try { 138 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); 139 } catch (Exception e) { 140 Log.e(TAG,"",e); 141 } 142 } 143 144 synchronized (mConnection) { 145 if (mService != null) { 146 try { 147 mService = null; 148 mContext.unbindService(mConnection); 149 } catch (Exception re) { 150 Log.e(TAG,"",re); 151 } 152 } 153 } 154 } 155 156 public void finalize() { 157 close(); 158 } 159 160 /** 161 * {@inheritDoc} 162 */ 163 public List<BluetoothDevice> getConnectedDevices() { 164 if (VDBG) log("getConnectedDevices()"); 165 if (mService != null && isEnabled()) { 166 try { 167 return mService.getConnectedDevices(); 168 } catch (RemoteException e) { 169 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 170 return new ArrayList<BluetoothDevice>(); 171 } 172 } 173 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 174 return new ArrayList<BluetoothDevice>(); 175 } 176 177 /** 178 * {@inheritDoc} 179 */ 180 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 181 if (VDBG) log("getDevicesMatchingStates()"); 182 if (mService != null && isEnabled()) { 183 try { 184 return mService.getDevicesMatchingConnectionStates(states); 185 } catch (RemoteException e) { 186 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 187 return new ArrayList<BluetoothDevice>(); 188 } 189 } 190 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 191 return new ArrayList<BluetoothDevice>(); 192 } 193 194 /** 195 * {@inheritDoc} 196 */ 197 public int getConnectionState(BluetoothDevice device) { 198 if (VDBG) log("getState(" + device + ")"); 199 if (mService != null && isEnabled() 200 && isValidDevice(device)) { 201 try { 202 return mService.getConnectionState(device); 203 } catch (RemoteException e) { 204 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 205 return BluetoothProfile.STATE_DISCONNECTED; 206 } 207 } 208 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 209 return BluetoothProfile.STATE_DISCONNECTED; 210 } 211 212 public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) { 213 if (DBG) Log.d(TAG, "sendPassThroughCmd"); 214 if (mService != null && isEnabled()) { 215 try { 216 mService.sendPassThroughCmd(device, keyCode, keyState); 217 return; 218 } catch (RemoteException e) { 219 Log.e(TAG, "Error talking to BT service in sendPassThroughCmd()", e); 220 return; 221 } 222 } 223 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 224 } 225 226 private final ServiceConnection mConnection = new ServiceConnection() { 227 public void onServiceConnected(ComponentName className, IBinder service) { 228 if (DBG) Log.d(TAG, "Proxy object connected"); 229 mService = IBluetoothAvrcpController.Stub.asInterface(service); 230 231 if (mServiceListener != null) { 232 mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER, 233 BluetoothAvrcpController.this); 234 } 235 } 236 public void onServiceDisconnected(ComponentName className) { 237 if (DBG) Log.d(TAG, "Proxy object disconnected"); 238 mService = null; 239 if (mServiceListener != null) { 240 mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER); 241 } 242 } 243 }; 244 245 private boolean isEnabled() { 246 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; 247 return false; 248 } 249 250 private boolean isValidDevice(BluetoothDevice device) { 251 if (device == null) return false; 252 253 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; 254 return false; 255 } 256 257 private static void log(String msg) { 258 Log.d(TAG, msg); 259 } 260 } 261