I wrote a simple HTTP-Proxy for Android which is listening on the device external IP Address and a specific port (e.g. 192.168.1.20:8080).
I can access IP/port from the device itself - but any tries from external devices will never be accepted in the run-loop.
The simplified code looks like this:
public class MainActivity extends AppCompatActivity implements Runnable {
private ServerSocket socket;
private int port = 8080;
private Thread thread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
String ip = Formatter.formatIpAddress(wm.getConnectionInfo().getIpAddress());
Log.v("TAG", "http://"+ip+":"+port);
try {
Log.v("TAG", InetAddress.getByName(ip).toString());
socket = new ServerSocket(port, 5, InetAddress.getByName(ip));
socket.setSoTimeout(5000);
thread = new Thread(this);
thread.start();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void run() {
while (true) {
try {
Socket client = socket.accept();
// Never reaches this line if connected externally
Log.d("TAG", "client connected");
} catch (SocketTimeoutException e) {
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
I also added the permissions in AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
But still no luck. Anybody out there who can give advise?
Thx.
Related
I am working on an Android app that will receive multicast packets from a network that already outputs reliable multicast data on 239.255.x.x . I have verified that my device can receive multicast with another application. I'm new to Java and Android but I did confirm that my original code to gather the Multicast info worked in a java application and have been struggling to get everything working in the Android side of things. I have permissions set in the manifest,
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
the networking portion of code runs in an Asynctask as to not crash the app but my Multicast.receive() calls all result in a time out.Is there something else I am missing or something that prevents multicast sockets from working in the asynctask class?
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new getACN.execute();
}
});
}
public class getACN extends AsyncTask<Void, Void, String> {
#Override
public String doInBackground(Void... Void) {
byte[] buf = new byte[1000];
try {
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
MulticastLock multicastLock = wifiManager.createMulticastLock("sACN");
multicastLock.acquire();
InetAddress group = InetAddress.getByName("239.255.0.3");
DatagramPacket recv = new DatagramPacket(buf,buf.length);
MulticastSocket sock = new MulticastSocket(5568);
sock.joinGroup(group);
sock.setSoTimeout(1000);
sock.receive(recv);
sock.leaveGroup(group);
} catch (IOException e) {
return e.getMessage();
} catch (SecurityException e) {
return e.getMessage();
}
return Arrays.toString(buf);
}
#Override
protected void onPostExecute(String result){
TextView textView = (TextView) findViewById(R.id.text01);
textView.setText(result);
}
}
}
I'm trying to implement a class to discover services on the network.
I've tried working with Android's NSD and it does discover the services fine, but it supports only API levels 16 and up, and I can't seem to retrieve the txtRecord field within the service info (it returns null for some reason). Turns out it's a known problem...
So now I'm trying to work with jmDNS, which doesn't seem to find services at all.
here's my class (I'm working with the AndroidAnnotations framework) MDnsHelper:
#EBean
public class MDnsHelper implements ServiceListener {
public static final String SERVICE_TYPE = "_http._tcp.local";
Activity activity;
private JmDNS jmdns;
private MulticastLock multicastLock;
WifiManager wm;
InetAddress bindingAddress;
boolean isDiscovering;
public void init(Activity activity) {
this.activity = activity;
isDiscovering = false;
wm = (WifiManager) activity.getSystemService(Context.WIFI_SERVICE);
multicastLock = wm.createMulticastLock(activity.getPackageName());
multicastLock.setReferenceCounted(false);
}
#Background
public void startDiscovery() {
if (isDiscovering)
return;
System.out.println("starting...");
multicastLock.acquire();
try {
System.out.println("creating jmdns");
jmdns = JmDNS.create();
System.out.println("jmdns created");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (jmdns != null) {
jmdns.addServiceListener(SERVICE_TYPE, MDnsHelper.this);
isDiscovering = true;
System.out.println("discovering services of type: " + SERVICE_TYPE);
}
}
}
#Background
public void stopDiscovery() {
if (!isDiscovering || jmdns == null)
return;
System.out.println("stopping...");
multicastLock.release();
jmdns.removeServiceListener(SERVICE_TYPE, MDnsHelper.this);
System.out.println("listener for " + SERVICE_TYPE + " removed");
try {
jmdns.close();
isDiscovering = false;
System.out.println("jmdns closed");
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void serviceAdded(ServiceEvent service) {
System.out.println("found: " + service.getInfo().toString());
}
#Override
public void serviceRemoved(ServiceEvent service) {
System.out.println("lost: " + service.getInfo().toString());
}
#Override
public void serviceResolved(ServiceEvent service) {
System.out.println("resolved: " + service.getInfo().toString());
}
}
And in my app I call:
init(getActivity());
And then startDiscovery(); to start scanning and stopDiscovery(); to stop scanning.
And of course, I gave the app the required permissions in the manifest...
What am I missing here?
If you need me to provide additional code/info - just ask.
thanks!!
I am the author of ZeroConf Browser for Android and I use the open source Library JmDNS for all my resolving. It works great but there are a few tricks to getting it to work properly.
In your Android manifest.xml make sure you have these permissions at least.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
Before starting the activity you must allow multi-cast packets by acquiring a multicast lock.
#Override
protected void onStart() {
Log.i(TAG, "Starting ServiceActivity...");
super.onStart();
try {
Log.i(TAG, "Starting Mutlicast Lock...");
WifiManager wifi = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
// get the device ip address
final InetAddress deviceIpAddress = getDeviceIpAddress(wifi);
multicastLock = wifi.createMulticastLock(getClass().getName());
multicastLock.setReferenceCounted(true);
multicastLock.acquire();
Log.i(TAG, "Starting ZeroConf probe....");
jmdns = JmDNS.create(deviceIpAddress, HOSTNAME);
jmdns.addServiceTypeListener(this);
} catch (IOException ex) {
Log.e(TAG, ex.getMessage(), ex);
}
Log.i(TAG, "Started ZeroConf probe....");
}
private InetAddress getDeviceIpAddress(WifiManager wifi) {
InetAddress result = null;
try {
// default to Android localhost
result = InetAddress.getByName("10.0.0.2");
// figure out our wifi address, otherwise bail
WifiInfo wifiinfo = wifi.getConnectionInfo();
int intaddr = wifiinfo.getIpAddress();
byte[] byteaddr = new byte[] { (byte) (intaddr & 0xff), (byte) (intaddr >> 8 & 0xff),
(byte) (intaddr >> 16 & 0xff), (byte) (intaddr >> 24 & 0xff) };
result = InetAddress.getByAddress(byteaddr);
} catch (UnknownHostException ex) {
Log.w(TAG, String.format("getDeviceIpAddress Error: %s", ex.getMessage()));
}
return result;
}
And don't forget on stopping the scan to unlock the multicast lock and shut down JmDNS.
#Override
protected void onStop() {
Log.i(TAG, "Stopping ServiceActivity...");
super.onStop();
stopScan();
}
private static void stopScan() {
try {
if (jmdns != null) {
Log.i(TAG, "Stopping ZeroConf probe....");
jmdns.unregisterAllServices();
jmdns.close();
jmdns = null;
}
if (multicastLock != null) {
Log.i(TAG, "Releasing Mutlicast Lock...");
multicastLock.release();
multicastLock = null;
}
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
}
}
Most importanty don't use the default constructor. You must use the IP Address Constructor. I noticed in your code you are just doing JmDNS.create(). I think for some reason the only way it works on Android is to use the contructor below.
jmdns = JmDNS.create(deviceIpAddress, HOSTNAME);
If you are having this error in Android Oreo 8.x, this might help you.
First, Remember to make sure you have added these permissions into your Android manifest.xml.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
Acquire a multicast lock to allow multi-cast packets.
WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
MulticastLock lock = wifi.createMulticastLock("jmdns-multicast-lock");
lock.setReferenceCounted(true);
lock.acquire();
Now, use only this constructor JmDNS.create(InetAddress addr, String name) to create an instance of JmDNS and bind it to a specific network interface given its IP-address, like this:
try {
jmDNS = JmDNS.create(InetAddress.getByName(obtainIPv4Address(info)), HOST_NAME);
} catch (IOException e) {
LogHelper.e(TAG, "Error in JmDNS creation: " + e);
}
Finally, just make sure to call JmDNSunreisterAllServices(), and JmDNS.close() to stop JmDNS stream and releases any system resources associated with it. Also, call MulticastLock.release() to unlock the multicast lock when you're done with it.
try {
if (jmDNS != null) {
jmDNS.unregisterAllServices();
jmDNS.close();
jmDNS = null;
}
if (lock != null) {
lock.release();
lock = null;
}
} catch (Exception e) {
e.printStackTrace();
}
I am trying to make an application like "Bluetooth auto tethering" on play store. I read on the forum that Android is very security-aware and will not enable this setting without user interaction.
I need some explanations about how enable bluetooth tethering.
Thank you
I don't know if this is still an issue or not, but I found that using the connect method in the reflection call works. Working off of the code that pmont used from the link in Lorelorelore's answer:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Class<?> classBluetoothPan = null;
Constructor<?> BTPanCtor = null;
Object BTSrvInstance = null;
Method mBTPanConnect;
try {
classBluetoothPan = Class.forName("android.bluetooth.BluetoothPan");
mBTPanConnect = classBluetoothPan.getDeclaredMethod("connect", BluetoothDevice.class);
BTPanCtor = classBluetoothPan.getDeclaredConstructor(Context.class, BluetoothProfile.ServiceListener.class);
BTPanCtor.setAccessible(true);
BTSrvInstance = BTPanCtor.newInstance(myContext, new BTPanServiceListener(myContext));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
// Loop through paired devices
for (BluetoothDevice device : pairedDevices) {
try{
mBTPanConnect.invoke(BTSrvInstance, device);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Of course, this assumes that the bluetooth is enabled, and you only have one paired device. But enabling bluetooth is pretty straightforward using standard (not reflection) calls, and you can just check for the paired device that you want to connect to in the for loop. Also, don't forget the BTPanServiceListener class from the other answer as well.
Hope this helps.
The solution above required some modification in order to work for me. Specifically, the code to enable tethering needs to be in the OnServiceConnected() method. Also I have the following permissions set in the manifest:
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
Here is my solution:
public class BluetoothTethering extends ActionBarActivity {
Object instance = null;
Method setTetheringOn = null;
Method isTetheringOn = null;
Object mutex = new Object();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bluetooth_tethering);
String sClassName = "android.bluetooth.BluetoothPan";
try {
Class<?> classBluetoothPan = Class.forName(sClassName);
Constructor<?> ctor = classBluetoothPan.getDeclaredConstructor(Context.class, BluetoothProfile.ServiceListener.class);
ctor.setAccessible(true);
// Set Tethering ON
Class[] paramSet = new Class[1];
paramSet[0] = boolean.class;
synchronized (mutex) {
setTetheringOn = classBluetoothPan.getDeclaredMethod("setBluetoothTethering", paramSet);
isTetheringOn = classBluetoothPan.getDeclaredMethod("isTetheringOn", null);
instance = ctor.newInstance(getApplicationContext(), new BTPanServiceListener(getApplicationContext()));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public class BTPanServiceListener implements BluetoothProfile.ServiceListener {
private final Context context;
public BTPanServiceListener(final Context context) {
this.context = context;
}
#Override
public void onServiceConnected(final int profile,
final BluetoothProfile proxy) {
//Some code must be here or the compiler will optimize away this callback.
try {
synchronized (mutex) {
setTetheringOn.invoke(instance, true);
if ((Boolean)isTetheringOn.invoke(instance, null)) {
Toast.makeText(getApplicationContext(), "BT Tethering is on", Toast.LENGTH_LONG).show();
}
else {
Toast.makeText(getApplicationContext(), "BT Tethering is off", Toast.LENGTH_LONG).show();
}
}
}
catch (InvocationTargetException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
}
#Override
public void onServiceDisconnected(final int profile) {
}
}
}
The below code works perfectly for me
String sClassName = "android.bluetooth.BluetoothPan";
try {
Class<?> classBluetoothPan = Class.forName(sClassName);
Constructor<?> ctor = classBluetoothPan.getDeclaredConstructor(Context.class, BluetoothProfile.ServiceListener.class);
ctor.setAccessible(true);
Object instance = ctor.newInstance(getApplicationContext(), new BTPanServiceListener(getApplicationContext()));
// Set Tethering ON
Class[] paramSet = new Class[1];
paramSet[0] = boolean.class;
Method setTetheringOn = classBluetoothPan.getDeclaredMethod("setBluetoothTethering", paramSet);
setTetheringOn.invoke(instance,true);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
public class BTPanServiceListener implements BluetoothProfile.ServiceListener {
private final Context context;
public BTPanServiceListener(final Context context) {
this.context = context;
}
#Override
public void onServiceConnected(final int profile,
final BluetoothProfile proxy) {
//Some code must be here or the compiler will optimize away this callback.
Log.e("MyApp", "BTPan proxy connected");
}
#Override
public void onServiceDisconnected(final int profile) {
}
}
Here you find a similar question: Bluetooth question
Just replace "isTetheringOn" with "setBluetoothTethering" in the reflection call and pass in a boolean parameter. It should work.
I'm playing a little bit with multicast sockets. I write a server which sends a message to a android client. Until yet the client should only log the received message.
I noticed that no multicast packet are received on my device.
Here is the code for the server (runs on the pc):
public class MulticastServer{
private int port;
private boolean running = false;
private MulticastSocket serverSocket;
private InetAddress group;
private String multicastAddress = "230.192.0.11";
public MulticastServer(int port) {
super();
this.port = port;
init();
}
public MusicStreamerServer() {
this(5500);
}
private void init() {
try {
group = InetAddress.getByName(multicastAddress);
serverSocket = new MulticastSocket(port);
serverSocket.joinGroup(group);
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() throws IOException {
System.out.println("server started");
if (running)
return;
running = true;
new Thread(new Runnable() {
#Override
public void run() {
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length,
group, port);
String msg = "msg";
while (running) {
packet.setData(msg.getBytes(), 0, msg.length());
try {
serverSocket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
public void stop() throws IOException {
running = false;
} }
Here is the code for the android client:
public class MainActivity extends Activity {
private MulticastSocket socket;
private InetAddress group;
private String multicastAddress = "230.192.0.11";
private int port = 5500;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
.permitAll().build();
StrictMode.setThreadPolicy(policy);
try {
group = InetAddress.getByName(multicastAddress);
socket = new MulticastSocket(port);
socket.joinGroup(group);
socket.setBroadcast(true);
} catch (IOException e) {
e.printStackTrace();
Log.wtf("init", e.getMessage());
}
new Thread(new Runnable() {
#Override
public void run() {
WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiManager.MulticastLock multicastLock = wm
.createMulticastLock("mylock");
multicastLock.acquire();
byte[] buf = new byte[1024];
while (true) {
try {
socket.receive(packet);
Log.d("receiver","received = " + (new String(packet.getData())));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}}
I've tested the code with 2 different devices. The Nexus4 and the Nexus7 (2013) both running the latest Android.
Could anybody help me?
Thanks
I've seen that the issue is really inconsistent.
Android version: 4.2.x
On Samsung S4 active:
Multicast is working as expected.
On Samsung Note 10.1 and Nexus 4.2.3
Multicast is not working as expected.
239.x.x.x is not supported (and sadly it's the one used to multicast television...)
224.0.0.251 is working as expected.
I think they have a bug with the mask.
A multicast address is normally
|1 1 1 0| MULTICAST Address | 224.0.0.0 - 239.255.255.255
11100000.00000000.00000000.00000001 = 224.0.0.1
11101111.00000000.00000000.00000001 = 239.0.0.1
So the mask should be 224.0.0.0/4 and not 224.0.0.0/8
Does your manifest request the proper permissions?
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
Also, you may want to play with the Advanced settings in the WiFi menu on your phone, both Wi-Fi optimizations and Keep Wi-Fi on during sleep may impact your ability to do multicasts.
I'm very new with programming for android. I have two classes : main and btmanager. When i try to test my app on phone, all I get is a information that procees was killed. What am I doing wrong ?
Code implementation :
Main class :
public class MainActivity extends Activity {
BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
Btmanager manager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (bluetooth == null)
{
Toast.makeText(this, "Bluetooth is not enabled on this device", Toast.LENGTH_LONG).show();
System.exit(0);
}
}
#Override
public void onStart()
{
super.onStart();
if (!bluetooth.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 2);
}
manager.run();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
public void closeApp (View view)
{
System.exit(0);
}
}
Btmanager class :
public class Btmanager extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public static final UUID myUUID = UUID.fromString("0x1101");
BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
public Btmanager(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(myUUID);
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it will slow down the connection
bluetooth.cancelDiscovery();
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
try {
mmSocket.close();
} catch (IOException closeException) { }
return;
}
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
2 problems I see in your code:
You don't instantiate the Btmanager object, it is still null when you call run. (Will cause a NullPointerException - your app will crash).
You call the run method instead of the start method of the Btmanager. If you want the code in the run method to run in a new thread, you have to call start. Calling run will cause it to run in the same thread. This blocks your UI thread which may cause your app to crash, if it blocks for too long.
For test purporses, I don't use BTmanager class - in onStart() method I add new thread with connection implentation, but still without any results - app crash.
public void onStart()
{
super.onStart();
if (!bluetooth.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 2);
}
new Thread(new Runnable()
{
public void run()
{
try {
//Create a Socket connection: need the server's UUID number of registered
socket = device.createRfcommSocketToServiceRecord(myUUID);
socket.connect();
Log.d("EF-BTBee", "Connectted");
}
catch (IOException e)
{
Log.e("EF-BTBee", "Error : ", e);
}
}
}).start();
}