From 523ee93d7566714ed1e4ed9ba0ac6c8a1431d4f5 Mon Sep 17 00:00:00 2001 From: Fabian Wiesel Date: Sat, 17 Feb 2024 22:10:18 +0100 Subject: [PATCH 1/5] Split-off MonitorService Same motivation as for the parent device: The monitor activity will be quickly destroyed, unless it is connected to a foreground service. By moving the thread and audio listener to a service, this should be avoided. --- app/src/main/AndroidManifest.xml | 6 +- .../childmonitor/MonitorActivity.java | 244 ++++---------- .../childmonitor/MonitorService.java | 314 ++++++++++++++++++ 3 files changed, 375 insertions(+), 189 deletions(-) create mode 100644 app/src/main/java/de/rochefort/childmonitor/MonitorService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d1c324..6c0b841 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,10 +26,15 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@android:style/Theme.Holo"> + + - \ No newline at end of file diff --git a/app/src/main/java/de/rochefort/childmonitor/MonitorActivity.java b/app/src/main/java/de/rochefort/childmonitor/MonitorActivity.java index df92769..f10b5ed 100644 --- a/app/src/main/java/de/rochefort/childmonitor/MonitorActivity.java +++ b/app/src/main/java/de/rochefort/childmonitor/MonitorActivity.java @@ -1,159 +1,76 @@ /* * This file is part of Child Monitor. - * + *

* Child Monitor is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + *

* Child Monitor is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + *

* You should have received a copy of the GNU General Public License * along with Child Monitor. If not, see . */ package de.rochefort.childmonitor; -import static de.rochefort.childmonitor.AudioCodecDefines.CODEC; - import android.app.Activity; +import android.content.ComponentName; import android.content.Context; -import android.media.AudioRecord; -import android.media.MediaRecorder; +import android.content.Intent; +import android.content.ServiceConnection; import android.net.ConnectivityManager; import android.net.LinkAddress; import android.net.Network; import android.net.NetworkInfo; -import android.net.nsd.NsdManager; -import android.net.nsd.NsdServiceInfo; -import android.os.Build; import android.os.Bundle; +import android.os.IBinder; +import android.support.v4.content.ContextCompat; import android.util.Log; import android.widget.TextView; -import java.io.IOException; -import java.io.OutputStream; import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; import java.util.ArrayList; import java.util.List; -import java.util.Objects; public class MonitorActivity extends Activity { final static String TAG = "ChildMonitor"; - - private NsdManager nsdManager; - - private NsdManager.RegistrationListener registrationListener; - - private ServerSocket currentSocket; - - private Object connectionToken; - - private int currentPort; - - private void serviceConnection(Socket socket) { - runOnUiThread(() -> { - final TextView statusText = findViewById(R.id.textStatus); - statusText.setText(R.string.streaming); - }); - - final int frequency = AudioCodecDefines.FREQUENCY; - final int channelConfiguration = AudioCodecDefines.CHANNEL_CONFIGURATION_IN; - final int audioEncoding = AudioCodecDefines.ENCODING; - - final int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding); - final AudioRecord audioRecord; - try { - audioRecord = new AudioRecord( - MediaRecorder.AudioSource.MIC, - frequency, - channelConfiguration, - audioEncoding, - bufferSize - ); - } catch (SecurityException e) { - // This should never happen, we asked for permission before - throw new RuntimeException(e); + private final ServiceConnection connection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + // This is called when the connection with the service has been + // established, giving us the service object we can use to + // interact with the service. Because we have bound to a explicit + // service that we know is running in our own process, we can + // cast its IBinder to a concrete class and directly access it. + MonitorService bs = ((MonitorService.MonitorBinder) service).getService(); + bs.setMonitorActivity(MonitorActivity.this); } - final int pcmBufferSize = bufferSize*2; - final short[] pcmBuffer = new short[pcmBufferSize]; - final byte[] ulawBuffer = new byte[pcmBufferSize]; - - try { - audioRecord.startRecording(); - final OutputStream out = socket.getOutputStream(); - - socket.setSendBufferSize(pcmBufferSize); - Log.d(TAG, "Socket send buffer size: " + socket.getSendBufferSize()); - - while (socket.isConnected() && currentSocket != null && !Thread.currentThread().isInterrupted()) { - final int read = audioRecord.read(pcmBuffer, 0, bufferSize); - int encoded = CODEC.encode(pcmBuffer, read, ulawBuffer, 0); - out.write(ulawBuffer, 0, encoded); - } - } catch (Exception e) { - Log.e(TAG, "Connection failed", e); - } finally { - audioRecord.stop(); - } - } - + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + // Because it is running in our same process, we should never + // see this happen. + } + }; + private boolean shouldUnbind; @Override protected void onCreate(Bundle savedInstanceState) { Log.i(TAG, "ChildMonitor start"); super.onCreate(savedInstanceState); setContentView(R.layout.activity_monitor); - - nsdManager = (NsdManager)this.getSystemService(Context.NSD_SERVICE); - currentPort = 10000; - currentSocket = null; - final Object currentToken = new Object(); - connectionToken = currentToken; - - new Thread(() -> { - while(Objects.equals(connectionToken, currentToken)) { - try (ServerSocket serverSocket = new ServerSocket(currentPort)) { - currentSocket = serverSocket; - // Store the chosen port. - final int localPort = serverSocket.getLocalPort(); - - // Register the service so that parent devices can - // locate the child device - registerService(localPort); - - // Wait for a parent to find us and connect - try (Socket socket = serverSocket.accept()) { - Log.i(TAG, "Connection from parent device received"); - - // We now have a client connection. - // Unregister so no other clients will - // attempt to connect - unregisterService(); - serviceConnection(socket); - } - } catch(Exception e) { - // Just in case - currentPort++; - Log.e(TAG, "Failed to open server socket. Port increased to " + currentPort, e); - } - } - }).start(); - final TextView addressText = findViewById(R.id.address); List listenAddresses = getListenAddresses(); - if(!listenAddresses.isEmpty()) { + if (!listenAddresses.isEmpty()) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < listenAddresses.size(); i++) { String listenAddress = listenAddresses.get(i); sb.append(listenAddress); - if (i != listenAddresses.size() -1) { + if (i != listenAddresses.size() - 1) { sb.append("\n\n"); } } @@ -162,10 +79,11 @@ public class MonitorActivity extends Activity { addressText.setText(R.string.notConnected); } + ensureServiceRunningAndBind(); } private List getListenAddresses() { - ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); List listenAddresses = new ArrayList<>(); if (cm != null) { for (Network network : cm.getAllNetworks()) { @@ -186,87 +104,37 @@ public class MonitorActivity extends Activity { } @Override - protected void onStop() { - Log.i(TAG, "ChildMonitor stop"); - - unregisterService(); - - connectionToken = null; - if(currentSocket != null) { - try { - currentSocket.close(); - currentSocket = null; - } catch (IOException e) { - Log.e(TAG, "Failed to close active socket on port "+currentPort); - } - } - super.onStop(); + public void onDestroy() { + doUnbindAndStopService(); + super.onDestroy(); } - private void registerService(final int port) { - final NsdServiceInfo serviceInfo = new NsdServiceInfo(); - serviceInfo.setServiceName("ChildMonitor on " + Build.MODEL); - serviceInfo.setServiceType("_childmonitor._tcp."); - serviceInfo.setPort(port); - - registrationListener = new NsdManager.RegistrationListener() { - @Override - public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { - // Save the service name. Android may have changed it in order to - // resolve a conflict, so update the name you initially requested - // with the name Android actually used. - final String serviceName = nsdServiceInfo.getServiceName(); - - Log.i(TAG, "Service name: " + serviceName); - - MonitorActivity.this.runOnUiThread(() -> { - final TextView statusText = findViewById(R.id.textStatus); - statusText.setText(R.string.waitingForParent); - - final TextView serviceText = findViewById(R.id.textService); - serviceText.setText(serviceName); - - final TextView portText = findViewById(R.id.port); - portText.setText(Integer.toString(port)); - }); - } - - @Override - public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { - // Registration failed! Put debugging code here to determine why. - Log.e(TAG, "Registration failed: " + errorCode); - } - - @Override - public void onServiceUnregistered(NsdServiceInfo arg0) { - // Service has been unregistered. This only happens when you call - // NsdManager.unregisterService() and pass in this listener. - - Log.i(TAG, "Unregistering service"); - } - - @Override - public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { - // Unregistration failed. Put debugging code here to determine why. - - Log.e(TAG, "Unregistration failed: " + errorCode); - } - }; - - nsdManager.registerService( - serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener); + void ensureServiceRunningAndBind() { + final Context context = this; + final Intent intent = new Intent(context, MonitorService.class); + ContextCompat.startForegroundService(context, intent); + // Attempts to establish a connection with the service. We use an + // explicit class name because we want a specific service + // implementation that we know will be running in our own process + // (and thus won't be supporting component replacement by other + // applications). + if (bindService(intent, connection, Context.BIND_AUTO_CREATE)) { + shouldUnbind = true; + Log.i(TAG, "Bound service"); + } else { + Log.e(TAG, "Error: The requested service doesn't " + + "exist, or this client isn't allowed access to it."); + } } - /** - * Uhregistered the service and assigns the listener - * to null. - */ - private void unregisterService() { - if(registrationListener != null) { - Log.i(TAG, "Unregistering monitoring service"); - - nsdManager.unregisterService(registrationListener); - registrationListener = null; + void doUnbindAndStopService() { + if (shouldUnbind) { + // Release information about the service's state. + unbindService(connection); + shouldUnbind = false; } + final Context context = this; + final Intent intent = new Intent(context, MonitorService.class); + context.stopService(intent); } } diff --git a/app/src/main/java/de/rochefort/childmonitor/MonitorService.java b/app/src/main/java/de/rochefort/childmonitor/MonitorService.java new file mode 100644 index 0000000..2cb8c91 --- /dev/null +++ b/app/src/main/java/de/rochefort/childmonitor/MonitorService.java @@ -0,0 +1,314 @@ +/** + * This file is part of Child Monitor. + *

+ * Child Monitor is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + *

+ * Child Monitor is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with Child Monitor. If not, see . + */ +package de.rochefort.childmonitor; + +import static de.rochefort.childmonitor.AudioCodecDefines.CODEC; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.media.AudioRecord; +import android.media.MediaRecorder; +import android.net.nsd.NsdManager; +import android.net.nsd.NsdServiceInfo; +import android.os.Binder; +import android.os.Build; +import android.os.IBinder; +import android.support.v4.app.NotificationCompat; +import android.util.Log; +import android.widget.TextView; +import android.widget.Toast; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Objects; + +public class MonitorService extends Service { + final static String TAG = "MonitorService"; + final static String CHANNEL_ID = TAG; + public static final int ID = 1338; + private final IBinder binder = new MonitorBinder(); + private NsdManager nsdManager; + private NsdManager.RegistrationListener registrationListener; + private ServerSocket currentSocket; + private Object connectionToken; + private int currentPort; + private NotificationManager notificationManager; + private Thread monitorThread; + private MonitorActivity monitorActivity; + + public void setMonitorActivity(MonitorActivity monitorActivity) { + this.monitorActivity = monitorActivity; + } + + private void serviceConnection(Socket socket) { + final MonitorActivity ma = monitorActivity; + if (ma != null) { + ma.runOnUiThread(() -> { + final TextView statusText = monitorActivity.findViewById(R.id.textStatus); + statusText.setText(R.string.streaming); + }); + } + + final int frequency = AudioCodecDefines.FREQUENCY; + final int channelConfiguration = AudioCodecDefines.CHANNEL_CONFIGURATION_IN; + final int audioEncoding = AudioCodecDefines.ENCODING; + + final int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding); + final AudioRecord audioRecord; + try { + audioRecord = new AudioRecord( + MediaRecorder.AudioSource.MIC, + frequency, + channelConfiguration, + audioEncoding, + bufferSize + ); + } catch (SecurityException e) { + // This should never happen, we asked for permission before + throw new RuntimeException(e); + } + + final int pcmBufferSize = bufferSize * 2; + final short[] pcmBuffer = new short[pcmBufferSize]; + final byte[] ulawBuffer = new byte[pcmBufferSize]; + + try { + audioRecord.startRecording(); + final OutputStream out = socket.getOutputStream(); + + socket.setSendBufferSize(pcmBufferSize); + Log.d(TAG, "Socket send buffer size: " + socket.getSendBufferSize()); + + while (socket.isConnected() && currentSocket != null && !Thread.currentThread().isInterrupted()) { + final int read = audioRecord.read(pcmBuffer, 0, bufferSize); + int encoded = CODEC.encode(pcmBuffer, read, ulawBuffer, 0); + out.write(ulawBuffer, 0, encoded); + } + } catch (Exception e) { + Log.e(TAG, "Connection failed", e); + } finally { + audioRecord.stop(); + } + } + + + @Override + public void onCreate() { + Log.i(TAG, "ChildMonitor start"); + super.onCreate(); + notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + + nsdManager = (NsdManager) this.getSystemService(Context.NSD_SERVICE); + currentPort = 10000; + currentSocket = null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.i(TAG, "Received start id " + startId + ": " + intent); + // Display a notification about us starting. We put an icon in the status bar. + createNotificationChannel(); + Notification n = buildNotification(); + startForeground(ID, n); + ensureMonitorThread(); + + return START_REDELIVER_INTENT; + } + + private void ensureMonitorThread() { + Thread mt = monitorThread; + if (mt != null && mt.isAlive()) { + return; + } + + final Object currentToken = new Object(); + connectionToken = currentToken; + + monitorThread = new Thread(() -> { + while (Objects.equals(connectionToken, currentToken)) { + try (ServerSocket serverSocket = new ServerSocket(currentPort)) { + currentSocket = serverSocket; + // Store the chosen port. + final int localPort = serverSocket.getLocalPort(); + + // Register the service so that parent devices can + // locate the child device + registerService(localPort); + + // Wait for a parent to find us and connect + try (Socket socket = serverSocket.accept()) { + Log.i(TAG, "Connection from parent device received"); + + // We now have a client connection. + // Unregister so no other clients will + // attempt to connect + unregisterService(); + serviceConnection(socket); + } + } catch (Exception e) { + // Just in case + currentPort++; + Log.e(TAG, "Failed to open server socket. Port increased to " + currentPort, e); + } + } + }); + monitorThread.start(); + } + + private void registerService(final int port) { + final NsdServiceInfo serviceInfo = new NsdServiceInfo(); + serviceInfo.setServiceName("ChildMonitor on " + Build.MODEL); + serviceInfo.setServiceType("_childmonitor._tcp."); + serviceInfo.setPort(port); + + registrationListener = new NsdManager.RegistrationListener() { + @Override + public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) { + // Save the service name. Android may have changed it in order to + // resolve a conflict, so update the name you initially requested + // with the name Android actually used. + final String serviceName = nsdServiceInfo.getServiceName(); + + Log.i(TAG, "Service name: " + serviceName); + + final MonitorActivity ma = monitorActivity; + if (ma != null) { + ma.runOnUiThread(() -> { + final TextView statusText = ma.findViewById(R.id.textStatus); + statusText.setText(R.string.waitingForParent); + + final TextView serviceText = ma.findViewById(R.id.textService); + serviceText.setText(serviceName); + + final TextView portText = ma.findViewById(R.id.port); + portText.setText(Integer.toString(port)); + }); + } + + } + + @Override + public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { + // Registration failed! Put debugging code here to determine why. + Log.e(TAG, "Registration failed: " + errorCode); + } + + @Override + public void onServiceUnregistered(NsdServiceInfo arg0) { + // Service has been unregistered. This only happens when you call + // NsdManager.unregisterService() and pass in this listener. + + Log.i(TAG, "Unregistering service"); + } + + @Override + public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { + // Unregistration failed. Put debugging code here to determine why. + + Log.e(TAG, "Unregistration failed: " + errorCode); + } + }; + + nsdManager.registerService( + serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener); + } + + /** + * Unregister the service and assigns the listener + * to null. + */ + private void unregisterService() { + if (registrationListener != null) { + Log.i(TAG, "Unregistering monitoring service"); + + nsdManager.unregisterService(registrationListener); + registrationListener = null; + } + } + + private void createNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel serviceChannel = new NotificationChannel( + CHANNEL_ID, + "Foreground Service Channel", + NotificationManager.IMPORTANCE_DEFAULT + ); + notificationManager.createNotificationChannel(serviceChannel); + } + } + + private Notification buildNotification() { + CharSequence text = "Child Device"; + // Set the info for the views that show in the notification panel. + NotificationCompat.Builder b = new NotificationCompat.Builder(this, CHANNEL_ID); + b.setSmallIcon(R.drawable.listening_notification) // the status icon + .setOngoing(true) + .setTicker(text) // the status text + .setContentTitle(text); // the label of the entry + return b.build(); + } + + + @Override + public void onDestroy() { + Thread mt = monitorThread; + if (mt != null) { + mt.interrupt(); + monitorThread = null; + } + + unregisterService(); + + connectionToken = null; + if (currentSocket != null) { + try { + currentSocket.close(); + currentSocket = null; + } catch (IOException e) { + Log.e(TAG, "Failed to close active socket on port " + currentPort); + } + } + + // Cancel the persistent notification. + int NOTIFICATION = R.string.listening; + notificationManager.cancel(NOTIFICATION); + + stopForeground(true); + // Tell the user we stopped. + Toast.makeText(this, R.string.stopped, Toast.LENGTH_SHORT).show(); + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) { + return binder; + } + + public class MonitorBinder extends Binder { + MonitorService getService() { + return MonitorService.this; + } + } + +} From 77c8456d4535f18b83a1e2539493d90fba77be05 Mon Sep 17 00:00:00 2001 From: Fabian Wiesel Date: Tue, 20 Feb 2024 22:12:32 +0100 Subject: [PATCH 2/5] Only log the exception when unexpected The socket is closed in another thread to stop the transmission. That causes an exception, but that is the expected behaviour, so do not log it. --- .../java/de/rochefort/childmonitor/MonitorService.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/de/rochefort/childmonitor/MonitorService.java b/app/src/main/java/de/rochefort/childmonitor/MonitorService.java index 2cb8c91..c632e06 100644 --- a/app/src/main/java/de/rochefort/childmonitor/MonitorService.java +++ b/app/src/main/java/de/rochefort/childmonitor/MonitorService.java @@ -167,9 +167,11 @@ public class MonitorService extends Service { serviceConnection(socket); } } catch (Exception e) { - // Just in case - currentPort++; - Log.e(TAG, "Failed to open server socket. Port increased to " + currentPort, e); + if (Objects.equals(connectionToken, currentToken)) { + // Just in case + currentPort++; + Log.e(TAG, "Failed to open server socket. Port increased to " + currentPort, e); + } } } }); From 1077b2e5276a3a7c9ee4a4408af4591d91ef0ae4 Mon Sep 17 00:00:00 2001 From: edr Date: Wed, 21 Feb 2024 19:27:01 +0100 Subject: [PATCH 3/5] Minor fixup in MonitorService.java --- .../childmonitor/MonitorService.java | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/de/rochefort/childmonitor/MonitorService.java b/app/src/main/java/de/rochefort/childmonitor/MonitorService.java index c632e06..9ac1642 100644 --- a/app/src/main/java/de/rochefort/childmonitor/MonitorService.java +++ b/app/src/main/java/de/rochefort/childmonitor/MonitorService.java @@ -1,16 +1,16 @@ -/** +/* * This file is part of Child Monitor. - *

+ * * Child Monitor is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - *

+ * * Child Monitor is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - *

+ * * You should have received a copy of the GNU General Public License * along with Child Monitor. If not, see . */ @@ -21,7 +21,6 @@ import static de.rochefort.childmonitor.AudioCodecDefines.CODEC; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; -import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; @@ -145,7 +144,7 @@ public class MonitorService extends Service { final Object currentToken = new Object(); connectionToken = currentToken; - monitorThread = new Thread(() -> { + mt = new Thread(() -> { while (Objects.equals(connectionToken, currentToken)) { try (ServerSocket serverSocket = new ServerSocket(currentPort)) { currentSocket = serverSocket; @@ -175,7 +174,8 @@ public class MonitorService extends Service { } } }); - monitorThread.start(); + monitorThread = mt; + mt.start(); } private void registerService(final int port) { @@ -236,15 +236,12 @@ public class MonitorService extends Service { serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener); } - /** - * Unregister the service and assigns the listener - * to null. - */ private void unregisterService() { - if (registrationListener != null) { + NsdManager.RegistrationListener currentListener = registrationListener; + if (currentListener != null) { Log.i(TAG, "Unregistering monitoring service"); - nsdManager.unregisterService(registrationListener); + nsdManager.unregisterService(currentListener); registrationListener = null; } } From 30a0c268264533a8ddfaa61ca5ddafe644615fdb Mon Sep 17 00:00:00 2001 From: edr Date: Wed, 21 Feb 2024 19:27:55 +0100 Subject: [PATCH 4/5] Minor fixup MonitorActivity.java --- .../de/rochefort/childmonitor/MonitorActivity.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/de/rochefort/childmonitor/MonitorActivity.java b/app/src/main/java/de/rochefort/childmonitor/MonitorActivity.java index f10b5ed..625697a 100644 --- a/app/src/main/java/de/rochefort/childmonitor/MonitorActivity.java +++ b/app/src/main/java/de/rochefort/childmonitor/MonitorActivity.java @@ -1,16 +1,16 @@ /* * This file is part of Child Monitor. - *

+ * * Child Monitor is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - *

+ * * Child Monitor is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - *

+ * * You should have received a copy of the GNU General Public License * along with Child Monitor. If not, see . */ @@ -30,6 +30,7 @@ import android.os.IBinder; import android.support.v4.content.ContextCompat; import android.util.Log; import android.widget.TextView; +import android.widget.Toast; import java.net.InetAddress; import java.util.ArrayList; @@ -41,7 +42,7 @@ public class MonitorActivity extends Activity { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the service object we can use to - // interact with the service. Because we have bound to a explicit + // interact with the service. Because we have bound to an explicit // service that we know is running in our own process, we can // cast its IBinder to a concrete class and directly access it. MonitorService bs = ((MonitorService.MonitorBinder) service).getService(); @@ -53,6 +54,8 @@ public class MonitorActivity extends Activity { // unexpectedly disconnected -- that is, its process crashed. // Because it is running in our same process, we should never // see this happen. + Toast.makeText(MonitorActivity.this, R.string.disconnected, + Toast.LENGTH_SHORT).show(); } }; private boolean shouldUnbind; @@ -120,7 +123,7 @@ public class MonitorActivity extends Activity { // applications). if (bindService(intent, connection, Context.BIND_AUTO_CREATE)) { shouldUnbind = true; - Log.i(TAG, "Bound service"); + Log.i(TAG, "Bound monitor service"); } else { Log.e(TAG, "Error: The requested service doesn't " + "exist, or this client isn't allowed access to it."); From 9526f23c0cbe0dfd4a00da3366250a9d724a1654 Mon Sep 17 00:00:00 2001 From: edr Date: Wed, 21 Feb 2024 19:28:55 +0100 Subject: [PATCH 5/5] More specific log message in ListenActivity.java --- app/src/main/java/de/rochefort/childmonitor/ListenActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/de/rochefort/childmonitor/ListenActivity.java b/app/src/main/java/de/rochefort/childmonitor/ListenActivity.java index d9edd31..557e823 100644 --- a/app/src/main/java/de/rochefort/childmonitor/ListenActivity.java +++ b/app/src/main/java/de/rochefort/childmonitor/ListenActivity.java @@ -82,7 +82,7 @@ public class ListenActivity extends Activity { // applications). if (bindService(intent, connection, Context.BIND_AUTO_CREATE)) { shouldUnbind = true; - Log.i(TAG, "Bound service"); + Log.i(TAG, "Bound listen service"); } else { Log.e(TAG, "Error: The requested service doesn't " + "exist, or this client isn't allowed access to it.");