// Path: relay-java/src/main/java/com/genymobile/gnirehtet/relay/Log.java
// public final class Log {
// 
//     enum Level {
//         VERBOSE("V"),
//         DEBUG("D"),
//         INFO("I"),
//         WARNING("W"),
//         ERROR("E");
// 
//         private final String id;
// 
//         Level(String id) {
//             this.id = id;
//         }
//     }
// 
//     private static Level threshold = Level.INFO;
// 
//     private static final DateFormat FORMAT = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss.SSS");
//     private static final Date DATE = new Date();
// 
//     private Log() {
//         // not instantiable
//     }
// 
//     public static Level getThreshold() {
//         return threshold;
//     }
// 
//     public static void setThreshold(Level threshold) {
//         Log.threshold = threshold;
//     }
// 
//     public static boolean isEnabled(Level level) {
//         return level.ordinal() >= threshold.ordinal();
//     }
// 
//     public static boolean isVerboseEnabled() {
//         return isEnabled(Level.VERBOSE);
//     }
// 
//     public static boolean isDebugEnabled() {
//         return isEnabled(Level.DEBUG);
//     }
// 
//     public static boolean isInfoEnabled() {
//         return isEnabled(Level.INFO);
//     }
// 
//     public static boolean isWarningEnabled() {
//         return isEnabled(Level.WARNING);
//     }
// 
//     public static boolean isErrorEnabled() {
//         return isEnabled(Level.ERROR);
//     }
// 
//     private static String getDate() {
//         DATE.setTime(System.currentTimeMillis());
//         return FORMAT.format(DATE);
//     }
// 
//     private static String format(Level level, String tag, String message) {
//         return getDate() + " " + level.id + " " + tag + ": " + message;
//     }
// 
//     private static void l(Level level, PrintStream stream, String tag, String message, Throwable e) {
//         if (isEnabled(level)) {
//             stream.println(format(level, tag, message));
//             if (e != null) {
//                 e.printStackTrace();
//             }
//         }
//     }
// 
//     public static void v(String tag, String message, Throwable e) {
//         l(Level.VERBOSE, System.out, tag, message, e);
//     }
// 
//     public static void v(String tag, String message) {
//         v(tag, message, null);
//     }
// 
//     public static void d(String tag, String message, Throwable e) {
//         l(Level.DEBUG, System.out, tag, message, e);
//     }
// 
//     public static void d(String tag, String message) {
//         d(tag, message, null);
//     }
// 
//     public static void i(String tag, String message, Throwable e) {
//         l(Level.INFO, System.out, tag, message, e);
//     }
// 
//     public static void i(String tag, String message) {
//         i(tag, message, null);
//     }
// 
//     public static void w(String tag, String message, Throwable e) {
//         l(Level.WARNING, System.out, tag, message, e);
//     }
// 
//     public static void w(String tag, String message) {
//         w(tag, message, null);
//     }
// 
//     public static void e(String tag, String message, Throwable e) {
//         l(Level.ERROR, System.err, tag, message, e);
//     }
// 
//     public static void e(String tag, String message) {
//         e(tag, message, null);
//     }
// }


import com.genymobile.gnirehtet.relay.Log;
import java.io.EOFException;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/*
 * Copyright (C) 2017 Genymobile
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.genymobile.gnirehtet;



public class AdbMonitor {

    public interface AdbDevicesCallback {
        void onNewDeviceConnected(String serial);
    }

    private static final String TAG = AdbMonitor.class.getSimpleName();
    private static final int ADBD_PORT = 5037;

    private static final String TRACK_DEVICES_REQUEST = "0012host:track-devices";
    private static final int BUFFER_SIZE = 1024;
    private static final int LENGTH_FIELD_SIZE = 4;
    private static final int OKAY_SIZE = 4;
    private static final long RETRY_DELAY_ADB_DAEMON_OK = 1000;
    private static final long RETRY_DELAY_ADB_DAEMON_KO = 5000;

    private List<String> connectedDevices = new ArrayList<>();

    private AdbDevicesCallback callback;

    private static final byte[] BUFFER = new byte[BUFFER_SIZE]; // used only locally to avoid allocations, so static is ok
    private final ByteBuffer socketBuffer = ByteBuffer.allocate(BUFFER_SIZE);

    public AdbMonitor(AdbDevicesCallback callback) {
        this.callback = callback;
    }

    public void monitor() {
        while (true) {
            try {
                trackDevices();
            } catch (Exception e) {
// Path: relay-java/src/main/java/com/genymobile/gnirehtet/relay/Log.java
// public final class Log {
// 
//     enum Level {
//         VERBOSE("V"),
//         DEBUG("D"),
//         INFO("I"),
//         WARNING("W"),
//         ERROR("E");
// 
//         private final String id;
// 
//         Level(String id) {
//             this.id = id;
//         }
//     }
// 
//     private static Level threshold = Level.INFO;
// 
//     private static final DateFormat FORMAT = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss.SSS");
//     private static final Date DATE = new Date();
// 
//     private Log() {
//         // not instantiable
//     }
// 
//     public static Level getThreshold() {
//         return threshold;
//     }
// 
//     public static void setThreshold(Level threshold) {
//         Log.threshold = threshold;
//     }
// 
//     public static boolean isEnabled(Level level) {
//         return level.ordinal() >= threshold.ordinal();
//     }
// 
//     public static boolean isVerboseEnabled() {
//         return isEnabled(Level.VERBOSE);
//     }
// 
//     public static boolean isDebugEnabled() {
//         return isEnabled(Level.DEBUG);
//     }
// 
//     public static boolean isInfoEnabled() {
//         return isEnabled(Level.INFO);
//     }
// 
//     public static boolean isWarningEnabled() {
//         return isEnabled(Level.WARNING);
//     }
// 
//     public static boolean isErrorEnabled() {
//         return isEnabled(Level.ERROR);
//     }
// 
//     private static String getDate() {
//         DATE.setTime(System.currentTimeMillis());
//         return FORMAT.format(DATE);
//     }
// 
//     private static String format(Level level, String tag, String message) {
//         return getDate() + " " + level.id + " " + tag + ": " + message;
//     }
// 
//     private static void l(Level level, PrintStream stream, String tag, String message, Throwable e) {
//         if (isEnabled(level)) {
//             stream.println(format(level, tag, message));
//             if (e != null) {
//                 e.printStackTrace();
//             }
//         }
//     }
// 
//     public static void v(String tag, String message, Throwable e) {
//         l(Level.VERBOSE, System.out, tag, message, e);
//     }
// 
//     public static void v(String tag, String message) {
//         v(tag, message, null);
//     }
// 
//     public static void d(String tag, String message, Throwable e) {
//         l(Level.DEBUG, System.out, tag, message, e);
//     }
// 
//     public static void d(String tag, String message) {
//         d(tag, message, null);
//     }
// 
//     public static void i(String tag, String message, Throwable e) {
//         l(Level.INFO, System.out, tag, message, e);
//     }
// 
//     public static void i(String tag, String message) {
//         i(tag, message, null);
//     }
// 
//     public static void w(String tag, String message, Throwable e) {
//         l(Level.WARNING, System.out, tag, message, e);
//     }
// 
//     public static void w(String tag, String message) {
//         w(tag, message, null);
//     }
// 
//     public static void e(String tag, String message, Throwable e) {
//         l(Level.ERROR, System.err, tag, message, e);
//     }
// 
//     public static void e(String tag, String message) {
//         e(tag, message, null);
//     }
// }


// Path: relay-java/src/main/java/com/genymobile/gnirehtet/AdbMonitor.java
import com.genymobile.gnirehtet.relay.Log;
import java.io.EOFException;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/*
 * Copyright (C) 2017 Genymobile
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.genymobile.gnirehtet;



public class AdbMonitor {

    public interface AdbDevicesCallback {
        void onNewDeviceConnected(String serial);
    }

    private static final String TAG = AdbMonitor.class.getSimpleName();
    private static final int ADBD_PORT = 5037;

    private static final String TRACK_DEVICES_REQUEST = "0012host:track-devices";
    private static final int BUFFER_SIZE = 1024;
    private static final int LENGTH_FIELD_SIZE = 4;
    private static final int OKAY_SIZE = 4;
    private static final long RETRY_DELAY_ADB_DAEMON_OK = 1000;
    private static final long RETRY_DELAY_ADB_DAEMON_KO = 5000;

    private List<String> connectedDevices = new ArrayList<>();

    private AdbDevicesCallback callback;

    private static final byte[] BUFFER = new byte[BUFFER_SIZE]; // used only locally to avoid allocations, so static is ok
    private final ByteBuffer socketBuffer = ByteBuffer.allocate(BUFFER_SIZE);

    public AdbMonitor(AdbDevicesCallback callback) {
        this.callback = callback;
    }

    public void monitor() {
        while (true) {
            try {
                trackDevices();
            } catch (Exception e) {
                Log.e(TAG, "Failed to monitor adb devices", e);