Implementation Using Android APIs

The following details how to implement the general Bluetooth process on the interactive, from when the seatback monitor boots up to device connection. During implementation, ensure each operation is fully completed before executing the next one.

For design guidelines and standards, refer to Bluetooth Heading Pairing on the IFE.

Interactive Bluetooth Implementation Flow

This flow assumes that only audio devices are allowed to connect to the interactive and only one device can be connected at a time.

Interactive Bluetooth process
Interactive Bluetooth process
  • If a device gets disconnected or is unpaired from the interactive, be sure to set the audio output device to another available audio device, if any. For more information, refer to Set Audio Output Device.

  • If the passenger is in the process of pairing and connecting their Bluetooth device to the seatback monitor while a Passenger Announcement (PA) or Video PA (VPA) is being played, be sure to continue handling all Bluetooth events. This includes setting the audio output device and volume accordingly when the device successfully connects to ensure audio is routed properly to the Bluetooth device after the PA or VPA finishes.

Functionality

The following are functionality implemented in the Bluetooth flow.

Code samples are provided in Kotlin.

Check If Bluetooth Is Enabled

Check if Bluetooth is enabled by doing the following:

  • Make sure Bluetooth is allowed.

    Use the PAC IO registerEvent API to register for the BLUETOOTH_CONTROL event. If BLUETOOTH_CONTROL is set to 1, Bluetooth is allowed. Otherwise if it's set to 0, Bluetooth is disallowed.

    For more information on allowing or disallowing Bluetooth, refer to Allow and Disallow Bluetooth.

    For information on the PAC IO registerEvent, refer to the PAC IO JSON API Reference For Interactives.

  • Make sure the following required permissions are in the AndroidManifest.xml file of the interactive:

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  • For NEXT seatback monitors, use PrivilegeBroker to set the following on bootup before any Bluetooth operations:

    mPrivilegeBroker.grantRuntimePermission(getPackageName(), 0)

    For more information on using PrivilegeBroker and code samples, refer to the PAC IO Android Developer Guide.

If Bluetooth is not enabled, disable the Bluetooth option or exclude Bluetooth from the interactive entirely.

Initialize Bluetooth

Initialize Bluetooth by getting the BluetoothAdapter object and the BluetoothProfile proxy object.

Example:

private val mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()

private fun getA2dpProfile(bluetoothDevice: BluetoothDevice) {
    if (mIsProfileReady) return
    var deviceA2dp = false
    var uuids = bluetoothDevice.getUuids()
    if (uuids == null) {
        Log.i(TAG, "UUID list is empty ...")
    } else {
        for (uuid in uuids) {
            Log.i(TAG, "Found UUID: " + uuid.toString())

            if (uuid == AUDIO_SINK_UUID) {
                Log.i(TAG, "Found A2DP sink device")
                deviceA2dp = true
                break
            }
        }
    }

    if (deviceA2dp) {
        mBluetoothAdapter?.getProfileProxy(application, object : BluetoothProfile.ServiceListener {
            override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
                mBluetoothProfile = proxy as BluetoothA2dp
                mIsProfileReady = true
                val connectedDevices =
                    mBluetoothProfile!!.connectedDevices

                if (connectedDevices != null) {
                    for (device in connectedDevices) {
                        handleAclConnectionChanged(device, true)
                    }
                }
                Log.d(
                    TAG, "onServiceConnected connectedDevices.size = " +
                        connectedDevices.size
                )
            }

            override fun onServiceDisconnected(profile: Int) {
                Log.d(TAG, "Bluetooth service disconnected");
                mBluetoothProfile = null
                mIsProfileReady = false;
            }
        },
            BluetoothProfile.A2DP
        )
    }
}

Check For Paired Devices

To determine if there are any paired devices on the interactive, use getBondedDevices.

Example:

val pairedDevices : Set<BluetoothDevice> = mBluetoothAdapter.bondedDevices

Turn On Bluetooth

Check if Bluetooth is turned on by calling isEnabled or getState. If Bluetooth is off, call enable to turn it on.

Never assume Bluetooth is on. Bluetooth is automatically turned off when there is no activity.

Start Device Scanning

Start the device scanning process using startDiscovery to discover and display nearby Bluetooth devices.

Bluetooth scanning uses resources in both the Bluetooth stack and Bluetooth radio. If scanning is on while audio is streaming through Bluetooth, there will be connection and audio quality issues such as dropouts.

To avoid this, scanning should be on only during the Bluetooth process while the passenger is actively searching for their device to pair with the monitor. Otherwise, ensure scanning is off.

Monitor when scanning process has started or finished

During the scanning process process, register and listen for the ACTION_DISCOVERY_STARTED and ACTION_DISCOVERY_FINISHED broadcast events to monitor when the discovery process has started or finished.

The Android scanning process lasts 12 seconds by default. After 12 seconds, the ACTION_DISCOVERY_FINISHED event will be broadcasted and scanning stops. To extend the scanning period, call startDiscovery() again. Continue this extension process until the passenger has selected a device to pair or the required scanning duration is reached.

For example, if program requirements specify the scanning duration to be 60 seconds instead of the default 12, do the following:

  1. Start an internal 60 second timer.

  2. Call startDiscovery().

  3. When the ACTION_DISCOVERY_FINISHED event is received but the internal timer is still running, call startDiscovery() again.

  4. Once the internal timer has completed, call cancelDiscovery().

Listen for when devices are found

Register and continually listen for ACTION_FOUND events, which are broadcast whenever a device is found, and display devices as they are discovered.

Display only Bluetooth audio devices

Additionally, filter out devices that are not audio devices. Currently, only audio devices can be paired to the seatback monitor. For more information on filtering logic, refer to Filter for Bluetooth Audio Devices Only.

Sort list of discovered devices by signal strength

The ACTION_FOUND event may broadcast multiple times for each device with potentially updated extra fields. One field is the EXTRA_RSSI field, whose value indicates the signal strength of the discovered device. To help the passenger more easily find their device, use this field to continually sort the list of discovered devices and placing devices with a stronger signal at the top. Devices with a signal strength value closer to zero have the strongest signal and are closest to the seatback monitor.

Example:

val rssi : Short? = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE)

data class PacBluetoothDevice(
    var bluetoothDevice : BluetoothDevice,
    var deviceName: String = "",
    var deviceMacAddress : String = "",
    var lastRssi : Short,
    var deviceType : String = "",
    var deviceMajorType : String = "",
    var deviceAudioType : String = "",
    var devicedPaired : Boolean,
    var deviceConnected : Boolean,
    var deviceA2DPSinkUUIDFound : Boolean
)

private fun sortDeviceList(deviceList : List<PacBluetoothDevice>) {

    Collections.sort(deviceList, Comparator { device1 : PacBluetoothDevice, device2 : PacBluetoothDevice
        -> if (device1.lastRssi < device2.lastRssi)
        1
    else if (device1.lastRssi > device2.lastRssi)
        -1
    else
        0
    } )
}

Discontinue Device Scanning

Discontinue scanning with cancelDiscovery, then wait and listen for the ACTION_DISCOVERY_FINISHED broadcast to ensure scanning has stopped before continuing to pairing.

Pair Device

Start pairing the passenger's device by calling createBond.

To determine the pairing status, listen for the following bonding states:

  • BOND_BONDING — Indicates that pairing is in progress with the passenger's device.

  • BOND_BONDED — Indicates that the passenger's device was successfully paired.

  • BOND_NONE — Indicates that the passenger's device may have failed to pair.

If the bonding state immediately goes from BOND_BONDING to BOND_NONE within one second, retry pairing again with createBond until a BOND_BONDED is received. However, if this situation occurs three times without receiving a BOND_BONDED state, stop the pairing process, notify the passenger that their device was unable to be paired, and advise that they ensure their device is on, in pairing mode, and within range of the seatback monitor.

Example:

fun pairDevice(bluetoothDevice: BluetoothDevice) {
    mPairingAttempt = 0
    mConnectionAttempt = 0
    mLastPairingDevice = bluetoothDevice
    performPairDevice(bluetoothDevice)
}

private fun performPairDevice(bluetoothDevice: BluetoothDevice) {
    mPairingAttempt++
    val result = bluetoothDevice.createBond()
    Log.d(TAG, "Pair to device " + bluetoothDevice.name + " ret = " + result)
}

val mReceiver = object: BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val action = intent?.action
        Log.d(TAG, "action = ${action}")

        when (action) {
            BluetoothAdapter.ACTION_STATE_CHANGED -> {
                val state =
                    intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);

                if (state == BluetoothAdapter.STATE_ON) {
                    _displayMessage.tryEmit(BtUserMessage(MessageType.MessageToast, "Bluetooth Enabled"))
                    updateBluetoothState(true)
                } else if (state == BluetoothAdapter.STATE_OFF) {
                    _displayMessage.tryEmit(BtUserMessage(MessageType.MessageToast,"Bluetooth Disabled"))
                    updateBluetoothState(false)
                }
            }
            BluetoothAdapter.ACTION_DISCOVERY_STARTED -> {
                Log.i(TAG, "ACTION_DISCOVERY_STARTED");
                mUncategorizedDeviceList.clear()
                btDeviceList.clear()
                // updatePairedDeviceList()
                updateBluetoothScanningInProgress(true)
                mHandler.removeCallbacks(mDiscoveryCancelRunnable)
                mHandler.postDelayed(
                    mDiscoveryCancelRunnable,
                    (MAX_DISCOVERY_TIME_SECONDS * 1000).toLong()
                )
            }
            BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
                Log.i(TAG, "ACTION_DISCOVERY_FINISHED");
                mHandler.removeCallbacks(mDiscoveryCancelRunnable)
                updateBluetoothScanningInProgress(false)
                checkUncategorizedDevices()
            }
            BluetoothDevice.ACTION_FOUND -> {
                handleFoundDevice(intent)
            }
            BluetoothDevice.ACTION_BOND_STATE_CHANGED -> {
                handleDevicePairStateChanged(intent)
            }
            BluetoothDevice.ACTION_ACL_CONNECTED -> {
                val device : BluetoothDevice? = intent?.getParcelableExtra (BluetoothDevice.EXTRA_DEVICE)
                handleAclConnectionChanged(device!!, true)
            }
            BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
                val device : BluetoothDevice? = intent?.getParcelableExtra (BluetoothDevice.EXTRA_DEVICE)
                handleAclConnectionChanged(device!!, false)
            }
            BluetoothDevice.ACTION_UUID -> {
                handleUuid(intent)
            }
            BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED -> {
                handleA2dpConnectionStateChanged(intent)
            }
        }
    }
}

private fun handleDevicePairStateChanged(intent : Intent) {
    val device : BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    if (device == null) return

    val state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
    val prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR);
    Log.d(TAG, "handleDevicePairStateChanged device = $device state = $state prevState = $prevState")

    if (state == BluetoothDevice.BOND_BONDED && prevState == BluetoothDevice.BOND_BONDING) {
        _displayMessage.tryEmit(BtUserMessage(MessageType.MessageToast, "Paired"))
        updatePairedDeviceList();
        removeDeviceFromScanList(device)
        Log.i(TAG, "attempt to connect after pairing ...");
        connectDevice(device);
    } else if (state == BluetoothDevice.BOND_NONE && prevState == BluetoothDevice.BOND_BONDED){
        updatePairedDeviceList()
        _displayMessage.tryEmit(BtUserMessage(MessageType.MessageToast,"Unpaired"))
    }
    else if (state == BluetoothDevice.BOND_NONE && prevState == BluetoothDevice.BOND_BONDING) {
        // add retry logic
        if (mPairingAttempt <= MAX_BOND_RETRIES && mLastPairingDevice != null) {
            Log.d(TAG, "Attempt to bond again")
            performPairDevice(mLastPairingDevice!!)
        }
        else {
            if (mLastPairingDevice != null) {
                _displayMessage.tryEmit(BtUserMessage(MessageType.MessageToast,
                    "Unable to pair ${mLastPairingDevice?.name}. Make sure device is in pairing mode"))
            }
            mLastPairingDevice = null
        }
    }
}

Example of retrying pairing logic:

private fun handleDevicePairStateChanged(intent : Intent) {
    val device : BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    if (device == null) return

    val state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
    val prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR);
    Log.d(TAG, "handleDevicePairStateChanged device = $device state = $state prevState = $prevState")

    if (state == BluetoothDevice.BOND_BONDED && prevState == BluetoothDevice.BOND_BONDING) {
        _displayMessage.tryEmit(BtUserMessage(MessageType.MessageToast, "Paired"))
        updatePairedDeviceList();
        removeDeviceFromScanList(device)
        Log.i(TAG, "attempt to connect after pairing ...");
        connectDevice(device);
    } else if (state == BluetoothDevice.BOND_NONE && prevState == BluetoothDevice.BOND_BONDED){
        updatePairedDeviceList()
    }
    else if (state == BluetoothDevice.BOND_NONE && prevState == BluetoothDevice.BOND_BONDING) {
        // add retry logic
        if (mPairingAttempt <= MAX_BOND_RETRIES && mLastPairingDevice != null) {
            Log.d(TAG, "Attempt to bond again")
            performPairDevice(mLastPairingDevice!!)
        }
        else {
            mLastPairingDevice = null
        }
    }
}

private fun performPairDevice(bluetoothDevice: BluetoothDevice) {
    mPairingAttempt++
    val result = bluetoothDevice.createBond()
    Log.d(TAG, "Pair to device " + bluetoothDevice.name + " ret = " + result)
}

Unpair Device

When the passenger wants to unpair their device completely from the seatback monitor, allow them the option to do so from the interactive.

Example:

fun unpairDevice(bluetoothDevice: BluetoothDevice) {
    try {
        if (mLastPairingDevice != null && mLastPairingDevice?.address.equals(bluetoothDevice.address)) {
            mKeepConnectionFlag = false
        }
        Log.d(TAG, "unpairDevice ${bluetoothDevice.name}")
        val method =
            bluetoothDevice.javaClass.getMethod("removeBond")
        method.invoke(bluetoothDevice)
        Log.d(TAG, "unpairDevice ${bluetoothDevice.name} succeeds")
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

Connect Paired Device

Once pairing is confirmed, connect the device through the Advanced Audio Distribution Profile (A2DP) profile using the BluetoothA2dp class.

The BluetoothA2dp class does not directly provide APIs for connecting or disconnecting devices. The interactive must get references to connect and disconnect using getMethod.

The device may automatically connect if it supports the A2DP profile or the Audio/Video Remote Control Profile (AVRCP). However, this is not guaranteed, so the interactive should always try to connect.

Example:

fun connectDevice(bluetoothDevice: BluetoothDevice) {
    Log.d(TAG, "connectDevice ${bluetoothDevice.name}")
    mKeepConnectionFlag = true
    mConnectionAttempt = 0
    mLastPairingDevice = bluetoothDevice
    connectA2DP(bluetoothDevice)
}

private fun connectA2DP(device : BluetoothDevice) {
    Log.i(TAG, "connectA2DP")
    if (!mIsProfileReady) {
        Log.i(TAG, "profile is not ready, starting thread to connect ...")

        GlobalScope.launch {
            getA2dpProfile(device)

            for (attempt in 0..5 step 1) {
                try {
                    Thread.sleep(500)
                } catch (e : InterruptedException) {
                    e.printStackTrace()
                }

                if (!mIsProfileReady) {
                    getA2dpProfile(device)
                } else {
                    break
                }
            }
            Log.i(TAG, "Attempting to connect ...");
            connectToA2DPProfile(device)
        }
    } else {
        Log.i(TAG, "profile ready, attempt to connect ...");
        connectToA2DPProfile(device)
    }
}

private fun connectToA2DPProfile(device : BluetoothDevice) : Boolean {
    var result = false

    Log.i(TAG, "Connect A2DP to $device")
    try {
        if (mBluetoothProfile != null) {
            Log.i(TAG,"Attempting to connect ...")
            mConnectionAttempt++
            mHandler.removeCallbacks(mConnectionRetryRunnable)
            mHandler.postDelayed(mConnectionRetryRunnable, MAX_CONNECTION_TIMEOUT)
            mBluetoothProfile!!.javaClass.getMethod("connect", BluetoothDevice::class.java).invoke(mBluetoothProfile, device)
            result = true
        } else {
            Log.e(TAG, "mBluetoothProfile is null")
        }
    } catch (e : IllegalAccessException) {
        e.printStackTrace()
    } catch (e : InvocationTargetException) {
        e.printStackTrace()
    } catch (e : NoSuchMethodException) {
        e.printStackTrace()
    }
    return result
}

If the device fails to connect, attempt to connect two more times. After three failed attempts, stop the connection process and notify the passenger that their device was unable to connect.

Example:

private val MAX_CONNECTION_TIMEOUT: Long = 8 * 1000

private val mConnectionRetryRunnable = object : Runnable {
    override fun run() {
        Log.d(TAG, "Connection attempt = $mConnectionAttempt")
        if (mConnectionAttempt <= MAX_CONNECT_RETRIES && mLastPairingDevice != null) {
            connectToA2DPProfile(mLastPairingDevice!!)
        }
    }
}

Disconnect Paired Device

The following is an example implementation of disconnecting a paired device:

private fun disconnectA2DP(device : BluetoothDevice) : Boolean {
    var result = false
    Log.i(TAG, "Disconnect A2DP from ${device}")
    try {
        if (mBluetoothProfile != null) {
            mBluetoothProfile!!.javaClass.getMethod("disconnect", BluetoothDevice::class.java).invoke(mBluetoothProfile, device)
            mBluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, mBluetoothProfile)
        }
        mIsProfileReady = false
        result = true
    } catch (e : IllegalAccessException) {
        e.printStackTrace()
    } catch (e : InvocationTargetException) {
        e.printStackTrace()
    } catch (e : NoSuchMethodException) {
        e.printStackTrace()
    }

    return result
}

Allow and Disallow Bluetooth

Bluetooth can be allowed by setting TD 315 TD_ID_BLUETOOTH_CONTROL to 1 or disallowed by setting it to 0. The crew can change this setting from the crew terminal to allow or disallow Bluetooth during a flight.

During development, TD 315 can be configured on the headend to allow Bluetooth without using the crew terminal. Example:

[root@ai1 ~]# tdget -t 315 | head
315 TD_ID_BLUETOOTH_CONTROL                          TD_UINT1[64]
[ 0] 1
[ 1] 1
[ 2] 1
[ 3] 1
[ 4] 1
[ 5] 1
[ 6] 1
[ 7] 0
[ 8] 0

Set Audio Output Device

Whenever the Bluetooth connection status changes, such as when a Bluetooth audio device is connected, disconnected, or unpaired, the interactive should always set the audio output device accordingly by doing the following:

  1. Get a list of available audio output devices by calling the PAC IO getAudioDevices API.

  2. Call the PAC IO setAudioDevices API to redirect the audio output to the appropriate device from the list.

    If a Bluetooth audio device was just connected, set the audio output device to the newly connected device. If a Bluetooth audio device was disconnected or unpaired, set the audio output device to another available audio device. The default output device is the audio jack when no other device is connected.

Additionally, be sure to update the volume level. Refer to Set Volume Level.

For information on the above APIs, refer to the PAC IO JSON API Reference for Interactives.

Set Volume Level

Update the volume by retrieving the current volume level using getStreamVolume, then setting the volume level in the interactive using setStreamVolume for Android 8.1 and higher and adjustStreamVolume for Android 5.1.

Filter for Bluetooth Audio Devices Only

During the Bluetooth device discovery phase, scanning will search for nearby Bluetooth devices and return all types, including mobile phones and computers. However, only audio devices are currently allowed to be paired. Because of this, be sure to include only audio devices in the discovered device list to the passenger and exclude all other devices.

The following defines conditions where a device should be filtered out.

Android 8.1 or higher

Filter out a device if:

Android 5.1

Filter out a device if:

Example

fun isDeviceValidAudioDevice(device: BluetoothDevice?): Boolean {
    if (device == null) {
        return false
    }
    val deviceName = device.name
    val deviceClass = device.bluetoothClass

    // filter out non-classic and non-dual mode devices
    var validType = (device.type == BluetoothDevice.DEVICE_TYPE_CLASSIC) || (device.type == BluetoothDevice.DEVICE_TYPE_DUAL)

    if (validType == false) {
        if (Build.VERSION.SDK_INT < 27 && device.type != BluetoothDevice.DEVICE_TYPE_UNKNOWN) {
            Log.d(TAG, "isDeviceValidAudioDevice invalid type = ${device.type}")
            return false
        }
    }
    
    // filter out devices without a "friendly name"
    if ((deviceName == null) || (deviceName.isEmpty() == true)) {
        return false
    }

    // filter out devices with a "friendly name" beginning with "LE-"
    if (deviceName.startsWith("LE-") == true) {
        Log.i(
            TAG,
            "filtering out devices with a friending name($deviceName) beginning with 'LE-' ..."
        )
        return false
    }

    if (deviceClass == null) {
        return false
    }

    var validMajorDeviceClass = (deviceClass.majorDeviceClass == BluetoothClass.Device.Major.AUDIO_VIDEO)
    Log.d(TAG, "isDeviceValidAudioDevice validMajorDeviceClass = $validMajorDeviceClass")
    if (!validMajorDeviceClass) {
        if (Build.VERSION.SDK_INT < 27 && deviceClass.majorDeviceClass == BluetoothClass.Device.Major.UNCATEGORIZED) {
            Log.d(TAG, "isDeviceValidAudioDevice found uncategorized device")
            return true
        }
    }

    if (validMajorDeviceClass && deviceClass.hasService(BluetoothClass.Service.AUDIO)) {
        Log.i(
            TAG,
            "Device with audio service found: " + device.name + "( " + device.address + " ) with device class: " + deviceClass.deviceClass
        )
        if (((deviceClass.deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES)
                    || (deviceClass.deviceClass == BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER)
                    || (deviceClass.deviceClass == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)
                    || (deviceClass.deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE)
                    || (deviceClass.deviceClass == BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO))
        ) {
            Log.i(
                TAG,
                "Audio device found: " + device.name + "( " + device.address + " ) with device class: " + deviceClass.deviceClass
            )
            return true
        }
    }

    return false
}

Example Bluetooth Interactive Screens

Option to Connect Device (No Paired Devices Found)

Interactive displaying option to connect device (no paired devices found)
Interactive displaying option to connect device (no paired devices found)

Option to Reconnect Device (Paired Device Exists)

Interactive displaying option to reconnect device (paired device exists)
Interactive displaying option to reconnect device (paired device exists)

Device is Currently Connected

Interactive displaying device is currently connected
Interactive displaying device is currently connected

Displaying Discovered Devices During Device Scanning

Interactive displaying discovered devices during device scanning
Interactive displaying discovered devices during device scanning

Device Scanning Timed Out

Interactive displaying device scanning timed out
Interactive displaying device scanning timed out

Device Selected For Connection

Interactive displaying device selected for connection
Interactive displaying device selected for connection

Connecting to Device In Progress

Interactive displaying device connection in progress
Interactive displaying device connection in progress

Connected to Device Successfully

Interactive displaying successful connection to device
Interactive displaying successful connection to device

When the passenger selects Disconnect in the interactive, the interactive should unpair the device instead of just disconnecting it.

Device Connection Lost Alert

Interactive displaying device connection lost alert
Interactive displaying device connection lost alert

Reconnect to Previously Paired Device

Interactive displaying option to reconnect to previously paired device
Interactive displaying option to reconnect to previously paired device

Reconnecting to Previously Paired Device In

Interactive displaying reconnection to previously paired device in progress
Interactive displaying reconnection to previously paired device in progress

Reconnection to Previously Paired Device Failed

Interactive displaying failed reconnection to previously paired device
Interactive displaying failed reconnection to previously paired device

Interactive Android Bluetooth APIs

The following are Android APIs that are used to implement Bluetooth on the interactive, using the BluetoothAdapter, BluetoothDevice, BluetoothA2dp, and AudioManager classes. For other Android Bluetooth APIs, refer to the Android Bluetooth Guide and Bluetooth Reference.

Methods

MethodClassUsage
cancelDiscoveryBluetoothAdapterUsed to discontinue device scanning during the device discovery phase.
createBondBluetoothDeviceStarts the pairing process with a Bluetooth device. To determine pairing status, listen for the BOND_BONDING, BOND_BONDED, and BOND_NONE states.
enableBluetoothAdapterTurns on Bluetooth.
getBondedDevicesBluetoothAdapterUsed to retrieve a list of Bluetooth devices that are paired with the seatback monitor.
getStateBluetoothAdapterChecks if Bluetooth is turned on.
getStreamVolumeAudioManagerRetrieves current volume level.
setStreamVolumeAudioManagerSets volume level.
startDiscoveryBluetoothAdapterStarts device scanning process to discover nearby Bluetooth devices.

Constants

ConstantClassUsage
ACTION_CONNECTION_STATE_CHANGEDBluetoothA2dpUsed to detect a change in Bluetooth connection to determine if Bluetooth is on or off.
ACTION_DISCOVERY_FINISHEDBluetoothAdapterIndicates when device scanning has finished. Broadcasts during the device discovery phase.
ACTION_DISCOVERY_STARTEDBluetoothAdapterIndicates when device scanning has started. Broadcasts during device discovery phase.
ACTION_FOUNDBluetoothDeviceUsed to detect a change in Bluetooth connection to determine if Bluetooth is on or off. Containts the EXTRA_RSSI field, which indicates the signal strength of the device.
ACTION_STATE_CHANGEDBluetoothAdapterAcquires the state of the Bluetooth connection. Used on NEXT seatback monitors when disabling Bluetooth through TD 315 does not send the ACTION_CONNECTION_STATE_CHANGED intent to signal a Bluetooth device disconnection.
BOND_BONDEDBluetoothDeviceIndicates that a Bluetooth device was successfully paired.
BOND_BONDINGBluetoothDeviceIndicates that pairing is in progress with a Bluetooth device.
BOND_NONEBluetoothDeviceIndicates that a Bluetooth device may have failed to pair.
EXTRA_RSSIBluetoothDevice

Indicates the signal strength of a discovered device. Used for sorting the list of discovered devices to place devices with the strongest signal at the top to help passengers find their device more easily. Devices with a signal strength value closer to zero have the strongest signal and are closest to the seatback monitor.

This field is found in ACTION_FOUND broadcasts.