diff --git a/Sensus.Android.Shared/Probes/Device/AndroidProcessorUtilizationProbe.cs b/Sensus.Android.Shared/Probes/Device/AndroidProcessorUtilizationProbe.cs new file mode 100644 index 000000000..131e9a7fd --- /dev/null +++ b/Sensus.Android.Shared/Probes/Device/AndroidProcessorUtilizationProbe.cs @@ -0,0 +1,139 @@ +// Copyright 2014 The Rector & Visitors of the University of Virginia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Sensus.Probes.Device; +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Sensus.Android.Probes.Device +{ + public class AndroidProcessorUtilizationProbe : ProcessorUtilizationProbe + { + private string _cmd; + protected override async Task InitializeAsync() + { + await base.InitializeAsync(); + + int pid = global::Android.OS.Process.MyPid(); + + //run the top terminal command -n 1 (only run 1 iteration) + //-b (use batch mode) + //-q (don't show the header info) + //-p {pid} (only return for the sensus) + //-o %CPU (only return the CPU% + //this is a different version of top than most of the documentation i found. The best thing to do is to run "top --help" on the device + _cmd = $"top -n 1 -b -q -p {pid} -o %CPU";//TODO: We could get mem% or process time from this command if we wanted using the follow in cmd $"top -n 1 -b -q -p {pid} -o %CPU,%MEM,TIME+" + } + + protected override async Task StartListeningAsync() + { + while (Running) + { + await RecordUtilization(); //remove the await + await Task.Delay(MinDataStoreDelay.HasValue ? MinDataStoreDelay.Value.Milliseconds : 1000); + } + } + + protected override Task StopListeningAsync() + { + return Task.CompletedTask; + } + + //https://stackoverflow.com/questions/2467579/how-to-get-cpu-usage-statistics-on-android This shows the basic idea i used but i had to modify the command quite a bit + private async Task RecordUtilization() + { + + try + { + Java.Lang.Process p = Java.Lang.Runtime.GetRuntime().Exec(_cmd); + + + var inputVal = await ReadStream(p.InputStream); + if(inputVal == null) + { + var errorOutput = await ReadStream(p.ErrorStream); + if(string.IsNullOrWhiteSpace(errorOutput) == false) + { + throw new NotSupportedException(errorOutput); + } + else + { + throw new NotSupportedException(); + } + } + + if(double.TryParse(inputVal.Trim(), out double cpuPercent) == false) + { + throw new InvalidDataException(inputVal + " cannot be converted into a double"); + } + + await StoreDatumAsync(new ProcessorUtilizationDatum(DateTimeOffset.UtcNow, cpuPercent)); + + } + catch (Exception exc) + { + SensusServiceHelper.Get().Logger.Log("Error getting CPU Utilization. msg:"+exc.Message, LoggingLevel.Normal, GetType()); + } + } + + private async Task ReadStream(Stream s) + { + string rVal = null; + try + { + using (var streamReader = new StreamReader(s)) + { + if (streamReader.EndOfStream == false) + { + rVal = await streamReader.ReadToEndAsync(); + System.Diagnostics.Trace.Write(rVal); + } + } + } + finally + { + s.Close(); + s.Dispose(); + } + return rVal; + } + + + //The link in the issue didn't show cpu utilization but just the cpu hardware info. I left the code here if that info would would useful for you. + //https://stackoverflow.com/questions/46714396/how-to-find-cpu-load-of-any-android-device-programmatically + //private async Task GetProcessorHardwareInfo() + //{ + // try + // { + // string holder = ""; + // string[] DATA = { "/system/bin/cat", "/proc/cpuinfo" }; + // Java.Lang.ProcessBuilder processBuilder = new Java.Lang.ProcessBuilder(DATA); + // Java.Lang.Process process = processBuilder.Start(); + // using (var inputStream = process.InputStream) + // { + // using (var streamReader = new StreamReader(inputStream)) + // { + // holder = await streamReader.ReadToEndAsync(); + // } + // } + // } + // catch (Exception) + // { + // } + //} + + } +} diff --git a/Sensus.Android.Shared/Sensus.Android.Shared.projitems b/Sensus.Android.Shared/Sensus.Android.Shared.projitems index db36551fa..c23c26ada 100644 --- a/Sensus.Android.Shared/Sensus.Android.Shared.projitems +++ b/Sensus.Android.Shared/Sensus.Android.Shared.projitems @@ -11,6 +11,7 @@ + diff --git a/Sensus.Shared/Probes/Device/ProcessorUtilizationDatum.cs b/Sensus.Shared/Probes/Device/ProcessorUtilizationDatum.cs new file mode 100644 index 000000000..2353ef95d --- /dev/null +++ b/Sensus.Shared/Probes/Device/ProcessorUtilizationDatum.cs @@ -0,0 +1,68 @@ +// Copyright 2014 The Rector & Visitors of the University of Virginia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using Sensus.Anonymization; +using Sensus.Anonymization.Anonymizers; +using Sensus.Probes.User.Scripts.ProbeTriggerProperties; + +namespace Sensus.Probes.Device +{ + public class ProcessorUtilizationDatum : Datum + { + private double _cpuPercent; + + [DoubleProbeTriggerProperty] + [Anonymizable(null, typeof(DoubleRoundingTensAnonymizer), false)] + public double CpuPercent + { + get { return _cpuPercent; } + set { _cpuPercent = value; } + } + + public override string DisplayDetail + { + get { return "Cpu Utilization: " + _cpuPercent +"%"; } + } + + /// + /// Gets the string placeholder value, which is the CPU Pervent. + /// + /// The string placeholder value. + public override object StringPlaceholderValue + { + get + { + return _cpuPercent+ "%"; + } + } + + /// + /// For JSON deserialization. + /// + private ProcessorUtilizationDatum() { } + + public ProcessorUtilizationDatum(DateTimeOffset timestamp, double cpuPercent) + : base(timestamp) + { + _cpuPercent = cpuPercent; + } + + public override string ToString() + { + return base.ToString() + Environment.NewLine + + DisplayDetail; + } + } +} diff --git a/Sensus.Shared/Probes/Device/ProcessorUtilizationProbe.cs b/Sensus.Shared/Probes/Device/ProcessorUtilizationProbe.cs new file mode 100644 index 000000000..396932aed --- /dev/null +++ b/Sensus.Shared/Probes/Device/ProcessorUtilizationProbe.cs @@ -0,0 +1,105 @@ +// Copyright 2014 The Rector & Visitors of the University of Virginia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Syncfusion.SfChart.XForms; + +namespace Sensus.Probes.Device +{ + /// + /// Provides the readings. + /// + public abstract class ProcessorUtilizationProbe : ListeningProbe + { + + [JsonIgnore] + protected override bool DefaultKeepDeviceAwake + { + get + { + return true; + } + } + + [JsonIgnore] + protected override string DeviceAwakeWarning + { + get + { + return "This setting does not affect iOS. Android devices will use additional power to report all updates."; + } + } + + [JsonIgnore] + protected override string DeviceAsleepWarning + { + get + { + return "This setting does not affect iOS. Android devices will sleep and pause updates."; + } + } + + public sealed override string DisplayName + { + get { return "Processor Utilization"; } + } + + public sealed override Type DatumType + { + get { return typeof(ProcessorUtilizationDatum); } + } + + protected override async Task InitializeAsync() + { + await base.InitializeAsync(); + } + + + + + protected override ChartSeries GetChartSeries() + { + throw new NotImplementedException(); + } + + protected override ChartDataPoint GetChartDataPointFromDatum(Datum datum) + { + return new ChartDataPoint(datum.Timestamp.LocalDateTime, (datum as ProcessorUtilizationDatum).CpuPercent); + } + + protected override ChartAxis GetChartPrimaryAxis() + { + return new DateTimeAxis + { + Title = new ChartAxisTitle + { + Text = "Time" + } + }; + } + + protected override RangeAxisBase GetChartSecondaryAxis() + { + return new NumericalAxis + { + Title = new ChartAxisTitle + { + Text = "CPU Utilization (%)" + } + }; + } + } +} \ No newline at end of file diff --git a/Sensus.Shared/Sensus.Shared.projitems b/Sensus.Shared/Sensus.Shared.projitems index 044490ef8..933ef5afd 100644 --- a/Sensus.Shared/Sensus.Shared.projitems +++ b/Sensus.Shared/Sensus.Shared.projitems @@ -38,6 +38,8 @@ + +