]> git.basschouten.com Git - openhab-addons.git/blob
d5aad994bb46ada083d106f5204d28040b708eaf
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.androiddebugbridge.internal;
14
15 import static org.openhab.binding.androiddebugbridge.internal.AndroidDebugBridgeBindingConstants.*;
16
17 import java.io.IOException;
18 import java.net.Inet4Address;
19 import java.net.InetAddress;
20 import java.net.NetworkInterface;
21 import java.net.SocketException;
22 import java.util.*;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.TimeoutException;
25 import java.util.function.Function;
26 import java.util.stream.Collectors;
27
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.core.config.discovery.AbstractDiscoveryService;
31 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
32 import org.openhab.core.config.discovery.DiscoveryService;
33 import org.openhab.core.thing.Thing;
34 import org.openhab.core.thing.ThingUID;
35 import org.osgi.service.cm.Configuration;
36 import org.osgi.service.cm.ConfigurationAdmin;
37 import org.osgi.service.component.annotations.Activate;
38 import org.osgi.service.component.annotations.Component;
39 import org.osgi.service.component.annotations.Reference;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * The {@link AndroidDebugBridgeDiscoveryService} discover Android ADB Instances in the network.
45  *
46  * @author Miguel Alvarez - Initial contribution
47  */
48 @NonNullByDefault
49 @Component(service = DiscoveryService.class, configurationPid = "discovery.androiddebugbridge")
50 public class AndroidDebugBridgeDiscoveryService extends AbstractDiscoveryService {
51     static final int TIMEOUT_MS = 60000;
52     private static final long DISCOVERY_RESULT_TTL_SEC = 300;
53     public static final String LOCAL_INTERFACE_IP = "127.0.0.1";
54     public static final int MAX_RETRIES = 2;
55     private final Logger logger = LoggerFactory.getLogger(AndroidDebugBridgeDiscoveryService.class);
56     private final ConfigurationAdmin admin;
57     private boolean discoveryRunning = false;
58
59     @Activate
60     public AndroidDebugBridgeDiscoveryService(@Reference ConfigurationAdmin admin) {
61         super(SUPPORTED_THING_TYPES, TIMEOUT_MS, false);
62         this.admin = admin;
63     }
64
65     @Override
66     protected void startScan() {
67         logger.debug("scan started: searching android devices");
68         discoveryRunning = true;
69         Enumeration<NetworkInterface> nets;
70         AndroidDebugBridgeBindingConfiguration configuration = getConfig();
71         if (configuration == null) {
72             return;
73         }
74         try {
75             nets = NetworkInterface.getNetworkInterfaces();
76             for (NetworkInterface netint : Collections.list(nets)) {
77                 Enumeration<InetAddress> inetAddresses = netint.getInetAddresses();
78                 for (InetAddress inetAddress : Collections.list(inetAddresses)) {
79                     if (!discoveryRunning) {
80                         break;
81                     }
82                     if (!(inetAddress instanceof Inet4Address)
83                             || inetAddress.getHostAddress().equals(LOCAL_INTERFACE_IP)) {
84                         continue;
85                     }
86                     String[] ipParts = inetAddress.getHostAddress().split("\\.");
87                     for (int i = configuration.discoveryIpRangeMin; i <= configuration.discoveryIpRangeMax; i++) {
88                         if (!discoveryRunning) {
89                             break;
90                         }
91                         ipParts[3] = Integer.toString(i);
92                         String currentIp = String.join(".", ipParts);
93                         try {
94                             var currentAddress = InetAddress.getByName(currentIp);
95                             logger.debug("address: {}", currentIp);
96                             if (currentAddress.isReachable(configuration.discoveryReachableMs)) {
97                                 logger.debug("Reachable ip: {}", currentIp);
98                                 int retries = 0;
99                                 while (retries < MAX_RETRIES) {
100                                     try {
101                                         discoverWithADB(currentIp, configuration.discoveryPort);
102                                     } catch (AndroidDebugBridgeDeviceReadException | TimeoutException e) {
103                                         retries++;
104                                         if (retries < MAX_RETRIES) {
105                                             logger.debug("retrying - pending {}", MAX_RETRIES - retries);
106                                             continue;
107                                         }
108                                         throw e;
109                                     }
110                                     break;
111                                 }
112                             }
113                         } catch (IOException | AndroidDebugBridgeDeviceException | AndroidDebugBridgeDeviceReadException
114                                 | TimeoutException | ExecutionException e) {
115                             logger.debug("Error connecting to device at {}: {}", currentIp, e.getMessage());
116                         }
117                     }
118                 }
119             }
120         } catch (SocketException | InterruptedException e) {
121             logger.warn("Error while discovering: {}", e.getMessage());
122         }
123     }
124
125     private void discoverWithADB(String ip, int port) throws InterruptedException, AndroidDebugBridgeDeviceException,
126             AndroidDebugBridgeDeviceReadException, TimeoutException, ExecutionException {
127         var device = new AndroidDebugBridgeDevice(scheduler);
128         device.configure(ip, port, 10);
129         try {
130             device.connect();
131             logger.debug("connected adb at {}:{}", ip, port);
132             String serialNo = device.getSerialNo();
133             String model = device.getModel();
134             String androidVersion = device.getAndroidVersion();
135             String brand = device.getBrand();
136             logger.debug("discovered: {} - {} - {} - {}", model, serialNo, androidVersion, brand);
137             onDiscoverResult(serialNo, ip, port, model, androidVersion, brand);
138         } finally {
139             device.disconnect();
140         }
141     }
142
143     @Override
144     protected void stopScan() {
145         super.stopScan();
146         discoveryRunning = false;
147         logger.debug("scan stopped");
148     }
149
150     private void onDiscoverResult(String serialNo, String ip, int port, String model, String androidVersion,
151             String brand) {
152         Map<String, Object> properties = new HashMap<>();
153         properties.put(Thing.PROPERTY_SERIAL_NUMBER, serialNo);
154         properties.put(PARAMETER_IP, ip);
155         properties.put(PARAMETER_PORT, port);
156         properties.put(Thing.PROPERTY_MODEL_ID, model);
157         properties.put(Thing.PROPERTY_VENDOR, brand);
158         properties.put(Thing.PROPERTY_FIRMWARE_VERSION, androidVersion);
159         thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_ANDROID_DEVICE, serialNo))
160                 .withTTL(DISCOVERY_RESULT_TTL_SEC).withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER)
161                 .withProperties(properties).withLabel(String.format("%s (%s)", model, serialNo)).build());
162     }
163
164     private @Nullable AndroidDebugBridgeBindingConfiguration getConfig() {
165         try {
166             Configuration configOnline = admin.getConfiguration(BINDING_CONFIGURATION_PID, null);
167             if (configOnline != null) {
168                 Dictionary<String, Object> props = configOnline.getProperties();
169                 if (props != null) {
170                     Map<String, Object> propMap = Collections.list(props.keys()).stream()
171                             .collect(Collectors.toMap(Function.identity(), props::get));
172                     return new org.openhab.core.config.core.Configuration(propMap)
173                             .as(AndroidDebugBridgeBindingConfiguration.class);
174                 }
175             }
176         } catch (IOException e) {
177             logger.warn("Unable to read configuration: {}", e.getMessage());
178         }
179         return null;
180     }
181 }