Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / android / CHIPTool / app / src / main / java / com / google / chip / chiptool / commissioner / thread / internal / BorderAgentDiscoverer.java
1 /*
2  *   Copyright (c) 2020 Project CHIP Authors
3  *   All rights reserved.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  *
17  */
18
19 package com.google.chip.chiptool.commissioner.thread.internal;
20
21 import android.Manifest.permission;
22 import android.content.Context;
23 import android.net.nsd.NsdManager;
24 import android.net.nsd.NsdServiceInfo;
25 import android.net.wifi.WifiManager;
26 import android.util.Log;
27 import androidx.annotation.RequiresPermission;
28 import com.google.chip.chiptool.commissioner.thread.BorderAgentInfo;
29 import java.util.Map;
30 import java.util.concurrent.ArrayBlockingQueue;
31 import java.util.concurrent.BlockingQueue;
32 import java.util.concurrent.ExecutorService;
33 import java.util.concurrent.Executors;
34 import java.util.concurrent.atomic.AtomicBoolean;
35
36 class BorderAgentDiscoverer implements NsdManager.DiscoveryListener {
37
38   private static final String TAG = BorderAgentDiscoverer.class.getSimpleName();
39
40   private static final String SERVICE_TYPE = "_meshcop._udp";
41   private static final String KEY_DISCRIMINATOR = "discriminator";
42   private static final String KEY_NETWORK_NAME = "nn";
43   private static final String KEY_EXTENDED_PAN_ID = "xp";
44
45   private WifiManager.MulticastLock wifiMulticastLock;
46   private NsdManager nsdManager;
47   private BorderAgentListener borderAgentListener;
48
49   private ExecutorService executor = Executors.newSingleThreadExecutor();
50   private BlockingQueue<NsdServiceInfo> unresolvedServices = new ArrayBlockingQueue<>(256);
51   private AtomicBoolean isResolvingService = new AtomicBoolean(false);
52
53   private boolean isScanning = false;
54
55   public interface BorderAgentListener {
56     void onBorderAgentFound(BorderAgentInfo borderAgentInfo);
57
58     void onBorderAgentLost(String discriminator);
59   }
60
61   @RequiresPermission(permission.INTERNET)
62   public BorderAgentDiscoverer(Context context, BorderAgentListener borderAgentListener) {
63     WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
64     wifiMulticastLock = wifi.createMulticastLock("multicastLock");
65
66     nsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
67
68     this.borderAgentListener = borderAgentListener;
69   }
70
71   public void start() {
72     if (isScanning) {
73       Log.w(TAG, "the Border Agent discoverer is already running!");
74       return;
75     }
76
77     isScanning = true;
78
79     wifiMulticastLock.setReferenceCounted(true);
80     wifiMulticastLock.acquire();
81
82     startResolver();
83     nsdManager.discoverServices(
84         BorderAgentDiscoverer.SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, this);
85   }
86
87   private void startResolver() {
88     NsdManager.ResolveListener listener =
89         new NsdManager.ResolveListener() {
90           @Override
91           public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
92             Log.e(
93                 TAG,
94                 String.format(
95                     "failed to resolve service %s, error: %d, this=%s",
96                     serviceInfo.toString(), errorCode, this));
97             isResolvingService.set(false);
98           }
99
100           @Override
101           public void onServiceResolved(NsdServiceInfo serviceInfo) {
102             BorderAgentInfo borderAgent = getBorderAgentInfo(serviceInfo);
103             if (borderAgent != null) {
104               Log.d(TAG, "successfully resolved service: " + serviceInfo.toString());
105               borderAgentListener.onBorderAgentFound(borderAgent);
106             }
107             isResolvingService.set(false);
108           }
109         };
110
111     Log.d(TAG, "mDNS resolve listener is " + listener);
112
113     if (executor.isTerminated()) {
114       executor = Executors.newSingleThreadExecutor();
115     }
116
117     executor.submit(
118         () -> {
119           while (true) {
120             if (!isResolvingService.get()) {
121               NsdServiceInfo serviceInfo = unresolvedServices.take();
122
123               isResolvingService.set(true);
124               nsdManager.resolveService(serviceInfo, listener);
125             }
126           }
127         });
128   }
129
130   private void stopResolver() {
131     if (!executor.isTerminated()) {
132       executor.shutdownNow();
133     }
134     isResolvingService.set(false);
135     unresolvedServices.clear();
136   }
137
138   public void stop() {
139     if (!isScanning) {
140       Log.w(TAG, "the Border Agent discoverer has already been stopped!");
141       return;
142     }
143
144     nsdManager.stopServiceDiscovery(this);
145     stopResolver();
146
147     if (wifiMulticastLock != null) {
148       wifiMulticastLock.release();
149     }
150
151     isScanning = false;
152   }
153
154   @Override
155   public void onDiscoveryStarted(String serviceType) {
156     Log.d(TAG, "start discovering Border Agent");
157   }
158
159   @Override
160   public void onDiscoveryStopped(String serviceType) {
161     Log.d(TAG, "stop discovering Border Agent");
162   }
163
164   @Override
165   public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
166     Log.d(TAG, "a Border Agent service found");
167
168     unresolvedServices.offer(nsdServiceInfo);
169   }
170
171   @Override
172   public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
173     String discriminator = getBorderAgentDiscriminator(nsdServiceInfo);
174     if (discriminator != null) {
175       Log.d(TAG, "a Border Agent service is gone");
176       borderAgentListener.onBorderAgentLost(discriminator);
177     }
178   }
179
180   @Override
181   public void onStartDiscoveryFailed(String serviceType, int errorCode) {
182     Log.d(TAG, "start discovering Border Agent failed: " + errorCode);
183   }
184
185   @Override
186   public void onStopDiscoveryFailed(String serviceType, int errorCode) {
187     Log.d(TAG, "stop discovering Border Agent failed: " + errorCode);
188   }
189
190   private BorderAgentInfo getBorderAgentInfo(NsdServiceInfo serviceInfo) {
191     Map<String, byte[]> attrs = serviceInfo.getAttributes();
192
193     // Use the host address as default discriminator.
194     String discriminator = serviceInfo.getHost().getHostAddress();
195
196     if (attrs.containsKey(KEY_DISCRIMINATOR)) {
197       discriminator = new String(attrs.get(KEY_DISCRIMINATOR));
198     }
199
200     if (!attrs.containsKey(KEY_NETWORK_NAME) || !attrs.containsKey(KEY_EXTENDED_PAN_ID)) {
201       return null;
202     }
203
204     return new BorderAgentInfo(
205         discriminator,
206         new String(attrs.get(KEY_NETWORK_NAME)),
207         attrs.get(KEY_EXTENDED_PAN_ID),
208         serviceInfo.getHost(),
209         serviceInfo.getPort());
210   }
211
212   private String getBorderAgentDiscriminator(NsdServiceInfo serviceInfo) {
213     Map<String, byte[]> attrs = serviceInfo.getAttributes();
214
215     if (attrs.containsKey(KEY_DISCRIMINATOR)) {
216       return new String(attrs.get(KEY_DISCRIMINATOR));
217     }
218
219     if (serviceInfo.getHost() != null) {
220       // Use the host address as default discriminator.
221       return serviceInfo.getHost().getHostAddress();
222     }
223
224     return null;
225   }
226 }