Detect if an android app is running on background - android

I want to check if my app is running on a background mode.
The problem is that i have many activities(list activities, map activities etc.). Initially I have tried in the life cycle's resume and pause(or the onUserLeaveHint) methods to set a static boolean as true or false and work with this way. But this obviously can't work because when I move from one activity to another, the previous one get paused.
Also, I've read here on stackoverflow that the getRunningTasks() should be used only for debugging purposes. I did a huge research but I can't find a solution. All I want to do is to be able to detect if a the app is running on a background. Can anyone propose me a way, or express any thought on how can I do that?

You can try the same mechanism (a boolean attribute) but on application side rather than activity side. Create a class which extends Application, declare it in the manifest file under <application android:name=YourClassApp>.
EDIT: I assume you know that activities aren't intended for background processing, if not you should take a look at the Services.

I don't know if this will help but you can use
getApplicaton().registerActivityLifecycleCallbacks(yourClass);
To get a birds eye view of how your activities are displayed in the FG. (For older s/w you can use this)
If your Application has a Service you could have a static get/set which accesses a static variable. Do not do this in Activities though, it causes mem leaks.
But realistically speaking there is no tidy way of tracking if your application is running or not.

I had the same problemen when overwriting the Firebase push messaging default behavior (show notifications only when in the background) I checked how Firebase did this by looking in the .class file com.google.firebase.messaging.zzb:53 (firebase-messaging:19.0.1) which appears to us getRunningAppProcesses. Mind you FireBase is created by Google them self. So I'm assuming it's pretty save to use. Cleaned up version:
List<ActivityManager.RunningAppProcessInfo> runningApps;
boolean isInForeground =false;
if ((runningApps = ((ActivityManager)this.getApplication().getSystemService(Context.ACTIVITY_SERVICE)).getRunningAppProcesses()) != null) {
Iterator runningApp = runningApps.iterator();
int myPid = Process.myPid();
while(runningApp.hasNext()) {
ActivityManager.RunningAppProcessInfo processInfo;
if ((processInfo = (ActivityManager.RunningAppProcessInfo)runningApp.next()).pid == myPid) {
isInForeground = processInfo.importance == 100;
break;
}
}
}

Related

Android Studio - showing new activity over other apps results in a blackscreen

I'm trying to develop an android app for the SDK version 30 that (when a button is clicked) starts listening to what apps are opened on the phone. If it detects the user opening Whatsapp, it is supposed to show a LockScreen activity over Whatsapp that makes you answer a math question before being able to use Whatsapp.
I know this can be done as their are apps like QualityTime or Forest that have similar features to restrict you from using certain apps, but I am a newbie when it comes to programming (probably obvious from my code) and feel totally stuck.
I have already figured out how to detect what app the user opened in the last second with code from stack overflow:
public String getCurrentApp() {
String topPackageName = "None";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService("usagestats");
long time = System.currentTimeMillis();
List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 1, time);
// Sort the stats by the last time used
if (stats != null) {
SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
for (UsageStats usageStats : stats) {
mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
}
if (!mySortedMap.isEmpty())
{
topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
}
}
}
return topPackageName;
}
I have another function that is started when the user clicks the button in my app to "activate" the listening process. This function keeps checking if the user opens Whatsapp and is then supposed to display the Lockscreen activity on top:
public void startListening(View view)
{
System.out.println("Lock activated.");
while (activated) {
String currentlyRunningApp;
currentlyRunningApp = getCurrentApp();
if (currentlyRunningApp.equals("com.whatsapp"))
{
System.out.println("Whatsapp detected. Showing Lockscreen...");
Intent i = new Intent(this,LockScreen.class);
startActivity(i);
}
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
(All of the code I have shown is in my MainActivity btw.)
I have the following permissions granted to my app:
android.permission.PACKAGE_USAGE_STATS (for the getCurrentApp() function)
android.permission.SYSTEM_ALERT_WINDOW (as suggested here)
android.permission.ACTION_MANAGE_OVERLAY_PERMISSION (although I am not sure I even need this one)
My problem is, that instead of showing the Lockscreen activity I created, it only shows a blackscreen for the user. The Lockscreen activity itself works fine if I let the user open it through a button on the mainActivity, so the issue seems to really be that I can not properly show an activity if my app is running in the background and I want to display it on top of Whatsapp.
I have tried to look through similar questions, but all of the posts on here with similar use cases seem to be very old and outdated (i.e. this or this), as the newer versions seem to have way tighter security restrictions.
I also tried to do it with a screen overlay instead of an activity (using this source), but this doesn't even give me a blackscreen - just does nothing...
I am also aware that there are probably better ways to program the whole "listening and checking for whatsapp" part - i.e. with a service instead of a while-loop or something, but I only found out about services while researching this problem and I'd like to fix the blackscreen issue first.
After lots of trial and error I figured out that the issues was indeed caused by a missing permission, but one that I could not find on any stack overflow answer related to black screen problems. On top of that, I believe it's an issue that only occurred because I used a Xiaomi device for testing.
There are currently two separate permissions for displaying screens over other apps that you will need to grant:
Display over other apps, also called Display pop-up window. This is the android.permission.ACTION_MANAGE_OVERLAY_PERMISSION that I wasn't sure was even needed. So to emphasize, I definetly do need this permission.
Display pop-up windows while running in the background. This is the permission I was missing.
After I allowed them both (which you can do under Settings > Apps > Manage Apps > Your App > Other Permissions) everything worked fine.
To direct the user directly to the settings menu where they can allow these permissions, I used the code from this stack overflow answer. This is also where I got the info that it's a xiaomi-specific "issue".

How to check if accessibility service crashed (Android Pie now displays this info in settings)?

I've found out that Android 9 now shows info if accessibility service stopped working.
That was always a pain for developers who try to leverage accessibility API.
Accessibility looks like enabled, but service is stopped. And to get it back to work it is required to turn accessibility off and back on.
I would be glad if Google fixes that completely, but now they just show a hint that it's good to disable-enable it manually.
Not the best stuff, but at least something.
So, I've tried to find out how the system gets to know if the service is crashed. There happened to be a class called AccessibilityUtil and it contains hasServiceCrashed method.
Unfortunately, it checks a hidden field crashed from AccessibilityNodeInfo, which is not available for third-party developers (because of reflection denial) as well as on previous android versions.
So I'm wondering if there is an alternative way to get the info from the system which clarifies that my accessibility service is crashed/stopped working and user's action is required. Starting from Lollipop. Hints appreciated.
I came up with an idea to use a static boolean indicating the status of Accessibility Service and compare it with Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES. I've tested on multiple devices, haven't found any issue with this method.
1.Declare a static boolean in Accessibility Service.
private static boolean bServiceRunning = false;
2.In Accessibility Service, set the boolean value in onServiceConnected and onUnbind
#Override
protected void onServiceConnected() {
super.onServiceConnected();
bServiceRunning = true; //put this at the very beginning to reduce time gap
}
#Override
public boolean onUnbind(Intent intent) {
bServiceRunning = false;
return super.onUnbind(intent);
}
3.Create a static function in Accessibility Service
public static boolean bGetServiceStatus(){
return bServiceRunning;
}
With the boolean flag, I can know if the accessibility service is running in the desired state. When the service is being forced to stop, onUnbind will be called so the boolean value turns into false.
4.We use Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES to get accessibility service switch status
public static boolean bIsAccessibilityServiceEnabled(Context context, Class<?> accessibilityService) {
ComponentName expectedComponentName = new ComponentName(context, accessibilityService);
String strServicesSettingResult = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (strServicesSettingResult == null){
return false;
}
TextUtils.SimpleStringSplitter colonSplitter = new TextUtils.SimpleStringSplitter(':');
colonSplitter.setString(strServicesSettingResult);
while (colonSplitter.hasNext()) {
String strComponentName = colonSplitter.next();
ComponentName enabledService = ComponentName.unflattenFromString(strComponentName);
if (enabledService != null && enabledService.equals(expectedComponentName))
return true;
}
return false;
}
5.And this is what we want, we check with the above two methods the determine the real state of accessibility service.
public static int intIsAccessibilityServiceEnabled_WithCrashCheck(Context context, Class<?> accessibilityService){
//return 0 if Accessibility Service enabled and running
//return -1 if Accessibility Service disabled and not running
//return -2 if Accessibility Service enabled but stopped working or crashed
//first check Accessibility Service boolean
if(bGetServiceStatus()){
//service is running
return 0;
}else{
//service not running, now double check with Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
boolean bResult = bIsAccessibilityServiceEnabled(context, accessibilityService);
if(!bResult){
//Accessibility Service is disabled
return -1;
}else{
//Accessibility Service is enabled, but service is not actually running, Accessibility Service is crashed
return -2;
}
}
}
Using "AccessibilityManager" also works the same, but I prefer a more "lightweight" version with static boolean for better performance.
Note: Using Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES without another doublecheck will cause a bug. The value is not synced. The result doesn't always represent the real service status. The following steps can create such a case :
Start an accessibility service then go to App settings force stop the App.
Now you can see the accessibility service switch is turned off. (Seems enough currently, but the next step will create a problem)
Start the accessibility service again,on Android 9.0+ devices you'll find out the switch is switched on, but the service is actually not running, and now it displays "Not
working. Tap for info."
2022/11/29 Edit: This bug is fixed according to this issue tracker. I can no longer reproduce this bug on my new Android 12 & 13 devices. However, devices with old Android firmware still has this bug. (The patch is also applied to the newest AVD images. To test this bug with AVD, now you must download old revisions of the AVD images.)
Android generally prevents apps from running if they crash repeatedly. This behavior for an accessibility service can obviously affect users who depend on the service, but since these services can effectively control the UI, having one that crashes repeatedly could also make the device unusable.
It hadn't occurred to me that anyone else would be interested in the crashed field in AccessibilityServiceInfo. I populated that field using data only available to the system unfortunately. I compare the list of services that are enabled with the list of those that are bound.
If you're interested if your service is prevented from running, you could probably do something similar by keeping track of when onBind and onUnbind is called and looking at the list of enabled services from AccessibilityManager.
I don't know if this is a solution. But I did find when it doesn't work: if I use "dumpsys accessibility", the services part is empty, looks like:
User state[attributes:{id=0,
currentUser=true,
touchExplorationEnabled=false,
displayMagnificationEnabled=false,
navBarMagnificationEnabled=false,
autoclickEnabled=false}
services:{}]
Maybe you can check if the services is empty.

How to detect process foreground for Android O

On our application there's a service that is normally started during Application.OnCreate (directly calling context.startService) and also later on via AlarmManager (refactor is in progress to migrate some of its work to JobScheduler).
Our application also have a BroadcastReceiver that gets launched with its direct intent.
Given the new limitations in Android Oreo (https://developer.android.com/about/versions/oreo/android-8.0-changes.html) we're having an issue as follows:
app/process is in background/dead
BroadcastReceiver gets fired by the OS
Application.onCreate() executes before the BroadcastReceiver
Application.onCreate() code tries to run the Service
this leads to crash with "IllegalStateException: Not allowed to start service Intent".
I'm aware of the new recommended ways of launching a Service as answered by CommonsWare here https://stackoverflow.com/a/44505719/906362, but for this specific case, I simply want to have if(process in foreground) { startService }. I'm currently using the following method and it seems to work:
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static boolean isProcessInForeground_V21(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.AppTask> tasks = am.getAppTasks();
return tasks.size() > 0;
}
But I can't find the exact checks Android Oreo is doing (I got as far as here https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ContextImpl.java on the startServiceCommon method, but from there requireForeground flag seems to go to some native implementation)
So my question:
For the specific purpose of Android Oreo new limitations, how to check if my process is foreground before calling startService?
To continue your investigation: (TL;DR: see between horizontal lines at the bottom)
Disclaimer, I don't know too much about Android, I just like digging in the source code.
Note: you can also navigate the code in Android Studio if you jump to file instead of class:
or searching for text in Project and Libraries.
IActivityManager is defined by AIDL, that's why there are no sources for it:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/app/IActivityManager.aidl#145
Based on how AIDL needs to be implemented I found that ActivityManagerService extends IActivityManager.Stub (God bless Google indexing).
Note I also found this, which might be an interesting read if you're really interested how things work internally.
https://programmer.group/android-9.0-source-app-startup-process.html
ActivityManagerService sources reveal that in Oreo startService is forwarded to ActiveServices which is located in the same package.
Assuming we're looking for an exception like this:
java.lang.IllegalStateException: Not allowed to start service Intent {...}: app is in background uid UidRecord{af72e61 u0a229 CAC bg:+3m52s273ms idle procs:1 seq(0,0,0)}
we have to continue down the rabbit hole: requireForeground gets assigned to fgRequired parameter and the message is here. The condition to allow this depends on the start mode returned by ActivityManagerService.getAppStartModeLocked(packageTargetSdk = 26 or greater, disabledOnly = false, forcedStandby = false).
There are 4 start modes:
APP_START_MODE_NORMAL (needs to be different than this, i.e. !=)
APP_START_MODE_DELAYED (this is ok, i.e. return null)
APP_START_MODE_DELAYED_RIGID
APP_START_MODE_DISABLED
Ephemeral apps will immediately return APP_START_MODE_DISABLED, but assuming this is a normal app, we end up in appServicesRestrictedInBackgroundLocked.
Note: this is where some of the whitelist mentioned in https://stackoverflow.com/a/46445436/253468 is decided.
Since all branches but last return APP_START_MODE_NORMAL, this redirects to appRestrictedInBackgroundLocked where we find our most likely suspect:
int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Apps that target O+ are always subject to background check
if (packageTargetSdk >= Build.VERSION_CODES.O) {
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
So the reason for denial is simply targeting O. I think the final answer to your question of how the OS decides if your app is foreground or background is this condition in getAppStartModeLocked
UidRecord uidRec = mActiveUids.get(uid);
if (uidRec == null || alwaysRestrict || uidRec.idle) {
My guess is that a missing record means it's not running (but then how is it starting a service?!), and idle means it's backgrounded. Notice that in my exception message the UidRecord is saying that it's idle and has been backgrounded for 3m52s.
I peeked into your getAppTasks and it's based on TaskRecord.effectiveUid, so I'm guessing that's quite close to listing UidRecords for your app.
Not sure if this helps, but I'll post it anyway, so if anyone wants to investigate more, they have more info.

make getSystemService() recognize our new system service

I'm using the book "Embedded Android".
I'm making a new System Service using AOSP(4.0.3_r1).
I want my system service to be registered in frameworks/base/core/java/android/content/app/ContextImpl.java so that I can use it through getSystemService() method.
The problem is, I can't find the app folder under content:androidroot/frameworks/base/core/java/android/content/app/ContextImpl.java
But, I found it in:androidroot/frameworks/base/core/java/android/app/ContextImpl.java
Are these 2 files the same? or is it just missing(the content/app folder)?
Any idea on what to do?
Karim wrote his book mostly orienting on Android 2.3.4 version. Something can be changed from this time. This is an example what has been changed.
Are these 2 files the same? or is it just missing(the content/app folder)?
These are the same files.
Any idea on what to do?
As I said the implementation has been changed. I looked into the code and here what you can change to make your code working (I can only suppose because I did not actually build my code). In the static block of ContextImpl class you need to add the following code:
registerService(ACCOUNT_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(OPERSYS_SERVICE);
IOpersysService service = IOpersysService.Stub.asInterface(b);
return new OpersysManager(service);
}});
You need to use SystemServer which holds all system services' names.
You should check this link out:
http://processors.wiki.ti.com/index.php/Android-Adding_SystemService

ContentObserver - observe changes only due to specific apps

I have a ContentObserver set up to check for audio changes in a service. I want to know if there's a way to restrain the triggering of the ContentObserver to only certain apps.
Say, I'm playing a game where I can change the volume, after which I change the volume in the settings. I want the service to trigger an alert only when the volume is changed by the game. How do I go about this?
NOTE: The game is a 3rd party app.
EDIT
Is there a way to receive the stack order of the commands? Say a program accesses settings to change the volume, obviously settings is gonna know which program is trying to change the volume. Is there a way to get settings to throw the app's package name? Or is there a way to know if it was changed directly in the settings.
Say I'm in the app drawer and change the volume, that would be categorized as direct change and via an app a third party change. Is there a way to find out when it's a direct change?
you can make a broadcast intent of your own and fire it when game makes the change. Then receive it in your second app.
If game is a third party app, then there would be no way I guess.
I found a way to circumvent this.
To the service with the ContentObserver, I added this piece of code
private RunningAppProcessInfo getForegroundApp() {
RunningAppProcessInfo result=null, info=null;
if(mActivityManager==null)
mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
List <RunningAppProcessInfo> l = mActivityManager.getRunningAppProcesses();
Iterator <RunningAppProcessInfo> i = l.iterator();
while(i.hasNext()){
info = i.next();
if(info.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND
&& !isRunningService(info.processName)){
result=info;
break;
}
}
return result;
}
So, now that I know the foreground app, the probability that it triggered the volume change is very high. So I can exclude it from triggering any respone. It's not flawless, but it works.

Categories

Resources