package de.tudarmstadt.informatik.hostage.sync.p2p; import android.app.Activity; import android.app.ProgressDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.IntentFilter; import android.net.wifi.p2p.WifiP2pConfig; import android.net.wifi.p2p.WifiP2pDevice; import android.net.wifi.p2p.WifiP2pDeviceList; import android.net.wifi.p2p.WifiP2pGroup; import android.net.wifi.p2p.WifiP2pInfo; import android.net.wifi.p2p.WifiP2pManager; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import android.widget.ViewAnimator; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; import de.tudarmstadt.informatik.hostage.R; import de.tudarmstadt.informatik.hostage.logging.Record; import de.tudarmstadt.informatik.hostage.logging.SyncData; import de.tudarmstadt.informatik.hostage.logging.SyncInfo; import de.tudarmstadt.informatik.hostage.ui.activity.MainActivity; public class P2PSyncActivity extends Activity implements WifiP2pManager.GroupInfoListener, WifiP2pManager.PeerListListener, WifiP2pManager.ChannelListener, AdapterView.OnItemClickListener, WifiP2pManager.ConnectionInfoListener { private WifiP2pManager mManager; private WifiP2pManager.Channel mChannel; private BroadcastReceiver mReceiver; private IntentFilter mIntentFilter; private List peers = new ArrayList(); private TextView mTxtP2PDeviceName; private TextView mTxtP2PDeviceStatus; private ViewAnimator mViewAnimator; private RelativeLayout mDevicesContainer; private TextView mTxtP2PSearchProgress; private ListView mLstP2PDevices; private RelativeLayout mWelcomeContainer; private TextView mTxtP2PNotAvailable; private TextView mTxtP2PChangeDeviceName; private WifiP2pDevice mDevice; private WifiP2pDevice mOtherDevice; private WifiP2pInfo mInfo; private final int OWNER_SERVER_PORT = 8988; private final int CLIENT_SERVER_PORT = 8989; private ClientAsyncTask clientAsync; private ServerAsyncTask serverAsync; private void extractFromView(){ mTxtP2PDeviceName = (TextView) findViewById(R.id.txt_p2p_device_name); mTxtP2PDeviceStatus = (TextView) findViewById(R.id.txt_p2p_device_status); mTxtP2PChangeDeviceName = (TextView) findViewById(R.id.txtP2PChangeDeviceName); //mTxtP2PHeader = (TextView) findViewById(R.id.txtP2PHeader); //mTxtP2PSubHeader = (TextView) findViewById(R.id.txtP2PSubheader); //mTxtP2PHelpBack = (TextView) findViewById(R.id.txtP2PHelpBack); mViewAnimator = (ViewAnimator) findViewById(R.id.viewAnimator); mDevicesContainer = (RelativeLayout) findViewById(R.id.devicesContainer); mWelcomeContainer = (RelativeLayout) findViewById(R.id.welcomeContainer); mTxtP2PSearchProgress = (TextView) findViewById(R.id.txtP2PSearchProgress); mLstP2PDevices = (ListView) findViewById(R.id.lstP2PDevices); //mBtnP2PSearch = (Button) findViewById(R.id.btnP2PSearch); mTxtP2PNotAvailable = (TextView) findViewById(R.id.txtP2PNotAvailable); } public void discoverPeers(){ mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() { @Override public void onSuccess() { mViewAnimator.showNext(); mTxtP2PSearchProgress.setVisibility(View.VISIBLE); setWifiDirectAvailable(); } @Override public void onFailure(int reason) { if(reason == WifiP2pManager.P2P_UNSUPPORTED) { setWifiDirectNotAvailable(); } } }); } public void setWifiDirectNotAvailable(){ //mBtnP2PSearch.setVisibility(View.GONE); mTxtP2PNotAvailable.setVisibility(View.VISIBLE); peers.clear(); ((WiFiPeerListAdapter) mLstP2PDevices.getAdapter()).notifyDataSetChanged(); mDevice = null; } public void setWifiDirectAvailable(){ //mBtnP2PSearch.setVisibility(View.VISIBLE); mTxtP2PNotAvailable.setVisibility(View.GONE); } public void closeConnection(){ if(this.serverAsync != null && !this.serverAsync.isCancelled()){ this.serverAsync.cancel(true); } if(this.clientAsync != null && !this.clientAsync.isCancelled()){ this.clientAsync.cancel(true); } } public void registerListeners(){ mTxtP2PChangeDeviceName.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Method method1 = null; try { method1 = mManager.getClass().getDeclaredMethod("setDeviceName", WifiP2pManager.Channel.class); method1.invoke(mManager, mChannel, "Android_fc546"); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_p2_psync); assert getActionBar() != null; getActionBar().setTitle("WifiDirect Synchronization"); this.extractFromView(); this.registerListeners(); this.mLstP2PDevices.setAdapter(new WiFiPeerListAdapter(this, R.layout.row_devices, peers)); this.mLstP2PDevices.setOnItemClickListener(this); mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); mChannel = mManager.initialize(this, getMainLooper(), new WifiP2pManager.ChannelListener() { @Override public void onChannelDisconnected() { System.out.println("---------------------> CHannel disconnect!!!"); } }); mIntentFilter = new IntentFilter(); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); this.discoverPeers(); } @Override protected void onResume() { super.onResume(); mReceiver = new P2PBroadcastReceiver(mManager, mChannel, this); registerReceiver(mReceiver, mIntentFilter); } @Override protected void onPause() { super.onPause(); unregisterReceiver(mReceiver); } @Override public void onPeersAvailable(WifiP2pDeviceList peerList){ mTxtP2PSearchProgress.setVisibility(View.GONE); peers.clear(); peers.addAll(peerList.getDeviceList()); ((WiFiPeerListAdapter) mLstP2PDevices.getAdapter()).notifyDataSetChanged(); if (peers.size() == 0) { return; } } private WifiP2pDevice getConnectedPeer(){ for(WifiP2pDevice device: peers){ if(device.status == WifiP2pDevice.CONNECTED){ return device; } } return null; } private static String getDeviceStatus(int deviceStatus) { switch (deviceStatus) { case WifiP2pDevice.AVAILABLE: return "Available"; case WifiP2pDevice.INVITED: return "Invited"; case WifiP2pDevice.CONNECTED: return "Connected"; case WifiP2pDevice.FAILED: return "Failed"; case WifiP2pDevice.UNAVAILABLE: return "Unavailable"; default: return "Unknown"; } } public void updateThisDevice(WifiP2pDevice device) { this.mDevice = device; mTxtP2PDeviceName.setText(device.deviceName); mTxtP2PDeviceStatus.setText(getDeviceStatus(device.status)); } @Override public void onChannelDisconnected() { closeConnection(); } @Override public void onItemClick(AdapterView parent, View view, int position, long id) { final WifiP2pDevice device = (WifiP2pDevice) this.mLstP2PDevices.getAdapter().getItem(position); WifiP2pConfig config = new WifiP2pConfig(); config.deviceAddress = device.deviceAddress; mManager.connect(mChannel, config, new WifiP2pManager.ActionListener() { @Override public void onSuccess() { mOtherDevice = device; } @Override public void onFailure(int reason) { //mOtherDevice = null; Toast.makeText(P2PSyncActivity.this, "Could not connect to device. Retry.", Toast.LENGTH_LONG).show(); } }); } @Override public void onConnectionInfoAvailable(WifiP2pInfo info) { mInfo = info; //if(mOtherDevice == null) return; if(info.groupFormed && info.isGroupOwner){ Log.i("server", "I am the group owner!"); WifiP2pDevice connectedPeer = getConnectedPeer(); if(connectedPeer != null && mInfo != null){ Log.i("peers", mInfo.groupFormed + " - " + mInfo.isGroupOwner); Log.i("server", "Starting async server!"); serverAsync = new ServerAsyncTask(this, OWNER_SERVER_PORT); serverAsync.execute(); } //new ClientAsyncTask(this, mOtherDevice.deviceAddress, CLIENT_SERVER_PORT).execute(); } else if(info.groupFormed){ //new ServerAsyncTask(this, CLIENT_SERVER_PORT).execute(); Log.i("client", "Starting async client!"); clientAsync = new ClientAsyncTask(this, mInfo.groupOwnerAddress.getHostAddress(), OWNER_SERVER_PORT); clientAsync.execute(); } } public void sendDatabase(boolean isOwner){ } @Override public void onGroupInfoAvailable(WifiP2pGroup group) { } /** * Array adapter for ListFragment that maintains WifiP2pDevice list. */ private class WiFiPeerListAdapter extends ArrayAdapter { private List items; /** * @param context * @param textViewResourceId * @param objects */ public WiFiPeerListAdapter(Context context, int textViewResourceId, List objects) { super(context, textViewResourceId, objects); items = objects; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if (v == null) { LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = vi.inflate(R.layout.row_devices, null); } WifiP2pDevice device = items.get(position); if (device != null) { TextView top = (TextView) v.findViewById(R.id.device_name); TextView bottom = (TextView) v.findViewById(R.id.device_details); if (top != null) { top.setText(device.deviceName); } if (bottom != null) { bottom.setText(getDeviceStatus(device.status)); } } return v; } } public static class ServerAsyncTask extends AsyncTask { private Activity context; private int port; private final ProgressDialog progressDialog; /** * @param context */ public ServerAsyncTask(Activity context, int port) { this.context = context; this.port = port; this.progressDialog = new ProgressDialog(context); } @Override protected void onPreExecute() { super.onPreExecute(); this.progressDialog.setMessage("Synchronizing data with other device..."); this.progressDialog.setIndeterminate(false); this.progressDialog.setMax(100); this.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); this.progressDialog.setCancelable(true); this.progressDialog.show(); } @Override protected Integer doInBackground(String... params) { try { ServerSocket serverSocket = new ServerSocket(this.port); Log.i("server", "Waiting for clients!"); Socket client = serverSocket.accept(); Log.i("server", "Client connected!"); publishProgress(1); ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream()); // -- client speaks first, receive syncinfo SyncInfo otherSyncInfo = null; try { otherSyncInfo = (SyncInfo) ois.readObject(); } catch (ClassNotFoundException e) { e.printStackTrace(); } publishProgress(2); // --- 1. write first message - syncinfo Log.i("server", "writing to client"); SyncInfo thisSyncInfo = new SyncInfo(); thisSyncInfo.deviceMap.put("dev1", 12L); thisSyncInfo.deviceMap.put("dev2", 13L); oos.writeObject(thisSyncInfo); oos.flush(); publishProgress(3); // --- 2. read sync data SyncData syncData = null; try { syncData = (SyncData) ois.readObject(); } catch (ClassNotFoundException e) { e.printStackTrace(); } publishProgress(4); // --- 3. send sync data SyncData mySyncData = new SyncData(); mySyncData.records.add(new Record()); oos.writeObject(mySyncData); oos.flush(); publishProgress(5); // --- 4. We are done! ois.close(); oos.close(); //copyFile(inputstream, new FileOutputStream(f)); serverSocket.close(); return 0; } catch (IOException e) { return -1; } } @Override protected void onProgressUpdate(Integer... progress) { this.progressDialog.setProgress(progress[0] * (100 / 5)); } @Override protected void onPostExecute(Integer unused) { this.progressDialog.dismiss(); } } public static class ClientAsyncTask extends AsyncTask { private Context context; private int port; private String host; private static final int SOCKET_TIMEOUT = 10000; private int tryNum = 0; private final ProgressDialog progressDialog; @Override protected void onPreExecute() { super.onPreExecute(); this.progressDialog.setMessage("Synchronizing data with other device..."); this.progressDialog.setIndeterminate(false); this.progressDialog.setMax(100); this.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); this.progressDialog.setCancelable(true); this.progressDialog.show(); } /** * @param context */ public ClientAsyncTask(Context context, String host, int port) { this.context = context; this.host = host; this.port = port; this.progressDialog = new ProgressDialog(context); } @Override protected Integer doInBackground(String... params) { Socket socket = new Socket(); tryNum++; try { Log.i("client", "trying to connect to " + host + ":" + port); socket.bind(null); socket.connect(new InetSocketAddress(host, port), SOCKET_TIMEOUT); Log.i("client", "connected to server"); publishProgress(1); ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); // Client sends first! SyncInfo thisSyncInfo = new SyncInfo(); thisSyncInfo.deviceMap.put("dev4", 12L); thisSyncInfo.deviceMap.put("dev5", 13L); oos.writeObject(thisSyncInfo); oos.flush(); publishProgress(2); // --- 1. Receive sync info SyncInfo otherSyncInfo = null; try { otherSyncInfo = (SyncInfo) ois.readObject(); } catch (ClassNotFoundException e) { e.printStackTrace(); } publishProgress(3); // --- 2. Send sync data SyncData thisSyncData = new SyncData(); thisSyncData.records.add(new Record()); oos.writeObject(thisSyncData); oos.flush(); publishProgress(4); // --- 3. Receive sync data SyncData otherSyncData = null; try { otherSyncData = (SyncData) ois.readObject(); } catch (ClassNotFoundException e) { e.printStackTrace(); } publishProgress(5); // -- ois.close(); oos.close(); socket.close(); } catch (IOException e) { if(tryNum >= 5) { return -1; } long seconds_to_wait = (long) Math.min(60, Math.pow(2, tryNum)); Log.i("client", "could not connect to server. Will try again in " + seconds_to_wait + "s"); try { Thread.sleep(seconds_to_wait * 1000); doInBackground(params); } catch (InterruptedException e1) { e1.printStackTrace(); } } return 0; } @Override protected void onProgressUpdate(Integer... progress) { this.progressDialog.setProgress(progress[0] * (100 / 5)); } @Override protected void onPostExecute(Integer unused) { this.progressDialog.dismiss(); } } public static boolean copyFile(InputStream inputStream, OutputStream out) { byte buf[] = new byte[1024]; int len; try { while ((len = inputStream.read(buf)) != -1) { out.write(buf, 0, len); } out.close(); inputStream.close(); } catch (IOException e) { return false; } return true; } }