Skip to content

Commit

Permalink
[DO NOT REVIEW][epskc] demo version impl for epskc
Browse files Browse the repository at this point in the history
  • Loading branch information
wgtdkp committed Aug 13, 2024
1 parent 4b80986 commit 0af637b
Show file tree
Hide file tree
Showing 35 changed files with 1,505 additions and 697 deletions.
2 changes: 1 addition & 1 deletion android/build-commissioner-libs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ cmake -GNinja \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_CXX_STANDARD=11 \
-DCMAKE_CXX_STANDARD_REQUIRED=ON \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_BUILD_TYPE=Debug \
-DOT_COMM_ANDROID=ON \
-DOT_COMM_JAVA_BINDING=ON \
-DOT_COMM_APP=OFF \
Expand Down
10 changes: 7 additions & 3 deletions android/openthread_commissioner/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,18 @@ dependencies {

implementation fileTree(dir: "libs", include: ["*.jar"])

implementation 'com.google.guava:guava:31.1-jre'
implementation 'androidx.appcompat:appcompat:1.2.0'
// Fix Duplicate class
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))

implementation 'com.google.guava:guava:33.2.1-android'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.activity:activity:1.9.1'
implementation "androidx.concurrent:concurrent-futures:1.1.0"
implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
implementation 'androidx.navigation:navigation-fragment:2.3.0'
implementation 'com.google.android.gms:play-services-vision:20.1.3+'
implementation 'com.google.android.gms:play-services-threadnetwork:16.0.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'com.google.android.material:material:1.9.0'
androidTestImplementation 'com.google.truth:truth:1.4.4'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package io.openthread.commissioner.app;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import androidx.annotation.Nullable;
import io.openthread.commissioner.app.BorderAgentDiscoverer.BorderAgentListener;
import java.net.Inet6Address;
import java.util.Map;
import java.util.Vector;

public class BorderAgentAdapter extends BaseAdapter implements BorderAgentListener {

private final Vector<BorderAgentInfo> borderAgentServices = new Vector<>();
private final Map<String, BorderAgentInfo> epskcServices = new ArrayMap<>();

private final LayoutInflater inflater;

BorderAgentAdapter(Context context) {
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

public void addBorderAgent(BorderAgentInfo serviceInfo) {
if (serviceInfo.isEpskcService) {
epskcServices.put(serviceInfo.instanceName, serviceInfo);
notifyDataSetChanged();
return;
}

boolean hasExistingBorderRouter = false;
for (int i = 0; i < borderAgentServices.size(); i++) {
if (borderAgentServices.get(i).instanceName.equals(serviceInfo.instanceName)) {
borderAgentServices.set(i, serviceInfo);
hasExistingBorderRouter = true;
}
}

if (!hasExistingBorderRouter) {
borderAgentServices.add(serviceInfo);
}

notifyDataSetChanged();
}

public void removeBorderAgent(boolean isEpskcService, String instanceName) {
if (isEpskcService) {
epskcServices.remove(instanceName);
} else {
borderAgentServices.removeIf(serviceInfo -> serviceInfo.instanceName.equals(instanceName));
}
notifyDataSetChanged();
}

public void clear() {
borderAgentServices.clear();
epskcServices.clear();
notifyDataSetChanged();
}

@Override
public int getCount() {
return borderAgentServices.size();
}

@Override
public Object getItem(int position) {
return borderAgentServices.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup container) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.border_agent_list_item, container, false);
}

BorderAgentInfo borderAgentInfo = borderAgentServices.get(position);

TextView instanceNameText = convertView.findViewById(R.id.border_agent_instance_name);
instanceNameText.setText(borderAgentInfo.instanceName);

TextView vendorNameText = convertView.findViewById(R.id.border_agent_vendor_name);
vendorNameText.setText(borderAgentInfo.vendorName);

TextView modelNameText = convertView.findViewById(R.id.border_agent_model_name);
modelNameText.setText(borderAgentInfo.modelName);

TextView adminModeText = convertView.findViewById(R.id.border_agent_admin_mode);
adminModeText.setText(
"In Administration Mode: " + (inAdministrationMode(borderAgentInfo) ? "YES" : "NO"));

TextView borderAgentIpAddrText = convertView.findViewById(R.id.border_agent_ip_addr);
int port =
inAdministrationMode(borderAgentInfo)
? getEpskcService(borderAgentInfo).port
: borderAgentInfo.port;
String socketAddress;
if (borderAgentInfo.host instanceof Inet6Address) {
socketAddress = "[" + borderAgentInfo.host.getHostAddress() + "]:" + port;
} else {
socketAddress = borderAgentInfo.host.getHostAddress() + ":" + port;
}
borderAgentIpAddrText.setText(socketAddress);
return convertView;
}

private boolean inAdministrationMode(BorderAgentInfo borderAgentInfo) {
return epskcServices.containsKey(borderAgentInfo.instanceName);
}

@Override
public void onBorderAgentFound(BorderAgentInfo borderAgentInfo) {
new Handler(Looper.getMainLooper()).post(() -> addBorderAgent(borderAgentInfo));
}

@Override
public void onBorderAgentLost(boolean isEpskcService, String instanceName) {
new Handler(Looper.getMainLooper()).post(() -> removeBorderAgent(isEpskcService, instanceName));
}

/**
* Returns the _meshcop-e._udp service which is associated with the given _meshcop._udp service,
* or {@code null} if such service doesn't exist.
*/
@Nullable
private BorderAgentInfo getEpskcService(BorderAgentInfo meshcopService) {
return epskcServices.get(meshcopService.instanceName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,17 @@

import android.Manifest.permission;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.net.wifi.WifiManager;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresPermission;
import com.google.common.net.InetAddresses;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
Expand All @@ -47,35 +52,43 @@ public class BorderAgentDiscoverer implements NsdManager.DiscoveryListener {

private static final String TAG = BorderAgentDiscoverer.class.getSimpleName();

private static final String SERVICE_TYPE = "_meshcop._udp";
public static final String MESHCOP_SERVICE_TYPE = "_meshcop._udp";
public static final String MESHCOP_E_SERVICE_TYPE = "_meshcop-e._udp";
private static final String KEY_ID = "id";
private static final String KEY_NETWORK_NAME = "nn";
private static final String KEY_EXTENDED_PAN_ID = "xp";
private static final String KEY_VENDOR_NAME = "vn";
private static final String KEY_MODEL_NAME = "mn";

private WifiManager.MulticastLock wifiMulticastLock;
private NsdManager nsdManager;
private BorderAgentListener borderAgentListener;
private final WifiManager.MulticastLock wifiMulticastLock;
private final NsdManager nsdManager;
private final ConnectivityManager connManager;
private final String serviceType;
private final BorderAgentListener borderAgentListener;

private ExecutorService executor = Executors.newSingleThreadExecutor();
private BlockingQueue<NsdServiceInfo> unresolvedServices = new ArrayBlockingQueue<>(256);
private AtomicBoolean isResolvingService = new AtomicBoolean(false);
private final BlockingQueue<NsdServiceInfo> unresolvedServices = new ArrayBlockingQueue<>(256);
private final AtomicBoolean isResolvingService = new AtomicBoolean(false);

private boolean isScanning = false;

public interface BorderAgentListener {

void onBorderAgentFound(BorderAgentInfo borderAgentInfo);

void onBorderAgentLost(byte[] id);
default void onBorderAgentLost(boolean isEpskcService, String instanceName) {}
}

@RequiresPermission(permission.INTERNET)
public BorderAgentDiscoverer(Context context, BorderAgentListener borderAgentListener) {
public BorderAgentDiscoverer(
Context context, String serviceType, BorderAgentListener borderAgentListener) {
WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
wifiMulticastLock = wifi.createMulticastLock("multicastLock");

nsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
nsdManager = context.getSystemService(NsdManager.class);
connManager = context.getSystemService(ConnectivityManager.class);

this.serviceType = serviceType;
this.borderAgentListener = borderAgentListener;
}

Expand All @@ -91,8 +104,8 @@ public void start() {
wifiMulticastLock.acquire();

startResolver();
nsdManager.discoverServices(
BorderAgentDiscoverer.SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, this);

nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, this);
}

private void startResolver() {
Expand All @@ -112,7 +125,7 @@ public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
public void onServiceResolved(NsdServiceInfo serviceInfo) {
BorderAgentInfo borderAgent = getBorderAgentInfo(serviceInfo);
if (borderAgent != null) {
Log.d(TAG, "successfully resolved service: " + serviceInfo.toString());
Log.d(TAG, "successfully resolved service: " + serviceInfo);
Log.d(
TAG,
"successfully resolved service: " + serviceInfo.getHost().getCanonicalHostName());
Expand Down Expand Up @@ -167,7 +180,7 @@ public void stop() {

@Override
public void onDiscoveryStarted(String serviceType) {
Log.d(TAG, "start discovering Border Agent");
Log.d(TAG, "start discovering Border Agent: " + serviceType);
}

@Override
Expand All @@ -184,11 +197,9 @@ public void onServiceFound(NsdServiceInfo nsdServiceInfo) {

@Override
public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
byte[] id = getBorderAgentId(nsdServiceInfo);
if (id != null) {
Log.d(TAG, "a Border Agent service is gone: " + nsdServiceInfo.getServiceName());
borderAgentListener.onBorderAgentLost(id);
}
Log.d(TAG, "a Border Agent service is gone: " + nsdServiceInfo.getServiceName());
borderAgentListener.onBorderAgentLost(
serviceType.equals(MESHCOP_E_SERVICE_TYPE), nsdServiceInfo.getServiceName());
}

@Override
Expand All @@ -204,26 +215,54 @@ public void onStopDiscoveryFailed(String serviceType, int errorCode) {
@Nullable
private BorderAgentInfo getBorderAgentInfo(NsdServiceInfo serviceInfo) {
Map<String, byte[]> attrs = serviceInfo.getAttributes();
byte[] id = getBorderAgentId(serviceInfo);

if (!attrs.containsKey(KEY_NETWORK_NAME) || !attrs.containsKey(KEY_EXTENDED_PAN_ID)) {
return null;
}

return new BorderAgentInfo(
id,
new String(attrs.get(KEY_NETWORK_NAME)),
serviceType.equals(MESHCOP_E_SERVICE_TYPE),
serviceInfo.getServiceName(),
handleNsdServiceAddress(serviceInfo.getHost()),
serviceInfo.getPort(),
attrs.get(KEY_ID),
getStringAttribute(attrs, KEY_NETWORK_NAME),
attrs.get(KEY_EXTENDED_PAN_ID),
serviceInfo.getHost(),
serviceInfo.getPort());
getStringAttribute(attrs, KEY_VENDOR_NAME),
getStringAttribute(attrs, KEY_MODEL_NAME));
}

@Nullable
private byte[] getBorderAgentId(NsdServiceInfo serviceInfo) {
Map<String, byte[]> attrs = serviceInfo.getAttributes();
if (attrs.containsKey(KEY_ID)) {
return attrs.get(KEY_ID).clone();
private static String getStringAttribute(Map<String, byte[]> attributes, String key) {
byte[] value = attributes.get(key);
if (value == null) {
return null;
}
return new String(value);
}

/**
* Properly handles the {@link InetAddress} within a discovered {@link NsdServiceInfo}.
*
* <p>For example, adds the scope ID to an IPv6 link-local address to make is usable.
*/
private InetAddress handleNsdServiceAddress(InetAddress address) {
if (!(address instanceof Inet6Address)) {
return address;
}
return null;

Inet6Address address6 = (Inet6Address) address;

// Sets the scope ID for IPv6 link-local address if it's missing. This can happen before
// Android U
if (address6.isLinkLocalAddress() && address6.getScopeId() == 0) {
// Assume the mDNS service is discovered on the current active default network
Network network = connManager.getActiveNetwork();
if (network == null) {
return address6;
}
String interfaceName = connManager.getLinkProperties(network).getInterfaceName();
if (interfaceName == null) {
return address6;
}
return InetAddresses.forString(address6.getHostAddress() + "%" + interfaceName);
}

return address6;
}
}
Loading

0 comments on commit 0af637b

Please sign in to comment.