Программное подключение Bluetooth-гарнитуры

Понадобилось автоматизировать процесс подключения телефона к bluetooth-гарнитуре. Гарнитура с телефоном вручную спарились успешно, они друг-друга знают, проблем вроде бы быть не должно, однако! Оказалось, что мой Galaxy S3 не подключается к ней самостоятельно, если просто включить bluetooth-модуль (здравствуй, android 4.3!!!). Т.е. делаю в программе так:

BluetoothAdapter.getDefaultAdapter().enable();

Блютуз-модуль послушно включается и… ничего. Подключения к гарнитуре не происходит, хотя телефон ее знает.

Пришлось усиленно рыть интернет в поисках того, как программно инициировать подключение к гарнитуре… День потерял, зато потом за 5 минут долетел…

Итак, столько всего пересмотрел, что в голове образовалась каша… Однако, уже отчаявшись, напал на один вражеский сайт, который содержал инструкцию. Ее и привожу, в своем вольном переводе и со своими дополнениями и пояснениями.

Автор пишет: «Мне стало интересно, есть ли способ подключить bluetooth-гарнитуру к моему android-устройству программно и который работает на всех версиях android. Наконец, после продолжительного гугления, у меня появилось решение.

В первых устройствах на Android классы, которые необходимы для подключения к bluetooth A2DP-устройству, скрыты на уровне API, а в более поздних версиях устройств с Android 4.0 и выше они сделаны частично видимыми. Я расскажу вам решение, которое будет работать на всех версиях Android выше 2.3.

По сути, для взаимодействия с A2DP устройствами, мы должны сделать некоторые IPC (Inter Process Communication) вызовы. Для этого мы должны знать, как делать вызовы IPC. В Android, это делается с помощью AIDL (Android Interface Definition Language). Мое решение также использует этот метод. Вы можете узнать о AIDL-вызовах на сайтах разработчиков android.

Класс, который понадобится нам чтобы подключиться к A2DP-устройству называется BluetoothA2dp. Этот класс скрыт в Android версии ниже 4, и видим с ограниченной функциональностью в Android 4 и выше.»

1. Наш первый шаг-это заполучить экземпляр класса BluetoothA2dp. Для этого нужно сначала создать 2 файлика AIDL в папке src вашего проекта Android с названием сборки android.bluetooth следующим образом:

В Eclipse, в вашем проекте, кликаем правой кнопкой на папке src и в меню выбираем: New -> Package и в мастере указываем имя package: «android.bluetooth«. Далее, в пакете android.bluetooth кликаем правой кнопкой мыши, и в меню выбираем: New -> File и указываем имя файла «IBluetooth.aidl». Да, именно так, никаких .java на конце!

В указанный файл копипастим следующий код:

Текст файла IBluetooth.aidl:

/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.bluetooth;

import android.bluetooth.BluetoothDevice;

/**
 * System private API for Bluetooth service
 *
 * {@hide}
 */
interface IBluetooth {
	String getRemoteAlias(in String address);
   boolean setRemoteAlias(in String address, in String name);
}

Далее, там же, в той же сборке, добавляем еще один файл, с названием IBluetoothA2dp.aidl и таким содержимым:

Текст файла IIBluetoothA2dp.aidl:

/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.bluetooth;

import android.bluetooth.BluetoothDevice;

/**
 * System private API for Bluetooth A2DP service
 *
 * {@hide}
 */
interface IBluetoothA2dp {
    boolean connectSink(in BluetoothDevice device); // Pre API 11 only
    boolean disconnectSink(in BluetoothDevice device); // Pre API 11 only
    boolean connect(in BluetoothDevice device); // API 11 and up only
    boolean disconnect(in BluetoothDevice device); // API 11 and up only
    boolean suspendSink(in BluetoothDevice device); // all
    boolean resumeSink(in BluetoothDevice device); // all
    BluetoothDevice[] getConnectedSinks();  // change to Set<> once AIDL supports, pre API 11 only
    BluetoothDevice[] getNonDisconnectedSinks();  // change to Set<> once AIDL supports,
    int getSinkState(in BluetoothDevice device); // pre API 11 only
    int getConnectionState(in BluetoothDevice device); // API 11 and up
    boolean setSinkPriority(in BluetoothDevice device, int priority); // Pre API 11 only
    boolean setPriority(in BluetoothDevice device, int priority); // API 11 and up only
    int getPriority(in BluetoothDevice device); // API 11 and up only
    int getSinkPriority(in BluetoothDevice device); // Pre API 11 only
    boolean isA2dpPlaying(in BluetoothDevice device); // API 11 and up only
}

После создания AIDL-файлов нужно просто скомпилировать проект и заглушки для интерфейсов в AIDL-файлах автоматически создадутся а папке gen (не редактируйте эти файлы).

2. Добавляем в манифест разрешения на доступ к bluetooth и сопутствующим сервисам:

android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.BIND_ACCESSIBILITY_SERVICE

3. Теперь надо идентифицировать устройство, которое будем подключать. Для этого можно использовать список спаренных с телефоном устройств:

Set devices=BluetoothAdapter.getDefaultAdapter().getBondedDevices();

Я сделал у себя так, по началу, просто чтобы попробовать:

Set pairedDevices = BluetoothAdapter.getDefaultAdapter().getBondedDevices();

// Если список спаренных устройств не пуст
if (pairedDevices.size() > 0) {
    // проходимся в цикле по этому списку
    for (BluetoothDevice device : pairedDevices) {
    	if (device.getName().toLowerCase().contains("jabra"))
    	{
    		connectUsingBluetoothA2dp(this, device);
    		break;
    	}
    }
}

Т.е. при нахождении нужного устройства (у меня если имя устройства содержит «jabra») мы передаем это устройство в функцию connectUsingBluetoothA2dp(context, device), которая и производит подключение. Вот эта функция, ее текст нужно добавить в свое приложение:

public void connectUsingBluetoothA2dp(Context context,
		final BluetoothDevice deviceToConnect) {

	try {
		Class<?> c2 = Class.forName("android.os.ServiceManager");
		Method m2 = c2.getDeclaredMethod("getService", String.class);
		IBinder b = (IBinder) m2.invoke(c2.newInstance(), "bluetooth_a2dp");
		if (b == null) {
			// For Android 4.2 Above Devices
			BluetoothAdapter.getDefaultAdapter().getProfileProxy(context,
					new ServiceListener() {

						@Override
						public void onServiceDisconnected(int profile) {

						}

						@Override
						public void onServiceConnected(int profile,
								BluetoothProfile proxy) {
							BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
							try {
								a2dp.getClass()
										.getMethod("connect",
												BluetoothDevice.class)
										.invoke(a2dp, deviceToConnect);
							} catch (Exception e) {
								e.printStackTrace();
							}
						}
					}, BluetoothProfile.A2DP);
		} else {
			// For Android below 4.2 devices
			Class<?> c3 = Class.forName("android.bluetooth.IBluetoothA2dp");
			Class<?>[] s2 = c3.getDeclaredClasses();
			Class<?> c = s2[0];
			Method m = c.getDeclaredMethod("asInterface", IBinder.class);
			m.setAccessible(true);
			IBluetoothA2dp a2dp = (IBluetoothA2dp) m.invoke(null, b);
			if (android.os.Build.VERSION.SDK_INT < 11)
				a2dp.connectSink(deviceToConnect);
			else
				a2dp.connect(deviceToConnect);		
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}

(не забываем нажать Ctrl+Shift+O, чтобы добавить в секцию Import необходимые объявления)

И — о радость! Это заработало! Я счастлив! Устройство подключилось без проблем.

Да, чуть не забыл: контекст, передаваемый в connectUsingBluetoothA2dp должен быть контекстом из Activity или Service. Иначе не работает.

UPD: Не смотря на заверения автора буржуйского сайта, данный код, видимо, не работает на андроиде ниже версии 3. Во всяком случае на подопытном Samsung Galaxy Ace с версией 2.3.6 результат оказался не таким как ожидалось. Гарнитура подключается, однако звонок через нее не идет. В настройках Bluetooth в параметрах подключенной гарнитуры при этом стоит галочка «Звук мультимедиа», но галочка «Звук во время вызова» не установлена. Как это победить — пока не знаю.

Один комментарий

  1. Антон:

    Здравствуйте!
    Есть одна задача по работе с bluetooth гарнитурой, нужна помощь на коммерческой основе! Можно как-то с Вами связаться по e-mail ? Контактов не нашел.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *