Visão geral do Wi-Fi Direct (ponto a ponto ou P2P)

O Wi-Fi Direct (P2P) permite que dispositivos com o hardware adequado se conectem diretamente por Wi-Fi sem um ponto de acesso intermediário. Usando essas APIs, você pode descobrir e se conectar a outros dispositivos compatíveis com o Wi-Fi P2P e depois se comunicar por meio de uma conexão rápida por distâncias muito mais longas que uma conexão Bluetooth. Isso é útil para aplicativos que compartilham dados entre usuários, como um jogo multijogador ou um aplicativo de compartilhamento de fotos.

As APIs de Wi-Fi P2P são constituídas das seguintes partes principais:

  • Métodos que permitem descobrir, solicitar e se conectar a pares, que são definidos na classe WifiP2pManager.
  • Os listeners que permitem que você seja notificado sobre a conclusão ou a falha das chamadas de método WifiP2pManager. Ao chamar métodos WifiP2pManager, cada um deles pode receber um listener específico transmitido como um parâmetro.
  • Intents que notificam você sobre eventos específicos detectados pela estrutura Wi-Fi P2P, como uma conexão interrompida ou um ponto recém-descoberto.

Normalmente, esses três componentes principais das APIs são usados juntos. Por exemplo, você pode fornecer um WifiP2pManager.ActionListener para uma chamada para discoverPeers() para que os métodos ActionListener.onSuccess() e ActionListener.onFailure() possam notificar você. Uma intent WIFI_P2P_PEERS_CHANGED_ACTION também será transmitida se o método discoverPeers() descobrir que a lista de pontos foi alterada.

Visão geral da API

A classe WifiP2pManager fornece métodos para permitir que você interaja com o hardware Wi-Fi do seu dispositivo e realize tarefas como encontrar e se conectar a pontos. As seguintes ações estão disponíveis:

Tabela 1. Métodos Wi-Fi P2P

Método Descrição
initialize() Registra o aplicativo com a biblioteca Wi-Fi. Chame esse método antes de chamar qualquer outro método Wi-Fi P2P.
connect() Inicia uma conexão ponto a ponto com um dispositivo com a configuração especificada.
cancelConnect() Cancela qualquer negociação de grupo ponto a ponto em andamento.
requestConnectInfo() Solicita informações de conexão de um dispositivo.
createGroup() Cria um grupo ponto a ponto com o dispositivo atual como proprietário do grupo.
removeGroup() Remove o atual grupo ponto a ponto.
requestGroupInfo() Solicita informações do grupo ponto a ponto.
discoverPeers() Inicia a descoberta de pontos.
requestPeers() Solicita a lista atual de pontos descobertos.

Os métodos WifiP2pManager permitem transmitir um listener para que a estrutura Wi-Fi P2P possa notificar sua atividade sobre o status de uma chamada. As interfaces disponíveis do listener e as chamadas do método WifiP2pManager que usam os listeners são descritas na tabela 2.

Tabela 2. Listeners Wi-Fi P2P

Interface do listener Ações associadas
WifiP2pManager.ActionListener connect(), cancelConnect(), createGroup(), removeGroup() e discoverPeers()
WifiP2pManager.ChannelListener initialize()
WifiP2pManager.ConnectionInfoListener requestConnectInfo()
WifiP2pManager.GroupInfoListener requestGroupInfo()
WifiP2pManager.PeerListListener requestPeers()

As APIs de Wi-Fi P2P definem intents transmitidos quando certos eventos de Wi-Fi P2P acontecem, como a descoberta de um novo ponto ou quando o estado do Wi-Fi de um dispositivo é alterado. Você pode fazer registro para receber esses intents no seu aplicativo criando um broadcast receiver que os processa:

Tabela 3. Intents de Wi-Fi P2P

Intent Descrição
WIFI_P2P_CONNECTION_CHANGED_ACTION Transmitido quando o estado da conexão Wi-Fi do dispositivo for alterado.
WIFI_P2P_PEERS_CHANGED_ACTION Transmitido quando você chamar discoverPeers(). Normalmente, você vai chamar requestPeers() para conferir uma lista atualizada de pontos, se processar essa intent no seu app.
WIFI_P2P_STATE_CHANGED_ACTION Transmitido quando o Wi-Fi P2P está ativado ou desativado no dispositivo.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION Transmitido quando os detalhes de um dispositivo foram alterados, como o nome dele.

Criar um broadcast receiver para intents de Wi-Fi P2P

Um broadcast receiver permite que você receba intents transmitidos pelo sistema Android para que o aplicativo possa responder aos eventos do seu interesse. Siga estas etapas básicas para criar um broadcast receiver e gerenciar intents de Wi-Fi P2P:

  1. Crie uma classe que estenda a BroadcastReceiver. No construtor da classe, você vai usar parâmetros para o WifiP2pManager, WifiP2pManager.Channel e a atividade em que esse broadcast receiver será registrado. Assim, o broadcast receiver envia atualizações para a atividade e tem acesso ao hardware Wi-Fi e a um canal de comunicação, se necessário.

  2. No broadcast receiver, verifique os intents em que você está interessado no método onReceive(). Realize as ações necessárias, dependendo da intent recebida. Por exemplo, se o broadcast receiver receber uma intent WIFI_P2P_PEERS_CHANGED_ACTION, você poderá chamar o método requestPeers() para conferir uma lista dos pontos descobertos recentemente.

O código a seguir mostra como criar um broadcast receiver típico. O broadcast receiver aciona um objeto WifiP2pManager e uma atividade como argumentos e usa essas duas classes para realizar adequadamente as ações necessárias quando o broadcast receiver recebe uma intent:

Kotlin

/**
* A BroadcastReceiver that notifies of important Wi-Fi p2p events.
*/
class WiFiDirectBroadcastReceiver(
       private val manager: WifiP2pManager,
       private val channel: WifiP2pManager.Channel,
       private val activity: MyWifiActivity
) : BroadcastReceiver() {

   override fun onReceive(context: Context, intent: Intent) {
       val action: String = intent.action
       when (action) {
           WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> {
               // Check to see if Wi-Fi is enabled and notify appropriate activity
           }
           WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {
               // Call WifiP2pManager.requestPeers() to get a list of current peers
           }
           WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> {
               // Respond to new connection or disconnections
           }
           WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> {
               // Respond to this device's wifi state changing
           }
       }
   }
}

Java

/**
* A BroadcastReceiver that notifies of important Wi-Fi p2p events.
*/
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {

   private WifiP2pManager manager;
   private Channel channel;
   private MyWiFiActivity activity;

   public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel,
           MyWifiActivity activity) {
       super();
       this.manager = manager;
       this.channel = channel;
       this.activity = activity;
   }

   @Override
   public void onReceive(Context context, Intent intent) {
       String action = intent.getAction();

       if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
           // Check to see if Wi-Fi is enabled and notify appropriate activity
       } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
           // Call WifiP2pManager.requestPeers() to get a list of current peers
       } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
           // Respond to new connection or disconnections
       } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
           // Respond to this device's wifi state changing
       }
   }
}

Em dispositivos com o Android 10 e versões mais recentes, as seguintes intents de transmissão não são fixas:

WIFI_P2P_CONNECTION_CHANGED_ACTION
Os aplicativos podem usar requestConnectionInfo(), requestNetworkInfo() ou requestGroupInfo() para extrair as informações de conexão atuais.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
Os aplicativos podem usar requestDeviceInfo() para extrair as informações de conexão atuais.

Criar um aplicativo Wi-Fi P2P

O desenvolvimento de um aplicativo Wi-Fi P2P envolve a criação e o registro de um broadcast receiver, a descoberta de pontos, a conexão e a transferência de dados para um ponto. As seções a seguir descrevem como fazer isso.

Configuração inicial

Antes de usar as APIs de Wi-Fi P2P, verifique se o aplicativo pode acessar o hardware e se o dispositivo é compatível com o protocolo Wi-Fi P2P. Se o Wi-Fi P2P tiver suporte, você poderá ter uma instância de WifiP2pManager, criar e registrar seu broadcast receiver e começar a usar as APIs de Wi-Fi P2P.

  1. Solicite permissão para usar o hardware Wi-Fi no dispositivo e declare que seu aplicativo tem a versão mínima correta do SDK no manifesto do Android:

    <uses-sdk android:minSdkVersion="14" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!-- If your app targets Android 13 (API level 33)
         or higher, you must declare the NEARBY_WIFI_DEVICES permission. -->
    <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
                     <!-- If your app derives location information from
                          Wi-Fi APIs, don't include the "usesPermissionFlags"
                          attribute. -->
                     android:usesPermissionFlags="neverForLocation" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
                     <!-- If any feature in your app relies on precise location
                          information, don't include the "maxSdkVersion"
                          attribute. -->
                     android:maxSdkVersion="32" />
    

    Além das permissões anteriores, as seguintes APIs também exigem que o Modo de localização esteja ativado:

  2. Verifique se o Wi-Fi P2P está ativado e é compatível. Um bom lugar para verificar isso é no seu broadcast receiver quando ele receber a intent WIFI_P2P_STATE_CHANGED_ACTION. Notifique sua atividade do estado do Wi-Fi P2P e defina as reações necessárias:

    Kotlin

    override fun onReceive(context: Context, intent: Intent) {
    ...
    val action: String = intent.action
    when (action) {
       WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> {
           val state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1)
           when (state) {
               WifiP2pManager.WIFI_P2P_STATE_ENABLED -> {
                   // Wifi P2P is enabled
               }
               else -> {
                   // Wi-Fi P2P is not enabled
               }
           }
       }
    }
    ...
    }

    Java

    @Override
    public void onReceive(Context context, Intent intent) {
    ...
    String action = intent.getAction();
    if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
       int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
       if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
           // Wifi P2P is enabled
       } else {
           // Wi-Fi P2P is not enabled
       }
    }
    ...
    }
  3. No método onCreate() da sua atividade, acesse uma instância de WifiP2pManager e registre seu aplicativo com a estrutura Wi-Fi P2P chamando initialize(). Esse método retorna um WifiP2pManager.Channel, que é usado para conectar seu aplicativo ao framework do Wi-Fi P2P. Você também precisa criar uma instância do seu broadcast receiver com os objetos WifiP2pManager e WifiP2pManager.Channel, além de uma referência à sua atividade. Isso permite que o broadcast receiver notifique sua atividade de eventos interessantes e faça as atualizações correspondentes. Também é possível gerenciar o estado do Wi-Fi do dispositivo, se necessário:

    Kotlin

    val manager: WifiP2pManager? by lazy(LazyThreadSafetyMode.NONE) {
       getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager?
    }
    
    var channel: WifiP2pManager.Channel? = null
    var receiver: BroadcastReceiver? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
       ...
    
       channel = manager?.initialize(this, mainLooper, null)
       channel?.also { channel ->
           receiver = WiFiDirectBroadcastReceiver(manager, channel, this)
       }
    }

    Java

    WifiP2pManager manager;
    Channel channel;
    BroadcastReceiver receiver;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState){
       ...
       manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
       channel = manager.initialize(this, getMainLooper(), null);
       receiver = new WiFiDirectBroadcastReceiver(manager, channel, this);
       ...
    }
  4. Crie um filtro de intent e adicione as mesmas intents verificadas pelo seu broadcast receiver:

    Kotlin

    val intentFilter = IntentFilter().apply {
       addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)
       addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)
       addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)
       addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION)
    }

    Java

    IntentFilter intentFilter;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState){
       ...
       intentFilter = new IntentFilter();
       intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
       intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
       intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
       intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
       ...
    }
  5. Faça o registro do broadcast receiver no método onResume() da atividade e cancele o registro no método onPause() da atividade:

    Kotlin

    /* register the broadcast receiver with the intent values to be matched */
    override fun onResume() {
       super.onResume()
       receiver?.also { receiver ->
           registerReceiver(receiver, intentFilter)
       }
    }
    
    /* unregister the broadcast receiver */
    override fun onPause() {
       super.onPause()
       receiver?.also { receiver ->
           unregisterReceiver(receiver)
       }
    }

    Java

    /* register the broadcast receiver with the intent values to be matched */
    @Override
    protected void onResume() {
       super.onResume();
       registerReceiver(receiver, intentFilter);
    }
    /* unregister the broadcast receiver */
    @Override
    protected void onPause() {
       super.onPause();
       unregisterReceiver(receiver);
    }
  6. Quando você receber uma WifiP2pManager.Channel e configurar um broadcast receiver, seu aplicativo poderá fazer chamadas de método Wi-Fi P2P e receber intents Wi-Fi P2P.

  7. Implemente seu aplicativo usando os recursos do Wi-Fi P2P chamando os métodos em WifiP2pManager.

As próximas seções descrevem como realizar ações comuns, como descobrir e se conectar a pontos.

Descobrir pontos

Chame discoverPeers() para detectar pontos de acesso disponíveis que estão no intervalo e disponíveis para conexão. A chamada para essa função é assíncrona, e uma conclusão ou falha é comunicada ao seu aplicativo com onSuccess() e onFailure() se você criou um WifiP2pManager.ActionListener. O método onSuccess() só notifica que o processo de descoberta foi bem-sucedido e não fornece nenhuma informação sobre os pontos encontrados. O exemplo de código a seguir mostra como configurar isso.

Kotlin

manager?.discoverPeers(channel, object : WifiP2pManager.ActionListener {

   override fun onSuccess() {
       ...
   }

   override fun onFailure(reasonCode: Int) {
       ...
   }
})

Java

manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
   @Override
   public void onSuccess() {
       ...
   }

   @Override
   public void onFailure(int reasonCode) {
       ...
   }
});

Se o processo de descoberta for bem-sucedido e detectar novos pontos, o sistema vai transmitir a intent WIFI_P2P_PEERS_CHANGED_ACTION, que você pode detectar em um broadcast receiver para acessar uma lista de pontos. Quando um aplicativo recebe a intent WIFI_P2P_PEERS_CHANGED_ACTION, é possível solicitar uma lista dos pontos descobertos com requestPeers(). O código a seguir mostra como configurar isso.

Kotlin

override fun onReceive(context: Context, intent: Intent) {
   val action: String = intent.action
   when (action) {
       ...
       WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {
           manager?.requestPeers(channel) { peers: WifiP2pDeviceList? ->
               // Handle peers list
           }
       }
       ...
   }
}

Java

PeerListListener myPeerListListener;
...
if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {

   // request available peers from the wifi p2p manager. This is an
   // asynchronous call and the calling activity is notified with a
   // callback on PeerListListener.onPeersAvailable()
   if (manager != null) {
       manager.requestPeers(channel, myPeerListListener);
   }
}

O método requestPeers() também é assíncrono e pode notificar sua atividade quando uma lista de pontos estiver disponível com onPeersAvailable(), que é definido na interface WifiP2pManager.PeerListListener. O método onPeersAvailable() fornece um WifiP2pDeviceList, que pode ser iterado para encontrar o ponto de conexão.

Conectar pontos

Depois de receber uma lista de possíveis pares e selecionar um dispositivo para se conectar, chame o método connect(). Essa chamada de método exige um objeto WifiP2pConfig que contenha informações sobre o dispositivo a ser conectado. WifiP2pManager.ActionListener pode notificar você sobre o sucesso ou a falha da conexão. O código a seguir mostra como criar uma conexão com um dispositivo.

Kotlin

val device: WifiP2pDevice = ...
val config = WifiP2pConfig()
config.deviceAddress = device.deviceAddress
channel?.also { channel ->
   manager?.connect(channel, config, object : WifiP2pManager.ActionListener {

       override fun onSuccess() {
           //success logic
       }

       override fun onFailure(reason: Int) {
           //failure logic
       }
   }
)}

Java

//obtain a peer from the WifiP2pDeviceList
WifiP2pDevice device;
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
manager.connect(channel, config, new ActionListener() {

   @Override
   public void onSuccess() {
       //success logic
   }

   @Override
   public void onFailure(int reason) {
       //failure logic
   }
});

Transferir dados

Depois que uma conexão é estabelecida, você pode transferir dados entre os dispositivos com soquetes. Veja as etapas básicas da transferência de dados:

  1. Crie um ServerSocket. Esse soquete aguarda uma conexão de um cliente em uma porta especificada e a bloqueia até que ela aconteça. Por isso, realize essa ação em um thread em segundo plano.
  2. Crie um cliente Socket. O cliente usa o endereço IP e a porta do soquete do servidor para se conectar ao dispositivo do servidor.
  3. Envie os dados do cliente para o servidor. Quando o soquete do cliente se conecta ao soquete do servidor, é possível enviar dados do cliente para o servidor com streams de bytes.
  4. O soquete do servidor aguarda uma conexão do cliente (com o método accept()). Essa chamada é bloqueada até que um cliente se conecte. Por isso, faça essa chamada em outro thread. Quando uma conexão acontece, o dispositivo do servidor pode receber os dados do cliente.

O exemplo a seguir, modificado da Demonstração de Wi-Fi P2P, mostra como criar essa comunicação de soquete cliente-servidor e transferir imagens JPEG de um cliente para um servidor com um serviço. Para um exemplo completo de funcionamento, compile e execute a demonstração.

Kotlin

class FileServerAsyncTask(
       private val context: Context,
       private var statusText: TextView
) : AsyncTask<Void, Void, String?>() {

   override fun doInBackground(vararg params: Void): String? {
       /**
        * Create a server socket.
        */
       val serverSocket = ServerSocket(8888)
       return serverSocket.use {
           /**
            * Wait for client connections. This call blocks until a
            * connection is accepted from a client.
            */
           val client = serverSocket.accept()
           /**
            * If this code is reached, a client has connected and transferred data
            * Save the input stream from the client as a JPEG file
            */
           val f = File(Environment.getExternalStorageDirectory().absolutePath +
                   "/${context.packageName}/wifip2pshared-${System.currentTimeMillis()}.jpg")
           val dirs = File(f.parent)

           dirs.takeIf { it.doesNotExist() }?.apply {
               mkdirs()
           }
           f.createNewFile()
           val inputstream = client.getInputStream()
           copyFile(inputstream, FileOutputStream(f))
           serverSocket.close()
           f.absolutePath
       }
   }

   private fun File.doesNotExist(): Boolean = !exists()

   /**
    * Start activity that can handle the JPEG image
    */
   override fun onPostExecute(result: String?) {
       result?.run {
           statusText.text = "File copied - $result"
           val intent = Intent(android.content.Intent.ACTION_VIEW).apply {
               setDataAndType(Uri.parse("file://$result"), "image/*")
           }
           context.startActivity(intent)
       }
   }
}

Java

public static class FileServerAsyncTask extends AsyncTask {

   private Context context;
   private TextView statusText;

   public FileServerAsyncTask(Context context, View statusText) {
       this.context = context;
       this.statusText = (TextView) statusText;
   }

   @Override
   protected String doInBackground(Void... params) {
       try {

           /**
            * Create a server socket and wait for client connections. This
            * call blocks until a connection is accepted from a client
            */
           ServerSocket serverSocket = new ServerSocket(8888);
           Socket client = serverSocket.accept();

           /**
            * If this code is reached, a client has connected and transferred data
            * Save the input stream from the client as a JPEG file
            */
           final File f = new File(Environment.getExternalStorageDirectory() + "/"
                   + context.getPackageName() + "/wifip2pshared-" + System.currentTimeMillis()
                   + ".jpg");

           File dirs = new File(f.getParent());
           if (!dirs.exists())
               dirs.mkdirs();
           f.createNewFile();
           InputStream inputstream = client.getInputStream();
           copyFile(inputstream, new FileOutputStream(f));
           serverSocket.close();
           return f.getAbsolutePath();
       } catch (IOException e) {
           Log.e(WiFiDirectActivity.TAG, e.getMessage());
           return null;
       }
   }

   /**
    * Start activity that can handle the JPEG image
    */
   @Override
   protected void onPostExecute(String result) {
       if (result != null) {
           statusText.setText("File copied - " + result);
           Intent intent = new Intent();
           intent.setAction(android.content.Intent.ACTION_VIEW);
           intent.setDataAndType(Uri.parse("file://" + result), "image/*");
           context.startActivity(intent);
       }
   }
}

No cliente, conecte-se ao soquete do servidor com um soquete do cliente e transfira dados. Este exemplo transfere um arquivo JPEG no sistema de arquivos do dispositivo cliente.

Kotlin

val context = applicationContext
val host: String
val port: Int
val len: Int
val socket = Socket()
val buf = ByteArray(1024)
...
try {
   /**
    * Create a client socket with the host,
    * port, and timeout information.
    */
   socket.bind(null)
   socket.connect((InetSocketAddress(host, port)), 500)

   /**
    * Create a byte stream from a JPEG file and pipe it to the output stream
    * of the socket. This data is retrieved by the server device.
    */
   val outputStream = socket.getOutputStream()
   val cr = context.contentResolver
   val inputStream: InputStream = cr.openInputStream(Uri.parse("path/to/picture.jpg"))
   while (inputStream.read(buf).also { len = it } != -1) {
       outputStream.write(buf, 0, len)
   }
   outputStream.close()
   inputStream.close()
} catch (e: FileNotFoundException) {
   //catch logic
} catch (e: IOException) {
   //catch logic
} finally {
   /**
    * Clean up any open sockets when done
    * transferring or if an exception occurred.
    */
   socket.takeIf { it.isConnected }?.apply {
       close()
   }
}

Java

Context context = this.getApplicationContext();
String host;
int port;
int len;
Socket socket = new Socket();
byte buf[]  = new byte[1024];
...
try {
   /**
    * Create a client socket with the host,
    * port, and timeout information.
    */
   socket.bind(null);
   socket.connect((new InetSocketAddress(host, port)), 500);

   /**
    * Create a byte stream from a JPEG file and pipe it to the output stream
    * of the socket. This data is retrieved by the server device.
    */
   OutputStream outputStream = socket.getOutputStream();
   ContentResolver cr = context.getContentResolver();
   InputStream inputStream = null;
   inputStream = cr.openInputStream(Uri.parse("path/to/picture.jpg"));
   while ((len = inputStream.read(buf)) != -1) {
       outputStream.write(buf, 0, len);
   }
   outputStream.close();
   inputStream.close();
} catch (FileNotFoundException e) {
   //catch logic
} catch (IOException e) {
   //catch logic
}

/**
* Clean up any open sockets when done
* transferring or if an exception occurred.
*/
finally {
   if (socket != null) {
       if (socket.isConnected()) {
           try {
               socket.close();
           } catch (IOException e) {
               //catch logic
           }
       }
   }
}