Example integration with WiFiDirect
Change-Id: Id65530c43d67894b46ce1aa0d523e6e99e621023
Refs:3939
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cc07744..948b73b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,6 +6,9 @@
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+ <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.ACCESS_NETWORK_STATE" />
<application
@@ -43,6 +46,7 @@
</intent-filter>
</receiver>
+ <service android:name="net.named_data.nfd.wifidirect.service.WDBroadcastReceiverService"/>
</application>
</manifest>
diff --git a/app/src/main/java/net/named_data/nfd/MainActivity.java b/app/src/main/java/net/named_data/nfd/MainActivity.java
index 4152545..ee19928 100644
--- a/app/src/main/java/net/named_data/nfd/MainActivity.java
+++ b/app/src/main/java/net/named_data/nfd/MainActivity.java
@@ -1,6 +1,6 @@
/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2015 Regents of the University of California
+ * Copyright (c) 2015-2017 Regents of the University of California
*
* This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
* See AUTHORS.md for complete list of NFD Android authors and contributors.
@@ -32,6 +32,7 @@
import com.intel.jndn.management.types.RibEntry;
import net.named_data.nfd.utils.G;
+import net.named_data.nfd.wifidirect.utils.NDNController;
import java.util.ArrayList;
@@ -44,6 +45,7 @@
FaceListFragment.Callbacks,
RouteListFragment.Callbacks
{
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -70,6 +72,11 @@
// DRAWER_ITEM_STRATEGIES));
items.add(new DrawerFragment.DrawerItem(R.string.drawer_item_logcat, 0,
DRAWER_ITEM_LOGCAT));
+ items.add(new DrawerFragment.DrawerItem(R.string.drawer_item_wifidirect, 0, DRAWER_ITEM_WIFIDIRECT));
+
+ // TODO here we are preloading the NDNController singleton to avoid UI slowdown
+ // it is due to building a test keychain: See NDNController.getInstance()
+ NDNController.getInstance();
m_drawerFragment = DrawerFragment.newInstance(items);
@@ -157,6 +164,9 @@
case DRAWER_ITEM_LOGCAT:
fragment = LogcatFragment.newInstance();
break;
+ case DRAWER_ITEM_WIFIDIRECT:
+ fragment = WiFiDirectFragment.newInstance();
+ break;
default:
// Invalid; Nothing else needs to be done
return;
@@ -202,4 +212,5 @@
public static final int DRAWER_ITEM_PING = 4;
//public static final int DRAWER_ITEM_STRATEGIES = 4;
public static final int DRAWER_ITEM_LOGCAT = 5;
+ public static final int DRAWER_ITEM_WIFIDIRECT = 6;
}
diff --git a/app/src/main/java/net/named_data/nfd/MainFragment.java b/app/src/main/java/net/named_data/nfd/MainFragment.java
index 898b504..72df064 100644
--- a/app/src/main/java/net/named_data/nfd/MainFragment.java
+++ b/app/src/main/java/net/named_data/nfd/MainFragment.java
@@ -1,18 +1,18 @@
/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2015 Regents of the University of California
- *
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
* This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
* See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
+ * <p>
* NFD Android is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
- *
+ * <p>
* NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
- *
+ * <p>
* You should have received a copy of the GNU General Public License along with
* NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -47,6 +47,7 @@
import net.named_data.nfd.service.NfdService;
import net.named_data.nfd.utils.G;
import net.named_data.nfd.utils.NfdcHelper;
+import net.named_data.nfd.wifidirect.utils.NDNController;
import org.joda.time.Period;
import org.joda.time.format.PeriodFormat;
@@ -59,8 +60,7 @@
}
@Override
- public void onCreate(Bundle savedInstanceState)
- {
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
m_handler = new Handler();
}
@@ -68,51 +68,47 @@
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState)
- {
+ @Nullable Bundle savedInstanceState) {
@SuppressLint("InflateParams")
- View v = inflater.inflate(R.layout.fragment_main, null);
-
- m_nfdStartStopSwitch = (Switch)v.findViewById(R.id.nfd_start_stop_switch);
+ View v = inflater.inflate(R.layout.fragment_main, null);
+ m_nfdStartStopSwitch = (Switch) v.findViewById(R.id.nfd_start_stop_switch);
m_nfdStartStopSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
- public void onCheckedChanged(CompoundButton compoundButton, boolean isOn)
- {
+ public void onCheckedChanged(CompoundButton compoundButton, boolean isOn) {
m_sharedPreferences.edit()
.putBoolean(PREF_NFD_SERVICE_STATUS, isOn)
.apply();
if (isOn) {
startNfdService();
- }
- else {
+ } else {
+ NDNController.getInstance().stop(); //stop wifi direct
stopNfdService();
}
}
});
- m_nfdStatusView = (ViewGroup)v.findViewById(R.id.status_view);
+ m_nfdStatusView = (ViewGroup) v.findViewById(R.id.status_view);
m_nfdStatusView.setVisibility(View.GONE);
- m_versionView = (TextView)v.findViewById(R.id.version);
- m_uptimeView = (TextView)v.findViewById(R.id.uptime);
- m_nameTreeEntriesView = (TextView)v.findViewById(R.id.name_tree_entries);
- m_fibEntriesView = (TextView)v.findViewById(R.id.fib_entries);
- m_pitEntriesView = (TextView)v.findViewById(R.id.pit_entries);
- m_measurementEntriesView = (TextView)v.findViewById(R.id.measurement_entries);
- m_csEntriesView = (TextView)v.findViewById(R.id.cs_entries);
- m_inInterestsView = (TextView)v.findViewById(R.id.in_interests);
- m_outInterestsView = (TextView)v.findViewById(R.id.out_interests);
- m_inDataView = (TextView)v.findViewById(R.id.in_data);
- m_outDataView = (TextView)v.findViewById(R.id.out_data);
- m_inNacksView = (TextView)v.findViewById(R.id.in_nacks);
- m_outNacksView = (TextView)v.findViewById(R.id.out_nacks);
+ m_versionView = (TextView) v.findViewById(R.id.version);
+ m_uptimeView = (TextView) v.findViewById(R.id.uptime);
+ m_nameTreeEntriesView = (TextView) v.findViewById(R.id.name_tree_entries);
+ m_fibEntriesView = (TextView) v.findViewById(R.id.fib_entries);
+ m_pitEntriesView = (TextView) v.findViewById(R.id.pit_entries);
+ m_measurementEntriesView = (TextView) v.findViewById(R.id.measurement_entries);
+ m_csEntriesView = (TextView) v.findViewById(R.id.cs_entries);
+ m_inInterestsView = (TextView) v.findViewById(R.id.in_interests);
+ m_outInterestsView = (TextView) v.findViewById(R.id.out_interests);
+ m_inDataView = (TextView) v.findViewById(R.id.in_data);
+ m_outDataView = (TextView) v.findViewById(R.id.out_data);
+ m_inNacksView = (TextView) v.findViewById(R.id.in_nacks);
+ m_outNacksView = (TextView) v.findViewById(R.id.out_nacks);
return v;
}
@Override
- public void onActivityCreated(@Nullable Bundle savedInstanceState)
- {
+ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
m_sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
}
@@ -143,7 +139,7 @@
if (!m_isNfdServiceConnected) {
// Bind to Service
getActivity().bindService(new Intent(getActivity(), NfdService.class),
- m_ServiceConnection, Context.BIND_AUTO_CREATE);
+ m_ServiceConnection, Context.BIND_AUTO_CREATE);
G.Log("MainFragment::bindNfdService()");
}
}
@@ -228,7 +224,7 @@
/**
* Client Message Handler.
- *
+ * <p>
* This handler is used to handle messages that are being sent back
* from the NfdService to the current application.
*/
@@ -297,15 +293,14 @@
/**
* Attempt to reconnect to the NfdService.
- *
+ * <p>
* This method attempts to reconnect the application to the NfdService
* when the NfdService has been killed (either by the user or by the OS).
*/
private Runnable m_retryConnectionToNfdService = new Runnable() {
@Override
public void
- run()
- {
+ run() {
G.Log("Retrying connection to NFD Service ...");
bindNfdService();
}
@@ -318,15 +313,13 @@
*/
@Override
protected ForwarderStatus
- doInBackground(Void... voids)
- {
+ doInBackground(Void... voids) {
try {
NfdcHelper nfdcHelper = new NfdcHelper();
ForwarderStatus fs = nfdcHelper.generalStatus();
nfdcHelper.shutdown();
return fs;
- }
- catch (Exception e) {
+ } catch (Exception e) {
G.Log("Error communicating with NFD (" + e.getMessage() + ")");
return null;
}
@@ -334,13 +327,11 @@
@Override
protected void
- onPostExecute(ForwarderStatus fs)
- {
+ onPostExecute(ForwarderStatus fs) {
if (fs == null) {
// when failed, try after 0.5 seconds
m_handler.postDelayed(m_statusUpdateRunnable, 500);
- }
- else {
+ } else {
m_versionView.setText(fs.getNfdVersion());
m_uptimeView.setText(PeriodFormat.getDefault().print(new Period(
fs.getCurrentTimestamp() - fs.getStartTimestamp())));
@@ -371,19 +362,29 @@
//////////////////////////////////////////////////////////////////////////////
- /** Button that starts and stops the NFD */
+ /**
+ * Button that starts and stops the NFD
+ */
private Switch m_nfdStartStopSwitch;
- /** Flag that marks that application is connected to the NfdService */
+ /**
+ * Flag that marks that application is connected to the NfdService
+ */
private boolean m_isNfdServiceConnected = false;
- /** Client Message Handler */
+ /**
+ * Client Message Handler
+ */
private final Messenger m_clientMessenger = new Messenger(new ClientHandler());
- /** Messenger connection to NfdService */
+ /**
+ * Messenger connection to NfdService
+ */
private Messenger m_nfdServiceMessenger = null;
- /** ListView holding NFD status information */
+ /**
+ * ListView holding NFD status information
+ */
private ViewGroup m_nfdStatusView;
private TextView m_versionView;
@@ -403,8 +404,7 @@
private Handler m_handler;
private Runnable m_statusUpdateRunnable = new Runnable() {
@Override
- public void run()
- {
+ public void run() {
new StatusUpdateTask().execute();
}
};
@@ -412,4 +412,4 @@
private SharedPreferences m_sharedPreferences;
private static final String PREF_NFD_SERVICE_STATUS = "NFD_SERVICE_STATUS";
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/named_data/nfd/WiFiDirectFragment.java b/app/src/main/java/net/named_data/nfd/WiFiDirectFragment.java
new file mode 100644
index 0000000..2d67c22
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/WiFiDirectFragment.java
@@ -0,0 +1,472 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.SharedPreferences;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.preference.PreferenceManager;
+import android.support.v4.app.Fragment;
+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.CompoundButton;
+import android.widget.ListView;
+import android.widget.Switch;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import net.named_data.nfd.wifidirect.model.Peer;
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.app.ProgressDialog.show;
+import static net.named_data.nfd.wifidirect.utils.NDNController.myAddress;
+import static net.named_data.nfd.wifidirect.utils.NDNController.myDeviceName;
+
+
+/**
+ * A simple {@link Fragment} subclass.
+ * Activities that contain this fragment must implement the
+ * Use the {@link WiFiDirectFragment#newInstance} factory method to
+ * create an instance of this fragment.
+ */
+public class WiFiDirectFragment extends Fragment {
+
+ private static final String TAG = "WiFiDirectFragment";
+
+ public WiFiDirectFragment() {
+ // Required empty public constructor
+ }
+
+ /**
+ * Use this factory method to create a new instance of
+ * this fragment using the provided parameters.
+ *
+ * @return A new instance of fragment WiFiDirectFragment.
+ */
+ public static WiFiDirectFragment newInstance() {
+ return new WiFiDirectFragment();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ m_sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
+ m_handler = new Handler();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ View view = inflater.inflate(R.layout.fragment_wifidirect, container, false);
+
+ // init UI elements
+ m_wdGroupConnStatus = (TextView) view.findViewById(R.id.wd_group_conn_status_textview);
+ m_wdIpAddress = (TextView) view.findViewById(R.id.wd_ip_address_textview);
+ m_wdDeviceName = (TextView) view.findViewById(R.id.wd_this_device_name_textview);
+ m_wdIsGroupOwner = (TextView) view.findViewById(R.id.wd_group_owner_textview);
+ m_wdSwitch = (Switch) view.findViewById(R.id.wd_switch);
+
+ if (m_sharedPreferences.getBoolean(PREF_WIFIDIRECT_STATUS, false)) {
+ m_wdSwitch.setChecked(true);
+ startNDNOverWifiDirect();
+ startUiUpdateLoop();
+ } else {
+ // the button was off, make any desired UI changes
+ m_wdGroupConnStatus.setText("");
+ }
+
+ m_wdSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ // store state of switch
+ m_sharedPreferences.edit().putBoolean(PREF_WIFIDIRECT_STATUS, isChecked).apply();
+
+ if (isChecked) {
+ startNDNOverWifiDirect();
+ startUiUpdateLoop();
+ } else {
+ stopNDNOverWifiDirect();
+ stopUiUpdateLoop();
+ resetUi();
+ }
+ }
+ });
+
+ // list view for displaying peers
+ m_wdConnectedPeerListview = (ListView) view.findViewById(R.id.wd_connected_peers_listview);
+ m_ConnectedPeers = new ArrayList<>(NDNController.getInstance().getConnectedPeers());
+ m_DicoveredPeers = new ArrayList<>(NDNController.getInstance().getDiscoveredPeers());
+
+ m_ConnectedPeersAdapter = new ConnectPeerListAdapter(getActivity(), R.layout.row_devices, m_ConnectedPeers);
+ m_wdConnectedPeerListview.setAdapter(m_ConnectedPeersAdapter);
+ m_wdConnectedPeerListview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Peer selectedPeer = (Peer) parent.getItemAtPosition(position);
+
+ // toast a quick message!
+ if (selectedPeer == null) {
+ Toast.makeText(getActivity(),
+ getResources().getString(R.string.fragment_wifidirect_toast_peer_no_longer_available),
+ Toast.LENGTH_LONG).show();
+ } else {
+ String peerInfo = selectedPeer.getNumProbeTimeouts() == 0 ?
+ getResources().getString(R.string.fragment_wifidirect_toast_connection_works_well) :
+ getResources().getString(R.string.fragment_wifidirect_toast_didnt_get_response) +
+ (selectedPeer.getNumProbeTimeouts() * NDNController.PROBE_DELAY / 1000) +
+ getResources().getString(R.string.fragment_wifidirect_toast_seconds);
+ Toast.makeText(getActivity(), peerInfo, Toast.LENGTH_LONG).show();
+ }
+ }
+ });
+
+ m_wdDiscoveredPeerListview = (ListView) view.findViewById(R.id.wd_discovered_peers_listview);
+ m_DiscoveredPeersAdapter = new DiscoveredPeerListAdapter(getActivity(), R.layout.row_devices, m_DicoveredPeers);
+ m_wdDiscoveredPeerListview.setAdapter(m_DiscoveredPeersAdapter);
+
+ m_wdDiscoveredPeerListview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final WifiP2pDevice device = (WifiP2pDevice) parent.getItemAtPosition(position);
+
+ if(device.status == WifiP2pDevice.INVITED) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(getResources().getString(R.string.fragment_wifidirect_dialog_cancel_invitation) + device.deviceName + getResources().getString(R.string.question_mark))
+ .setCancelable(false)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ m_progressDialog = show(getActivity(), getResources().getString(R.string.fragment_wifidirect_dialog_cancelling),
+ getResources().getString(R.string.fragment_wifidirect_dialog_cancelling_invitation) + device.deviceName, true, true
+ );
+ m_progressDialog.setCancelable(false);
+
+ NDNController.getInstance().cancelConnect();
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ return;
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ return;
+ }
+
+ if(device.status == WifiP2pDevice.CONNECTED) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ String alertMessage = getResources().getString(R.string.fragment_wifidirect_dialog_disconnect_from) + device.deviceName + getResources().getString(R.string.question_mark);
+ if(NDNController.getInstance().getIsGroupOwner()) {
+ alertMessage = alertMessage + getResources().getString(R.string.fragment_wifidirect_dialog_destroy_group_alter);
+ }
+ builder.setMessage(alertMessage)
+ .setCancelable(false)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ m_progressDialog = show(getActivity(), getResources().getString(R.string.fragment_wifidirect_dialog_disconnecting),
+ getResources().getString(R.string.fragment_wifidirect_dialog_disconnecting_from) + device.deviceName, true, true
+ );
+ m_progressDialog.setCancelable(false);
+
+ NDNController.getInstance().disconnect();
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ return;
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ return;
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(getResources().getString(R.string.fragment_wifidirect_dialog_invite) +
+ device.deviceName + getResources().getString(R.string.fragment_wifidirect_dialog_join_group)
+ + getResources().getString(R.string.question_mark))
+ .setCancelable(false)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ m_progressDialog = ProgressDialog.show(getActivity(), getResources().getString(R.string.fragment_wifidirect_dialog_inviting),
+ getResources().getString(R.string.fragment_wifidirect_dialog_inviting) + device.deviceName + getResources().getString(R.string.fragment_wifidirect_dialog_join_group), true, true
+ );
+ m_progressDialog.setCancelable(false);
+
+ NDNController.getInstance().connect(device);
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ return;
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+ });
+ return view;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ stopUiUpdateLoop();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ startUiUpdateLoop();
+ }
+
+ private void startNDNOverWifiDirect() {
+ // this is set so that NDNController has context to start appropriate services
+ NDNController.getInstance().setWifiDirectContext(getActivity());
+
+ // only start if the device is not currently connected to a group
+ // otherwise, the protocol must have been running to begin with!
+ if (myAddress == null) {
+ // main wrapper function that begins all elements of the protocol
+ NDNController.getInstance().start();
+ }
+ }
+
+ private void stopNDNOverWifiDirect() {
+ // main wrapper function that stops all elements of the protocol
+ NDNController.getInstance().stop();
+ }
+
+ private void startUiUpdateLoop() {
+ // periodically check for changed state
+ // to display to user
+ m_handler.post(m_UiUpdateRunnable);
+ }
+
+ private void stopUiUpdateLoop() {
+ m_handler.removeCallbacks(m_UiUpdateRunnable);
+ }
+
+ private void resetUi() {
+ // simply resets what is displayed to user
+ m_wdIpAddress.setText(getResources().getString(R.string.empty_string));
+ m_wdGroupConnStatus.setText(getResources().getString(R.string.empty_string));
+ m_wdIsGroupOwner.setText(getResources().getString(R.string.empty_string));
+ m_ConnectedPeers.clear();
+ m_DicoveredPeers.clear();
+ m_ConnectedPeersAdapter.notifyDataSetChanged();
+ m_DiscoveredPeersAdapter.notifyDataSetChanged();
+ }
+
+ /**
+ * Array adapter for ListFragment that maintains WifiP2pDevice list.
+ */
+ private class ConnectPeerListAdapter extends ArrayAdapter<Peer> {
+
+ private List<Peer> items;
+
+ /**
+ * @param context
+ * @param textViewResourceId
+ * @param objects
+ */
+ public ConnectPeerListAdapter(Context context, int textViewResourceId,
+ List<Peer> 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) getActivity().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ v = vi.inflate(R.layout.row_devices, null);
+ }
+ Peer peer = items.get(position);
+ if (peer != null) {
+ TextView top = (TextView) v.findViewById(R.id.device_name);
+ TextView bottom = (TextView) v.findViewById(R.id.device_details);
+ if (top != null) {
+ top.setText(peer.getDevice() == null ? getResources().getString(R.string.empty_string) : peer.getDevice().deviceName);
+ }
+ if (bottom != null) {
+ bottom.setText(peer.getIpAddress());
+ }
+ }
+
+ return v;
+ }
+ }
+
+ /**
+ * Array adapter for ListFragment that maintains WifiP2pDevice list.
+ */
+ private class DiscoveredPeerListAdapter extends ArrayAdapter<WifiP2pDevice> {
+
+ private List<WifiP2pDevice> items;
+
+ /**
+ * @param context
+ * @param textViewResourceId
+ * @param objects
+ */
+ public DiscoveredPeerListAdapter(Context context, int textViewResourceId,
+ List<WifiP2pDevice> 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) getActivity().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;
+ }
+ }
+ ////////////////////////////////////////////////////////////////////////////////////
+ private ListView m_wdConnectedPeerListview;
+ private ListView m_wdDiscoveredPeerListview;
+ private Switch m_wdSwitch;
+ private TextView m_wdGroupConnStatus;
+ private TextView m_wdIpAddress;
+ private TextView m_wdDeviceName;
+ private TextView m_wdIsGroupOwner;
+
+ private Handler m_handler;
+ private Runnable m_UiUpdateRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (m_progressDialog != null && m_progressDialog.isShowing()) {
+ m_progressDialog.dismiss();
+ }
+ if (myAddress != null) {
+ m_wdGroupConnStatus.setText(getResources().getString(R.string.fragment_wifidirect_text_group_connected));
+ m_wdIpAddress.setText(myAddress);
+ if(NDNController.getInstance().getIsGroupOwner()) {
+ m_wdIsGroupOwner.setText(getResources().getString(R.string.yes));
+ } else {
+ m_wdIsGroupOwner.setText(getResources().getString(R.string.no));
+ }
+ } else {
+ if (!m_sharedPreferences.getBoolean(PREF_WIFIDIRECT_STATUS, false)) {
+ m_wdGroupConnStatus.setText("");
+ } else {
+ if(NDNController.getInstance().WIFI_STATE == WifiP2pManager.WIFI_P2P_STATE_ENABLED)
+ m_wdGroupConnStatus.setText(getResources().getString(R.string.fragment_wifidirect_text_group_scanning));
+ else {
+ m_wdGroupConnStatus.setText(getResources().getString(R.string.fragment_wifidirect_text_group_wifi_p2p_disabled));
+ }
+ }
+
+ m_wdIpAddress.setText(getResources().getString(R.string.empty_string));
+ m_wdIsGroupOwner.setText(getResources().getString(R.string.empty_string));
+ }
+ if (myDeviceName != null) {
+ m_wdDeviceName.setText(myDeviceName);
+ } else {
+ m_wdDeviceName.setText(getResources().getString(R.string.empty_string));
+ }
+
+ // refresh the list of peers
+ m_ConnectedPeers.clear();
+ m_DicoveredPeers.clear();
+ m_ConnectedPeers.addAll(NDNController.getInstance().getConnectedPeers());
+ m_DicoveredPeers.addAll(NDNController.getInstance().getDiscoveredPeers());
+ m_ConnectedPeersAdapter.notifyDataSetChanged();
+ m_DiscoveredPeersAdapter.notifyDataSetChanged();
+
+ // call again in X seconds
+ m_handler.postDelayed(m_UiUpdateRunnable, UI_UPDATE_DELAY_MS);
+ }
+ };
+
+ private static String getDeviceStatus(int deviceStatus) {
+ Log.d(TAG, "Peer status :" + 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";
+ }
+ }
+
+ private SharedPreferences m_sharedPreferences;
+ private List<Peer> m_ConnectedPeers;
+ private List<WifiP2pDevice> m_DicoveredPeers;
+ private ConnectPeerListAdapter m_ConnectedPeersAdapter;
+ private DiscoveredPeerListAdapter m_DiscoveredPeersAdapter;
+ private ProgressDialog m_progressDialog;
+
+ private final int UI_UPDATE_DELAY_MS = 5000;
+
+ private static final String PREF_WIFIDIRECT_STATUS = "WIFIDIRECT_STATUS";
+}
diff --git a/app/src/main/java/net/named_data/nfd/utils/NfdcHelper.java b/app/src/main/java/net/named_data/nfd/utils/NfdcHelper.java
index 178870c..672003e 100644
--- a/app/src/main/java/net/named_data/nfd/utils/NfdcHelper.java
+++ b/app/src/main/java/net/named_data/nfd/utils/NfdcHelper.java
@@ -1,6 +1,6 @@
/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2015-2016 Regents of the University of California
+ * Copyright (c) 2015-2017 Regents of the University of California
* <p>
* This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
* See AUTHORS.md for complete list of NFD Android authors and contributors.
@@ -26,6 +26,7 @@
import com.intel.jndn.management.Nfdc;
import com.intel.jndn.management.enums.FacePersistency;
import com.intel.jndn.management.types.FaceStatus;
+import com.intel.jndn.management.types.FibEntry;
import com.intel.jndn.management.types.ForwarderStatus;
import com.intel.jndn.management.types.RibEntry;
import com.intel.jndn.management.types.Route;
@@ -33,9 +34,8 @@
import net.named_data.jndn.ControlParameters;
import net.named_data.jndn.Face;
import net.named_data.jndn.ForwardingFlags;
-import net.named_data.jndn.Interest;
import net.named_data.jndn.Name;
-import net.named_data.jndn.security.*;
+import net.named_data.jndn.security.KeyChain;
import net.named_data.jndn.security.SecurityException;
import net.named_data.jndn.security.identity.IdentityManager;
import net.named_data.jndn.security.identity.MemoryIdentityStorage;
@@ -45,7 +45,6 @@
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -112,6 +111,14 @@
}
/**
+ * Unregisters prefix
+ */
+ public void
+ ribUnregisterPrefix(Name prefix) throws ManagementException {
+ Nfdc.unregister(m_face, prefix);
+ }
+
+ /**
* List all of routes (RIB entries)
*/
public List<RibEntry>
@@ -119,6 +126,10 @@
return Nfdc.getRouteList(m_face);
}
+ public List<FibEntry> fibList() throws ManagementException {
+ return Nfdc.getFibList(m_face);
+ }
+
public SparseArray<Set<Name>>
ribAsFaceIdPrefixNameArray() throws ManagementException {
List<RibEntry> ribEntryList = ribList();
@@ -172,6 +183,17 @@
return result;
}
+ /**
+ * List all faces
+ * @return
+ * @throws ManagementException
+ */
+ public List<FaceStatus>
+ faceList() throws ManagementException
+ {
+ return Nfdc.getFaceList(m_face);
+ }
+
public SparseArray<FaceStatus>
faceListAsSparseArray(Context context) throws ManagementException {
List<FaceStatus> faceList = faceList(context);
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/callback/GenericCallback.java b/app/src/main/java/net/named_data/nfd/wifidirect/callback/GenericCallback.java
new file mode 100644
index 0000000..547a410
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/callback/GenericCallback.java
@@ -0,0 +1,34 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.callback;
+
+/**
+ * Generic callback interface used for representing
+ * anonymous functions.
+ */
+
+public interface GenericCallback {
+
+ /**
+ * No-arg function that all implementing classes must
+ * define.
+ */
+ public void doJob();
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/callback/NDNCallBackOnInterest.java b/app/src/main/java/net/named_data/nfd/wifidirect/callback/NDNCallBackOnInterest.java
new file mode 100644
index 0000000..b97e369
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/callback/NDNCallBackOnInterest.java
@@ -0,0 +1,45 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.callback;
+
+import net.named_data.jndn.Face;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.InterestFilter;
+import net.named_data.jndn.Name;
+
+/**
+ * Interface specification of a general callback
+ * that takes in all input from a regular NDN OnInterest
+ * callback, but fulfills a certain job.
+ */
+public interface NDNCallBackOnInterest {
+
+ /**
+ * Do something particular with available information onInterest.
+ *
+ * @param prefix NDN Name
+ * @param interest NDN Interest
+ * @param face NDN Face
+ * @param interestFilterId the interest filter ID
+ * @param filter the InterestFilter
+ */
+ public void doJob(Name prefix, Interest interest, Face face,
+ long interestFilterId, InterestFilter filter);
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/callback/NDNCallbackOnData.java b/app/src/main/java/net/named_data/nfd/wifidirect/callback/NDNCallbackOnData.java
new file mode 100644
index 0000000..e2c0ac6
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/callback/NDNCallbackOnData.java
@@ -0,0 +1,39 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.callback;
+
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Interest;
+
+/**
+ * Interface specification of a general callback
+ * that takes in all input from a regular NDN OnData
+ * callback, but fulfills a certain job.
+ */
+public interface NDNCallbackOnData {
+
+ /**
+ * Do something particular with the interest and data packets.
+ *
+ * @param interest NDN Interest
+ * @param data NDN Data corresponding to the Interest
+ */
+ public void doJob(Interest interest, Data data);
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/callback/ProbeOnData.java b/app/src/main/java/net/named_data/nfd/wifidirect/callback/ProbeOnData.java
new file mode 100644
index 0000000..0e815af
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/callback/ProbeOnData.java
@@ -0,0 +1,125 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.callback;
+
+import android.util.Log;
+
+import com.intel.jndn.management.types.FibEntry;
+import com.intel.jndn.management.types.NextHopRecord;
+
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Handle OnData events for outgoing probe interests.
+ */
+public class ProbeOnData implements NDNCallbackOnData {
+
+ private static final String TAG = "ProbeOnData";
+ private NDNController mController = NDNController.getInstance();
+ private Face mFace = mController.getLocalHostFace();
+
+ @Override
+ public void doJob(Interest interest, Data data) {
+ // interest name = /localhop/wifidirect/<toIp>/<fromIp>/probe%timestamp
+ Log.d(TAG, "Got data for interest: " + interest.getName().toString());
+
+ String[] nameArr = interest.getName().toString().split("/");
+ String peerIp = nameArr[nameArr.length - 3];
+ int peerFaceId = mController.getFaceIdForPeer(peerIp);
+
+ // parse the data, update controller prefix map
+ /**
+ * Data is in form:
+ * {numPrefixes}\n
+ * prefix1\n
+ * prefix2\n
+ * ...
+ */
+ String[] responseArr = data.getContent().toString().split("\n");
+
+ // validation
+ if (peerFaceId == -1) {
+ Log.e(TAG, "Undocumented peer.");
+ return;
+ }
+
+ int numPrefixes = Integer.parseInt(responseArr[0]);
+ HashSet<String> prefixesInResp = new HashSet<>(numPrefixes);
+ for (int i = 1; i <= numPrefixes; i++) {
+ prefixesInResp.add(responseArr[i]);
+ }
+
+ // enumerate FIB entries, and collect the set of data prefixes towards this peer
+ HashSet<String> prefixesRegisteredForPeer = new HashSet<>();
+ try {
+ List<FibEntry> fibEntries = mController.getNfdcHelper().fibList();
+ for (FibEntry fibEntry : fibEntries) {
+ //if (fibEntry.getPrefix().toString().startsWith(NDNController.DATA_PREFIX)) {
+ String fibEntryPrefix = fibEntry.getPrefix().toString();
+ if (!fibEntryPrefix.startsWith("/localhop") && !fibEntryPrefix.startsWith("/localhost")) {
+ List<NextHopRecord> nextHopRecords = fibEntry.getNextHopRecords();
+ for (NextHopRecord nextHopRecord : nextHopRecords) {
+ if (nextHopRecord.getFaceId() == peerFaceId) {
+ prefixesRegisteredForPeer.add(fibEntryPrefix);
+ }
+ }
+ }
+ }
+
+ // iterate through prefixes found in response,
+ // removing any already registered prefixes for this peer
+ // any prefix remaining in prefixesRegisteredForPeer after this
+ // is no longer advertised by peer
+ Iterator<String> it = prefixesInResp.iterator();
+ while (it.hasNext()) {
+ String prefix = it.next();
+ if (prefixesRegisteredForPeer.contains(prefix)) {
+ it.remove();
+ prefixesRegisteredForPeer.remove(prefix);
+ }
+ }
+
+ // register new prefixes in response
+ if (prefixesInResp.size() > 0) {
+ Log.d(TAG, prefixesInResp.size() + " new prefixes to add.");
+ mController.ribRegisterPrefix(peerFaceId, prefixesInResp.toArray(new String[0]));
+ } else {
+ Log.d(TAG, "No new prefixes to register.");
+ }
+
+ // unregister all prefixes that no longer are supported via this face
+ for (String toRemovePrefix : prefixesRegisteredForPeer) {
+ Log.d(TAG, "Removing from FIB: " + toRemovePrefix + " " + peerFaceId);
+ mController.getNfdcHelper().ribUnregisterPrefix(new Name(toRemovePrefix), peerFaceId);
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/callback/ProbeOnInterest.java b/app/src/main/java/net/named_data/nfd/wifidirect/callback/ProbeOnInterest.java
new file mode 100644
index 0000000..9dd8a41
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/callback/ProbeOnInterest.java
@@ -0,0 +1,138 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.callback;
+
+import android.util.Log;
+
+import com.intel.jndn.management.types.FaceStatus;
+import com.intel.jndn.management.types.FibEntry;
+import com.intel.jndn.management.types.NextHopRecord;
+
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.InterestFilter;
+import net.named_data.jndn.MetaInfo;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.util.Blob;
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Handle OnInterest events for incoming probe interests.
+ */
+public class ProbeOnInterest implements NDNCallBackOnInterest {
+
+ private static final String TAG = "ProbeOnInterest";
+ private static final int DATA_LIFE_TIME = 500; // this should be smaller than NDNController.PROBE_INTEREST_LIFETIME
+
+ private NDNController mController = NDNController.getInstance();
+
+
+ @Override
+ public void doJob(Name prefix, Interest interest, Face face, long interestFilterId, InterestFilter filter) {
+ Log.d(TAG, "Got an interest for: " + interest.getName().toString());
+
+ // /localhop/wifidirect/192.168.49.x/192.168.49.y/probe?mustBeFresh=1
+ String[] prefixArr = interest.getName().toString().split("/");
+
+ // validate
+ if (prefixArr.length != 6) {
+ Log.e(TAG, "Error with this interest, skipping...");
+ }
+
+ final String peerIp = prefixArr[prefixArr.length - 2];
+
+ // if not logged (a face created for this probing peer), should then create a face (mainly for GO)
+ if (mController.getFaceIdForPeer(peerIp) == -1) {
+
+ mController.createFace(peerIp, NDNController.URI_TRANSPORT_PREFIX, new GenericCallback() {
+ @Override
+ public void doJob() {
+ Log.d(TAG, "Registering localhop for: " + peerIp);
+ String[] prefixes = new String[1];
+ prefixes[0] = NDNController.PROBE_PREFIX + "/" + peerIp;
+ mController.ribRegisterPrefix(mController.getFaceIdForPeer(peerIp),
+ prefixes);
+ }
+ });
+ }
+
+ // enumerate RIB, look for all /ndn/wifidirect/* data prefixes, return to user as described in slides
+ try {
+ // set of prefixes to return to interest sender
+ HashSet<String> prefixesToReturn = new HashSet<>();
+ String response = "";
+ int num = 0;
+
+ // consult NFD to get all entries in FIB
+ List<FibEntry> fibEntries = mController.getNfdcHelper().fibList();
+
+ // enumerate all faces
+ List<FaceStatus> faceStatuses = mController.getNfdcHelper().faceList();
+ HashSet<Integer> faceIds = new HashSet<>();
+ for (FaceStatus faceStatus : faceStatuses) {
+ faceIds.add(faceStatus.getFaceId());
+ }
+
+ // remove the interest incomming face id
+ faceIds.remove(mController.getFaceIdForPeer(peerIp));
+
+ // return only those prefixes that are handled by faces except for the interest incomming face
+ for (FibEntry fibEntry : fibEntries) {
+ if (!fibEntry.getPrefix().toString().startsWith("/localhop") &&
+ !fibEntry.getPrefix().toString().startsWith("/localhost")) {
+ // added constraint that the prefix must be served from devices except for the interest
+ // incomming device (e.g. by an upper layer application)
+ List<NextHopRecord> nextHopRecords = fibEntry.getNextHopRecords();
+ for (NextHopRecord nextHopRecord : nextHopRecords) {
+ if (faceIds.contains(nextHopRecord.getFaceId())) {
+ prefixesToReturn.add(fibEntry.getPrefix().toString());
+ num++;
+ break;
+ }
+ }
+ }
+ }
+
+ Data data = new Data();
+ data.setName(new Name(interest.getName().toUri()));
+ MetaInfo metaInfo = new MetaInfo();
+ metaInfo.setFreshnessPeriod(DATA_LIFE_TIME);
+ data.setMetaInfo(metaInfo);
+
+ // format payload, for now ignore hopcount as it is not clear whether
+ // it is useful
+ for (String pre : prefixesToReturn) {
+ response += ("\n" + pre);
+ }
+
+ Blob payload = new Blob(num + response); // num + ("\nprefix1\nprefix2...")
+ data.setContent(payload);
+
+ face.putData(data);
+ Log.d(TAG, "Send data for: " + interest.getName().toString());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/model/Peer.java b/app/src/main/java/net/named_data/nfd/wifidirect/model/Peer.java
new file mode 100644
index 0000000..7bd418f
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/model/Peer.java
@@ -0,0 +1,80 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.model;
+
+import android.net.wifi.p2p.WifiP2pDevice;
+
+/**
+ * Represents a WifiDirect Peer.
+ */
+public class Peer {
+
+ // members
+ private WifiP2pDevice device;
+ private String ipAddress;
+ private int faceId;
+ private int numProbeTimeouts = 0; // number of timeouts while probing prefixes from this peer
+
+ public Peer() {
+ }
+
+ public WifiP2pDevice getDevice() {
+ return device;
+ }
+
+ public void setDevice(WifiP2pDevice device) {
+ this.device = device;
+ }
+
+ public String getIpAddress() {
+ return ipAddress;
+ }
+
+ public void setIpAddress(String ipAddress) {
+ this.ipAddress = ipAddress;
+ }
+
+ public int getFaceId() {
+ return faceId;
+ }
+
+ public void setFaceId(int faceId) {
+ this.faceId = faceId;
+ }
+
+ public int getNumProbeTimeouts() {
+ return numProbeTimeouts;
+ }
+
+ public void setNumProbeTimeouts(int numProbeTimeouts) {
+ this.numProbeTimeouts = numProbeTimeouts;
+ }
+
+ @Override
+ public String toString() {
+ return "Peer{" +
+ "name=\"" + device.deviceName + "\"" +
+ ", macAddress=\"" + device.deviceAddress + "\"" +
+ ", ipAddress=\"" + ipAddress + "\"" +
+ ", faceId=" + faceId +
+ ", numProbeTimeouts=" + numProbeTimeouts +
+ '}';
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/runnable/DiscoverPeersRunnable.java b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/DiscoverPeersRunnable.java
new file mode 100644
index 0000000..190f7c2
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/DiscoverPeersRunnable.java
@@ -0,0 +1,37 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.runnable;
+
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+/**
+ * Initiates peer discovery.
+ */
+
+public class DiscoverPeersRunnable implements Runnable {
+ @Override
+ public void run() {
+ try {
+ NDNController.getInstance().discoverPeers();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/runnable/FaceAndRouteConsistencyRunnable.java b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/FaceAndRouteConsistencyRunnable.java
new file mode 100644
index 0000000..1f5738e
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/FaceAndRouteConsistencyRunnable.java
@@ -0,0 +1,128 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.runnable;
+
+import android.util.Log;
+
+import com.intel.jndn.management.ManagementException;
+import com.intel.jndn.management.types.FaceStatus;
+import com.intel.jndn.management.types.RibEntry;
+
+import net.named_data.nfd.wifidirect.callback.GenericCallback;
+import net.named_data.nfd.wifidirect.model.Peer;
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Checks for the consistency between NDNController's view of
+ * the logged peers and the NFD's. Specifically, this is carried
+ * out by comparing views on active Faces.
+ * <p>
+ * This case occurs when the user delete the face manually. May be some other cases.
+ */
+public class FaceAndRouteConsistencyRunnable implements Runnable {
+ private static final String TAG = "FaceAndRouteConsistency";
+
+ @Override
+ public void run() {
+
+ Log.d(TAG, "Running periodic Face and route consistency check...");
+
+ // first, let's retrieve a set of active FaceIds from NFD.
+ // then, let's compare this set with what NDNController has
+ try {
+ List<FaceStatus> faceStatuses = NDNController.getInstance().getNfdcHelper().faceList();
+
+ List<RibEntry> routeStatus = NDNController.getInstance().getNfdcHelper().ribList();
+
+ // put face ids in an easy to access manner
+ HashSet<Integer> nfdActiveFaceIds = new HashSet<>(faceStatuses.size());
+ for (FaceStatus faceStatus : faceStatuses) {
+ nfdActiveFaceIds.add(faceStatus.getFaceId());
+ }
+
+ List<String> peersWithoutFace = new ArrayList<>();
+
+ Map<String, Peer> connectedPeers = NDNController.getInstance().getConnectedPeersMap();
+
+ // create faces if needed
+ for (final String ip : connectedPeers.keySet()) {
+ int peerFaceId = connectedPeers.get(ip).getFaceId();
+ if ((peerFaceId != -1) && (!nfdActiveFaceIds.contains(peerFaceId))) {
+ // create the face but not destroy the logged peers
+ peersWithoutFace.add(ip);
+ Log.d(TAG, "create face for IP " + ip);
+ NDNController.getInstance().createFace(ip, NDNController.URI_TRANSPORT_PREFIX, new GenericCallback() {
+ @Override
+ public void doJob() {
+ Log.d(TAG, "Registering localhop for: " + ip);
+ String[] prefixes = new String[1];
+ prefixes[0] = NDNController.PROBE_PREFIX + "/" + ip;
+ NDNController.getInstance().ribRegisterPrefix(NDNController.getInstance().getFaceIdForPeer(ip),
+ prefixes);
+ }
+ });
+ }
+ }
+
+ //create routes if needed
+ for (final String ip : connectedPeers.keySet()) {
+ if (peersWithoutFace.contains(ip)) {
+ continue;
+ }
+ String prefix = NDNController.PROBE_PREFIX + "/" + ip;
+ boolean exist = false;
+ for (RibEntry oneRoute : routeStatus) {
+ if (prefix.equals(oneRoute.getName().toUri())) {
+ exist = true;
+ break;
+ }
+ }
+ if (!exist) {
+ Log.d(TAG, "create route " + prefix);
+ NDNController.getInstance().ribRegisterPrefix(NDNController.getInstance().getFaceIdForPeer(ip),
+ new String[]{prefix});
+ }
+ }
+
+ //register own prefix if needed
+ if (NDNController.myAddress != null) {
+ String myPrefix = NDNController.PROBE_PREFIX + "/" + NDNController.myAddress;
+ boolean exist = false;
+ for (RibEntry oneRoute : routeStatus) {
+ if (myPrefix.equals(oneRoute.getName().toUri())) {
+ exist = true;
+ break;
+ }
+ }
+ if (!exist) {
+ NDNController.getInstance().registerOwnLocalhop();
+ }
+ }
+
+ } catch (ManagementException me) {
+ Log.e(TAG, "There was an issue retrieving FaceList from NFD");
+ }
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/runnable/FaceCreateRunnable.java b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/FaceCreateRunnable.java
new file mode 100644
index 0000000..5221c29
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/FaceCreateRunnable.java
@@ -0,0 +1,86 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.runnable;
+
+import android.util.Log;
+
+import com.intel.jndn.management.ManagementException;
+
+import net.named_data.nfd.wifidirect.callback.GenericCallback;
+import net.named_data.nfd.wifidirect.model.Peer;
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+/**
+ * Convenience class that creates a Face with the forwarder. A callback
+ * is accpeted via the public setCallback(...) method, and will be called
+ * if and only if face creation succeeds.
+ */
+// task to create a network face without using main thread
+public class FaceCreateRunnable implements Runnable {
+
+ private static final String TAG = "FaceCreateRunnable";
+ private String peerIp;
+ private String faceUri;
+ private NDNController mController = NDNController.getInstance();
+ private GenericCallback callback = null;
+
+ public FaceCreateRunnable(String peerIp, String faceUri) {
+ this.peerIp = peerIp;
+ this.faceUri = faceUri;
+ }
+
+ public void setCallback(GenericCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void run() {
+ int faceId = -1;
+
+ try {
+ Log.d(TAG, "-------- Inside face create runnable --------");
+
+ faceId = mController.getNfdcHelper().faceCreate(faceUri);
+
+ Log.d(TAG, "Created Face with Face id: " + faceId);
+ if (faceId != -1) {
+
+ // if face creation successful, log the new peer
+ Peer peer = new Peer();
+ peer.setFaceId(faceId);
+ peer.setIpAddress(peerIp);
+ mController.logPeer(peerIp, peer);
+
+ // invoke callback, if any
+ if (callback != null) {
+ callback.doJob();
+ }
+ }
+
+ } catch (ManagementException me) {
+ Log.e(TAG, me.getMessage());
+ } catch (Exception e) {
+ Log.e(TAG, "" + e.getMessage());
+ e.printStackTrace();
+ }
+
+ Log.d(TAG, "---------- END face create runnable -----------");
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/runnable/FaceDestroyRunnable.java b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/FaceDestroyRunnable.java
new file mode 100644
index 0000000..add85dd
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/FaceDestroyRunnable.java
@@ -0,0 +1,54 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.runnable;
+
+import android.util.Log;
+
+import com.intel.jndn.management.ManagementException;
+
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+/**
+ * Attempts to destroy a given Face, denoted by its Face id.
+ */
+public class FaceDestroyRunnable implements Runnable {
+ private int faceId;
+
+ public FaceDestroyRunnable(int faceId) {
+ this.faceId = faceId;
+ }
+
+ private static final String TAG = "FaceDestroyRunnable";
+
+ @Override
+ public void run() {
+ try {
+ Log.d(TAG, "-------- Inside face destroy task --------");
+ // attempt to destroy Face Id, specified as the first and only parameter
+ NDNController.getInstance().getNfdcHelper().faceDestroy(faceId);
+ Log.d(TAG, "Successfully destroyed Face with Face id: " + faceId);
+ } catch (ManagementException me) {
+ Log.e(TAG, me.getMessage());
+ } catch (Exception e) {
+ Log.e(TAG, e.getMessage());
+ }
+ Log.d(TAG, "---------- END face destroy task -----------");
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/runnable/FaceEventProcessRunnable.java b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/FaceEventProcessRunnable.java
new file mode 100644
index 0000000..237bce6
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/FaceEventProcessRunnable.java
@@ -0,0 +1,35 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.runnable;
+
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+public class FaceEventProcessRunnable implements Runnable {
+ private static final String TAG = "FaceEventProcess";
+
+ @Override
+ public void run() {
+ try {
+ NDNController.getInstance().getLocalHostFace().processEvents();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/runnable/GroupStatusConsistencyRunnable.java b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/GroupStatusConsistencyRunnable.java
new file mode 100644
index 0000000..c85183c
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/GroupStatusConsistencyRunnable.java
@@ -0,0 +1,56 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.runnable;
+
+import android.util.Log;
+
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+/**
+ * If connected peers list keeps empty, while myaddress keeps un-empty for 1 minute, remove the group
+ */
+
+public class GroupStatusConsistencyRunnable implements Runnable {
+
+ private static final String TAG = "GroupStatusConsistency";
+
+
+ //Notice: (this number) * (running interval) should equal to 1 minute
+ public static final int MAX_TIMEOUTS_ALLOWED = 6;
+ public static int TIME_OUT_TIMES = 0;
+
+ public static void resetTimeoutTimes() {
+ TIME_OUT_TIMES = 0;
+ }
+
+ @Override
+ public void run() {
+ Log.d(TAG, "Check GroupStatusConsistency");
+ if(NDNController.getInstance().isNumOfConnectedPeersZero() && NDNController.myAddress != null) {
+ TIME_OUT_TIMES ++;
+ } else {
+ TIME_OUT_TIMES = 0;
+ }
+ if(TIME_OUT_TIMES >= MAX_TIMEOUTS_ALLOWED) {
+ NDNController.getInstance().disconnect();
+ TIME_OUT_TIMES = 0;
+ }
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/runnable/ProbeRunnable.java b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/ProbeRunnable.java
new file mode 100644
index 0000000..9741633
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/ProbeRunnable.java
@@ -0,0 +1,133 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.runnable;
+
+import android.util.Log;
+
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.OnData;
+import net.named_data.jndn.OnTimeout;
+import net.named_data.nfd.wifidirect.callback.ProbeOnData;
+import net.named_data.nfd.wifidirect.model.Peer;
+import net.named_data.nfd.wifidirect.utils.IPAddress;
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+import java.io.IOException;
+
+import static net.named_data.nfd.wifidirect.utils.NDNController.myAddress;
+
+/**
+ * Probes network for data prefixes, as specified in protocol.
+ */
+public class ProbeRunnable implements Runnable {
+ private static final String TAG = "ProbeRunnable";
+ //Notice: (this number) * (running interval) should equal to 1 minute
+ private static final int MAX_TIMEOUTS_ALLOWED = 60;
+ private NDNController mController = NDNController.getInstance();
+
+ private OnData onData = new OnData() {
+ private ProbeOnData probeOnData = new ProbeOnData();
+
+ @Override
+ public void onData(Interest interest, Data data) {
+ Name interestName = interest.getName();
+ Log.d(TAG, "Got data for interest " + interestName);
+ String peerIp = interestName.get(interestName.size() - 3).toEscapedString();
+ probeOnData.doJob(interest, data);
+ Peer peer = NDNController.getInstance().getPeerByIp(peerIp);
+ if (peer != null)
+ peer.setNumProbeTimeouts(0); // peer responded, so reset timeout counter
+ }
+ };
+
+ private OnTimeout onTimeout = new OnTimeout() {
+ @Override
+ public void onTimeout(Interest interest) {
+ Name interestName = interest.getName();
+ Log.d(TAG, "interest " + interestName + " times out");
+ String peerIp = interestName.get(interestName.size() - 3).toEscapedString();
+ Peer peer = NDNController.getInstance().getPeerByIp(peerIp);
+ if (peer == null) {
+ Log.d(TAG, "No peer information available to track timeout.");
+ return;
+ }
+
+ Log.d(TAG, "Timeout for interest: " + interest.getName().toString() +
+ " Attempts: " + (peer.getNumProbeTimeouts() + 1));
+
+ if (peer.getNumProbeTimeouts() + 1 >= MAX_TIMEOUTS_ALLOWED) {
+ // This case means, remove a peer which
+ // (1) is indicatated connected by Wifi-Direct
+ // (2) but doesn't response to probeInterest
+ // so remove it (disconnect it and remove saved states)
+ NDNController.getInstance().removePeer(peerIp);
+ } else {
+ peer.setNumProbeTimeouts(peer.getNumProbeTimeouts() + 1);
+ }
+ }
+ };
+
+ @Override
+ public void run() {
+ Log.d(TAG, "start to probe");
+ try {
+ if (IPAddress.getLocalIPAddress() == null) {
+
+ // this means that a disconnect has recently occurred and this device
+ // is no longer a part of a group (WDBroadcastReceiver.myAddress is this
+ // device's previous WD IP)
+ if (myAddress != null) {
+ Log.d(TAG, "A disconnect has been detected, refreshing state...");
+
+ // unregister the previous "/localhop/wifidirect/..." prefix
+ mController.cleanUpConnections();
+ // recreateFace for future connection
+ mController.recreateFace();
+
+ // most likely will have a new IP to register "/localhop/wifidirect/<IP>"
+ // call this so that the next time a group is joined a new local prefix
+ // registration will occur
+ mController.setHasRegisteredOwnLocalhop(false);
+
+ // ensure that peer diiscovery is running, if it had not been before
+ mController.startDiscoveringPeers();
+ } else {
+ Log.d(TAG, "Skip this iteration due to null WD ip.");
+ }
+
+ } else {
+ for(String ip : NDNController.getInstance().getIpsOfConnectedPeers()) {
+ //send interest to this peer
+ Interest interest = new Interest(new Name(NDNController.PROBE_PREFIX + "/" + ip + "/" + myAddress + "/probe"));
+ interest.setMustBeFresh(true);
+ interest.setInterestLifetimeMilliseconds(NDNController.PROBE_INTEREST_LIFETIME);
+ Log.d(TAG, "Sending interest: " + interest.getName().toString());
+ NDNController.getInstance().getLocalHostFace().expressInterest(interest, onData, onTimeout);
+ }
+ }
+ } catch (IOException ioe) {
+ Log.e(TAG, "Something went wrong with sending a probe interest.");
+ ioe.printStackTrace();
+ }
+ }
+
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/runnable/RegisterPrefixRunnable.java b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/RegisterPrefixRunnable.java
new file mode 100644
index 0000000..9053c36
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/RegisterPrefixRunnable.java
@@ -0,0 +1,79 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.runnable;
+
+import android.util.Log;
+
+import net.named_data.jndn.ForwardingFlags;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.OnInterestCallback;
+import net.named_data.jndn.OnRegisterFailed;
+import net.named_data.jndn.OnRegisterSuccess;
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+/**
+ * Task that provides the ability to register a prefix to the specified face.
+ * TODO: Given that multiple successive prefix registration calls can fail (NFD timeout),
+ * This task needs to attempt to register prefixes 5 times or until success. Each
+ * attempt is separated (e.g. 500ms) to increase chance of registration.
+ */
+public class RegisterPrefixRunnable implements Runnable {
+ private final String TAG = "RegisterPrefixRunnable";
+ private OnInterestCallback onInterestCallback;
+
+ private String prefixToRegister;
+
+ public RegisterPrefixRunnable(String prefix, OnInterestCallback cb) {
+ this.prefixToRegister = prefix;
+ this.onInterestCallback = cb;
+ }
+
+ @Override
+ public void run() {
+ Log.d(TAG, "try to register local prefix" + prefixToRegister);
+ try {
+ // allow child inherit
+ final ForwardingFlags flags = new ForwardingFlags();
+ flags.setChildInherit(true);
+
+ Name prefix = new Name(prefixToRegister);
+
+ long registerPrefixId = NDNController.getInstance().getLocalHostFace().registerPrefix(prefix, onInterestCallback,
+ new OnRegisterFailed() {
+ @Override
+ public void onRegisterFailed(Name prefix) {
+ Log.d(TAG, "Failed to register prefix: " + prefix.toString());
+ }
+ }, new OnRegisterSuccess() {
+ @Override
+ public void onRegisterSuccess(Name prefix, long registeredPrefixId) {
+ Log.d(TAG, "Prefix registered successfully: " + prefixToRegister);
+
+ }
+ },
+ flags);
+ Log.d(TAG, "registered prefix id is " + registerPrefixId);
+ NDNController.getInstance().setRegisteredPrefixId(registerPrefixId);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/runnable/RibRegisterPrefixRunnable.java b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/RibRegisterPrefixRunnable.java
new file mode 100644
index 0000000..4d10e33
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/RibRegisterPrefixRunnable.java
@@ -0,0 +1,67 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.runnable;
+
+import android.util.Log;
+
+import net.named_data.jndn.ForwardingFlags;
+import net.named_data.jndn.Name;
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+/**
+ * Convenience class used for registering a prefix towards some Face, denoted by
+ * its Face ID. Note that this class differs from RegisterPrefixRunnable, as the latter
+ * deals with registering prefixes to a localhost face, while this class does not make
+ * that assumption.
+ */
+public class RibRegisterPrefixRunnable implements Runnable {
+
+ private final String TAG = "RibRegister";
+
+ private String prefixToRegister;
+ private int faceId;
+ private int cost;
+ private boolean childInherit;
+ private boolean capture;
+
+ public RibRegisterPrefixRunnable(String prefixToRegister, int faceId, int cost,
+ boolean childInherit, boolean capture) {
+ this.prefixToRegister = prefixToRegister;
+ this.capture = capture;
+ this.childInherit = childInherit;
+ this.cost = cost;
+ this.faceId = faceId;
+ }
+
+ @Override
+ public void run() {
+ try {
+ ForwardingFlags flags = new ForwardingFlags();
+ flags.setChildInherit(childInherit);
+ flags.setCapture(capture);
+ NDNController.getInstance().getNfdcHelper().ribRegisterPrefix(new Name(prefixToRegister),
+ faceId, cost, childInherit, capture);
+
+ Log.d(TAG, "registered rib prefix: " + prefixToRegister);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/runnable/RibUnregisterPrefixRunnable.java b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/RibUnregisterPrefixRunnable.java
new file mode 100644
index 0000000..80a0e7f
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/RibUnregisterPrefixRunnable.java
@@ -0,0 +1,53 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.runnable;
+
+import android.util.Log;
+
+import net.named_data.jndn.Name;
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+/**
+ * Convenience class used for registering a prefix towards some Face, denoted by
+ * its Face ID. Note that this class differs from RegisterPrefixRunnable, as the latter
+ * deals with registering prefixes to a localhost face, while this class does not make
+ * that assumption.
+ */
+public class RibUnregisterPrefixRunnable implements Runnable {
+
+ private static final String TAG = "RibUnregisterTask";
+
+ private String prefixToUnregister;
+
+ public RibUnregisterPrefixRunnable(String prefixToUnregister) {
+ this.prefixToUnregister = prefixToUnregister;
+ }
+
+ @Override
+ public void run() {
+ try {
+ NDNController.getInstance().getNfdcHelper().ribUnregisterPrefix(new Name(prefixToUnregister));
+
+ Log.d(TAG, "Unregistered rib prefix: " + prefixToUnregister);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/runnable/UnregisterPrefixRunnable.java b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/UnregisterPrefixRunnable.java
new file mode 100644
index 0000000..b0a7d08
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/runnable/UnregisterPrefixRunnable.java
@@ -0,0 +1,43 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.runnable;
+
+import android.util.Log;
+
+import net.named_data.nfd.wifidirect.utils.NDNController;
+
+public class UnregisterPrefixRunnable implements Runnable {
+ private static final String TAG = "UnregisterPrefix";
+
+ private long registeredPrefixId;
+
+ public UnregisterPrefixRunnable(long registeredPrefixId) {
+ this.registeredPrefixId = registeredPrefixId;
+ }
+
+ @Override
+ public void run() {
+ if (registeredPrefixId != -1) {
+ NDNController.getInstance().getLocalHostFace().removeRegisteredPrefix(registeredPrefixId);
+ Log.d(TAG, "unregister prefix whose id is " + registeredPrefixId + " on local face");
+ NDNController.getInstance().setRegisteredPrefixId(-1);
+ }
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/service/WDBroadcastReceiverService.java b/app/src/main/java/net/named_data/nfd/wifidirect/service/WDBroadcastReceiverService.java
new file mode 100644
index 0000000..98b3007
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/service/WDBroadcastReceiverService.java
@@ -0,0 +1,96 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.service;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.os.IBinder;
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+import net.named_data.nfd.wifidirect.utils.NDNController;
+import net.named_data.nfd.wifidirect.utils.WDBroadcastReceiver;
+
+/**
+ * Service that registers a WDBroadcastReceiver to listen to WiFi Direct
+ * broadcasted intents.
+ */
+
+public class WDBroadcastReceiverService extends Service {
+
+ private final static String TAG = "WDBRService";
+
+ private WDBroadcastReceiver mReceiver = null;
+ private WifiP2pManager mManager;
+ private WifiP2pManager.Channel mChannel;
+ private IntentFilter mIntentFilter;
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onCreate() {
+ Log.d(TAG, "initWifiP2p() service");
+ initWifiP2p();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.d(TAG, "registerReceiver()");
+ registerReceiver(mReceiver, mIntentFilter);
+
+ // If we get killed, after returning from here, restart
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d(TAG, "onDestroy()");
+
+ if (mReceiver != null) {
+ Log.d(TAG, "unregisterReceiver()");
+ unregisterReceiver(mReceiver);
+ }
+
+ super.onDestroy();
+ }
+
+ /* initialize manager and receiver for activity */
+ private void initWifiP2p() {
+ mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
+ mChannel = mManager.initialize(this, getMainLooper(), null);
+ mReceiver = new WDBroadcastReceiver(mManager, mChannel);
+
+ 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);
+
+ NDNController.getInstance().recordWifiP2pResources(mManager, mChannel);
+ }
+}
+
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/utils/IPAddress.java b/app/src/main/java/net/named_data/nfd/wifidirect/utils/IPAddress.java
new file mode 100644
index 0000000..5b20b73
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/utils/IPAddress.java
@@ -0,0 +1,158 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.utils;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Enumeration;
+
+/**
+ * Helps retrieve the current WifiP2p interface IP address.
+ * See: http://stackoverflow.com/questions/10053385/how-to-get-each-devices-ip-address-in-wi-fi-direct-scenario
+ */
+public class IPAddress {
+
+ /**
+ * Returns the WiFi Direct (WifiP2p interface) IP address, or null if not available.
+ *
+ * @return String representation of the WD Ip address if it exists, otherwise null.
+ */
+ public static String getLocalIPAddress() {
+ try {
+ byte[] ip = null;
+ for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
+ NetworkInterface intf = en.nextElement();
+ for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
+ InetAddress inetAddress = enumIpAddr.nextElement();
+ if (!inetAddress.isLoopbackAddress()) {
+ if (inetAddress instanceof Inet4Address) { // fix for Galaxy Nexus. IPv4 is easy to use :-)
+ ip = inetAddress.getAddress();
+ String niceIp = getDottedDecimalIP(ip);
+ if (niceIp.startsWith("192.168.49")) { // wifid ip's are all in 192.168.49.x range
+ return niceIp;
+ }
+ }
+ //return inetAddress.getHostAddress().toString(); // Galaxy Nexus returns IPv6
+ }
+ }
+ }
+
+ return null;
+ } catch (SocketException ex) {
+ //Log.e("AndroidNetworkAddressFactory", "getLocalIPAddress()", ex);
+ } catch (NullPointerException ex) {
+ ex.printStackTrace();
+ //Log.e("AndroidNetworkAddressFactory", "getLocalIPAddress()", ex);
+ }
+ return null;
+ }
+
+ private static String getDottedDecimalIP(byte[] ipAddr) {
+ //convert to dotted decimal notation:
+ String ipAddrStr = "";
+ for (int i = 0; i < ipAddr.length; i++) {
+ if (i > 0) {
+ ipAddrStr += ".";
+ }
+ ipAddrStr += ipAddr[i] & 0xFF;
+ }
+ return ipAddrStr;
+ }
+
+ /**
+ * Try to extract a hardware MAC address from a given IP address using the
+ * ARP cache (/proc/net/arp).<br>
+ * <br>
+ * We assume that the file has this structure:<br>
+ * <br>
+ * IP address HW type Flags HW address Mask Device
+ * 192.168.18.11 0x1 0x2 00:04:20:06:55:1a * eth0
+ * 192.168.18.36 0x1 0x2 00:22:43:ab:2a:5b * eth0
+ *
+ * @param ip
+ * @return the MAC from the ARP cache
+ */
+ public static String getMacFromArpCache(String ip) {
+ if (ip == null)
+ return null;
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader("/proc/net/arp"));
+ String line;
+ while ((line = br.readLine()) != null) {
+ String[] splitted = line.split(" +");
+ if (splitted != null && splitted.length >= 4 && ip.equals(splitted[0])) {
+ // Basic sanity check
+ String mac = splitted[3];
+ if (mac.matches("..:..:..:..:..:..")) {
+ return mac;
+ } else {
+ return null;
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ br.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return null;
+ }
+
+ public static String getIPFromMac(String MAC) {
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader("/proc/net/arp"));
+ String line;
+ while ((line = br.readLine()) != null) {
+
+ String[] splitted = line.split(" +");
+ if (splitted != null && splitted.length >= 4) {
+ // Basic sanity check
+ String device = splitted[5];
+ if (device.matches(".*p2p-p2p0.*")) {
+ String mac = splitted[3];
+ if (mac.matches(MAC)) {
+ return splitted[0];
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ br.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return null;
+ }
+}
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/utils/NDNController.java b/app/src/main/java/net/named_data/nfd/wifidirect/utils/NDNController.java
new file mode 100644
index 0000000..09701ff
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/utils/NDNController.java
@@ -0,0 +1,957 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.utils;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.wifi.WpsInfo;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pDeviceList;
+import android.net.wifi.p2p.WifiP2pInfo;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.util.Log;
+
+import com.intel.jndn.management.ManagementException;
+
+import net.named_data.jndn.Face;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.InterestFilter;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.OnInterestCallback;
+import net.named_data.jndn.security.KeyChain;
+import net.named_data.jndn.security.SecurityException;
+import net.named_data.jndn.security.identity.IdentityManager;
+import net.named_data.jndn.security.identity.MemoryIdentityStorage;
+import net.named_data.jndn.security.identity.MemoryPrivateKeyStorage;
+import net.named_data.nfd.utils.NfdcHelper;
+import net.named_data.nfd.wifidirect.callback.GenericCallback;
+import net.named_data.nfd.wifidirect.callback.ProbeOnInterest;
+import net.named_data.nfd.wifidirect.model.Peer;
+import net.named_data.nfd.wifidirect.runnable.DiscoverPeersRunnable;
+import net.named_data.nfd.wifidirect.runnable.FaceAndRouteConsistencyRunnable;
+import net.named_data.nfd.wifidirect.runnable.GroupStatusConsistencyRunnable;
+import net.named_data.nfd.wifidirect.runnable.ProbeRunnable;
+import net.named_data.nfd.wifidirect.service.WDBroadcastReceiverService;
+import net.named_data.nfd.wifidirect.runnable.FaceCreateRunnable;
+import net.named_data.nfd.wifidirect.runnable.FaceDestroyRunnable;
+import net.named_data.nfd.wifidirect.runnable.FaceEventProcessRunnable;
+import net.named_data.nfd.wifidirect.runnable.RegisterPrefixRunnable;
+import net.named_data.nfd.wifidirect.runnable.RibRegisterPrefixRunnable;
+import net.named_data.nfd.wifidirect.runnable.RibUnregisterPrefixRunnable;
+import net.named_data.nfd.wifidirect.runnable.UnregisterPrefixRunnable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * New streamlined NDNOverWifiDirect controller. This class acts as the
+ * manager (hence controller) of the protocol, and applications using
+ * this project need only interface with the returned instance via getInstance().
+ */
+
+public class NDNController implements WifiP2pManager.PeerListListener,
+ WifiP2pManager.ConnectionInfoListener, WifiP2pManager.ChannelListener {
+ // volatile variables accessed in multiple threads
+ public static volatile String groupOwnerAddress;
+ public static volatile String myAddress;
+ public static volatile String myDeviceName;
+ public static volatile int WIFI_STATE = WifiP2pManager.WIFI_P2P_STATE_ENABLED;
+
+ public static final String URI_UDP_PREFIX = "udp://";
+ public static final String URI_TCP_PREFIX = "tcp://";
+ public static final String URI_TRANSPORT_PREFIX = URI_UDP_PREFIX; // transport portion of uri that rest of project should use
+ public static final String PROBE_PREFIX = "/localhop/wifidirect"; // prefix of prefix used in probing
+
+ private static final String TAG = "NDNController";
+ private static final int DISCOVER_PEERS_DELAY = 5000; // in ms
+ public static final int PROBE_DELAY = 1000; // in ms
+ public static final int PROBE_INTEREST_LIFETIME = 1000; // in ms (the network delay should not be large)
+ private static final int FACE_AND_ROUTE_CONSISTENCY_CHECK_DELAY = 5000;
+ private static final int GROUP_STATUS_CONSISTENCY_CHECK_DELAY = 10000;
+
+ // Singleton
+ private static NDNController mController = null;
+ private static KeyChain mKeyChain = null;
+
+ // WiFi Direct related resources
+ private WifiP2pManager wifiP2pManager = null;
+ private WifiP2pManager.Channel channel = null;
+ private Context wifiDirectContext = null; // context in which WiFi direct operations begin (an activity/fragment)
+ private List<WifiP2pDevice> discoverdPeers = new ArrayList<>();
+
+ // Relevant tasks, services, etc.
+ private WDBroadcastReceiverService brService = null;
+ private Future discoverPeersFuture = null;
+ private Future probeFuture = null;
+ private Future faceAndRouteConsistencyFuture = null;
+ private Future groupStatusConsistencyFuture = null;
+ // keep 1 thread to serialize all the tasks
+ private ScheduledThreadPoolExecutor faceEventProcessExecutor = new ScheduledThreadPoolExecutor(1);
+ private ScheduledThreadPoolExecutor localFaceCommandExecutor = new ScheduledThreadPoolExecutor(1);
+ private ScheduledThreadPoolExecutor nfdcFaceCommandExecutor = new ScheduledThreadPoolExecutor(1);
+ private ScheduledThreadPoolExecutor generalExecutor = new ScheduledThreadPoolExecutor(1);
+
+ // Useful flags
+ private boolean hasRegisteredOwnLocalhop = false;
+ private boolean isGroupOwner; // set in broadcast receiver, used primarily in ProbeOnInterest
+
+ // we have some redundancy here in data, but difficult to avoid given WiFi Direct API
+ private HashMap<String, Peer> ipPeerMapOfConnectedPeers = new HashMap<>(); // { peerIp : PeerInstance }, contains at least Face id info
+
+ // single shared Face instance at localhost
+ private Face mFace = null;
+ private final NfdcHelper nfdcHelper = new NfdcHelper();
+ long registeredPrefixId = -1;
+
+ private FaceEventProcessRunnable faceEventProcessRunnable = null;
+ private Future faceEventProcessFuture = null;
+
+
+ /**
+ * Private constructor to prevent outside instantiation.
+ */
+ private NDNController() {
+
+ if (mKeyChain == null) {
+ try {
+ // this is an expensive operation, so minimize it's invocation
+ mKeyChain = buildTestKeyChain();
+ } catch (SecurityException e) {
+ Log.e(TAG, "Unable to build the test keychain.");
+ }
+ }
+ }
+
+ /**
+ * Returns a shared instance of NDNController for use across the library.
+ *
+ * @return NDNController instance
+ */
+ public static NDNController getInstance() {
+ if (mController == null) {
+ mController = new NDNController();
+ }
+
+ return mController;
+ }
+
+ public void setRegisteredPrefixId(long registeredPrefixId) {
+ this.registeredPrefixId = registeredPrefixId;
+ }
+
+ public long getRegisteredPrefixId() {
+ return registeredPrefixId;
+ }
+
+ /**
+ * @return
+ */
+ public List<WifiP2pDevice> getDiscoveredPeers() {
+ return discoverdPeers;
+ }
+
+
+ /**
+ * Logs the peer with the corresponding WD IP address. If a previous logging of
+ * the peer exists, this will replace it.
+ *
+ * @param peerIp The peer's WD IP address
+ * @param peer A Peer instance with at least FaceId set.
+ */
+ public void logPeer(String peerIp, Peer peer) {
+ ipPeerMapOfConnectedPeers.put(peerIp, peer);
+ }
+
+ /**
+ * Returns the Face id associated with the given peer, denoted by IP address.
+ *
+ * @param peerIp The WiFi Direct IP address of the peer
+ * @return the Face id of the peer or -1 if no mapping exists.
+ */
+ public int getFaceIdForPeer(String peerIp) {
+ if (ipPeerMapOfConnectedPeers.containsKey(peerIp)) {
+ return ipPeerMapOfConnectedPeers.get(peerIp).getFaceId();
+ }
+
+ return -1;
+ }
+
+ /**
+ * Returns the logged peer instance (via logPeer()) by its
+ * WiFi Direct IP address.
+ *
+ * @param ip WiFi Direct IP address of peer
+ * @return the peer instance logged earlier by a call to logPeer(), or null
+ * if none.
+ */
+ public Peer getPeerByIp(String ip) {
+ return ipPeerMapOfConnectedPeers.get(ip);
+ }
+
+ /**
+ * Similar to getConnectPeers, except this returns the IP addresses
+ * of the currently logged peers.
+ *
+ * @return A set backed by the underlying map of logged peers. Thus,
+ * be advised that changes to the returned set will be reflected in
+ * the underlying map.
+ */
+ public Set<String> getIpsOfConnectedPeers() {
+ return ipPeerMapOfConnectedPeers.keySet();
+ }
+
+ public Collection<Peer> getConnectedPeers() {
+ return ipPeerMapOfConnectedPeers.values();
+ }
+
+ public boolean isNumOfConnectedPeersZero() {
+ return ipPeerMapOfConnectedPeers.isEmpty();
+ }
+
+ public Map<String, Peer> getConnectedPeersMap() {
+ return ipPeerMapOfConnectedPeers;
+ }
+
+ /**
+ * Removes mapping to the logged peer, and destroys any state of that peer (e.g. any created
+ * faces and registered prefixes).
+ *
+ * @param ip WiFi Direct IP address of peer
+ */
+ public void removePeer(String ip) {
+ // for now, if the current device is not group owner, it only has one connection, so simply disconnect
+ if (!isGroupOwner) {
+ disconnect();
+ }
+ // if the current device is the group owner, we cannot disconnect the group, but simply remove the
+ // group member.
+ else {
+ FaceDestroyRunnable runnable = new FaceDestroyRunnable(ipPeerMapOfConnectedPeers.get(ip).getFaceId());
+ nfdcFaceCommandExecutor.execute(runnable);
+ ipPeerMapOfConnectedPeers.remove(ip);
+ }
+ }
+
+ /**
+ * Returns whether this device is the group owner
+ *
+ * @return true if this device is GO, false otherwise
+ */
+ public boolean getIsGroupOwner() {
+ return isGroupOwner;
+ }
+
+ /**
+ * Sets whether this device is the group owner
+ *
+ * @param b whether the device is the GO.
+ */
+ public void setIsGroupOwner(boolean b) {
+ isGroupOwner = b;
+ }
+
+ /**
+ * Initializes the WifiP2p context, channel and manager, for use with discovering peers.
+ * This must be done before ever calling discoverPeers().
+ *
+ * @param wifiP2pManager the WifiP2pManager
+ * @param channel the WifiP2p Channel
+ */
+ public void recordWifiP2pResources(WifiP2pManager wifiP2pManager, WifiP2pManager.Channel channel) {
+ this.wifiP2pManager = wifiP2pManager;
+ this.channel = channel;
+ }
+
+ /**
+ * Must be done before we can start the Broadcast Receiver Service - in the future we can also
+ * move the starting to somewhere within an activity or fragment in order to avoid this.
+ *
+ * @param context A valid Android context
+ */
+ public void setWifiDirectContext(Context context) {
+ this.wifiDirectContext = context;
+ }
+
+ /**
+ * Creates a face to the specified peer (IP), with the
+ * uriPrefix (e.g. tcp://). Optional callback parameter
+ * for adding a callback function to be called after successful
+ * face creation. Passing in null for callback means no callback.
+ *
+ * @param peerIp the peer's WiFi Direct IP
+ * @param uriPrefix uri prefix
+ * @param callback An implementation of GenericCallback, or null. Is called AFTER face
+ * creation succeeds.
+ */
+ public void createFace(String peerIp, String uriPrefix, GenericCallback callback) {
+
+ if (peerIp.equals(IPAddress.getLocalIPAddress())) {
+ return; //never add yourself as a face
+ }
+
+ // need to create a new face for this peer
+ FaceCreateRunnable runnable = new FaceCreateRunnable(peerIp, uriPrefix + peerIp);
+
+ if (callback != null) {
+ runnable.setCallback(callback);
+ }
+
+ nfdcFaceCommandExecutor.execute(runnable);
+ }
+
+ /**
+ * Registers the array of prefixes with the given Face, denoted by
+ * its face id.
+ *
+ * @param faceId The Face Id to register the prefixes to.
+ * @param prefixes array of prefixes to register.
+ */
+ public void ribRegisterPrefix(int faceId, String[] prefixes) {
+ Log.d(TAG, "ribRegisterPrefix called with: " + faceId + " and " + prefixes.length + " prefixes");
+
+ HashSet<Integer> faceIds = new HashSet<>(ipPeerMapOfConnectedPeers.size());
+ for (Peer p : ipPeerMapOfConnectedPeers.values()) {
+ faceIds.add(p.getFaceId());
+ }
+
+ if (faceIds.contains(faceId)) {
+ for (String prefix : prefixes) {
+ Log.d(TAG, "ribRegisterPrefix() with prefix: " + prefix);
+ RibRegisterPrefixRunnable runnable = new RibRegisterPrefixRunnable(prefix, faceId,
+ 0, true, false);
+ nfdcFaceCommandExecutor.execute(runnable);
+ }
+ }
+ }
+
+ /**
+ * Begins periodically looking for peers, and connecting
+ * to them.
+ */
+ public void startDiscoveringPeers() {
+ if (discoverPeersFuture == null) {
+ Log.d(TAG, "Start discovering peers every " + DISCOVER_PEERS_DELAY + "ms");
+ DiscoverPeersRunnable runnable = new DiscoverPeersRunnable();
+ discoverPeersFuture = generalExecutor.scheduleWithFixedDelay(runnable, 100, DISCOVER_PEERS_DELAY, TimeUnit.MILLISECONDS);
+ } else {
+ Log.d(TAG, "Discovering peers already running!");
+ }
+ }
+
+ /**
+ * Stops periodically discovering peers and connecting to them.
+ */
+ public void stopDiscoveringPeers() {
+ if (discoverPeersFuture != null) {
+ discoverPeersFuture.cancel(true);
+ discoverPeersFuture = null;
+ Log.d(TAG, "Stopped discovering peers.");
+ }
+ }
+
+ /**
+ * Begins probing the network for data prefixes.
+ */
+ public void startProbing() {
+ if (probeFuture == null) {
+ Log.d(TAG, "Start probing for data prefixes every " + PROBE_DELAY + "ms");
+ ProbeRunnable runnable = new ProbeRunnable();
+ probeFuture = localFaceCommandExecutor.scheduleWithFixedDelay(runnable, 200, PROBE_DELAY, TimeUnit.MILLISECONDS);
+ } else {
+ Log.d(TAG, "Probing task already running!");
+ }
+ }
+
+ /**
+ * Stops probing the network for data prefixes.
+ */
+ public void stopProbing() {
+ if (probeFuture != null) {
+ probeFuture.cancel(true);
+ probeFuture = null;
+ Log.d(TAG, "Stopped probing.");
+ } else {
+ Log.d(TAG, "Pprobing already stopped");
+ }
+ }
+
+ /**
+ * Starts service that registers the broadcast receiver for handling peer discovery
+ */
+ public void startBroadcastReceiverService() {
+ if (brService == null) {
+ Log.d(TAG, "Starting WDBR service...");
+ brService = new WDBroadcastReceiverService();
+ Intent intent = new Intent(wifiDirectContext, WDBroadcastReceiverService.class);
+ wifiDirectContext.startService(intent);
+ } else {
+ Log.d(TAG, "BroadcastReceiverService already started.");
+ }
+ }
+
+ /**
+ * Stops the service that registers the broadcast recevier for handling peer discovery.
+ */
+ public void stopBroadcastReceiverService() {
+ if (brService == null) {
+ Log.d(TAG, "BroadcastReceiverService not running, no need to stop.");
+ } else {
+ if (wifiDirectContext != null) {
+ wifiDirectContext.stopService(new Intent(wifiDirectContext, WDBroadcastReceiverService.class));
+ }
+
+ brService = null;
+ Log.d(TAG, "Stopped WDBR service.");
+ }
+ }
+
+ /**
+ * Starts periodically checking for consistency between NFD and NDNController's view of
+ * active faces.
+ */
+ public void startFaceAndRouteConsistencyChecker() {
+ if (faceAndRouteConsistencyFuture == null) {
+ Log.d(TAG, "Start checking consistency of logged Faces every " +
+ FACE_AND_ROUTE_CONSISTENCY_CHECK_DELAY + "ms");
+ FaceAndRouteConsistencyRunnable runnable = new FaceAndRouteConsistencyRunnable();
+ faceAndRouteConsistencyFuture = nfdcFaceCommandExecutor.scheduleWithFixedDelay(runnable,
+ 300, FACE_AND_ROUTE_CONSISTENCY_CHECK_DELAY, TimeUnit.MILLISECONDS);
+ } else {
+ Log.d(TAG, "Face consistency checker already running!");
+ }
+ }
+
+ /**
+ * Stops periodically checking for consistency between NFD and NDNController's view
+ * of active faces.
+ */
+ public void stopFaceAndRouteConsistencyChecker() {
+ if (faceAndRouteConsistencyFuture != null) {
+ faceAndRouteConsistencyFuture.cancel(false); // do not interrupt if running, but cancel further execution
+ faceAndRouteConsistencyFuture = null;
+
+ Log.d(TAG, "Stopped checking for Face consistency.");
+ } else {
+ Log.d(TAG, "Face consistency checker is already stopped");
+ }
+ }
+
+ /**
+ * Starts periodically checking for consistency between whether there are connected peers and this
+ * device's myAddress.
+ */
+ public void startGroupConsistencyChecker() {
+ if (groupStatusConsistencyFuture == null) {
+ Log.d(TAG, "Start checking consistency of group status every " +
+ GROUP_STATUS_CONSISTENCY_CHECK_DELAY + "ms");
+ GroupStatusConsistencyRunnable.resetTimeoutTimes();
+ GroupStatusConsistencyRunnable runnable = new GroupStatusConsistencyRunnable();
+ groupStatusConsistencyFuture = generalExecutor.scheduleWithFixedDelay(runnable,
+ 300, GROUP_STATUS_CONSISTENCY_CHECK_DELAY, TimeUnit.MILLISECONDS);
+ } else {
+ Log.d(TAG, "Group status consistency checker already running!");
+ }
+ }
+
+ /**
+ * Stops periodically checking for consistency between NFD and NDNController's view
+ * of active faces.
+ */
+ public void stopGroupConsistencyChecker() {
+ if (groupStatusConsistencyFuture != null) {
+ groupStatusConsistencyFuture.cancel(false); // do not interrupt if running, but cancel further execution
+ groupStatusConsistencyFuture = null;
+ GroupStatusConsistencyRunnable.resetTimeoutTimes();
+ Log.d(TAG, "Stopped checking for Face consistency.");
+ } else {
+ Log.d(TAG, "Group status consistency checker is already stopped");
+ }
+ }
+
+ /**
+ * Main convenience wrapper method to start all background
+ * tasks/services for this protocol.
+ */
+ public void start() {
+ recreateFace();
+ startDiscoveringPeers();
+ startProbing();
+ startBroadcastReceiverService();
+ startFaceAndRouteConsistencyChecker();
+ startGroupConsistencyChecker();
+ }
+
+ /**
+ * Main convenience wrapper method to stop all background
+ * tasks/services for this protocol.
+ */
+ public void stop() {
+ cleanUp();
+ stopGroupConsistencyChecker();
+ stopFaceAndRouteConsistencyChecker();
+ stopBroadcastReceiverService();
+ stopProbing();
+ stopDiscoveringPeers();
+ }
+
+ /**
+ * Whether or not /localhop/wifidirect/xxx.xxx.xxx.xxx has
+ * been registered. Here, the ip is specifically that of this device.
+ *
+ * @return true if so, false otherwise
+ */
+ public boolean getHasRegisteredOwnLocalhop() {
+ return this.hasRegisteredOwnLocalhop;
+ }
+
+ /**
+ * Sets the flag for whether the /localhop/wifidiret/xxx.xxx.xxx.xxx prefix is registered.
+ *
+ * @param set true or false
+ */
+ public void setHasRegisteredOwnLocalhop(boolean set) {
+ this.hasRegisteredOwnLocalhop = set;
+ }
+
+ /**
+ * Convenience function to register the important /localhop prefix, necessary for
+ * probe communication.
+ */
+ public void registerOwnLocalhop() {
+ if (!hasRegisteredOwnLocalhop) {
+ Log.d(TAG, "registerOwnLocalhop() starts to work");
+ RegisterPrefixRunnable runnable = new RegisterPrefixRunnable(
+ PROBE_PREFIX + "/" + myAddress, new OnInterestCallback() {
+ @Override
+ public void onInterest(Name prefix, Interest interest, Face face, long interestFilterId, InterestFilter filter) {
+ (new ProbeOnInterest()).doJob(prefix, interest, face, interestFilterId, filter);
+ }
+ });
+ localFaceCommandExecutor.execute(runnable);
+ }
+
+ setHasRegisteredOwnLocalhop(true);
+ }
+
+ /**
+ * Convenience function to unregister the important /localhop prefix, if it was registered
+ * previously.
+ */
+ public void unregisterOwnLocalhop() {
+ if (myAddress != null) {
+ RibUnregisterPrefixRunnable runnable = new RibUnregisterPrefixRunnable(PROBE_PREFIX + "/" + myAddress);
+ nfdcFaceCommandExecutor.execute(runnable);
+ }
+
+ // unregister the prefix, no longer handled if logic gets to here
+ UnregisterPrefixRunnable runnable1 = new UnregisterPrefixRunnable(registeredPrefixId);
+ localFaceCommandExecutor.execute(runnable1);
+ NDNController.getInstance().setHasRegisteredOwnLocalhop(false);
+ }
+
+ /**
+ * Attempts to scan nearby network for WD peers. This can be a one-off
+ * operation, or can be called periodically over time.
+ *
+ * @throws Exception
+ */
+ public void discoverPeers() throws Exception {
+
+ if (wifiP2pManager == null || channel == null) {
+ Log.e(TAG, "Unable to discover peers, did you recordWifiP2pResources() yet?");
+ return;
+ }
+
+ wifiP2pManager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
+ @Override
+ public void onSuccess() {
+ // WIFI_P2P_PEERS_CHANGED_ACTION intent sent!!
+ Log.d(TAG, "Success on discovering peers");
+ }
+
+ @Override
+ public void onFailure(int reasonCode) {
+ String reasonString = WDBroadcastReceiver
+ .getWifiP2pManagerMessageFromReasonCode(reasonCode);
+ Log.d(TAG, "Fail discover peers, reason: " + reasonString);
+ }
+ });
+ }
+
+ /**
+ * Returns a face to localhost, to avoid multiple creations of localhost
+ * faces.
+ *
+ * @return the localhost Face instance.
+ */
+ public Face getLocalHostFace() {
+ return mFace;
+ }
+
+ public NfdcHelper getNfdcHelper() {
+ return nfdcHelper;
+ }
+
+ /**
+ * Resets all state. (including disconnecting group, and reseting saved states)
+ */
+ public void cleanUp() {
+ cancelConnect();
+ disconnect();
+ cleanUpConnections();
+ }
+
+ /**
+ * cancel ongoing negotiation
+ */
+ public void cancelConnect() {
+ // if you are negotiating with others, cancel the connection
+ if (wifiP2pManager != null) {
+ wifiP2pManager.cancelConnect(channel, new WifiP2pManager.ActionListener() {
+ @Override
+ public void onSuccess() {
+ Log.d(TAG, "Successfully removed self from WifiP2p group.");
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ String reasonString = WDBroadcastReceiver
+ .getWifiP2pManagerMessageFromReasonCode(reason);
+ Log.e(TAG, "Unable to remove self from WifiP2p group, reason: " + reasonString);
+ }
+ });
+ }
+ }
+
+ /**
+ * disconnect from a group
+ */
+ public void disconnect() {
+ // if you are in a group, remove yourself from it
+ // note that if you are the group owner, this will cause a disruption in connectivity
+ // for the other peers
+ if (wifiP2pManager != null) {
+ wifiP2pManager.removeGroup(channel, new WifiP2pManager.ActionListener() {
+ @Override
+ public void onSuccess() {
+ Log.d(TAG, "Successfully removed self from WifiP2p group.");
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ String reasonString = WDBroadcastReceiver
+ .getWifiP2pManagerMessageFromReasonCode(reason);
+ Log.e(TAG, "Unable to remove self from WifiP2p group, reason: " + reasonString);
+ }
+ });
+ }
+ }
+
+ /**
+ * start all runnable, keep saved Wifi-Direct states unchanged
+ */
+ public void startRunnables() {
+ startDiscoveringPeers();
+ startProbing();
+ startFaceAndRouteConsistencyChecker();
+ startGroupConsistencyChecker();
+ }
+
+ /**
+ * stop all runnable, keep saved Wifi-Direct states unchanged
+ */
+ public void stopRunnables() {
+ startGroupConsistencyChecker();
+ stopFaceAndRouteConsistencyChecker();
+ stopProbing();
+ stopDiscoveringPeers();
+ }
+
+ /**
+ * Resets all saved states.
+ */
+ public void cleanUpConnections() {
+ unregisterOwnLocalhop();
+
+ // Remove all faces created to peers, and shut down the localhost face we used
+ // for communication with NFD.
+ // Make use of our handy executor
+ Runnable cleanUpRunnable = new Runnable() {
+ @Override
+ public void run() {
+ Log.d(TAG, "before cleaning up connected peers, the size of ipPeerMapOfConnectedPeers is " + ipPeerMapOfConnectedPeers.size());
+
+ for (String peerIp : ipPeerMapOfConnectedPeers.keySet()) {
+ try {
+ Log.d(TAG, "Cleaning up face towards peer: " + peerIp);
+ nfdcHelper.faceDestroy(ipPeerMapOfConnectedPeers.get(peerIp).getFaceId());
+ } catch (ManagementException me) {
+ Log.e(TAG, "Unable to destroy face to: " + peerIp);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to destroy face to: " + peerIp);
+ }
+ }
+
+ myAddress = null;
+ groupOwnerAddress = null;
+ hasRegisteredOwnLocalhop = false;
+ isGroupOwner = false;
+ ipPeerMapOfConnectedPeers.clear();
+ discoverdPeers.clear();
+ }
+ };
+
+ nfdcFaceCommandExecutor.execute(cleanUpRunnable);
+
+ if (faceEventProcessFuture != null) {
+ faceEventProcessFuture.cancel(false);
+ faceEventProcessFuture = null;
+ }
+ if (mFace != null) {
+ mFace.shutdown();
+ mFace = null;
+ }
+ }
+
+ public void recreateFace() {
+ if (faceEventProcessFuture != null) {
+ faceEventProcessFuture.cancel(false);
+ faceEventProcessFuture = null;
+ }
+ if (mFace != null) {
+ mFace.shutdown();
+ mFace = null;
+ }
+ mFace = new Face();
+ faceEventProcessRunnable = new FaceEventProcessRunnable();
+ try {
+ mFace.setCommandSigningInfo(mKeyChain, mKeyChain.getDefaultCertificateName());
+ } catch (SecurityException e) {
+ Log.e(TAG, "Unable to set command signing info for localhost face.");
+ }
+ faceEventProcessFuture = faceEventProcessExecutor.scheduleWithFixedDelay(
+ faceEventProcessRunnable, 0, 5, TimeUnit.MILLISECONDS);
+ Log.d(TAG, "create face and start to process event");
+ }
+
+ /**
+ * misc
+ **/
+ private KeyChain buildTestKeyChain() throws SecurityException {
+ MemoryIdentityStorage identityStorage = new MemoryIdentityStorage();
+ MemoryPrivateKeyStorage privateKeyStorage = new MemoryPrivateKeyStorage();
+ IdentityManager identityManager = new IdentityManager(identityStorage, privateKeyStorage);
+ KeyChain keyChain = new KeyChain(identityManager);
+ try {
+ keyChain.getDefaultCertificateName();
+ } catch (SecurityException e) {
+ keyChain.createIdentity(new Name("/test/identity"));
+ keyChain.getIdentityManager().setDefaultIdentity(new Name("/test/identity"));
+ }
+ return keyChain;
+
+ }
+
+ /* In the future, we should allow users to implement this method so they can provide their own keychain */
+ public KeyChain getKeyChain() throws SecurityException {
+ return buildTestKeyChain();
+ }
+
+ public void requestConnectionInfo() {
+ wifiP2pManager.requestConnectionInfo(channel, this);
+ }
+
+ /**
+ * update the connected peers info
+ */
+ private void updateIpPeerMapOfConnectedPeers() {
+ Log.d(TAG, "update IpPeerMapOfConnectedPeers");
+ for (String peerIp : ipPeerMapOfConnectedPeers.keySet()) {
+ Peer peer = ipPeerMapOfConnectedPeers.get(peerIp);
+ String macAddress = IPAddress.getMacFromArpCache(peerIp);
+ if (macAddress == null) {
+ continue;
+ }
+ for (WifiP2pDevice one : discoverdPeers) {
+ // TODO: figure out why the 13th char in arp table is 8 smaller than that in device info
+ if (one.deviceAddress.startsWith(macAddress.substring(0, 12))
+ && one.deviceAddress.endsWith(macAddress.substring(13))) {
+ peer.setDevice(one);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove the unconnected peers (those peers are not removed by the user, but disconnected for
+ * some other reasons, e.g., shut down or out of range) from connected peers map.
+ */
+ private void removeDisconnectedPeersFromIpPeerMapOfConnectedPeers() {
+ Log.d(TAG, "remove unconnected peers from IpPeerMapOfConnectedPeers");
+ Log.d(TAG, "before removing, the size of IpPeerMapOfConnectedPeers is " + ipPeerMapOfConnectedPeers.size());
+ for (String peerIp : ipPeerMapOfConnectedPeers.keySet()) {
+ WifiP2pDevice peer = ipPeerMapOfConnectedPeers.get(peerIp).getDevice();
+ if (peer != null && (!discoverdPeers.contains(peer) || peer.status != WifiP2pDevice.CONNECTED)) {
+ FaceDestroyRunnable runnable = new FaceDestroyRunnable(ipPeerMapOfConnectedPeers.get(peerIp).getFaceId());
+ nfdcFaceCommandExecutor.execute(runnable);
+ ipPeerMapOfConnectedPeers.remove(peerIp);
+ }
+ }
+ Log.d(TAG, "after removing, the size of IpPeerMapOfConnectedPeers is " + ipPeerMapOfConnectedPeers.size());
+ }
+
+ /**
+ * Check Wifi-Direct state, change saved states when needed.
+ * (1) When Wifi-Direct is connected, but myAddress is null, request connection info and save states
+ * (2) When Wifi-Direct is disconnected, but myAddress is not null, clean all the states
+ */
+ private void checkConnectionConsistency() {
+ Log.d(TAG, "start to check ConnectionConsistency");
+ boolean hasConnection = false;
+ for (WifiP2pDevice one : discoverdPeers) {
+ if (one.status == WifiP2pDevice.CONNECTED) {
+ hasConnection = true;
+ break;
+ }
+ }
+ if (hasConnection && myAddress == null) {
+ requestConnectionInfo();
+ return;
+ }
+ if (!hasConnection && myAddress != null) {
+ cleanUpConnections();
+ recreateFace();
+ return;
+ }
+ }
+
+ @Override
+ public void onPeersAvailable(WifiP2pDeviceList peerList) {
+ Log.d(TAG,
+ String.format("Peers available: %d", peerList.getDeviceList().size()));
+
+ discoverdPeers.clear();
+ discoverdPeers.addAll(peerList.getDeviceList());
+
+ updateIpPeerMapOfConnectedPeers();
+ removeDisconnectedPeersFromIpPeerMapOfConnectedPeers();
+ checkConnectionConsistency();
+
+ // If an AdapterView is backed by this data, notify it
+ // of the change. For instance, if you have a ListView of available
+ // peers, trigger an update.
+ if (discoverdPeers.size() == 0) {
+ Log.d(TAG, "No devices found");
+ return;
+ }
+ }
+
+ @Override
+ public void onConnectionInfoAvailable(WifiP2pInfo info) {
+ Log.d(TAG, "connection info is available!!");
+
+ // check if group formation was successful
+ if (info.groupFormed) {
+
+ // group owner address
+ groupOwnerAddress = info.groupOwnerAddress.getHostAddress();
+
+ // this device's address, which is now available
+ myAddress = IPAddress.getLocalIPAddress();
+ Log.d(TAG, "My WiFi Direct IP address is: " + myAddress);
+
+ if (!getHasRegisteredOwnLocalhop()) {
+ // do so now
+ registerOwnLocalhop();
+ Log.d(TAG, "registerOwnLocalhop() called...");
+ } else {
+ Log.d(TAG, "already registered own /localhop prefix.");
+ }
+
+ if (info.isGroupOwner) {
+ // Do whatever tasks are specific to the group owner.
+ // One common case is creating a server thread and accepting
+ // incoming connections.
+ Log.d(TAG, "I am the group owner, wait for probe interests from peers...");
+ setIsGroupOwner(true);
+ } else {
+ // non group owner
+ // The other device acts as the client. In this case,
+ // you'll want to create a client thread that connects to the group
+ // owner.
+ Log.d(TAG, "I am NOT the group owner.");
+ setIsGroupOwner(false);
+
+ // create a callback that will register the /localhop/wifidirect/<go-addr> prefix
+ GenericCallback cb = new GenericCallback() {
+ @Override
+ public void doJob() {
+ Log.d(TAG, "registering " + NDNController.PROBE_PREFIX + "/" + groupOwnerAddress);
+ String[] prefixes = new String[1];
+ prefixes[0] = NDNController.PROBE_PREFIX + "/" + groupOwnerAddress;
+ ribRegisterPrefix(getFaceIdForPeer(groupOwnerAddress),
+ prefixes);
+ }
+ };
+
+ // create face towards GO, with callback to register /localhop/... prefix
+ createFace(groupOwnerAddress, NDNController.URI_TRANSPORT_PREFIX, cb);
+ }
+ }
+
+ }
+
+ @Override
+ public void onChannelDisconnected() {
+ cleanUpConnections();
+ recreateFace();
+ }
+
+ public void connect(WifiP2pDevice peerDevice) {
+ WifiP2pConfig config = new WifiP2pConfig();
+ config.deviceAddress = peerDevice.deviceAddress;
+ config.wps.setup = WpsInfo.PBC;
+ wifiP2pManager.connect(channel, config, new WifiP2pManager.ActionListener() {
+ @Override
+ public void onSuccess() {
+ // onReceive() in WDBroadcastReceiver will receive an intent
+ }
+
+ @Override
+ public void onFailure(int reason) {
+
+ String reasonString = WDBroadcastReceiver
+ .getWifiP2pManagerMessageFromReasonCode(reason);
+
+ Log.e(TAG, "There was an issue with initiating connection reason: " + reasonString);
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/named_data/nfd/wifidirect/utils/WDBroadcastReceiver.java b/app/src/main/java/net/named_data/nfd/wifidirect/utils/WDBroadcastReceiver.java
new file mode 100644
index 0000000..b6b5fa7
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/wifidirect/utils/WDBroadcastReceiver.java
@@ -0,0 +1,161 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2017 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ * <p>
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd.wifidirect.utils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkInfo;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.util.Log;
+
+import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_STATE_DISABLED;
+import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_STATE_ENABLED;
+
+/**
+ * WiFi Direct Broadcast receiver. Does not deviate too much
+ * from the standard WiFi Direct broadcast receiver seen in the official
+ * android docs.
+ */
+
+public class WDBroadcastReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "WDBroadcastReceiver";
+
+ // WifiP2p
+ private WifiP2pManager mManager;
+ private WifiP2pManager.Channel mChannel;
+
+ // the controller
+ private NDNController mController;
+
+ public WDBroadcastReceiver(WifiP2pManager manager, WifiP2pManager.Channel channel) {
+ super();
+
+ this.mManager = manager;
+ this.mChannel = channel;
+ this.mController = NDNController.getInstance();
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final 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
+
+ Log.d(TAG, "wifi enabled check");
+ int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
+ if (state == WIFI_P2P_STATE_ENABLED) {
+ // Wifi P2P is enabled
+ Log.d(TAG, "WIFI IS ENABLED");
+ mController.WIFI_STATE = WIFI_P2P_STATE_ENABLED;
+ mController.startRunnables();
+ } else {
+ // Wi-Fi P2P is not enabled
+ Log.d(TAG, "WIFI IS NOT ENABLED");
+ mController.WIFI_STATE = WIFI_P2P_STATE_DISABLED;
+ mController.stopRunnables();
+ mController.cleanUpConnections();
+ mController.recreateFace();
+ }
+
+ } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
+ // Call WifiP2pManager.requestPeers() to get a list of current peers
+
+ Log.d(TAG, "peers changed!");
+
+ // 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 (mManager != null) {
+ mManager.requestPeers(mChannel, mController);
+ }
+
+ } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
+ // Respond to new connection or disconnections
+
+ Log.d(TAG, "p2pconnection changed check");
+ if (mManager == null) {
+ Log.d(TAG, "mManager is null, skipping...");
+ return;
+ }
+
+ NetworkInfo networkInfo = (NetworkInfo) intent
+ .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
+
+ if (networkInfo.isConnected()) {
+ // We are connected with the other device, request connection
+ // info to find group owner IP
+ mManager.requestConnectionInfo(mChannel, mController);
+ } else {
+ // Sometimes the framework sends a disconnect event when connecting to a new peer
+ Log.d(TAG, "Received a disconnect notification!");
+ mController.cleanUpConnections();
+ mController.recreateFace();
+ }
+
+ } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
+ // TODO: Respond to this device's state changing
+ Log.d(TAG, "wifi state changed check");
+ WifiP2pDevice device = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
+ NDNController.myDeviceName = device.deviceName;
+ } else if (WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION.equals(action)) {
+ // if discovery (scanning) has either stopped or resumed
+ switch (intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1)) {
+ case WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED:
+ Log.d(TAG, "Wifip2p discovery started.");
+ break;
+ case WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED:
+ Log.d(TAG, "Wifipsp discovery stopped.");
+ break;
+ default:
+ Log.d(TAG, "WIFI_P2P_DISCOVERY_CHANGED_ACTION returned other reason.");
+ }
+ }
+ }
+
+ /**
+ * Given an integer reason code returned by a WifiP2pManager.ActionListener,
+ * returns a human-readable message detailing the cause of the error.
+ *
+ * @param reasonCode integer reason code
+ * @return a human-readable message
+ */
+ public static String getWifiP2pManagerMessageFromReasonCode(int reasonCode) {
+ String reasonString;
+ switch (reasonCode) {
+ case WifiP2pManager.BUSY:
+ reasonString = "Framework is busy.";
+ break;
+ case WifiP2pManager.ERROR:
+ reasonString = "There was an error with the request.";
+ break;
+ case WifiP2pManager.P2P_UNSUPPORTED:
+ reasonString = "P2P is unsupported on this device.";
+ break;
+ default:
+ reasonString = "Unknown reason.";
+ }
+
+ return reasonString;
+ }
+}
diff --git a/app/src/main/res/drawable-hdpi/machine.png b/app/src/main/res/drawable-hdpi/machine.png
new file mode 100755
index 0000000..d61609a
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/machine.png
Binary files differ
diff --git a/app/src/main/res/layout/fragment_wifidirect.xml b/app/src/main/res/layout/fragment_wifidirect.xml
new file mode 100644
index 0000000..cb3136b
--- /dev/null
+++ b/app/src/main/res/layout/fragment_wifidirect.xml
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layout_margin="20dp"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+ <ScrollView
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ tools:context="net.named_data.nfd.WiFiDirectFragment">
+
+ <!-- Row with title and on/off switch -->
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="10dp">
+ <TextView
+ android:text="@string/fragment_wifidirect_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Switch
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/wd_switch"
+ android:layout_alignParentRight="true" />
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:weightSum="1">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/fragment_wifidirect_label_me"
+ android:layout_weight="0.10" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/wd_this_device_name_textview" />
+ </LinearLayout>
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dp"
+ android:gravity="center_vertical"
+ android:background="@color/android_color_gray" />
+
+ <!-- Row for Group connection status -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:weightSum="1">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/fragment_wifidirect_group_conn_status"
+ android:layout_weight="0.10" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/wd_group_conn_status_textview" />
+ </LinearLayout>
+
+ <!-- Row for IP address display -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:weightSum="1">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/fragment_wifidirect_curr_ip"
+ android:layout_weight="0.10"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/wd_ip_address_textview" />
+ </LinearLayout>
+
+ <!-- Row for group owner indicator -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:weightSum="1">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/fragment_wifidirect_is_group_own"
+ android:layout_weight="0.10"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/wd_group_owner_textview" />
+ </LinearLayout>
+ </LinearLayout>
+ </ScrollView>
+
+ <!-- Column of currently connected peers -->
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/fragment_wifidirect_connected_peers"
+ android:layout_marginTop="10dp"/>
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dp"
+ android:gravity="center_vertical"
+ android:background="@color/android_color_gray" />
+ <ListView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/wd_connected_peers_listview"/>
+
+ <!-- Column of discovered peers -->
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/fragment_wifidirect_discovered_peers"
+ android:layout_marginTop="10dp"/>
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dp"
+ android:gravity="center_vertical"
+ android:background="@color/android_color_gray" />
+ <ListView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/wd_discovered_peers_listview"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/row_devices.xml b/app/src/main/res/layout/row_devices.xml
new file mode 100755
index 0000000..baf49e3
--- /dev/null
+++ b/app/src/main/res/layout/row_devices.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:padding="6dip">
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_marginRight="2dip"
+ android:src="@drawable/machine" />
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="fill_parent">
+ <TextView
+ android:id="@+id/device_name"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:gravity="center_vertical" />
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:id="@+id/device_details"
+ android:ellipsize="marquee" />
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ff141d1..5aa76b3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -2,6 +2,10 @@
<string name="start">Start</string>
<string name="stop">Stop</string>
<string name="app_name">NFD</string>
+ <string name="yes">Yes</string>
+ <string name="no">No</string>
+ <string name="empty_string">""</string>
+ <string name="question_mark">\?</string>
<string name="service_name">NFD Service</string>
<string name="action_settings">Settings</string>
<string name="nfd_stopped">NFD is stopped</string>
@@ -20,6 +24,32 @@
<string name="drawer_item_ping">Ping</string>
<string name="drawer_item_strategies">Strategies</string>
<string name="drawer_item_logcat">Logcat</string>
+ <string name="drawer_item_wifidirect">WiFi Direct</string>
+ <string name="fragment_wifidirect_label_me">Me: </string>
+ <string name="fragment_wifidirect_title">NDN Over WiFi Direct</string>
+ <string name="fragment_wifidirect_group_conn_status">Group connection status: </string>
+ <string name="fragment_wifidirect_is_group_own">Is group owner: </string>
+ <string name="fragment_wifidirect_text_group_connected">Connected to group.</string>
+ <string name="fragment_wifidirect_text_group_scanning">Scanning...</string>
+ <string name="fragment_wifidirect_text_group_wifi_p2p_disabled">WIFI p2p is disabled</string>
+ <string name="fragment_wifidirect_curr_ip">Current WiFi Direct IP: </string>
+ <string name="fragment_wifidirect_connected_peers">Connected Peers: </string>
+ <string name="fragment_wifidirect_discovered_peers">Discovered Peers: </string>
+ <string name="fragment_wifidirect_toast_connected_to_group">Currently connected to a group, please disconnect first</string>
+ <string name="fragment_wifidirect_toast_peer_no_longer_available">The peer is no longer available</string>
+ <string name="fragment_wifidirect_toast_connection_works_well">Connection is working correctly</string>
+ <string name="fragment_wifidirect_toast_didnt_get_response">Didn\'t get responses in the last\u0020</string>
+ <string name="fragment_wifidirect_toast_seconds">\u0020seconds</string>
+ <string name="fragment_wifidirect_dialog_cancel_invitation">Cancel invitation for\u0020</string>
+ <string name="fragment_wifidirect_dialog_cancelling">Cancelling</string>
+ <string name="fragment_wifidirect_dialog_cancelling_invitation">Cancelling invitation for\u0020</string>
+ <string name="fragment_wifidirect_dialog_disconnect_from">Disconnect from\u0020</string>
+ <string name="fragment_wifidirect_dialog_disconnecting">Disconnecting</string>
+ <string name="fragment_wifidirect_dialog_disconnecting_from">Disconnecting from\u0020</string>
+ <string name="fragment_wifidirect_dialog_invite">Invite\u0020</string>
+ <string name="fragment_wifidirect_dialog_inviting">Inviting\u0020</string>
+ <string name="fragment_wifidirect_dialog_join_group">\u0020to join a group</string>
+ <string name="fragment_wifidirect_dialog_destroy_group_alter">\u0020Because you are the group owner, disconnecting from a device will also destroy the group.</string>
<string name="menu_item_delete_setting_item">Delete</string>
<string name="fragment_logcat_general_operations_category">General Actions</string>
<string name="fragment_logcat_tags_n_log_levels">Tags & Log Levels</string>