//---------------------------------------------------------------------------- // Construction //---------------------------------------------------------------------------- public TrayApplicationContext() { this.disposed = false; InitializeComponent(); this.statusText = null; this.bugBotMessageQueue = new SharedMemTaggedBlobQueue(false, TaggedBlob.BugBotMessageQueueUniquifier); this.bugBotCommandQueue = new SharedMemTaggedBlobQueue(false, TaggedBlob.BugBotCommandQueueUniquifier); this.shutdownMonitor = new ShutdownMonitor(Program.TrayUniquifier); this.shutdownMonitor.ShutdownEvent += (sender, e) => ShutdownApp(); this.threadStarter = new HandshakeThreadStarter("Swerve Tray Notification Thread", this.NotificationThreadLoop); Application.ApplicationExit += (object sender, EventArgs e) => RemoveIcon(); this.trayIcon.Visible = true; this.shutdownMonitor.StartMonitoring(); StartBotBugNotificationThread(); // Tell the service that we started try { this.bugBotCommandQueue.InitializeIfNecessary(); this.bugBotCommandQueue.Write(new TaggedBlob(TaggedBlob.TagSwerveToolsTrayStarted, new byte[0]), 100); } catch (Exception) { // ignore } }
//--------------------------------------------------------------------------------------------- // Construction // --------------------------------------------------------------------------------------------- public DeviceTracker(AndroidDebugBridge bridge) { this.bridge = bridge; this.Devices = new List<Device>(); this.serverFailedConnects = 0; this.serverRestarts = 0; this.threadStarter = new HandshakeThreadStarter("Device List Monitor", DeviceTrackingThread); }
protected virtual void Dispose(bool notFinalizer) { if (!disposed) { this.disposed = true; if (notFinalizer) { lock (this) { StopMonitoring(); } } this.shutdownRequestedEvent?.Dispose(); this.shutdownRequestedEvent = null; this.threadStarter?.Dispose(); this.threadStarter = null; } }
void ShutdownMonitorThread(HandshakeThreadStarter starter) { starter.DoHandshake(); while (!starter.StopRequested) { try { this.shutdownRequestedEvent.WaitOne(); this.ShutdownEvent?.Invoke(this, EventArgs.Empty); return; } catch (ThreadInterruptedException) { return; } } }
//----------------------------------------------------------------------------------------- // Construction //----------------------------------------------------------------------------------------- public USBMonitor(IDeviceEvents eventRaiser, ITracer tracer, IntPtr notificationHandle, bool notificationHandleIsService) { this.eventRaiser = eventRaiser; this.tracer = tracer; this.notificationHandle = notificationHandle; this.notificationHandleIsService = notificationHandleIsService; this.bugbotMessageQueue = new SharedMemTaggedBlobQueue(true, TaggedBlob.BugBotMessageQueueUniquifier); this.bugbotMessageQueue.InitializeIfNecessary(); this.bugBotCommandQueue = new SharedMemTaggedBlobQueue(true, TaggedBlob.BugBotCommandQueueUniquifier); this.bugBotCommandQueue.InitializeIfNecessary(); this.commandQueueStarter = new HandshakeThreadStarter("Command Q Listener", CommandQueueListenerThread); this.androidDeviceDatabase = new AndroidDeviceDatabase(); UpdateStatusNoRememberedConnections(); this.bugbotMessageQueue.Write(TaggedBlob.TagBugBotMessage, Resources.StartingMessage); this.deviceInterfacesOfInterest = new List<Guid>(); this.deviceNotificationHandles = new List<IntPtr>(); this.started = false; }
void CommandQueueListenerThread(HandshakeThreadStarter starter) { starter.DoHandshake(); while (!starter.StopRequested) { List<TaggedBlob> blobs = this.bugBotCommandQueue.Read(); foreach (TaggedBlob blob in blobs) { switch (blob.Tag) { case TaggedBlob.TagForgetLastConnection: this.tracer.Trace($"TagForgetLastConnection command received"); ForgetLastTCPIPDevice(); break; case TaggedBlob.TagSwerveToolsTrayStarted: UpdateTrayStatus(); break; case TaggedBlob.TagDisarmService: this.armed = false; NotifyDisarmed(); UpdateTrayStatus(); break; case TaggedBlob.TagArmService: this.armed = true; NotifyArmed(); UpdateTrayStatus(); break; } } } }
//---------------------------------------------------------------------------- // Construction //---------------------------------------------------------------------------- public ShutdownMonitor(string uniquifier) { this.shutdownRequestedEvent = new EventWaitHandle(false, EventResetMode.ManualReset, Util.GlobalName("ShutDownMonitor", uniquifier, "Event")); this.threadStarter = new HandshakeThreadStarter("ShutdownMonitorThread", ShutdownMonitorThread); this.disposed = false; }
void ShutdownMonitorThread(HandshakeThreadStarter starter) { starter.DoHandshake(); while (!starter.StopRequested) { try { this.shutdownRequestedEvent.WaitOne(); this.ShutdownEvent?.Invoke(this, EventArgs.Empty) ; return; } catch (ThreadInterruptedException) { return; } } }
// RULE: Once a stop is requested, we NEVER create a new socket. // NOTE: With that rule in place, we probably could now get by w/o doing a handshake. // The issue was that our (old) code here was racing with StopDeviceTrackign(), the // former creating a socket and the latter closing it to wake us up. If that happened // in the wrong order, we would never wake up. void DeviceTrackingThread(HandshakeThreadStarter starter) { Log.d(loggingTag, "::: DeviceTrackingThread started :::"); // Right here we know that Start() hasn't yet returned. Do the interlock and let it return. starter.DoHandshake(); // Loop until asked to stop. Do that even in the face of failures and exceptions while (!starter.StopRequested) { try { if (OpenSocketIfNecessary(starter)) { // Opened a new socket. Ask the ADB server to give us device notifications this.IsTrackingDevices = RequestDeviceNotifications(); } if (this.IsTrackingDevices) { // read the length of the incoming message int length = ReadLength(this.socketTrackDevices, new byte[4]); if (length >= 0) { // read the incoming message ProcessTrackingDevicesNotification(length); // flag the fact that we have build the list at least once. this.HasDeviceList = true; } } } catch (Exception e) { Log.w(loggingTag, $"exception in DeviceTrackingThread: {e.Message}"); this.IsTrackingDevices = false; CloseSocket(ref this.socketTrackDevices); } } Log.d(loggingTag, "::: DeviceTrackingThread stopped :::"); }
protected virtual void Dispose(bool notFromFinalizer) { if (!this.disposed) { this.disposed = true; if (notFromFinalizer) { } this.threadStarter?.Dispose(); this.threadStarter = null; } }
void NotificationThreadLoop(HandshakeThreadStarter starter) { Trace(Program.LoggingTag, "===== NotificationThreadLoop start ... "); try { // Interlock with StartNotificationThread starter.DoHandshake(); // Spin, waiting for kernel to make the section for us for (bool thrown = true; !starter.StopRequested && thrown; ) { try { thrown = false; this.bugBotMessageQueue.InitializeIfNecessary(); } catch (FileNotFoundException) { Trace(Program.LoggingTag, "service hasn't created shared mem"); thrown = true; Thread.Sleep(2000); } catch (Exception e) { Trace(Program.LoggingTag, $"exception thrown: {e}"); } } Trace(Program.LoggingTag, "===== NotificationThreadLoop listening"); while (!starter.StopRequested) { try { // Get messages from writer. This will block until there's // (probably) messages for us to read Trace(Program.LoggingTag, "waiting for message..."); List<TaggedBlob> messages = this.bugBotMessageQueue.Read(); Trace(Program.LoggingTag, "...messages received"); if (messages.Count > 0) { // Separate the messages with newlines. StringBuilder balloonText = new StringBuilder(); foreach (TaggedBlob blob in messages) { switch (blob.Tag) { case TaggedBlob.TagBugBotMessage: if (balloonText.Length > 0) balloonText.Append("\n"); balloonText.Append(blob.Message); break; case TaggedBlob.TagBugBotStatus: // Update the status text this.statusText = blob.Message; UpdateIconText(); break; } } // Display them to the user if (balloonText.Length > 0) ShowBalloon(balloonText.ToString()); } } catch (ThreadInterruptedException) { return; } } } finally { Trace(Program.LoggingTag, "===== ... NotificationThreadLoop stop"); } }
protected override void Dispose(bool notFinalizer) { if (!disposed) { this.disposed = true; if (notFinalizer) { // Called from user's code. Can / should cleanup managed objects StopBotBugNotificationThread(); this.shutdownMonitor?.StopMonitoring(); } // Called from finalizers (and user code). Avoid referencing other objects. this.trayIcon?.Dispose(); this.trayIcon = null; this.bugBotMessageQueue?.Dispose(); this.bugBotMessageQueue = null; this.bugBotCommandQueue?.Dispose(); this.bugBotCommandQueue = null; this.threadStarter?.Dispose(); this.threadStarter = null; } base.Dispose(notFinalizer); }
protected virtual void Dispose(bool notFromFinalizer) { if (!disposed) { this.disposed = true; if (notFromFinalizer) { this.bugbotMessageQueue?.Write(TaggedBlob.TagBugBotMessage, Resources.StoppingMessage); } this.bugbotMessageQueue?.Dispose(); this.bugbotMessageQueue = null; this.bugBotCommandQueue?.Dispose(); this.bugBotCommandQueue = null; this.commandQueueStarter?.Dispose(); this.commandQueueStarter = null; this.ReleaseDeviceNotificationHandles(); } }
// RULE: We NEVER create a new socket if a stop has been requested /** * Try, only once, to get a socket to the ADB server. If we can't connect, then * (perhaps) restart the server. * * @return true if we opened a *new* socket */ bool OpenSocketIfNecessary(HandshakeThreadStarter starter) { bool result = false; this.AcquireSocketLock(); try { // If we haven't a socket, try to open one if (this.socketTrackDevices == null || !this.socketTrackDevices.Connected) { CloseSocket(ref this.socketTrackDevices); this.socketTrackDevices = ConnectToServer(); // if (this.socketTrackDevices == null) { // Connect attempt failed. Restart the server if we can this.serverFailedConnects++; if (this.serverFailedConnects > 0) { this.serverRestarts++; if (starter.StopRequested) return result; this.bridge.KillServer(); // takes seconds if (starter.StopRequested) return result; this.bridge.EnsureServerStarted(); // takes seconds if (starter.StopRequested) return result; } if (this.serverRestarts > 1) { // Wait a bit before attempting another socket open this.ReleaseSocketLock(); Log.d(loggingTag, "sleeping 1s"); Thread.Sleep(1000); this.AcquireSocketLock(); } } else { result = true; Log.d(loggingTag, "Connected to adb for device monitoring"); this.serverFailedConnects = 0; this.serverRestarts = 0; } } } finally { this.ReleaseSocketLock(); } return result; }
/** * Get the stderr/stdout outputs of a process and return when the process is done. Both * <b>must</b> be read or the process will block on windows. * * @exception ArgumentNullException Thrown when one or more required arguments are null. * * @param process The process to get the ouput from. * @param errorOutput The array to store the stderr output. cannot be null. * @param stdOutput The array to store the stdout output. cannot be null. * @param waitforReaders if true, this will wait for the reader threads. * * @return the process return code. */ private static int GrabProcessOutput(Process process, List<string> errorOutput, List<string> stdOutput, bool waitforReaders) { if (errorOutput == null) throw new ArgumentNullException(nameof(errorOutput)); if (stdOutput == null) throw new ArgumentNullException(nameof(stdOutput)); // read the lines as they come. if null is returned, it's // because the process finished HandshakeThreadStarter t1 = new HandshakeThreadStarter("StdErr reader", (starter) => { // create a buffer to read the stdoutput try { using (StreamReader sr = process.StandardError) { starter.DoHandshake(); while (!starter.StopRequested && !sr.EndOfStream) { string line = sr.ReadLine(); if (!string.IsNullOrEmpty(line)) { Log.e(ADB_EXE, line); errorOutput.Add(line); } } } } catch (Exception) { // do nothing. } }); HandshakeThreadStarter t2 = new HandshakeThreadStarter("StdOut reader", (starter) => { // create a buffer to read the std output try { using (StreamReader sr = process.StandardOutput) { starter.DoHandshake(); while (!starter.StopRequested && !sr.EndOfStream) { string line = sr.ReadLine(); if (!string.IsNullOrEmpty(line)) { stdOutput.Add(line); } } } } catch (Exception) { // do nothing. } }); t1.Start(); t2.Start(); // it looks like on windows process#waitFor() can return // before the thread have filled the arrays, so we wait for both threads and the // process itself. if (waitforReaders) { try { t1.Join(); } catch (ThreadInterruptedException) { } try { t2.Join(); } catch (ThreadInterruptedException) { } } // get the return code from the process process.WaitForExit(); return process.ExitCode; }