I have been struggling with an issue where I want to access the package name of apps
running in foreground and not being the same app as mine. But as far as I am concerned
it is not possible as long as I have to give a local Context parametrized into the method
which returns exactly that.
Therefore I wanted to ask if there is a way of doing exactly this but not only on my App itself but globally?
I tried doing this from another StackOverflow thread:
fun getCurrentForegroundRunningApp(context: Context): String {
val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val appProcesses = am.runningAppProcesses
for (appProcessInfo in appProcesses) {
Log.i("For all:: ",""+appProcessInfo.processName)
if (appProcessInfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return appProcessInfo.processName
} else {
return "There is no app in the foreground"
}
}
return "No App in the foreground"
}
I expected for example that it returns how I am running com.etc.somepackage that isn't what
the app package my app is called is. It only returns such value on my app but not on any other.
Related
Android 12 came up with a new Privacy Settings to disable access to the Camera and Mic sensors, which is referred as Toggles in the docs.
As it is mentioned in the docs:
the system reminds the user that the device-wide toggle is turned off
However, it seems that it only reminds the user when requesting the Camera permission and not when trying to authenticate the user using biometrics (face authentication on Pixel phones, which guess what!? It uses the camera). [I'm using AndroidX biometrics library]
Is there any way to find out if the Camera access has been blocked by the user without requesting any permission?
I guess the note in the docs didn't take into account that the app might use face authentication:
Note: The toggles mentioned in this section shouldn't require changes to your app's logic, as long as you follow privacy best practices.
Notes:
You can't register a new face in Settings when camera access is blocked. The Settings app does not show any error, just a blank camera feed
I am using Pixel 4 (Android 12)
The feature 'Join Wi-Fi by scanning a QR code' does not work and neither shows a feedback to the user if Camera access is blocked (Pixel 5)
So, I also looking for a solution - a have a biometric library and few reports appear in DM with the same problem - FaceUnlock doesn't work on Pixel 4 when the camera 'muted'
For now, still now fix, but maybe my research can help someone.
1. I checked the new API for PrivacyToggle's.
Android 12 introduces a new SensorPrivacyManager with supportsSensorToggle() method - it returns TRUE in case of device able to 'mute' camera or mic.
val sensorPrivacyManager = applicationContext
.getSystemService(SensorPrivacyManager::class.java)
as SensorPrivacyManager
val supportsMicrophoneToggle = sensorPrivacyManager
.supportsSensorToggle(Sensors.MICROPHONE)
val supportsCameraToggle = sensorPrivacyManager
.supportsSensorToggle(Sensors.CAMERA)
If you look into SensorPrivacyManager, you can find that it provides some more useful methods, so I develop the next code:
fun isCameraAccessible(): Boolean {
return !checkIsPrivacyToggled(SensorPrivacyManager.Sensors.CAMERA)
}
#SuppressLint("PrivateApi")
private fun checkIsPrivacyToggled(sensor: Int): Boolean {
val sensorPrivacyManager: SensorPrivacyManager =
appContext.getSystemService(SensorPrivacyManager::class.java)
if (sensorPrivacyManager.supportsSensorToggle(sensor)) {
val userHandleField = UserHandle::class.java.getDeclaredField("USER_CURRENT")
userHandleField.isAccessible = true
val userHandle = userHandleField.get(null) as Int
val m = SensorPrivacyManager::class.java.getDeclaredMethod(
"isSensorPrivacyEnabled",
Int::class.javaPrimitiveType,
Int::class.javaPrimitiveType
)
m.isAccessible = true
return m.invoke(
sensorPrivacyManager,
sensor,
userHandle
) as Boolean
}
return false
}
Unfortunately, the service rejects this call due to SecurityException - missing android.permission.OBSERVE_SENSOR_PRIVACY, even if we declare it in Manifest.
At least on emulator.
2. We can try to identify a new "sensor-in-use" indicator
fun checkForIndicator(){
findViewById<View>(Window.ID_ANDROID_CONTENT)?.let {
it.setOnApplyWindowInsetsListener { view, windowInsets ->
val indicatorBounds = windowInsets.privacyIndicatorBounds
if(indicatorBounds !=null){
Toast.makeText(view.context, "Camera-in-use detected", Toast.LENGTH_LONG).show()
}
// change your UI to avoid overlapping
windowInsets
}
}
}
I didn't test this code (no real device), but as for me - it's not very useful, because we can check the camera indicator only AFTER we start Biometric Auth flow, when I need to understand is camera accessible BEFORE Biometric Auth started.
3. Because of PrivicyToogle related to QuickSettings, I decide that perhaps exists a way how Tiles determinate current Privacy Toggle state.
But this API use a very interesting solution - it does not use Settings.Global or Settings.Security section, instead, all preferences saved in "system/sensor_privacy.xml" and not accessible for 3rd party apps.
See SensorPrivacyService.java
I believe that exists a way how to find that Camera is blocked, but seems like some deeper research required
UPDATED 28/10/2021
So after some digging in AOSP sources, I found that APP_OP_CAMERA permission reflects the "blocking" state.
Just call if(SensorPrivacyCheck.isCameraBlocked()){ return } - this call also notify the system to show the "Unblock" dialog
Example
Solution:
#TargetApi(Build.VERSION_CODES.S)
#RestrictTo(RestrictTo.Scope.LIBRARY)
object SensorPrivacyCheck {
fun isMicrophoneBlocked(): Boolean {
return Utils.isAtLeastS && checkIsPrivacyToggled(SensorPrivacyManager.Sensors.MICROPHONE)
}
fun isCameraBlocked(): Boolean {
return Utils.isAtLeastS && checkIsPrivacyToggled(SensorPrivacyManager.Sensors.CAMERA)
}
#SuppressLint("PrivateApi", "BlockedPrivateApi")
private fun checkIsPrivacyToggled(sensor: Int): Boolean {
val sensorPrivacyManager: SensorPrivacyManager =
AndroidContext.appContext.getSystemService(SensorPrivacyManager::class.java)
if (sensorPrivacyManager.supportsSensorToggle(sensor)) {
try {
val permissionToOp: String =
AppOpCompatConstants.getAppOpFromPermission(
if (sensor == SensorPrivacyManager.Sensors.CAMERA)
Manifest.permission.CAMERA else Manifest.permission.RECORD_AUDIO
) ?: return false
val noteOp: Int = try {
AppOpsManagerCompat.noteOpNoThrow(
AndroidContext.appContext,
permissionToOp,
Process.myUid(),
AndroidContext.appContext.packageName
)
} catch (ignored: Throwable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
PermissionUtils.appOpPermissionsCheckMiui(
permissionToOp,
Process.myUid(),
AndroidContext.appContext.packageName
) else AppOpsManagerCompat.MODE_IGNORED
}
return noteOp != AppOpsManagerCompat.MODE_ALLOWED
} catch (e: Throwable) {
e.printStackTrace()
}
}
return false
}
}
I have a list of package name that executed my app.
and the list is like this.
android (?)
com.gau.go.launcherex
android (?)
I confused I never seen this package name (android).
anyone has idea? thx for help.
here is the code to make list of package name
Uri ref = getReferrer();
if (ref != null) {
String host = ref.getHost();
if (host != null && !host.equals("")) {
boolean isWhite = false;
for (String item : whitelist) {
if (item.equals(host)) {
isWhite = true;
break;
}
}
if (!isWhite) {
// add to list
}
}
}
which package you've never seen? can you post more complete log and code which logged these lines? com.gau.go.launcherex is available HERE in Google Play, this is device launcher (what is launcher in HERE). and simple android package may mean app is bring-back from background using some system option, e.g. recents button/menu
edit due code posted in question - some suggestions: instead of for loop just use whiteList.contains(host), if you need to null/empty check host String then use TextUtils.isEmpty
I am trying to startService only while in foreground, but to determine foreground I use Activity.onStart, this however is called sometimes when app is in background as per that piece of code that throws java.lang.IllegalStateException: Not allowed to start service Intent when starting the service.
-- concretly, when starting activity with screen off.
Does anyone know what the actual check is? How can I "if" this, i.e. I need to && onStart with some processIsInForeground() to get true onStart
So far I have
val appProcesses = activityManager.runningAppProcesses
if (appProcesses == null) return false
val thisProcess = appProcesses.firstOrNull { it.processName == packageName }
if (thisProcess == null) return false
val isProcessForeground == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
But this only works like 90% of the time, some times importance is IMPORTANCE_SERVICE, sometimes IMPORTANCE_TOP_SLEEPING
Also, docs say that api is mostly just for debug, etc.
Im developing an app with the latest android version (4.2.1 API-Level 17) for tablets with multiuser capabilities.
I want to restrict certain features (like the access to the app preferences) to the owner of the tablet (that is the user who can add and remove other user accounts)
is there any way i can find out if the current user is the owner?
i read through the UserManager and UserHandle API docs but couldn't find a function that allows me to check for it.
have i missed something or is there another way to do that?
Similar but without reflection:
static boolean isAdminUser(Context context)
{
UserHandle uh = Process.myUserHandle();
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
if(null != um)
{
long userSerialNumber = um.getSerialNumberForUser(uh);
Log.d(TAG, "userSerialNumber = " + userSerialNumber);
return 0 == userSerialNumber;
}
else
return false;
}
You can create an extension property in Kotlin to make it simpler:
val UserManager.isCurrentUserDeviceOwner: Boolean
get() = if (SDK_INT >= 23) isSystemUser
else if (SDK_INT >= 17) getSerialNumberForUser(Process.myUserHandle()) == 0L
else true
Then, using it is as simple as the following:
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
if (userManager.isCurrentUserDeviceOwner) TODO() else TODO()
You can further reduce boilerplate by using global system services definitions that makes userManager and other Android System Services available anywhere in your Kotlin code, with code included in this library I made: https://github.com/LouisCAD/Splitties/tree/master/systemservices
After researching further i found out that the multiuser api is not functional yet, it cant really be used for anything. there is a hack though for checking if the user is the owner using reflections:
public boolean isCurrentUserOwner(Context context)
{
try
{
Method getUserHandle = UserManager.class.getMethod("getUserHandle");
int userHandle = (Integer) getUserHandle.invoke(context.getSystemService(Context.USER_SERVICE));
return userHandle == 0;
}
catch (Exception ex)
{
return false;
}
}
This works for me on the Nexus 7 and Nexus 10 with Android 4.2.1
Its very dirty. so i wouldnt recommend using it unless you are making an app thats device and version specific
I developed an application that contains a homescreen with an article list.
If you click on it, you access the detail in another screen.
I implemented the ActionBarSherlock, so I used the "up" button pattern for this activity.
Then I added a widget to this application. When you click on the widget, you access directly the detail activity.
The "up" button has been implemented following the Google recommandations (http://developer.android.com/training/implementing-navigation/ancestral.html).
My problem is that on API Level 15 and below, it works perfectly. It calls the following code :
#Override
public boolean shouldUpRecreateTask(Activity activity, Intent targetIntent) {
String action = activity.getIntent().getAction();
return action != null && !action.equals(Intent.ACTION_MAIN);
}
But on JellyBean, the code used is :
public boolean shouldUpRecreateTask(Intent targetIntent) {
try {
PackageManager pm = getPackageManager();
ComponentName cn = targetIntent.getComponent();
if (cn == null) {
cn = targetIntent.resolveActivity(pm);
}
ActivityInfo info = pm.getActivityInfo(cn, 0);
if (info.taskAffinity == null) {
return false;
}
return !ActivityManagerNative.getDefault().targetTaskAffinityMatchesActivity(mToken, info.taskAffinity);
} catch (RemoteException e) {
return false;
} catch (NameNotFoundException e) {
return false;
}
}
The first part of the method retrieves information on the activity that should be loaded if stack must be recreated.
But I still don't understand what does the line :
!ActivityManagerNative.getDefault().targetTaskAffinityMatchesActivity(mToken, info.taskAffinity);
Can anyone help me on this line, I really need to find out how to obtain true by initializing everything well ?
Its a boolean method it has to return something. If it needs to return a true boolean variable for it work, you have to do so!
From the official Documentation:
Returns true if the app should recreate the task when navigating 'up'
from this activity by using targetIntent.
If this method returns false the app can trivially call navigateUpTo(Intent)
using the same parameters to correctly perform up navigation.
If this method returns false, the app should synthesize a new task stack by using
TaskStackBuilder or another similar mechanism to perform up navigation.
The affinity indicates which task an activity prefers to belong to. By default, all the activities from the same application have an affinity for each other. So, by default, all activities in the same application prefer to be in the same task. However, you can modify the default affinity for an activity. Activities defined in different applications can share an affinity, or activities defined in the same application can be assigned different task affinities.