Aplicaciones universales

Equipe sus aplicaciones con OBEX

Uday Gupta

Descargar el código de muestra

Durante la última década, Bluetooth se ha convertido en una tecnología usada ampliamente para establecer comunicaciones inalámbricas a corta distancia entre dispositivos como teléfonos móviles, equipos personales y auriculares. La asociación Bluetooth Special Interest Group (Grupo de interés especial en Bluetooth o BTSIG en sus siglas inglesas), es el órgano perteneciente a este sector que se encarga de definir los estándares para los servicios inalámbricos en las especificaciones de los perfiles de Bluetooth.

Uno de esos perfiles es el llamado Object Push Profile (perfil de envío de objeto u OPP en sus siglas inglesas), que se utiliza para enviar archivos de un dispositivo a otro. El protocolo de intercambio de datos Object Exchange Protocol (OBEX) es una parte básica del perfil OPP. OBEX no solo se utiliza en OPP; al ser un protocolo genérico, otros perfiles pueden utilizarlo para transferir objetos entre dispositivos.

Para aquellos desarrolladores que quieran utilizar OBEX en sus aplicaciones, he desarrollado un conjunto de API para el Bluetooth de Windows en tiempo de ejecución (WinRT), que proporciona el protocolo OBEX en la aplicación. Estas API se presentan como un paquete de biblioteca para aplicaciones universales, así que tanto las aplicaciones de la Tienda Windows como las aplicaciones de Windows Phone Silverlight pueden aprovechar el mismo conjunto de API.

OBEX y OPP

Ante todo, es importante comprender qué son OBEX y OPP y cómo funcionan. OPP permite a un dispositivo Bluetooth enviar un archivo u objeto a otro dispositivo Bluetooth que disponga de un perfil OPP. En un principio, el propósito de la creación de OBEX era compartir archivos a través de canales de infrarrojos, pero el BTSIG decidió reutilizar este protocolo para compartir archivos a través de Bluetooth. Excepto por el medio de transporte subyacente, tanto el protocolo OBEX para infrarrojos como el de para Bluetooth son similares.

OBEX se basa en un modelo cliente-servidor en el cual el dispositivo Bluetooth de destino ejecuta un servidor OBEX que atiende y acepta conexiones de cliente. El dispositivo Bluetooth cliente se conecta al dispositivo Bluetooth servidor como un canal de transmisión a través del Bluetooth. Los requisitos de autenticación para permitir la conexión y la transferencia de objetos, dependen de un servicio o aplicación que use OBEX. Por ejemplo, el perfil OPP puede permitir conexiones sin autentificar para simplificar y agilizar el intercambio de tarjetas de presentación. Es posible que otros servicios que utilicen OBEX solo permitan conexiones autenticadas.

Los archivos se comparten utilizando tres tipos de paquetes: primer PUT, PUT intermedio y último PUT. El primer paquete PUT marca el inicio de la transferencia de archivos y el último paquete PUT indica marca su finalización. Los múltiples paquetes PUT intermedios contienen el grueso de los datos. Cuando el servidor recibe cada paquete PUT, devuelve un paquete de confirmación al cliente.

Normalmente, los paquetes OBEX se envían como se indica a continuación:

  1. El cliente OBEX se conecta al dispositivo receptor mediante el envío de un paquete de conexión. Este paquete especifica el tamaño máximo del paquete que el cliente puede recibir.
  2. Al recibir la respuesta del servidor indicando que se ha aceptado la conexión, el cliente OBEX envía el primer paquete PUT que contiene los metadatos que describen el objeto, incluyendo el nombre del archivo y su tamaño. (El protocolo OBEX permite a este primer paquete PUT incluir los datos del objeto, pero la implementación de OBEX en la biblioteca que he desarrollado no envía ninguno de esos datos en el primer paquete PUT.)
  3. Cuando se recibe la confirmación de que el servidor tiene el primer paquete PUT, el cliente OBEX envía múltiples paquetes PUT que contienen los datos del objeto. La capacidad de estos paquetes está limitada por el tamaño máximo de paquetes que el servidor puede recibir, establecido por la respuesta del servidor al paquete de conexión que se envía en el primer paso.
  4. El paquete PUT final contiene la última constante PUT y la última parte de los datos del objeto.
  5. Cuando se completa el intercambio de archivos, el cliente OBEX envía un paquete de desconexión y cierra la conexión Bluetooth. El protocolo OBEX permite la repetición de los pasos dos y tres para así poder enviar múltiples objetos durante la misma conexión.

El cliente OBEX puede abortar en cualquier momento el proceso de uso compartido enviando un paquete de anulación. El uso compartido se cancela inmediatamente. En la biblioteca que he escrito, los detalles de implementación del protocolo OBEX están ocultos y solo verá API de alto nivel.

La biblioteca OBEX

La biblioteca cliente de Bluetooth OBEX para las aplicaciones de la Tienda Windows, está diseñada para ser una biblioteca portátil para las aplicaciones de la Tienda Windows y las aplicaciones de Windows Phone Silverlight 8.1. Contiene tres DLL que forman una biblioteca en tiempo de ejecución para el cliente OBEX. Cada DLL está diseñada para tratar una tarea específica: Bluetooth.Core.Service, Bluetooth.Core.Sockets y Bluetooth.Services.Obex.

Servicio principal de Bluetooth: el archivo Bluetooth.Core.Service.dll contiene el espacio de nombres Bluetooth.Core.Service. Esta biblioteca está diseñada para localizar y contar aquellos dispositivos Bluetooth que estén emparejados con el dispositivo cliente (vea la Ilustración 1). En este momento está restringida a un recuento único de dispositivos emparejados, pero versiones futuras contendrán un monitor que seguirá buscando dispositivos Bluetooth adicionales.

Ilustración 1. Métodos y eventos asociados de BluetoothService

Método (con parámetros) Eventos asociados
[static] GetDefault Sin evento asociado
SearchForPairedDevicesAsync

Éxito - SearchForDevicesSucceeded

Error - SearchForPairedDevicesFailed

El servicio principal de Bluetooth está representado por una clase estática llamada BluetoothService tal y como se muestra en la Ilustración 2. Esta clase tiene una API para contar dispositivos de forma asincrónica.

Ilustración 2. Recuento de BluetoothService de dispositivos emparejados

BluetoothService btService = BluetoothService.GetDefault();
btService.SearchForPairedDevicesFailed 
  += btService_SearchForPairedDevicesFailed;
btService.SearchForPairedDevicesSucceeded 
  += btService_SearchForPairedDevicesSucceeded;
await btService.SearchForPairedDevicesAsync();
void btService_SearchForPairedDevicesSucceeded(object sender,
  SearchForPairedDevicesSucceededEventArgs e)
{
  // Get list of paired devices from e.PairedDevices collection
}
void btService_SearchForPairedDevicesFailed(object sender,
  SearchForPairedDevicesFailedEventArgs e)
{
  // Get the failure reason from e.FailureReason enumeration
}

Sockets principales de Bluetooth: el archivo Bluetooth.Core.Sockets.dll contiene el espacio de nombres Bluetooth.Core.Sockets y está diseñado para admitir operaciones de socket basadas en transmisiones por medio de conexiones Bluetooth. La funcionalidad de los sockets se muestra por medio de la clase BluetoothSockets (vea la Ilustración 3). Este socket no es ni TCP ni UDP. Toda comunicación con el dispositivo receptor se lleva a cabo mediante BluetoothSockets.

Ilustración 3. Métodos y eventos asociados de BluetoothSockets

Método (con parámetros) Eventos asociados

Constructor (Bluetooth.Core.Services.BluetoothDevice)

Constructor (Bluetooth.Core.Services.BluetoothDevice, System.UInt32)

Constructor (Bluetooth.Core.Services.BluetoothDevice, System.String)

Constructor (Bluetooth.Core.Services.BluetoothDevice, System.UInt32, System.String)

Sin evento asociado
PrepareSocketAsync

Éxito – SocketConnected

Error – ErrorOccured

SendDataAsync(System.Byte[])

SendDataAsync(System.String)

Sin evento asociado
CloseSocket SocketClosed
Sin método asociado DataReceived

Servicios de Bluetooth Obex: el archivo Bluetooth.Services.Obex.dll contiene el espacio de nombres Bluetooth.Services.Obex. Esta es la implementación principal de OBEX, que se muestra a través de la clase denominada ObexService. Esta clase proporciona una vista resumida de la especificación Bluetooth OPP. Muestra el método que permite conectarse, enviar y desconectarse del dispositivo Bluetooth receptor. La Ilustración 4 tiene una lista de API y eventos asociados que muestra esta clase y la Ilustración 5 indica el uso.

Ilustración 4. Métodos y eventos asociados de ObexService

Método (con parámetros) Eventos asociados
[static] GetDefaultForBluetoothDevice(Bluetooth.Core.Services.BluetoothDevice) Sin evento asociado
ConnectAsync

Éxito – DeviceConnected

Error – ConnectionFailed

SendFileAsync(Windows.Storage.IStorageFile)

Éxito:

ServiceConnected

DataTransferProgressed

DataTransferSucceeded

Desconectando

Desconectado

Error:

ConnectionFailed

DataTransferFailed

AbortAsync Anulado

Ilustración 5. Uso del servicio Obex

protected async override void OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
  base.OnNavigatedTo(e);
  ObexService obexService = ObexService.GetDefaultForBluetoothDevice(null);
  obexService.DeviceConnected += obexService_DeviceConnected;
  obexService.ServiceConnected += obexService_ServiceConnected;
  obexService.ConnectionFailed += obexService_ConnectionFailed;
  obexService.DataTransferProgressed += obexService_DataTransferProgressed;
  obexService.DataTransferSucceeded += obexService_DataTransferSucceeded;
  obexService.DataTransferFailed += obexService_DataTransferFailed;
  obexService.Disconnecting += obexService_Disconnecting;
  obexService.Disconnected += obexService_Disconnected;
  obexService.Aborted += obexService_Aborted;
  await obexService.ConnectAsync();
}
async void obexService_DeviceConnected(object sender, EventArgs e)
{
  // Device is connected, now send file
  await (sender as ObexService).SendFileAsync(fileToSend);
}
void obexService_ServiceConnected(object sender, EventArgs e)
{
  // Device connected to Obex Service on target device
}
void obexService_ConnectionFailed(object sender, 
  ConnectionFailedEventArgs e)
{
  // Connection to service failed
}
void obexService_DataTransferProgressed(object sender, 
  DataTransferProgressedEventArgs e)
{
  // Get data transfer progress from DataTransferProgressedEventArgs
}
void obexService_DataTransferSucceeded(object sender, EventArgs e)
{
  // Data transfer succeeded
}
void obexService_DataTransferFailed(object sender, DataTransferFailedEventArgs e)
{
  // Data transfer failed, get the reason from DataTransferFailedEventArgs
}
void obexService_Disconnecting(object sender, EventArgs e)
{
  // Device is disconnecting from service
  }
void obexService_Disconnected(object sender, EventArgs e)
{
  // Device is now disconnected from targeted device and service
}
void obexService_Aborted(object sender, EventArgs e)
{
  // Data transfer operation is aborted
}

Normalmente, estas bibliotecas se utilizan para:

  • Utilizar las API en Blue-­tooth.Core.Service y contar todos los dispositivos Bluetooth con OPP emparejados. La comprobación de funcionalidad con OPP ya está implementada.
  • Conseguir la instancia de BluetoothDevice con la que desea compartir archivos.
  • Obtener una instancia de ObexService para el dispositivo Bluetooth receptor al pasar la instancia de BluetoothDevice al Factory Method. La clase ObexService creará de manera interna una instancia de BluetoothSocket, mediante la cual ObexService compartirá el archivo.
  • Cuando se complete el intercambio de archivos, ObexService se desconectará de forma automática.

Introducción

Ya que mi biblioteca está dirigida a aplicaciones de la Tienda Windows y a aplicaciones de Windows Phone 8.1, comenzaré con una aplicación universal. Estas aplicaciones son una forma excelente de desarrollar aplicaciones para todos los dispositivos de Windows. Para ver más información acerca de las aplicaciones universales, visite bit.ly/1h3AQeu. Para comenzar, utilizaré Visual Studio 2013 y crearé un proyecto nuevo de aplicaciones universales en el nodo de la Tienda de aplicaciones (vea la Ilustración 6). Yo he usado Visual C# pero, si lo desea, también puede usar Visual Basic y Visual C++.

Aplicación universal en blanco para crear un proyecto nuevo
Ilustración 6. Aplicación universal en blanco para crear un proyecto nuevo

Antes de comenzar a programar una aplicación cliente de Bluetooth OBEX, actualizaré el archivo package.appx­manifest para ambos proyectos (aplicaciones para Windows 8.1 y Windows Phone 8.1):

<Capabilities>
  <m2:DeviceCapability Name="bluetooth.rfcomm">
    <m2:Device Id="any">
      <m2:Function Type="name:obexObjectPush"/>
    </m2:Device>
  </m2:DeviceCapability>
</Capabilities>

Para aplicar esta actualización, abro package.appx­­manifest como un archivo de código eligiendo Ver código en el menú contextual del Explorador de soluciones. Colocaré este fragmento de código donde acaba la etiqueta <Aplicación>. Este fragmento de código es necesario para proporcionar una capacidad a nivel de dispositivo y así poder utilizar el servicio de comunicación por radiofrecuencia Bluetooth (RFCOMM) con esta aplicación. También he especificado todos los servicios y tipos de dispositivo compatibles con este dispositivo.

Para mi escenario, necesitaré el soporte obexObjectPush del dispositivo que es compatible con cualquier dispositivo que tenga capacidad para usar OPP. Para obtener más información acerca de los Perfiles compatibles en Windows 8.1 y Windows Phone 8.1, visite bit.ly/1pG6rYO. Si no se menciona esa capacidad, entonces la enumeración de dispositivos fallará con la constante de enumeración CapabilityNotDefined.

Antes de empezar a codificar, agregaré la referencia a los tres archivos de biblioteca que mencioné anteriormente para poder utilizar la implementación OBEX en esas bibliotecas. Tengo que agregar la referencia a esas bibliotecas para ambos proyectos por separado. Si no se agrega la referencia, el proyecto no podrá utilizar ninguna característica.

Seguiré estos patrones y prácticas de codificación:

  • Implementar un diseño de experiencia de usuario para la aplicación de la Tienda Windows 8.1 en el proyecto Windows.
  • Implementar un diseño de experiencia de usuario para aplicaciones de la Tienda Windows 8.1 en el proyecto Windows Phone.
  • Implementar una característica común en el proyecto Shared.
  • Realizar una implementación específica para la plataforma en el proyecto Shared utilizando constantes de compilador específicas para la plataforma. Para Windows 8.1, utilizaré WINDOWS_APP. Para Windows Phone 8.1, utilizaré WINDOWS_PHONE_APP. Estas constantes de compilador ya están definidas como parte del proyecto.

Descargue el código de muestra para practicar con estas bibliotecas y las prácticas de programación que deberá seguir para desarrollar aplicaciones universales. En la Ilustración 7 encontrará la ventana del Explorador de soluciones del proyecto de muestra con la estructura y patrones de los archivos.

Explorador de soluciones de la aplicación BluetoothDemo
Ilustración 7. Explorador de soluciones de la aplicación BluetoothDemo

Enumeración de dispositivos emparejados

Para poder compartir archivos con un dispositivo emparejado, necesito ver la lista de los dispositivos emparejados y seleccionar uno de ellos. Podré manejar la instancia de Bluetooth.Core.Services.BluetoothService, que representa el servicio Bluetooth principal proporcionado por mi dispositivo. He conseguido esta instancia utilizando el patrón de diseño estático Factory Method denominado GetDefault, ya que solo hay un único servicio Bluetooth disponible por dispositivo.

Para la enumeración, llamaré al método SearchForPairedDevicesAsync. Este método comenzará a enumerar aquellos dispositivos emparejados con el mío. Para aplicaciones de la Tienda Windows 8.1, tengo que permitir el uso de un dispositivo emparejado para conseguir los dispositivos enumerados. Si impido este uso, no se enumerará el dispositivo emparejado.

Si la API tiene éxito, generará el evento SearchForPairedDevicesSucceeded y capturará la colección de dispositivos emparejados de su argumento de eventos. De lo contrario, se generará el evento SearchForPairedDevicesFailed pero con un error en la constante de enumeración disponible en su argumento de eventos. En la Ilustración 8 se muestra el código para enumerar dispositivos.

Ilustración 8. Enumeración de dispositivos emparejados

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
  base.OnNavigatedTo(e);
  await EnumerateDevicesAsync();
}
public async Task EnumerateDevicesAsync()
{
  BluetoothService btService = BluetoothService.GetDefault();
  btService.SearchForPairedDevicesFailed +=
    btService_SearchForPairedDevicesFailed;
  btService.SearchForPairedDevicesSucceeded +=
    btService_SearchForPairedDevicesSucceeded;
  await btService.SearchForPairedDevicesAsync();
}
void btService_SearchForPairedDevicesSucceeded(object sender,
  SearchForPairedDevicesSucceededEventArgs e)
{
  (sender as BluetoothService).SearchForPairedDevicesFailed -=
    btService_SearchForPairedDevicesFailed;
  (sender as BluetoothService).SearchForPairedDevicesSucceeded -=
    btService_SearchForPairedDevicesSucceeded;
  this.cvBtDevices.Source = e.PairedDevices;
}
void btService_SearchForPairedDevicesFailed(object sender,
  SearchForPairedDevicesFailedEventArgs e)
{
  (sender as BluetoothService).SearchForPairedDevicesFailed -=
    btService_SearchForPairedDevicesFailed;
  (sender as BluetoothService).SearchForPairedDevicesSucceeded -=
    btService_SearchForPairedDevicesSucceeded;
  txtblkErrorBtDevices.Text = e.FailureReason.ToString();
}

También he proporcionado el botón Buscar en BottomAppBar para Windows 8.1 y en ApplicationBar para Windows Phone 8.1. De este modo, cuando haya un nuevo dispositivo emparejado, cualquier usuario de la aplicación podrá volver a buscar dispositivos.

Cuando se enumeren dispositivos en Windows Phone 8.1, se enumerarán todos aquellos que admitan OBEX, independientemente de si están presentes de forma física o de si la radio Bluetooth está encendida. Por otro lado, cuando se enumeren dispositivos en Windows 8.1, este solo mostrará aquellos dispositivos cercanos físicamente al dispositivo Windows 8.1 y que tengan la radio Bluetooth encendida.

Cuando tenga enumerados todos los dispositivos emparejados, puedo seleccionar uno de ellos para compartir los archivos. Cada dispositivo se representa como un objeto de la clase Bluetooth.Core.Services.BluetoothDevice. El objeto contiene detalles de la conexión y el nombre para mostrar del dispositivo emparejado. Bluetooth.Services.Obex.ObexService usará los detalles de la conexión internamente para crear una instancia de Bluetooth.Core.Sock­ets.BluetoothSocket y conectarla al dispositivo emparejado.

Uso de ObexService

Una vez conseguida la instancia del objeto Bluetooth.Core.Services.BluetoothDevice que representa al dispositivo que he seleccionado para compartir archivos, puedo utilizar Bluetooth.Services.Obex.ObexService para compartir archivos utilizando OPP. También necesito la lista de archivos para poder ponerlos a la cola y compartirlos. En la muestra del código solo he proporcionado un puñado de archivos. Por lo demás, podría utilizar Windows.Storage.Pickers.FileOpenPicker (vea bit.ly/1qtiLeh) o una lógica personalizada de mi Windows.Storage.ApplicationData.Current.LocalFolder (vea bit.ly/1qtiSGI) para seleccionar varios archivos.

Por ahora, solo puedo compartir un archivo por conexión. Cuando se completa el intercambio, se cierra la conexión al dispositivo que he elegido. En el caso de tener que enviar varios archivos, deberé manejar varias veces la instancia Blue­tooth.Services.Obex.ObexService. Puedo conseguir esta instancia utilizando el patrón de diseño Factory Method estático GetDefaultForBluetoothDevice(Bluetooth.Core.Services.BluetoothDevice). Este método devuelve la instancia única Bluetooth.Services.Obex.ObexService que representa al servicio Obex del dispositivo.

Para representar el archivo a compartir, utilizaré la clase FileItemToShare. Esta contiene el nombre, la ruta de acceso y el tamaño del archivo y la instancia Windows.Storage.IStorageFile (vea bit.ly/1qMcZlB) que representa la instancia de archivo del disco. En cuanto a los objetos de estructura de datos, pondré en cola todos aquellos archivos que tengo que compartir. Cuando se comparten varios archivos, el primero de la lista es el que se está compartiendo en ese momento. Se elimina de la lista cuando finaliza el intercambio. En la Ilustración 9 se muestra cómo enlazar ObexService y sus eventos para compartir archivos.

Ilustración 9. Enlace de ObexService y sus eventos

ObexService obexService = null;
BluetoothDevice BtDevice = null;
ObservableCollection<FileItemToShare> filesToShare = null;
async override void OnNavigatedTo(NavigationEventArgs e)
{
  base.OnNavigatedTo(e);
  if (e.Parameter == null || !(e.Parameter is BluetoothDevice))
  {
    MessageDialog messageBox = new MessageDialog(
      "Invalid navigation detected. Moving to Main Page", "Bluetooth Hub");
    messageBox.Commands.Add(new UICommand("OK", (uiCommand) =>
    {
      this.Frame.Navigate(typeof(MainPage));
    }));
    await messageBox.ShowAsync();
    return;
  }
  BtDevice = e.Parameter as BluetoothDevice;
  filesToShare = GetFilesFromLocalStorage();
  this.cvFileItems.Source = filesToShare;
  obexService = ObexService.GetDefaultForBluetoothDevice(BtDevice);
  obexService.Aborted += obexService_Aborted;
  obexService.ConnectionFailed += obexService_ConnectionFailed;
  obexService.DataTransferFailed += obexService_DataTransferFailed;
  obexService.DataTransferProgressed += obexService_DataTransferProgressed;
  obexService.DataTransferSucceeded += obexService_DataTransferSucceeded;
  obexService.DeviceConnected += obexService_DeviceConnected;
  obexService.Disconnected += obexService_Disconnected;
  obexService.Disconnecting += obexService_Disconnecting;
  obexService.ServiceConnected += obexService_ServiceConnected;
  await obexService.ConnectAsync();
}

Cuando llamo al método ConnectAsync, el objeto ObexService obtiene las propiedades de conexión del objeto BluetoothDevice, que se pasó en el patrón de diseño Factory Method. Intenta crear una conexión con el BluetoothDevice seleccionado a través del canal Bluetooth. Si la conexión es correcta, genera el evento DeviceConnected. En la Ilustración 10 se muestra el controlador de eventos DeviceConnected de ObexService.

Ilustración 10. Método de controlador de eventos DeviceConnected

async void obexService_DeviceConnected(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
  {
    System.Diagnostics.Debug.WriteLine("device connected");
    if (filesToShare.Count > 0)
    {
      filesToShare.ShareStatus = FileShareStatus.Connecting;
      await obexService.SendFileAsync(filesToShare[0].FileToShare);
    }
    ...
  });
}

Tan pronto como el dispositivo se conecte al dispositivo seleccionado, comenzaré a compartir el archivo del índice 0 desde la lista de archivos. El archivo se comparte al llamar a SendFileAsync(Windows.Storage.IStorageFile) y pasar el objeto del archivo representado por IStorageFile desde el objeto del tipo de estructura de datos FileToShare. Cuando se llama a este método, ObexService intenta conectar al servidor OBEX que esté ejecutando el dispositivo de destino. Si la conexión es correcta, generará el evento ServiceConnected. De lo contrario, generará el evento ConnectionFailed. Este código muestra el controlador de eventos ServiceConnected:

async void obexService_ServiceConnected(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  {
    System.Diagnostics.Debug.WriteLine("service connected");
    filesToShare[0].ShareStatus = FileShareStatus.Sharing;
  });
}

En la Ilustración 11 se muestra el controlador de eventos ConnectionFailed de ObexService.

Ilustración 11. Método de controlador de eventos ConnectionFailed

async void obexService_ConnectionFailed(object sender, 
  ConnectionFailedEventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
  {
    System.Diagnostics.Debug.WriteLine("connection failed");
    filesToShare[0].ShareStatus = FileShareStatus.Error;
    filesToShare[0].Progress = 0;
    FileItemToShare currentItem = filesToShare[0];
    filesToShare.RemoveAt(0);
    filesToShare.Add(currentItem);
  });
}

Cuando mi dispositivo se conecta al servidor OBEX del dispositivo seleccionado, comienza el proceso de intercambio de archivos. El progreso de los archivos compartidos se puede determinar con el evento DataTransferProgressed. En el siguiente código se muestra el método DataTransferProgressed:

async void obexService_DataTransferProgressed(object sender,
  DataTransferProgressedEventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  {
    System.Diagnostics.Debug.WriteLine("Bytes {0}, Percentage {1}",
      e.TransferInBytes, e.TransferInPercentage);
    filesToShare[0].Progress = e.TransferInBytes;
  });
}

Cuando se completa el intercambio del archivo, se genera el evento DataTransfer­Succeeded. En caso contrario, se genera DataTransferFailedEvent. En el siguiente código se muestra el controlador de eventos DataTransfer­Succeeded:

async void obexService_DataTransferSucceeded(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  {
    System.Diagnostics.Debug.WriteLine("data transfer succeeded");
    filesToShare.RemoveAt(0);
  });
}

En el caso de que se produzca un error en el intercambio de un archivo, se generará el evento DataTransferFailed. En el siguiente código se muestra el controlador de eventos:

async void obexService_DataTransferFailed(object sender,
  DataTransferFailedEventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
  {
    System.Diagnostics.Debug.WriteLine("Data transfer failed {0}",
      e.ExceptionObject.ToString());
    filesToShare[0].ShareStatus = FileShareStatus.Error;
    filesToShare[0].Progress = 0;
    FileItemToShare fileToShare = this.filesToShare[0];
    filesToShare.RemoveAt(0);
    this.filesToShare.Add(fileToShare);
  });
}

Cuando finaliza la transferencia de datos, el archivo compartido se elimina de la lista y se desconecta ObexService. Cuando ObexService se está desconectando, se genera el evento Disconnecting. Asimismo, el evento Disconnected se genera cuando la desconexión se realiza correctamente. A continuación, se muestra el controlador de eventos Disconnecting:

void obexService_Disconnecting(object sender, EventArgs e)
{
  System.Diagnostics.Debug.WriteLine("disconnecting");
}

Una vez se realiza la desconexión correctamente, el código de los controladores de la Ilustración 12 genera el evento Disconnected.

Ilustración 12. Método de controlador de eventos Disconnected

async void obexService_Disconnected(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
  {
    System.Diagnostics.Debug.WriteLine("disconnected");
    obexService.Aborted -= obexService_Aborted;
    obexService.ConnectionFailed -= obexService_ConnectionFailed;
    obexService.DataTransferFailed -= obexService_DataTransferFailed;
    obexService.DataTransferProgressed -= 
      obexService_DataTransferProgressed;
    obexService.DataTransferSucceeded -= 
      obexService_DataTransferSucceeded;
    obexService.DeviceConnected -= obexService_DeviceConnected;
    obexService.Disconnected -= obexService_Disconnected;
    obexService.Disconnecting -= obexService_Disconnecting;
    obexService.ServiceConnected -= obexService_ServiceConnected;
    obexService = null;
    if (filesToShare.Count.Equals(0))
    {
      ...
      MessageDialog messageBox =
        new MessageDialog("All files are shared successfully",
        "Bluetooth DemoApp");
      messageBox.Commands.Add(new UICommand("OK", (uiCommand) =>
      {
        this.Frame.Navigate(typeof(MainPage));
      }));
      await messageBox.ShowAsync();
    }
    else
    {
      obexService = ObexService.GetDefaultForBluetoothDevice(BtDevice);
      obexService.Aborted += obexService_Aborted;
      obexService.ConnectionFailed += obexService_ConnectionFailed;
      obexService.DataTransferFailed += obexService_DataTransferFailed;
      obexService.DataTransferProgressed += 
        obexService_DataTransferProgressed;
      obexService.DataTransferSucceeded += 
        obexService_DataTransferSucceeded;
      obexService.DeviceConnected += obexService_DeviceConnected;
      obexService.Disconnected += obexService_Disconnected;
      obexService.Disconnecting += obexService_Disconnecting;
      obexService.ServiceConnected += obexService_ServiceConnected;
      await obexService.ConnectAsync();
    }
  });
}

Cuando se genere el evento Disconnected, elimine todos los controladores y borre la instancia ObexService. Durante la transferencia de datos, podrían darse ciertas circunstancias que le obligarían a anularla. Para anular la transferencia actual, llame a AbortAsync. Se genera el evento Aborted con el siguiente código y termina la conexión con el dispositivo seleccionado:

async void obexService_Aborted(object sender, EventArgs e)
{
  await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  {
    System.Diagnostics.Debug.WriteLine("Aborted");
    if (!filesToShare.Count.Equals(0))
    {
      filesToShare.RemoveAt(0);
    }
  });
}

Resumen

Ya ha completado la aplicación de demostración. El concepto de las aplicaciones universales es realmente útil ya que puede escribir una única parte de código para varias plataformas de Windows y factores de forma, reduciendo así el esfuerzo general de desarrollo.

He utilizado estas librerías en varias aplicaciones de la Tienda Windows y de Windows Phone. Busque Code Create (una aplicación gratuita de Windows Phone) u OBEX (aplicación universal) para ver cómo funcionan estas API en conjunto con la aplicación. Puede descargar estas bibliotecas en el repositorio NuGet. Solo tiene que buscar “Bluetooth OBEX para aplicaciones de la Tienda” en el diálogo en línea de NuGet que se encuentra en la solución de Visual Studio, e importar estas bibliotecas a modo de referencia para los proyectos.


Uday Gupta es ingeniero superior de desarrollo de productos en Symphony Teleca Corp. Pvt Ltd., en la India. Tiene experiencia en una gran variedad de tecnologías .NET y en especial en Windows Presentation Foundation, Silverlight, Windows Phone y Windows 8.x.

Gracias a los siguientes expertos técnicos de Microsoft por revisar este artículo: Jeff Kelley y Guruprasad Subbarayan