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

O Wi-Fi Direct (P2P) permite que dispositivos Android 4.0 (API de nível 14) ou posterior, com o hardware apropriado, conectem-se diretamente via Wi-Fi sem um ponto de acesso intermediário. Usando essas APIs, você pode encontrar 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 para aplicativos de compartilhamento de fotos.

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

  • Os métodos que permitem descobrir, solicitar e conectar-se aos pontos são definidos na classe WifiP2pManager.

  • Os listeners que permitem que você seja notificado sobre a conclusão ou a falha dos métodos de chamada WifiP2pManager. Ao chamar os métodos WifiP2pManager, cada um deles poderá receber um listener específico transmitido como um parâmetro.

  • Os intents que notificam você sobre eventos específicos detectados pela biblioteca 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 WifiP2pManager.ActionListener para chamar discoverPeers(). Assim, você será notificado com os métodos ActionListener.onSuccess() e ActionListener.onFailure(). Um intent WIFI_P2P_PEERS_CHANGED_ACTION também será transmitido 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. Esse método precisa ser chamado antes de 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 do ponto.
requestPeers() Solicita a lista atual de pontos descobertos.

Os métodosWifiP2pManager permitem que você transmita um listener para que a biblioteca 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 correspondentes que usam os listeners são descritas na tabela a seguir:

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 gerencia estes intents:

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, use a chamada de requestPeers() para ver uma lista atualizada de pontos, se estiver gerenciando esse intent no seu aplicativo.
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 estende BroadcastReceiver. No criador da classe, tenha os parâmetros para WifiP2pManager, WifiP2pManager.Channel e a atividade em que este broadcast receiver será registrado. Assim, o broadcast receiver enviará atualizações para a atividade e terá 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 em onReceive(). Realize as ações necessárias, dependendo do intent recebido. Por exemplo, se o broadcast receiver receber um intent WIFI_P2P_PEERS_CHANGED_ACTION, você poderá chamar o método requestPeers() para ver uma lista de 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 executar adequadamente as ações necessárias ao receber um 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 mManager;
    private Channel mChannel;
    private MyWiFiActivity mActivity;

    public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel,
            MyWifiActivity activity) {
        super();
        this.mManager = manager;
        this.mChannel = channel;
        this.mActivity = 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
        }
    }
}

Com o Android Q, os seguintes intents de transmissão foram alterados de fixos para não fixos:

WIFI_P2P_CONNECTION_CHANGED_ACTION
Os aplicativos podem usar requestConnectionInfo(), requestNetworkInfo() ou requestGroupInfo() para recuperar as informações de conexão atuais.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
Os aplicativos podem usar requestDeviceInfo() para recuperar 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, além da conexão e 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 houver compatibilidade com Wi-Fi P2P, você poderá ter uma instância de WifiP2pManager, criar e fazer registro do 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 também declare que seu aplicativo tem a versão mínima correta do SDK no manifest 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.ACCESS_FINE_LOCATION" />
    <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" />
    

    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 o 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 da sua atividade onCreate(), acesse a instância WifiP2pManager e faça registro do seu aplicativo com a biblioteca Wi-Fi P2P chamando initialize(). Esse método retornará WifiP2pManager.Channel, que é usado para conectar seu aplicativo à biblioteca Wi-Fi P2P. Você também deve criar uma instância do seu broadcast receiver com os objetos WifiP2pManager e WifiP2pManager.Channel junto com uma referência à sua atividade. Isso permite que o broadcast receiver notifique sua atividade de eventos interessantes e faça as atualizações correspondentes. Assim, 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 mChannel: WifiP2pManager.Channel? = null
    var receiver: BroadcastReceiver? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
    
        mChannel = manager?.initialize(this, mainLooper, null)
        mChannel?.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, mChannel, this);
        ...
    }
    
  4. Crie um filtro e adicione os mesmos intents verificados 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 registro do broadcast receiver no método onResume() da sua atividade e cancele o método onPause():

    Kotlin

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

    Java

    /* register the broadcast receiver with the intent values to be matched */
    @Override
    protected void onResume() {
        super.onResume();
        registerReceiver(mReceiver, intentFilter);
    }
    /* unregister the broadcast receiver */
    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(mReceiver);
    }
    

    Quando você tiver WifiP2pManager.Channel e configurar um broadcast receiver, seu aplicativo poderá fazer chamadas de método Wi-Fi P2P e receber os intents relacionados.

    Agora você pode implementar seu aplicativo e usar os recursos Wi-Fi P2P chamando os métodos em WifiP2pManager. As próximas seções descrevem como executar ações comuns, como encontrar e se conectar a pontos.

Descobrir pontos

Para descobrir pontos disponíveis para conexão, chame discoverPeers() para detectá-los. A chamada para esta função é assíncrona e uma conclusão ou falha é comunicada ao seu aplicativo com onSuccess() e onFailure() se você tiver criado um WifiP2pManager.ActionListener. O método onSuccess() só avisará que o processo de descoberta foi bem-sucedido e não fornecerá nenhuma informação sobre os pontos encontrados:

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 transmitirá o intent WIFI_P2P_PEERS_CHANGED_ACTION, que você poderá interceptar em um broadcast receiver para acessar uma lista de pontos. Quando um aplicativo recebe o intent WIFI_P2P_PEERS_CHANGED_ACTION, você pode 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 usando onPeersAvailable(), que é definido na interface WifiP2pManager.PeerListListener. O método onPeersAvailable() fornece uma WifiP2pDeviceList, que você pode analisar para encontrar o ponto usado para conexão.

Conectar pontos

Ao encontrar o dispositivo com que você quer fazer a conexão após acessar uma lista de possíveis pontos, chame o método connect(). Esse método exige um objeto WifiP2pConfig, que tem informações do dispositivo para conexão. Você pode ser notificado sobre um sucesso ou falha na conexão por meio do WifiP2pManager.ActionListener. 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
mChannel?.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 isso 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 estiver conectado ao do servidor, você poderá 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 de outro thread. Quando uma conexão acontecer, o dispositivo do servidor poderá receber os dados do cliente. Realize qualquer ação com esses dados, como salvá-los em um arquivo ou apresentá-los ao usuário.

O exemplo a seguir, uma atualização da amostra 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, compile e execute a amostra Demonstração de Wi-Fi P2P.

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 os dados. Este exemplo mostra como transferir 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
            }
        }
    }
}