Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT REVIEW][epskc] demo version impl for epskc #261

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
8 changes: 6 additions & 2 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"])

// 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.2.0'
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 @@ -52,14 +52,18 @@ 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 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();
Expand All @@ -72,17 +76,19 @@ 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 = context.getSystemService(NsdManager.class);
connManager = context.getSystemService(ConnectivityManager.class);

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

Expand All @@ -98,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 Down Expand Up @@ -174,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 @@ -191,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 @@ -211,18 +215,16 @@ 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)),
attrs.get(KEY_EXTENDED_PAN_ID),
serviceType.equals(MESHCOP_E_SERVICE_TYPE),
serviceInfo.getServiceName(),
handleNsdServiceAddress(serviceInfo.getHost()),
serviceInfo.getPort());
serviceInfo.getPort(),
attrs.get(KEY_ID),
getStringAttribute(attrs, KEY_NETWORK_NAME),
attrs.get(KEY_EXTENDED_PAN_ID),
getStringAttribute(attrs, KEY_VENDOR_NAME),
getStringAttribute(attrs, KEY_MODEL_NAME));
}

/**
Expand Down Expand Up @@ -258,11 +260,11 @@ private InetAddress handleNsdServiceAddress(InetAddress address) {
}

@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 null;
return new String(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,49 +30,65 @@

import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.common.net.InetAddresses;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class BorderAgentInfo implements Parcelable {

public byte[] id;
public String networkName;
public byte[] extendedPanId;
public InetAddress host;
public int port;
public final boolean isEpskcService;
public final String instanceName;
public final InetAddress host;
public final int port;
@Nullable public final byte[] id;
@Nullable public final String networkName;
@Nullable public final byte[] extendedPanId;
@Nullable public final String vendorName;
@Nullable public final String modelName;

public BorderAgentInfo(
@NonNull byte[] id,
@NonNull String networkName,
@NonNull byte[] extendedPanId,
@NonNull InetAddress host,
@NonNull int port) {
this.id = (id == null ? null : id.clone());
this.networkName = networkName;
this.extendedPanId = (extendedPanId == null ? null : extendedPanId.clone());
boolean isEpskcService,
String instanceName,
InetAddress host,
int port,
@Nullable byte[] id,
@Nullable String networkName,
@Nullable byte[] extendedPanId,
@Nullable String vendorName,
@Nullable String modelName) {
this.isEpskcService = isEpskcService;
this.instanceName = instanceName;
this.host = host;
this.port = port;
this.id = id == null ? null : id.clone();
this.networkName = networkName;
this.extendedPanId = extendedPanId == null ? null : extendedPanId.clone();
this.vendorName = vendorName;
this.modelName = modelName;
}

protected BorderAgentInfo(Parcel in) {
isEpskcService = in.readInt() != 0;
instanceName = in.readString();
host = InetAddresses.forString(in.readString());
port = in.readInt();
id = in.createByteArray();
networkName = in.readString();
extendedPanId = in.createByteArray();
try {
host = InetAddress.getByAddress(in.createByteArray());
} catch (UnknownHostException e) {
}
port = in.readInt();
vendorName = in.readString();
modelName = in.readString();
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(isEpskcService ? 1 : 0);
dest.writeString(instanceName);
dest.writeString(host.getHostAddress());
dest.writeInt(port);
dest.writeByteArray(id);
dest.writeString(networkName);
dest.writeByteArray(extendedPanId);
dest.writeByteArray(host.getAddress());
dest.writeInt(port);
dest.writeString(vendorName);
dest.writeString(modelName);
}

@Override
Expand Down
Loading
Loading