package com.limelight.preferences; import java.net.Inet4Address; import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Collections; import java.util.concurrent.LinkedBlockingQueue; import com.limelight.computers.ComputerManagerService; import com.limelight.R; import com.limelight.utils.Dialog; import com.limelight.utils.SpinnerDialog; import com.limelight.utils.UiHelper; import android.app.Activity; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.view.KeyEvent; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.TextView; import android.widget.Toast; public class AddComputerManually extends Activity { private TextView hostText; private ComputerManagerService.ComputerManagerBinder managerBinder; private final LinkedBlockingQueue computersToAdd = new LinkedBlockingQueue<>(); private Thread addThread; private final ServiceConnection serviceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, final IBinder binder) { managerBinder = ((ComputerManagerService.ComputerManagerBinder)binder); startAddThread(); } public void onServiceDisconnected(ComponentName className) { joinAddThread(); managerBinder = null; } }; private boolean isWrongSubnetSiteLocalAddress(String address) { try { InetAddress targetAddress = InetAddress.getByName(address); if (!(targetAddress instanceof Inet4Address) || !targetAddress.isSiteLocalAddress()) { return false; } // We have a site-local address. Look for a matching local interface. for (NetworkInterface iface : Collections.list(NetworkInterface.getNetworkInterfaces())) { for (InterfaceAddress addr : iface.getInterfaceAddresses()) { if (!(addr.getAddress() instanceof Inet4Address) || !addr.getAddress().isSiteLocalAddress()) { // Skip non-site-local or non-IPv4 addresses continue; } byte[] targetAddrBytes = targetAddress.getAddress(); byte[] ifaceAddrBytes = addr.getAddress().getAddress(); // Compare prefix to ensure it's the same boolean addressMatches = true; for (int i = 0; i < addr.getNetworkPrefixLength(); i++) { if ((ifaceAddrBytes[i / 8] & (1 << (i % 8))) != (targetAddrBytes[i / 8] & (1 << (i % 8)))) { addressMatches = false; break; } } if (addressMatches) { return false; } } } // Couldn't find a matching interface return true; } catch (SocketException e) { e.printStackTrace(); return false; } catch (UnknownHostException e) { return false; } } private void doAddPc(String host) { String msg; boolean wrongSiteLocal = false; boolean success; SpinnerDialog dialog = SpinnerDialog.displayDialog(this, getResources().getString(R.string.title_add_pc), getResources().getString(R.string.msg_add_pc), false); success = managerBinder.addComputerBlocking(host, true); if (!success){ wrongSiteLocal = isWrongSubnetSiteLocalAddress(host); } dialog.dismiss(); if (wrongSiteLocal) { Dialog.displayDialog(this, getResources().getString(R.string.conn_error_title), getResources().getString(R.string.addpc_wrong_sitelocal), false); } else if (!success) { Dialog.displayDialog(this, getResources().getString(R.string.conn_error_title), getResources().getString(R.string.addpc_fail), false); } else { AddComputerManually.this.runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(AddComputerManually.this, getResources().getString(R.string.addpc_success), Toast.LENGTH_LONG).show(); if (!isFinishing()) { // Close the activity AddComputerManually.this.finish(); } } }); } } private void startAddThread() { addThread = new Thread() { @Override public void run() { while (!isInterrupted()) { String computer; try { computer = computersToAdd.take(); } catch (InterruptedException e) { return; } doAddPc(computer); } } }; addThread.setName("UI - AddComputerManually"); addThread.start(); } private void joinAddThread() { if (addThread != null) { addThread.interrupt(); try { addThread.join(); } catch (InterruptedException ignored) {} addThread = null; } } @Override protected void onStop() { super.onStop(); Dialog.closeDialogs(); SpinnerDialog.closeDialogs(this); } @Override protected void onDestroy() { super.onDestroy(); if (managerBinder != null) { joinAddThread(); unbindService(serviceConnection); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); UiHelper.setLocale(this); setContentView(R.layout.activity_add_computer_manually); UiHelper.notifyNewRootView(this); this.hostText = findViewById(R.id.hostTextView); hostText.setImeOptions(EditorInfo.IME_ACTION_DONE); hostText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) { if (actionId == EditorInfo.IME_ACTION_DONE || (keyEvent != null && keyEvent.getAction() == KeyEvent.ACTION_DOWN && keyEvent.getKeyCode() == KeyEvent.KEYCODE_ENTER)) { if (hostText.getText().length() == 0) { Toast.makeText(AddComputerManually.this, getResources().getString(R.string.addpc_enter_ip), Toast.LENGTH_LONG).show(); return true; } computersToAdd.add(hostText.getText().toString().trim()); } else if (actionId == EditorInfo.IME_ACTION_PREVIOUS) { // This is how the Fire TV dismisses the keyboard InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(hostText.getWindowToken(), 0); return false; } return false; } }); // Bind to the ComputerManager service bindService(new Intent(AddComputerManually.this, ComputerManagerService.class), serviceConnection, Service.BIND_AUTO_CREATE); } }