How to detect if any application requests the microphone in Android? - android

I'm developing an application that needs to know when other third party app requests the microphone.
Is there any way to detect it?
Thanks in advance!

After some research, it seems to be impossible to know when another third party application requests the MICROPHONE due to security reasons.
If you want to perform actions according to actual state of the media recorder, you may have to handle that as explained in this post:
How to detect if MediaRecorder is used by another application?
And about detecting other apps permissions access, I think this can provide an answer: How to detect permission access of other apps?
But if you want to check apps that needs RECORD_AUDIO permission, you can continue reading the following lines:
Based on the answer of Kopi-B at this question
To get the list of apps requesting microphone you can then proceed as follows:
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List pkgAppsList = getPackageManager().queryIntentActivities(mainIntent, 0);
//create a package names list
List<String> packageNames=new ArrayList<>();
for (Object obj : pkgAppsList) {
ResolveInfo resolveInfo = (ResolveInfo) obj;
PackageInfo packageInfo = null;
try {
packageInfo = getPackageManager().getPackageInfo(resolveInfo.activityInfo.packageName, PackageManager.GET_PERMISSIONS);
} catch (PackageManager.NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String[] requestedPermissions = packageInfo.requestedPermissions;
//check the microphone permission
if (requestedPermissions!=null) {
for (String packagePermission : requestedPermissions) {
if (packagePermission == Manifest.permission.RECORD_AUDIO) {
packageNames.add(packageInfo.packageName);
break;
}
}
}
}

Related

Detect user using fake GPS Using isMockProvider [duplicate]

I will block a user from using my app if they fake the location.
So I use isFromMockProvider to check if the location is fake (follow here). But isFromMockProvider() may return false for faked locations in some cases.
public void onLocationChanged(Location location) {
textView.append("long:"+location.getLatitude()+" - lat:"+location.getLongitude()+" - isMock :"+location.isFromMockProvider() + "\n");
}
My case is: I use app Fake GPS location for fake to a location then I disable fake location and go to my app. Then the onLocationChanged returns the fake location with isFromMockProvider() = false
Video recorder: https://www.youtube.com/watch?v=rWVvjOCaZiI (in this video, my current location is 16.06, 108.21, the fake location is 36.26,138.28. You can see in last video the location is 36.26,138.28 but isFromMockProvider=false)
Is there any way to detect if a user uses a fake location in this case? Any help or suggestion would be great appreciated.
DEMO project
Risking a Realistic Answer
I'd like to provide an answer that helps the developer understand the public relations aspect of product design, taking the risk of criticism. Frankly, one cannot write great apps in a computer science vacuum. Satisfying user needs and balancing them with security is one of the primary issues in software interface and behavioral design today, especially in the mobile space.
From this perspective, your question, "Is there any way to detect if a user uses a fake location in this case?" may not be the most pertinent question you face. I'm not being evasive by asking this other question that may help you more and it is something I can answer well: "Is there any way to securely get the data from the user's device's geocoordinate firmware such that it cannot be spoofed?"
The answer to this one is, "No."
Android HTTP Client Contract
It is not part of the Android client-server contract or that of its competitors to guarantee user device location information.
Practical Reason
There is actually a market force that will probably push against such a guarantee indefinitely. Many device owners (and your users) want control over whether people know their true location for privacy and home and family security reasons.
Solution
The next question you can ask yourself as a designer of your software is, "How can the app or library work and provide for the needs I seek to fill with a certain percentage of the user community using today's (or tomorrow's) location spoofing software?"
If you are writing business intelligence software or there is some other statistical aspect to your system, then you need the software equivalent of error bars. If you display the stats, then the error bars would be an appropriate graphing feature. Estimating the percentage of location spoofers out of a population of users would require further study.
I use two ways to identify fake locations.
First, i check mock location, like in other code here.
public static boolean isMockLocationOn(Location location, Context context) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
return location.isFromMockProvider();
} else {
String mockLocation = "0";
try {
mockLocation = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION);
} catch (Exception e) {
e.printStackTrace();
}
return !mockLocation.equals("0");
}
}
Second, i check running apps and services, that need permission to access mock location.
public static List<String> getListOfFakeLocationApps(Context context) {
List<String> runningApps = getRunningApps(context);
List<String> fakeApps = new ArrayList<>();
for (String app : runningApps) {
if(!isSystemPackage(context, app) && hasAppPermission(context, app, "android.permission.ACCESS_MOCK_LOCATION")){
fakeApps.add(getApplicationName(context, app));
}
}
return fakeApps;
}
public static List<String> getRunningApps(Context context, boolean includeSystem) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
HashSet<String> runningApps = new HashSet<>();
try {
List<ActivityManager.RunningAppProcessInfo> runAppsList = activityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : runAppsList) {
runningApps.addAll(Arrays.asList(processInfo.pkgList));
}
} catch (Exception ex) {
ex.printStackTrace();
}
try {
//can throw securityException at api<18 (maybe need "android.permission.GET_TASKS")
List<ActivityManager.RunningTaskInfo> runningTasks = activityManager.getRunningTasks(1000);
for (ActivityManager.RunningTaskInfo taskInfo : runningTasks) {
runningApps.add(taskInfo.topActivity.getPackageName());
}
} catch (Exception ex) {
ex.printStackTrace();
}
try {
List<ActivityManager.RunningServiceInfo> runningServices = activityManager.getRunningServices(1000);
for (ActivityManager.RunningServiceInfo serviceInfo : runningServices) {
runningApps.add(serviceInfo.service.getPackageName());
}
} catch (Exception ex) {
ex.printStackTrace();
}
return new ArrayList<>(runningApps);
}
public static boolean isSystemPackage(Context context, String app){
PackageManager packageManager = context.getPackageManager();
try {
PackageInfo pkgInfo = packageManager.getPackageInfo(app, 0);
return (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return false;
}
public static boolean hasAppPermission(Context context, String app, String permission){
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo;
try {
packageInfo = packageManager.getPackageInfo(app, PackageManager.GET_PERMISSIONS);
if(packageInfo.requestedPermissions!= null){
for (String requestedPermission : packageInfo.requestedPermissions) {
if (requestedPermission.equals(permission)) {
return true;
}
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return false;
}
public static String getApplicationName(Context context, String packageName) {
String appName = packageName;
PackageManager packageManager = context.getPackageManager();
try {
appName = packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)).toString();
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return appName;
}
(Update)
Unfortunately, google forbid applications from receiving the list of currently running apps.
(It was since 5.1.1, but i still can get app list in test devices runned android 7.1)
Now you can get only list of recently used apps (with request runtime permission for it) by using UsageStatsManager, for example like here Android 5.1.1 and above - getRunningAppProcesses() returns my application package only
So if user close or exit from fake location app, i can't determinate it.
And now, to get list of fake location apps, i try to get locations, and if location.isFromMockProvider() return true, i scan device for all installed apps, that need permission to access mock location like this:
public static List<String> getListOfFakeLocationAppsFromAll(Context context) {
List<String> fakeApps = new ArrayList<>();
List<ApplicationInfo> packages = context.getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo aPackage : packages) {
boolean isSystemPackage = ((aPackage.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
if(!isSystemPackage && hasAppPermission(context, aPackage.packageName, "android.permission.ACCESS_MOCK_LOCATION")){
fakeApps.add(getApplicationName(context, aPackage.packageName));
}
}
return fakeApps;
}
Answers on This SO question and to a lesser extent the answers on This SO question seem to indicate you are suffering from an unfortunate Caching issue in the FusedLocationApi caused by onLocationChanged being called with an out of date timestamp (thus ignoring the result as it thinks there is already newer data).
To quote Reno's answer:
Unless you have not changed ... so that new APs can be discovered, I'm afraid you will get only cached locations. If you want fresh locations use the GPS provider.
The solution will be to instead call a location from the GPS Provider like so:
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
LocationListener locationListener = new MyLocationListener();
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 10, locationListener);
(The code above comes from a longer example here)
I use this method in my projects and it work perfectly till now:
for api < 18
//returns true if mock location enabled, false if not enabled.
public static boolean isMockLocationOn(Context context) {
if (Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
return false;
else
return true;
}
For api >= 18 you should use
location.isFromMockProvider();
The point is location.isFromMockProvider is buggy and some times it will show a mocked location as its OK !!!
There is a workaround in this link with full detail
Location on Android: Stop Mocking Me!
the approach is :
Remember the most recent location labeled as a mock
If a new “non-mock” reading is within 1km of the last mock, reject
it.
Only clear the last mock location after 20 consecutive “non-mock”
readings.

Detect or prevent if user uses fake location

I will block a user from using my app if they fake the location.
So I use isFromMockProvider to check if the location is fake (follow here). But isFromMockProvider() may return false for faked locations in some cases.
public void onLocationChanged(Location location) {
textView.append("long:"+location.getLatitude()+" - lat:"+location.getLongitude()+" - isMock :"+location.isFromMockProvider() + "\n");
}
My case is: I use app Fake GPS location for fake to a location then I disable fake location and go to my app. Then the onLocationChanged returns the fake location with isFromMockProvider() = false
Video recorder: https://www.youtube.com/watch?v=rWVvjOCaZiI (in this video, my current location is 16.06, 108.21, the fake location is 36.26,138.28. You can see in last video the location is 36.26,138.28 but isFromMockProvider=false)
Is there any way to detect if a user uses a fake location in this case? Any help or suggestion would be great appreciated.
DEMO project
Risking a Realistic Answer
I'd like to provide an answer that helps the developer understand the public relations aspect of product design, taking the risk of criticism. Frankly, one cannot write great apps in a computer science vacuum. Satisfying user needs and balancing them with security is one of the primary issues in software interface and behavioral design today, especially in the mobile space.
From this perspective, your question, "Is there any way to detect if a user uses a fake location in this case?" may not be the most pertinent question you face. I'm not being evasive by asking this other question that may help you more and it is something I can answer well: "Is there any way to securely get the data from the user's device's geocoordinate firmware such that it cannot be spoofed?"
The answer to this one is, "No."
Android HTTP Client Contract
It is not part of the Android client-server contract or that of its competitors to guarantee user device location information.
Practical Reason
There is actually a market force that will probably push against such a guarantee indefinitely. Many device owners (and your users) want control over whether people know their true location for privacy and home and family security reasons.
Solution
The next question you can ask yourself as a designer of your software is, "How can the app or library work and provide for the needs I seek to fill with a certain percentage of the user community using today's (or tomorrow's) location spoofing software?"
If you are writing business intelligence software or there is some other statistical aspect to your system, then you need the software equivalent of error bars. If you display the stats, then the error bars would be an appropriate graphing feature. Estimating the percentage of location spoofers out of a population of users would require further study.
I use two ways to identify fake locations.
First, i check mock location, like in other code here.
public static boolean isMockLocationOn(Location location, Context context) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
return location.isFromMockProvider();
} else {
String mockLocation = "0";
try {
mockLocation = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION);
} catch (Exception e) {
e.printStackTrace();
}
return !mockLocation.equals("0");
}
}
Second, i check running apps and services, that need permission to access mock location.
public static List<String> getListOfFakeLocationApps(Context context) {
List<String> runningApps = getRunningApps(context);
List<String> fakeApps = new ArrayList<>();
for (String app : runningApps) {
if(!isSystemPackage(context, app) && hasAppPermission(context, app, "android.permission.ACCESS_MOCK_LOCATION")){
fakeApps.add(getApplicationName(context, app));
}
}
return fakeApps;
}
public static List<String> getRunningApps(Context context, boolean includeSystem) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
HashSet<String> runningApps = new HashSet<>();
try {
List<ActivityManager.RunningAppProcessInfo> runAppsList = activityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : runAppsList) {
runningApps.addAll(Arrays.asList(processInfo.pkgList));
}
} catch (Exception ex) {
ex.printStackTrace();
}
try {
//can throw securityException at api<18 (maybe need "android.permission.GET_TASKS")
List<ActivityManager.RunningTaskInfo> runningTasks = activityManager.getRunningTasks(1000);
for (ActivityManager.RunningTaskInfo taskInfo : runningTasks) {
runningApps.add(taskInfo.topActivity.getPackageName());
}
} catch (Exception ex) {
ex.printStackTrace();
}
try {
List<ActivityManager.RunningServiceInfo> runningServices = activityManager.getRunningServices(1000);
for (ActivityManager.RunningServiceInfo serviceInfo : runningServices) {
runningApps.add(serviceInfo.service.getPackageName());
}
} catch (Exception ex) {
ex.printStackTrace();
}
return new ArrayList<>(runningApps);
}
public static boolean isSystemPackage(Context context, String app){
PackageManager packageManager = context.getPackageManager();
try {
PackageInfo pkgInfo = packageManager.getPackageInfo(app, 0);
return (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return false;
}
public static boolean hasAppPermission(Context context, String app, String permission){
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo;
try {
packageInfo = packageManager.getPackageInfo(app, PackageManager.GET_PERMISSIONS);
if(packageInfo.requestedPermissions!= null){
for (String requestedPermission : packageInfo.requestedPermissions) {
if (requestedPermission.equals(permission)) {
return true;
}
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return false;
}
public static String getApplicationName(Context context, String packageName) {
String appName = packageName;
PackageManager packageManager = context.getPackageManager();
try {
appName = packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)).toString();
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return appName;
}
(Update)
Unfortunately, google forbid applications from receiving the list of currently running apps.
(It was since 5.1.1, but i still can get app list in test devices runned android 7.1)
Now you can get only list of recently used apps (with request runtime permission for it) by using UsageStatsManager, for example like here Android 5.1.1 and above - getRunningAppProcesses() returns my application package only
So if user close or exit from fake location app, i can't determinate it.
And now, to get list of fake location apps, i try to get locations, and if location.isFromMockProvider() return true, i scan device for all installed apps, that need permission to access mock location like this:
public static List<String> getListOfFakeLocationAppsFromAll(Context context) {
List<String> fakeApps = new ArrayList<>();
List<ApplicationInfo> packages = context.getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo aPackage : packages) {
boolean isSystemPackage = ((aPackage.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
if(!isSystemPackage && hasAppPermission(context, aPackage.packageName, "android.permission.ACCESS_MOCK_LOCATION")){
fakeApps.add(getApplicationName(context, aPackage.packageName));
}
}
return fakeApps;
}
Answers on This SO question and to a lesser extent the answers on This SO question seem to indicate you are suffering from an unfortunate Caching issue in the FusedLocationApi caused by onLocationChanged being called with an out of date timestamp (thus ignoring the result as it thinks there is already newer data).
To quote Reno's answer:
Unless you have not changed ... so that new APs can be discovered, I'm afraid you will get only cached locations. If you want fresh locations use the GPS provider.
The solution will be to instead call a location from the GPS Provider like so:
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
LocationListener locationListener = new MyLocationListener();
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 10, locationListener);
(The code above comes from a longer example here)
I use this method in my projects and it work perfectly till now:
for api < 18
//returns true if mock location enabled, false if not enabled.
public static boolean isMockLocationOn(Context context) {
if (Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
return false;
else
return true;
}
For api >= 18 you should use
location.isFromMockProvider();
The point is location.isFromMockProvider is buggy and some times it will show a mocked location as its OK !!!
There is a workaround in this link with full detail
Location on Android: Stop Mocking Me!
the approach is :
Remember the most recent location labeled as a mock
If a new “non-mock” reading is within 1km of the last mock, reject
it.
Only clear the last mock location after 20 consecutive “non-mock”
readings.

How to profile the App uses in Android?

I want to collect App activation sequence in an Android system.
E.g.
If a user first open Youtube App, then switch to Gmail App, then switch back to Youtube App and so on, then the sequence is like:
Youtube Gmail Youtube ...
Is there available App existing in Google Play or somewhere else to achieve this?
Is it straightforward to implement? Is it possible to achieve the goal with pure App solution?
Is it require rooting the device?
You need to poll this function on some regular interval.
You can further write a logic to remove subsequently duplicate Application Label.
private string getActiveApplicationLabel(){
String appLabel = null;
ActivityManager am = (ActivityManager)this.getSystemService(ACTIVITY_SERVICE);
List l = am.getRunningAppProcesses();
Iterator i = l.iterator();
PackageManager pm = this.getPackageManager();
while(i.hasNext()) {
ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo)(i.next());
try {
CharSequence c = pm.getApplicationLabel(pm.getApplicationInfo(info.processName,
PackageManager.GET_META_DATA));
appLabel = c.toString();
}catch(Exception e) {
//Name Not FOund Exception
}
}
return appLabel;
}

Duplicate entries of installed applications after for loop

I am trying to get a list of applications that uses my location permission in my applications and I'm trying to get the applications that uses the location permission:
public void getPermissions(Context context) {
PackageManager packageManager = context.getPackageManager();
final List<PackageInfo> apps = context.getPackageManager()
.getInstalledPackages(PackageManager.GET_PERMISSIONS);
for (PackageInfo pi : apps) {
String[] permissions = pi.requestedPermissions;
if (permissions != null) {
for (String permission : permissions) {
Log.d("TAG", permission);
if (permission.equals("android.permission.ACCESS_FINE_LOCATION") || permission.equals("android.permission.ACCESS_COARSE_LOCATION") || permission.equals("android.permission.ACCESS_MOCK_LOCATION") || permission.equals("android.permission.ACCESS_LOCATION_EXTRA_COMMANDS") || permission.equals("android.permission.ACCESS_INSTALL_LOCATION_PROVIDER")) {
String appname=pi.applicationInfo.loadLabel(packageManager).toString();
//ImageView appicon;
//appicon = pi.applicationInfo.setImageDrawable(packageManager);
// Log.e("TAG", "Permission found for "+ appname);
locationArray.add(appname);
}
}
}
}
} //end of getPermissions method
But with this i will get multiple entries like
Angry Birds
Angry Birds
CWM
CWM
CWM
Facebook
Is there any workaround to solve this?
It's normal to get duplicate entries because you step over all the permissions of a PackageInfo and test each one to see if it is one of the location related permission. For example, as you iterate the permission of the Angry Birds game you'll come across two permission(if I'm not mistaken ACCESS_FINE and ACCESS_COARSE) and because those permissions both fulfill the if condition you'll end up adding the Angry Birds two times.
The trick is to break out of the for (String permission : permissions) when you find the first permission:
for (String permission : permissions) {
Log.d("TAG", permission);
if (permission.equals("android.permission.ACCESS_FINE_LOCATION") || permission.equals("android.permission.ACCESS_COARSE_LOCATION") || permission.equals("android.permission.ACCESS_MOCK_LOCATION") || permission.equals("android.permission.ACCESS_LOCATION_EXTRA_COMMANDS") || permission.equals("android.permission.ACCESS_INSTALL_LOCATION_PROVIDER")) {
String appname=pi.applicationInfo.loadLabel(packageManager).toString();
//ImageView appicon;
//appicon = pi.applicationInfo.setImageDrawable(packageManager);
// Log.e("TAG", "Permission found for "+ appname);
locationArray.add(appname);
break;
}
}
If You do not want duplicates You can add all contents of locationArray to HashSet(which will not allow duplicates) and add HashSet back to locationArray.
// add elements to hs, including duplicates
HashSet hs = new HashSet();
hs.addAll(localArray);
localArray.clear();
localArray.addAll(hs);
Now localArray will not have any duplicates.

Is there a way to check for manifest permission from code?

How do I check for a specific permission in the manifest.xml from code? I want to throw some exception if some permissions that are necessay for my application are missing.
For example, FINE_LOCATION and COARSE_LOCATION I know that android will also throw an exception on the launch of the specific activiy that is using GPS, but I need to check the manifest and throw an exception at the launch of the application itself. This holds not only for location access, but also for other permissions.
Any suggestions would be helpful.
You can check whether the permission is granted or not for specific permission by using PackageManager. For example
PackageManager pm = getPackageManager();
if (pm.checkPermission(permission.FINE_LOCATION, getPackageName()) == PackageManager.PERMISSION_GRANTED) {
// do something
} else {
// do something
}
You can read the available <uses-permission> tags at runtime using the following. Tested on older Android versions AND Android 6 and 7
PackageManager pm = getPackageManager();
try
{
PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), PackageManager.GET_PERMISSIONS);
String[] requestedPermissions = null;
if (packageInfo != null) {
requestedPermissions = packageInfo.requestedPermissions;
}
if (requestedPermissions != null && requestedPermissions.length > 0)
{
List<String> requestedPermissionsList = Arrays.asList(requestedPermissions);
ArrayList<String> requestedPermissionsArrayList = new ArrayList<String>();
requestedPermissionsArrayList.addAll(requestedPermissionsList);
Log.i(ExConsts.TAG, ""+requestedPermissionsArrayList);
}
}
catch (PackageManager.NameNotFoundException e)
{
e.printStackTrace();
}

Categories

Resources