B4A Library OS metrics: MSOS lib and OSStats and GetForegroundApp

thedesolatesoul

Expert
Licensed User
MSOS Library

This library contains many OS functions from the Activity Manager.
This library is constantly evolving.
It consists of 2 librarys now. MSOS is java code, OSStats is B4A code. Both are packed jars.

Requirements:
At the moment getSystemTotalMemorySize works only on API 16+ thanks to google for implementing incomplete API. Courtesy testing (read bashing) of NJDude.

MSOS
Author:
thedesolatesoul
Version: 0.01
  • MSOS
    Fields:
    • IMPORTANCE_FOREGROUND As Int
    • IMPORTANCE_VISIBLE As Int
    Methods:
    • getForegroundApp (pImportance As Int) As List
    • getForegroundTask As List
    • getFormattedSystemAvailabeMemorySize As String
    • getFormattedSystemTotalMemorySize As String
    • getProcessMemoryInfo (pid As Int) As Int
    • getSystemAvailabeMemorySize As Long
    • getSystemTotalMemorySize As Long
    Permissions:
    • android.permission.GET_TASKS
    Properties:
    • NumCores As Int [read only]
      Gets the number of cores available in this device, across all processors.
      Requires: Ability to peruse the filesystem at "/sys/devices/system/cpu"
OSStats
  • Class_Globals
    Class module
  • EndStats
    Stop collecting the stats
    Call this in Activity_Pause
  • Initialize (Interval As Int, BufferSize As Int, Module As Object, EventName As String)
    Initializes the object.
    Interval is the gap to keep between capturing stats
    Buffer size is size of buffer to store stats (for graphing etc)
    Module is the parent object where the events will be delivered
    Eventname is th prefix of the events raised
  • StartStats
    Start collecting stats
    The eventname_Update(CpuEfficientcy() as Float,RAMUsage as Float) will be raised
  • getNumCores As Int
    Returns the number of CPU cores


It calculated the CPU Usage and Memory Usage.
You can start/stop tracking the OSStats.
This is stored in a buffer for graphing/smoothing etc.

I am trying to figure out out how to get which app is in the foreground or is visible.
This is so that I can use either a transparent activity or a service, and find out which app is on top.

//Ignore
getRunningApps: Uses this to find which apps have IMPORTANCE_FOREGROUND or IMPORTANCE_VISIBLE set
getRunningTasks: Uses this to find running tasks. tasks(0) will be the most recent one.
You can run these in a service and see what values they return.
I am still not sure which method is more accurate depending on what a 'task' and what an 'app' is.
For instance the last 'task' may have ended itself, whereas IMPORTANCE_VISIBLE will return whatever the user is viewing however this returns a number of items.
If someone could test this, and figure out what is doing what it would be great!
//
 

Attachments

Last edited:

bsnqt

Active Member
Licensed User
Thanks for the library.
I try to run the sample from NJDude but got error...
Is this related to what thedesolatesould mentioned "getSystemTotalMemorySize works only on API 16+"?
My test device is Xperia S (Android 4.1.2)
java.lang.NoSuchFieldError: android.app.ActivityManager$MemoryInfo.totalMem
at com.maximussoft.msos.MSOS.getSystemTotalMemorySize(MSOS.java:127)
at b4a.example.osstats._tmrstats_tick(osstats.java:363)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:170)
at anywheresoftware.b4a.objects.Timer$TickTack.run(Timer.java:105)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4441)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:823)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:590)
at dalvik.system.NativeStart.main(Native Method)
When I run the sample, it just stopped with error "Unfortunately, MSOS Example has stopped".
 

thedesolatesoul

Expert
Licensed User
I have 4.1.1 and it is working for me.
API16 = 4.1 , I am not sure how it doesnt work on your 4.1.2
Nevertheless I am implementing an alternative method. Initially I was using the API version to determine which method to use, but may be I shall use try/catch and catch the NoSuchField exception and then use the alternate method (which may be blocked by Google in the future!)
 

thedesolatesoul

Expert
Licensed User
I have update the library to be working with API16 and lesser too.
getSystemTotalMemorySize should work now.
It would be great if someone could test it.
Thanks
 

NJDude

Expert
Licensed User
Excellent!, it seems to work on all versions, I tried even on Froyo and it works, however, the RAM usage on older versions gives the wrong results, see THIS
 

cambopad

Active Member
Licensed User
@thedesolatesoul, @NJDude , and @Erel


How can I use this in a service? I tried to use it in a service, but MSOS seems to be an activity object. As a result I got the following message:

B4X:
Error description: Cannot access activity object from sub Process_Globals.
Occurred on line: 10
Dim xmsos As MSOS
Word: msos
Does anyone know how to fix this and get it working in a service? I need this feature for my upcoming project :(


Thanks you!
 

thedesolatesoul

Expert
Licensed User
It doesnt need to be an ActivityObject. I have definitely used it in a service.
I might fix it one day.
Although be wary as most of this API is deprecated by Google in Android5, so I dont see a lot of point in putting any effort into this.
 

cambopad

Active Member
Licensed User
It doesnt need to be an ActivityObject. I have definitely used it in a service.
I might fix it one day.
Although be wary as most of this API is deprecated by Google in Android5, so I dont see a lot of point in putting any effort into this.
Can you provide sample code to use it in a service? Or does the lib need to be fixed by you first before I can use it in a service?
 

thedesolatesoul

Expert
Licensed User
@thedesolatesoul Thanks you! But are these two features also deprecated?

  • getForegroundApp
  • getForegroundTask
I really need this two features :(
Yes they are deprecated:
http://developer.android.com/reference/android/app/ActivityManager.html#getRecentTasks(int, int)
http://developer.android.com/reference/android/app/ActivityManager.html#getRunningTasks(int)

Can you provide sample code to use it in a service? Or does the lib need to be fixed by you first before I can use it in a service?
I would need to fix the lib.
Try the one attached.
 

Attachments

G-ShadoW

Active Member
Licensed User
Android 5.1.1 not working

B4X:
** Activity (main) Create, isFirst = true **
main_activity_create (B4A line: 68)
xOSStats.Initialize(400, 50, Me, "myStats")
java.lang.IncompatibleClassChangeError: The method 'int com.maximussoft.msos.MSOS.getNumCores()' was expected to be of type virtual but instead was found to be of type static (declaration of 'java.lang.reflect.ArtMethod' appears in /system/framework/core-libart.jar)
    at b4a.example.osstats._initialize(osstats.java:208)
    at njdude.msos.sample.main._activity_create(main.java:396)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:187)
    at njdude.msos.sample.main.afterFirstLayout(main.java:100)
    at njdude.msos.sample.main.access$100(main.java:17)
    at njdude.msos.sample.main$WaitForLayout.run(main.java:78)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:135)
    at android.app.ActivityThread.main(ActivityThread.java:5289)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)
** Activity (main) Resume **
WakeLock already held.
---- 1 ----
---- 2 ----
---- 3 ----
---- 4 ----
---- In onAdFailedToLoad ----Server Message: no results. Try again in 10 seconds
 

Rick Harris

Well-Known Member
Licensed User
Android 5.1.1 not working

B4X:
** Activity (main) Create, isFirst = true **
main_activity_create (B4A line: 68)
xOSStats.Initialize(400, 50, Me, "myStats")
java.lang.IncompatibleClassChangeError: The method 'int com.maximussoft.msos.MSOS.getNumCores()' was expected to be of type virtual but instead was found to be of type static (declaration of 'java.lang.reflect.ArtMethod' appears in /system/framework/core-libart.jar)
    at b4a.example.osstats._initialize(osstats.java:208)
    at njdude.msos.sample.main._activity_create(main.java:396)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:187)
    at njdude.msos.sample.main.afterFirstLayout(main.java:100)
    at njdude.msos.sample.main.access$100(main.java:17)
    at njdude.msos.sample.main$WaitForLayout.run(main.java:78)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:135)
    at android.app.ActivityThread.main(ActivityThread.java:5289)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)
** Activity (main) Resume **
WakeLock already held.
---- 1 ----
---- 2 ----
---- 3 ----
---- 4 ----
---- In onAdFailedToLoad ----Server Message: no results. Try again in 10 seconds
I can also confirm that MSOS does not work in Android 5.0 and higher. Pity Google deprecates (=messes up) good working code. What can I use in stead to determine which application is running in the foreground (i.e. has the focus)?
 
Last edited:

Rick Harris

Well-Known Member
Licensed User
To answer my own question: A good solution can be found under:
https://www.b4x.com/android/forum/threads/get-the-currently-running-activity.39387/#content

In particular I recommend thread #7.

I slightly modified the code:
B4X:
Sub Timer1_Tick
    Dim AppName1 As String
    Dim n As Long
    AppName1 = GET_RUNNINGTASK1                        '25-05-2016
    If AppName1.trim="" Then Return
    AppName1 = AppName1.Replace("ComponentInfo{","")   'Example: "ComponentInfo(blindnav1.dsh/blindnav1.dsh.main)"
    n=AppName1.IndexOf("/")                            'Need to strip superfluous info to get the real app name!
    If n=-1 Then n=AppName1.length
    AppName1 = AppName1.SubString2(0,n)                'Gives: "blindnav1.dsh"
    'Do something with the derived foreground appname (in AppName1)
end sub

Sub GET_RUNNINGTASK1 As String
    Private RunList1 As List = OS1.getRunningTasks(1)
    Private RunTask1 As String = ""
    Private r1 As Reflector
    RunTask1=""
    If RunList1.Size > 0 Then
      r1.Target = RunList1.Get(0)
      RunTask1  = r1.getField("topActivity")
    End If
    Return RunTask1
End Sub
Use Dim OS1 As OperatingSystem under Process Globals and OS1.Initialize("") under ServiceCreate or ActivityCreate.
The sub GET_RUNNINGTASKS1 returns too much information about the running foreground app and therefore needs to be filtered (as shown above). I prefer the use of the OS library. It also does not require any permissions in the Manifest.
 
Last edited:

Rick Harris

Well-Known Member
Licensed User
To answer my own question: A good solution can be found under:
https://www.b4x.com/android/forum/threads/get-the-currently-running-activity.39387/#content

In particular I recommend thread #7.

I slightly modified the code:
B4X:
Sub Timer1_Tick
    Dim AppName1 As String
    Dim n As Long
    AppName1 = GET_RUNNINGTASK1                        '25-05-2016
    If AppName1.trim="" Then Return
    AppName1 = AppName1.Replace("ComponentInfo{","")   'Example: "ComponentInfo(blindnav1.dsh/blindnav1.dsh.main)"
    n=AppName1.IndexOf("/")                            'Need to strip superfluous info to get the real app name!
    If n=-1 Then n=AppName1.length
    AppName1 = AppName1.SubString2(0,n)                'Gives: "blindnav1.dsh"
    'Do something with the derived foreground appname (in AppName1)
end sub

Sub GET_RUNNINGTASK1 As String
    Private RunList1 As List = OS1.getRunningTasks(1)
    Private RunTask1 As String = ""
    Private r1 As Reflector
    RunTask1=""
    If RunList1.Size > 0 Then
      r1.Target = RunList1.Get(0)
      RunTask1  = r1.getField("topActivity")
    End If
    Return RunTask1
End Sub
Use Dim OS1 As OperatingSystem under Process Globals and OS1.Initialize("") under ServiceCreate or ActivityCreate.
The sub GET_RUNNINGTASKS1 returns too much information about the running foreground app and therefore needs to be filtered (as shown above). I prefer the use of the OS library. It also does not require any permissions in the Manifest.
Update (June 2016): The above GET_RUNNINGTASKS function sadly also no longer works (at least not in Android 5.0 and higher).
 
Top