blob: 48aeb3f62954085746c9e45d4d140a7f17b0ac25 [file] [log] [blame]
package com.android.clockwork.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import com.android.clockwork.common.DebugAssert;
import java.io.Closeable;
import java.io.PrintWriter;
/**
* A Shard that insists on establishing a client connection to the HandsFree profile on the
* companion.
*/
/* package private */ class HandsFreeClientShard implements Closeable {
private static final String TAG = HandsFreeClientShard.class.getSimpleName();
private static final int POLL_PERIOD_MS = 30_000;
private static final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
private final Context context;
private final BluetoothDevice companionDevice;
private BluetoothHeadsetClient handsFreeProfile;
private boolean isClosed;
public HandsFreeClientShard(final Context context, final BluetoothDevice device) {
DebugAssert.isMainThread();
this.context = context;
companionDevice = device;
adapter.getProfileProxy(context, profileListener, BluetoothProfile.HEADSET_CLIENT);
context.registerReceiver(stateChangeReceiver,
new IntentFilter(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED));
}
@Override
public void close() {
DebugAssert.isMainThread();
if (isClosed) {
return;
}
// Remove the retry message since this shard is closed.
handler.removeMessages(0);
context.unregisterReceiver(stateChangeReceiver);
if (handsFreeProfile != null) {
handsFreeProfile.disconnect(companionDevice);
adapter.closeProfileProxy(BluetoothProfile.HEADSET_CLIENT, handsFreeProfile);
}
isClosed = true;
}
private void onProfileAvailable() {
// Nothing to do if the profile is already connected.
if (handsFreeProfile.getConnectionState(companionDevice) == BluetoothProfile.STATE_CONNECTED) {
Log.i(TAG, "HandsFree client profile is already connected.");
return;
}
// Try to connect and set up a retry loop in case it fails.
Log.i(TAG, "Connecting HandsFree client profile (startup).");
handsFreeProfile.connect(companionDevice);
handler.removeMessages(0);
handler.sendEmptyMessageDelayed(0, POLL_PERIOD_MS);
}
private final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (handsFreeProfile != null) {
Log.d(TAG, "Connecting HandsFree client profile (retry).");
handsFreeProfile.disconnect(companionDevice);
handsFreeProfile.connect(companionDevice);
handler.sendEmptyMessageDelayed(0, POLL_PERIOD_MS);
}
}
};
private final BroadcastReceiver stateChangeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
throw new IllegalStateException(
"Expected ACTION_CONNECTION_STATE_CHANGED, received " + intent.getAction());
}
final int newState =
intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED);
if (handsFreeProfile != null && newState == BluetoothProfile.STATE_DISCONNECTED) {
// If there's a retry scheduled already, we shouldn't preempt it here.
if (!handler.hasMessages(0)) {
Log.d(TAG, "Connecting HandsFree client profile (disconnect).");
handsFreeProfile.connect(companionDevice);
handler.sendEmptyMessageDelayed(0, POLL_PERIOD_MS);
}
} else if (newState == BluetoothProfile.STATE_CONNECTED) {
// Stop polling since we just connected.
handler.removeMessages(0);
}
}
};
private final BluetoothProfile.ServiceListener profileListener =
new BluetoothProfile.ServiceListener() {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
DebugAssert.isMainThread();
handsFreeProfile = (BluetoothHeadsetClient) proxy;
if (isClosed) {
onServiceDisconnected(profile);
return;
}
onProfileAvailable();
}
@Override
public void onServiceDisconnected(int profile) {
DebugAssert.isMainThread();
handler.removeMessages(0);
adapter.closeProfileProxy(BluetoothProfile.HEADSET_CLIENT, handsFreeProfile);
handsFreeProfile = null;
}
};
public void dump(final PrintWriter writer) {
writer.printf("HandsFreeClient [%s]\n", companionDevice);
if (handsFreeProfile != null) {
final int state = handsFreeProfile.getConnectionState(companionDevice);
writer.printf(" Profile state: %d\n", state);
writer.printf(" Retry scheduled: %b\n", handler.hasMessages(0));
} else {
writer.println(" Profile unavailable.");
}
}
}