0

I am developing a UDP chat app. All network processing is inside thread in a service. I am still getting this error message for 3.1 and 4.0 oeprating system. For versions 2.3 and below it is working fine. Question: should I create two apps, one for version 2.3 and below and another one for version 3.0 and higher? The error happens when the write(byte[] out) method is called according to LogCat.

If I disable StrictMode for ICS the app is working fine.

public class ChatService extends Service { 
    private Binder binder;
    private ComThread comThread;

    public IBinder onBind(Intent intent) {
        return binder;
    }

    public void onCreate() {
    }

    public int onStartCommand(Intent intent, int flags, int startId) {
        binder = new ChatServiceBinder();
        start();
        return super.onStartCommand(intent, flags, startId);
    }

    public synchronized void start() {
        comThread = new ComThread();
        comThread.start();
    }

    public void onDestroy() {
        stop();
    }

    public void write(byte[] out) {
        comThread.write(out);
    }

    public synchronized void stop() {
        if (comThread != null) {
            comThread.cancel();
            comThread = null;
        }
    }

    private class ComThread extends Thread {
        private static final int BCAST_PORT = 2562;
    DatagramSocket mSocket;
    InetAddress myBcastIP, myLocalIP;

    public ComThread() {

        try {
            myBcastIP = getBroadcastAddress();
            if (D)
                Log.d(TAG, "my bcast ip : " + myBcastIP);

            myLocalIP = getLocalAddress();
            if (D)
                Log.d(TAG, "my local ip : " + myLocalIP);

            mSocket = new DatagramSocket(BCAST_PORT);
            mSocket.setBroadcast(true);

        } catch (IOException e) {
            Log.e(TAG, "Could not make socket", e);
        }
    }

    public void run() {

        try {

            byte[] buf = new byte[1024];
            if (D)
                Log.d(TAG, "run(), com thread startet");
            // Listen on socket to receive messages
            while (true) {
                DatagramPacket packet = new DatagramPacket(buf, buf.length);
                mSocket.receive(packet);

                InetAddress remoteIP = packet.getAddress();
                if (remoteIP.equals(myLocalIP))
                    continue;

                String s = new String(packet.getData(), 0,
                        packet.getLength());
                if (D)
                    Log.d(TAG, "run(), " + s);

                Message msg = new Message();
                msg.obj = s;
                msg.arg1 = MessageHandler.MSG_IN;
                state.getHandler().sendMessage(msg);

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Write broadcast packet.
     */
    public void write(byte[] buffer) {
        try {
            String data = new String(buffer);
            DatagramPacket packet = new DatagramPacket(data.getBytes(),
                    data.length(), myBcastIP, BCAST_PORT);
            mSocket.send(packet);
        } catch (Exception e) {
            Log.e(TAG, "write(), Exception during write", e);
        }
    }

    /**
     * Calculate the broadcast IP we need to send the packet along.
     */
    private InetAddress getBroadcastAddress() throws IOException {
        WifiManager mWifi = (WifiManager) state
                .getSystemService(Context.WIFI_SERVICE);

        WifiInfo info = mWifi.getConnectionInfo();
        if (D)
            Log.d(TAG, "\nWiFi Status: " + info.toString());

        // DhcpInfo is a simple object for retrieving the results of a DHCP
        // request
        DhcpInfo dhcp = mWifi.getDhcpInfo();
        if (dhcp == null) {
            Log.d(TAG, "Could not get dhcp info");
            return null;
        }

        int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
        byte[] quads = new byte[4];
        for (int k = 0; k < 4; k++)
            quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);

        // Returns the InetAddress corresponding to the array of bytes.
        return InetAddress.getByAddress(quads); // The high order byte is
                                                // quads[0].
    }

    private InetAddress getLocalAddress() throws IOException {

        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface
                    .getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf
                        .getInetAddresses(); enumIpAddr.hasMoreElements();) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress()) {
                        return inetAddress;
                    }
                }
            }
        } catch (SocketException ex) {
            Log.e(TAG, ex.toString());
        }
        return null;
    }

    public void cancel() {
        try {
            mSocket.close();
        } catch (Exception e) {
            Log.e(TAG, "close() of connect socket failed", e);
        }
    }
}

public class ChatServiceBinder extends Binder {
    private ChatService service = ChatService.this;

    public ChatService getService() {
        return service;
    }
}        

}

}

Thanks.

SeanZhang2012
  • 31
  • 1
  • 5
  • 1
    If you are getting that message then it seems you aren't implementing the thread correctly. Post your code. – Squonk Jun 05 '12 at 19:43
  • 1
    You should NOT create two apps. Please. – Benoit Duffez Jun 05 '12 at 19:45
  • My current app is working fine for OS versions 2.3 and lower. To make it work under ICS I have to disable the StrictMode. The 2.3 OS doesn't recognize StrictMode and I have to go to project properties and change project build target to Android 4.0. So I have created one app with build target 2.3 and another with build target 4.0 and StrictMode disabled. What would be a right solution to this problem? Thanks. – SeanZhang2012 Jun 05 '12 at 20:03
  • 1
    @SeanZhang2012 : Did you read my comment? Post your code - it's what people here on SO do to get answers to their problems. Without code, it is almost impossible for anyone to help you. That's why after 3 hours since you asked your question, nobody has submitted a single answer. – Squonk Jun 05 '12 at 23:07
  • 1
    We'll also need to see the code for ComThread.write() (and probably also ComThread.run() if the ComThread constructor does not specify a Runnable). – Joe Jun 06 '12 at 07:32
  • I have posted the whole service class code – SeanZhang2012 Jun 06 '12 at 13:35
  • @SeanZhang2012 : **The error happens when the write(byte[] out) method is called...** - With that statement and the full `ComThread` code, it makes sense. Calling `comThread.write(out)` from `write(byte[] out)` method (on the UI thread) is done in a synchronous manner. In other words, calling `comThread.write(out)` in that way can potentially block the UI thread if the network operations take a long time. I haven't posted this as an answer as I don't know a fix - Java threading isn't my strong point. It seems to me you need a separate `Runnable` to handle sending data though. – Squonk Jun 06 '12 at 23:42
  • Your app is NOT running fine on 2.3 and lower. It is just that Google added this error for 3.0 in error to give a warning to beginners. If you get it, you are doing network on the main thread, which will result in shitty perfs for any version of any OS. – Teovald Jul 05 '13 at 23:16

1 Answers1

0

A little late, and not a super great answer, but on Android 3+ Runnable won't be interpreted as permitted unless it's inside the service (not a sub-class as you have it). I know its a limiting check given the freedom you have to create pretty much anything you want however you want, but then again UDP multicasting isn't something all Android developers mess with. Hope this helps.

dubmojo
  • 6,660
  • 8
  • 41
  • 68
  • The problem is likely much simpler than that - as Squonk sort of pointed out in a comment, calling comThread.write() from the UI thread does not result in the operation being done in the background thread, rather it results in the network operation occurring on the main UI thread which called the method. The fix is to pass a message to the thread, or start it up for this specific purpose and make the call from its run method - just calling arbitrary methods of a Thread object does not accomplish threading. – Chris Stratton Jul 05 '13 at 23:19
  • Ah! SO now truncates comments to 3 with a show/hide link. I didn't even see the last 3 comments, including Squonk's beyond his complaints of missing code. – dubmojo Jul 09 '13 at 19:09