Home | History | Annotate | Download | only in bluetooth
      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.Binder;
     24 import android.os.IBinder;
     25 import android.os.RemoteException;
     26 import android.util.Log;
     27 
     28 import java.util.ArrayList;
     29 import java.util.List;
     30 
     31 /**
     32  * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
     33  * supports player information, playback support and track metadata.
     34  *
     35  * <p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP
     36  * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
     37  * the BluetoothAvrcpController proxy object.
     38  *
     39  * {@hide}
     40  */
     41 public final class BluetoothAvrcpController implements BluetoothProfile {
     42     private static final String TAG = "BluetoothAvrcpController";
     43     private static final boolean DBG = false;
     44     private static final boolean VDBG = false;
     45 
     46     /**
     47      * Intent used to broadcast the change in connection state of the AVRCP Controller
     48      * profile.
     49      *
     50      * <p>This intent will have 3 extras:
     51      * <ul>
     52      * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
     53      * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
     54      * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
     55      * </ul>
     56      *
     57      * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
     58      * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
     59      * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
     60      *
     61      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
     62      * receive.
     63      */
     64     public static final String ACTION_CONNECTION_STATE_CHANGED =
     65             "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
     66 
     67     /**
     68      * Intent used to broadcast the change in player application setting state on AVRCP AG.
     69      *
     70      * <p>This intent will have the following extras:
     71      * <ul>
     72      * <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
     73      * most recent player setting. </li>
     74      * </ul>
     75      */
     76     public static final String ACTION_PLAYER_SETTING =
     77             "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
     78 
     79     public static final String EXTRA_PLAYER_SETTING =
     80             "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
     81 
     82     private Context mContext;
     83     private ServiceListener mServiceListener;
     84     private volatile IBluetoothAvrcpController mService;
     85     private BluetoothAdapter mAdapter;
     86 
     87     private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
     88             new IBluetoothStateChangeCallback.Stub() {
     89                 public void onBluetoothStateChange(boolean up) {
     90                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
     91                     if (!up) {
     92                         if (VDBG) Log.d(TAG, "Unbinding service...");
     93                         synchronized (mConnection) {
     94                             try {
     95                                 mService = null;
     96                                 mContext.unbindService(mConnection);
     97                             } catch (Exception re) {
     98                                 Log.e(TAG, "", re);
     99                             }
    100                         }
    101                     } else {
    102                         synchronized (mConnection) {
    103                             try {
    104                                 if (mService == null) {
    105                                     if (VDBG) Log.d(TAG, "Binding service...");
    106                                     doBind();
    107                                 }
    108                             } catch (Exception re) {
    109                                 Log.e(TAG, "", re);
    110                             }
    111                         }
    112                     }
    113                 }
    114             };
    115 
    116     /**
    117      * Create a BluetoothAvrcpController proxy object for interacting with the local
    118      * Bluetooth AVRCP service.
    119      */
    120     /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) {
    121         mContext = context;
    122         mServiceListener = l;
    123         mAdapter = BluetoothAdapter.getDefaultAdapter();
    124         IBluetoothManager mgr = mAdapter.getBluetoothManager();
    125         if (mgr != null) {
    126             try {
    127                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
    128             } catch (RemoteException e) {
    129                 Log.e(TAG, "", e);
    130             }
    131         }
    132 
    133         doBind();
    134     }
    135 
    136     boolean doBind() {
    137         Intent intent = new Intent(IBluetoothAvrcpController.class.getName());
    138         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
    139         intent.setComponent(comp);
    140         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
    141                 mContext.getUser())) {
    142             Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
    143             return false;
    144         }
    145         return true;
    146     }
    147 
    148     /*package*/ void close() {
    149         mServiceListener = null;
    150         IBluetoothManager mgr = mAdapter.getBluetoothManager();
    151         if (mgr != null) {
    152             try {
    153                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
    154             } catch (Exception e) {
    155                 Log.e(TAG, "", e);
    156             }
    157         }
    158 
    159         synchronized (mConnection) {
    160             if (mService != null) {
    161                 try {
    162                     mService = null;
    163                     mContext.unbindService(mConnection);
    164                 } catch (Exception re) {
    165                     Log.e(TAG, "", re);
    166                 }
    167             }
    168         }
    169     }
    170 
    171     @Override
    172     public void finalize() {
    173         close();
    174     }
    175 
    176     /**
    177      * {@inheritDoc}
    178      */
    179     @Override
    180     public List<BluetoothDevice> getConnectedDevices() {
    181         if (VDBG) log("getConnectedDevices()");
    182         final IBluetoothAvrcpController service = mService;
    183         if (service != null && isEnabled()) {
    184             try {
    185                 return service.getConnectedDevices();
    186             } catch (RemoteException e) {
    187                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    188                 return new ArrayList<BluetoothDevice>();
    189             }
    190         }
    191         if (service == null) Log.w(TAG, "Proxy not attached to service");
    192         return new ArrayList<BluetoothDevice>();
    193     }
    194 
    195     /**
    196      * {@inheritDoc}
    197      */
    198     @Override
    199     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    200         if (VDBG) log("getDevicesMatchingStates()");
    201         final IBluetoothAvrcpController service = mService;
    202         if (service != null && isEnabled()) {
    203             try {
    204                 return service.getDevicesMatchingConnectionStates(states);
    205             } catch (RemoteException e) {
    206                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    207                 return new ArrayList<BluetoothDevice>();
    208             }
    209         }
    210         if (service == null) Log.w(TAG, "Proxy not attached to service");
    211         return new ArrayList<BluetoothDevice>();
    212     }
    213 
    214     /**
    215      * {@inheritDoc}
    216      */
    217     @Override
    218     public int getConnectionState(BluetoothDevice device) {
    219         if (VDBG) log("getState(" + device + ")");
    220         final IBluetoothAvrcpController service = mService;
    221         if (service != null && isEnabled() && isValidDevice(device)) {
    222             try {
    223                 return service.getConnectionState(device);
    224             } catch (RemoteException e) {
    225                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    226                 return BluetoothProfile.STATE_DISCONNECTED;
    227             }
    228         }
    229         if (service == null) Log.w(TAG, "Proxy not attached to service");
    230         return BluetoothProfile.STATE_DISCONNECTED;
    231     }
    232 
    233     /**
    234      * Gets the player application settings.
    235      *
    236      * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
    237      */
    238     public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
    239         if (DBG) Log.d(TAG, "getPlayerSettings");
    240         BluetoothAvrcpPlayerSettings settings = null;
    241         final IBluetoothAvrcpController service = mService;
    242         if (service != null && isEnabled()) {
    243             try {
    244                 settings = service.getPlayerSettings(device);
    245             } catch (RemoteException e) {
    246                 Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
    247                 return null;
    248             }
    249         }
    250         return settings;
    251     }
    252 
    253     /**
    254      * Sets the player app setting for current player.
    255      * returns true in case setting is supported by remote, false otherwise
    256      */
    257     public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
    258         if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
    259         final IBluetoothAvrcpController service = mService;
    260         if (service != null && isEnabled()) {
    261             try {
    262                 return service.setPlayerApplicationSetting(plAppSetting);
    263             } catch (RemoteException e) {
    264                 Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
    265                 return false;
    266             }
    267         }
    268         if (service == null) Log.w(TAG, "Proxy not attached to service");
    269         return false;
    270     }
    271 
    272     /**
    273      * Send Group Navigation Command to Remote.
    274      * possible keycode values: next_grp, previous_grp defined above
    275      */
    276     public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
    277         Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
    278                 + keyState);
    279         final IBluetoothAvrcpController service = mService;
    280         if (service != null && isEnabled()) {
    281             try {
    282                 service.sendGroupNavigationCmd(device, keyCode, keyState);
    283                 return;
    284             } catch (RemoteException e) {
    285                 Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
    286                 return;
    287             }
    288         }
    289         if (service == null) Log.w(TAG, "Proxy not attached to service");
    290     }
    291 
    292     private final ServiceConnection mConnection = new ServiceConnection() {
    293         public void onServiceConnected(ComponentName className, IBinder service) {
    294             if (DBG) Log.d(TAG, "Proxy object connected");
    295             mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service));
    296             if (mServiceListener != null) {
    297                 mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER,
    298                         BluetoothAvrcpController.this);
    299             }
    300         }
    301 
    302         public void onServiceDisconnected(ComponentName className) {
    303             if (DBG) Log.d(TAG, "Proxy object disconnected");
    304             mService = null;
    305             if (mServiceListener != null) {
    306                 mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER);
    307             }
    308         }
    309     };
    310 
    311     private boolean isEnabled() {
    312         return mAdapter.getState() == BluetoothAdapter.STATE_ON;
    313     }
    314 
    315     private static boolean isValidDevice(BluetoothDevice device) {
    316         return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
    317     }
    318 
    319     private static void log(String msg) {
    320         Log.d(TAG, msg);
    321     }
    322 }
    323