Detect or prevent if user uses fake location - android

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.

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.

Youtube usage calculation using TrafficStats

Using TrafficStats i was checking the youtube app data usage.In some devices it is working fine but not with many other devices.
I found that from developer site, These statistics may not be available on all platforms. If the statistics are not supported by this device, UNSUPPORTED will be returned.
So in these case how can I get the device app usage ?
I was using
TrafficStats.getUidRxBytes(packageInfo.uid) + TrafficStats.getUidTxBytes(packageInfo.uid);
this is returning -1 everytime.
We can use NetworkStats.
https://developer.android.com/reference/android/app/usage/NetworkStats.html
Please see a sample repo which I got the clue.
https://github.com/RobertZagorski/NetworkStats
We can see a similar stackoverflow question as well.
Getting mobile data usage history using NetworkStatsManager
Then I needed to modify this logic for some particular devices. In these devices the normal method won't return proper usage values. So I modified is as
/*
getting youtube usage for both mobile and wifi.
*/
public long getYoutubeTotalusage(Context context) {
String subId = getSubscriberId(context, ConnectivityManager.TYPE_MOBILE);
//both mobile and wifi usage is calculating. For mobile usage we need subscriberid. For wifi we can give it as empty string value.
return getYoutubeUsage(ConnectivityManager.TYPE_MOBILE, subId) + getYoutubeUsage(ConnectivityManager.TYPE_WIFI, "");
}
private long getYoutubeUsage(int networkType, String subScriberId) {
NetworkStats networkStatsByApp;
long currentYoutubeUsage = 0L;
try {
networkStatsByApp = networkStatsManager.querySummary(networkType, subScriberId, 0, System.currentTimeMillis());
do {
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
networkStatsByApp.getNextBucket(bucket);
if (bucket.getUid() == packageUid) {
//rajeesh : in some devices this is immediately looping twice and the second iteration is returning correct value. So result returning is moved to the end.
currentYoutubeUsage = (bucket.getRxBytes() + bucket.getTxBytes());
}
} while (networkStatsByApp.hasNextBucket());
} catch (RemoteException e) {
e.printStackTrace();
}
return currentYoutubeUsage;
}
private String getSubscriberId(Context context, int networkType) {
if (ConnectivityManager.TYPE_MOBILE == networkType) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
return tm.getSubscriberId();
}
return "";
}

getRunningTasks doesn't work in Android L

In Android L, Google has disabled getRunningTasks. Now it can only return own apps task and the home launcher. I can no longer get other apps tasks.
Our app needs that method to determine current top app.
Any one has another method to do this?
I have searched in Google, no more topics about this except this:
https://code.google.com/p/android-developer-preview/issues/detail?id=29
For a recent project that I worked on, I also need to detect when certain applications are launched. All my research lead to the getRunningTasks method, which is deprecated starting from Lollipop.
However, to my surprises, I discovered that some of the app lock apps still work on lollipop devices, so they must have come up with a solution to get around this. So I dug a little deeper. Here is what I found out:
On pre-L devices, they still use getRunningTasks
On L devices, they use getRunningAppProcesses, which returns a list of processes currently running on the devices. You might think "well, that is not useful". Each processInfo has a attributed called importance. When an app becomes top activity, its processInfo importance also changes to IMPORTANCE_FOREGROUND. So you can filter out those processes that are not in foreground. From a each ProcessInfo, you can also ask a list of packages it loaded. You can then check if the list contains the same package that the app when are trying "protected".
Some sample code to detect when the default calendar app is launched:
public class DetectCalendarLaunchRunnable implements Runnable {
#Override
public void run() {
String[] activePackages;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
activePackages = getActivePackages();
} else {
activePackages = getActivePackagesCompat();
}
if (activePackages != null) {
for (String activePackage : activePackages) {
if (activePackage.equals("com.google.android.calendar")) {
//Calendar app is launched, do something
}
}
}
mHandler.postDelayed(this, 1000);
}
String[] getActivePackagesCompat() {
final List<ActivityManager.RunningTaskInfo> taskInfo = mActivityManager.getRunningTasks(1);
final ComponentName componentName = taskInfo.get(0).topActivity;
final String[] activePackages = new String[1];
activePackages[0] = componentName.getPackageName();
return activePackages;
}
String[] getActivePackages() {
final Set<String> activePackages = new HashSet<String>();
final List<ActivityManager.RunningAppProcessInfo> processInfos = mActivityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : processInfos) {
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
activePackages.addAll(Arrays.asList(processInfo.pkgList));
}
}
return activePackages.toArray(new String[activePackages.size()]);
}
}
Note: getRunningAppProcesses is also intended for debugging or "building a user-facing process management UI". Not sure if google will close this backdoor the similar way they did to getRunningTasks.
So no, you can't get the topActivity anymore. But with a little bit hack you can achieve similar result.
As MKY mentioned, getRunningTasks() method does not work for getting the current application in Lollipop.
As sunxin8086 wrote, the one way for getting the running applications is by using getRunningAppsProcesses() method. However, the condition info.importance == IMPORTANCE_FOREGROUND can not determine the current app uniquely.
The better approach to determine the current foreground application may be checking the processState field in RunningAppProcessInfo object. This field is a hidden field, but you can see it in the RunningAppProcessInfo class. If this value is ActivityManager.PROCESS_STATE_TOP (which is also
hidden static constant), the process is the current foreground process.
For example the code is
final int PROCESS_STATE_TOP = 2;
ActivityManager.RunningAppProcessInfo currentInfo = null;
Field field = null;
try {
field = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");
} catch (Exception ignored) {
}
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appList = am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo app : appList) {
if (app.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
&& app.importanceReasonCode == ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) {
Integer state = null;
try {
state = field.getInt(app);
} catch (Exception e) {
}
if (state != null && state == PROCESS_STATE_TOP) {
currentInfo = app;
break;
}
}
}
return currentInfo;
Note: processState field does not exist in pre-Lolipop. Please check that Build.VERSION.SDK_INT >= 21 before running the above code. The above code works only for Lollipop+.
The other approach, by Gaston (which is quite different), and the meaning of 'current application' is slightly different from this approach.
Please choose one for your purpose.
[EDIT]
As Sam pointed out, I modified START_TASK_TO_FRONT by PROCESS_STATE_TOP. (Both values are 2)
[EDIT2]
Sam has a new find! To determine the foreground application uniquely, one more
condition
process.importanceReasonCode == 0
is necessary. The above code has been updated. Thanks!
Here's an exact solution to get current top activity on your Android L/Lollipop devices and Android M/Marshmallow devices.
First call this line of code:(One time)
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);
The above code will open a screen named "Apps with usage access". Just check the radio button to on/true to allow usage access.
Now call the following method in your service or anywhere you want:
public void getTopActivtyFromLolipopOnwards() {
String topPackageName;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
long time = System.currentTimeMillis();
// We get usage stats for the last 10 seconds
List < UsageStats > stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 10, 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 != null && !mySortedMap.isEmpty()) {
topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
Log.e("TopPackage Name", topPackageName);
}
}
}
}
add permission
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
This will return the package name of currently running activity , whether it is facebook or whatsapp.
The only complication of this method is you need to prompt user for allowing app usage stats ... i.e. the first step.
Hope! this helps everyone.
private String getProcess() throws Exception {
if (Build.VERSION.SDK_INT >= 21) {
return getProcessNew();
} else {
return getProcessOld();
}
}
//API 21 and above
private String getProcessNew() throws Exception {
String topPackageName = null;
UsageStatsManager usage = (UsageStatsManager) context.getSystemService(Constant.USAGE_STATS_SERVICE);
long time = System.currentTimeMillis();
List<UsageStats> stats = usage.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - ONE_SECOND * 10, time);
if (stats != null) {
SortedMap<Long, UsageStats> runningTask = new TreeMap<Long,UsageStats>();
for (UsageStats usageStats : stats) {
runningTask.put(usageStats.getLastTimeUsed(), usageStats);
}
if (runningTask.isEmpty()) {
return null;
}
topPackageName = runningTask.get(runningTask.lastKey()).getPackageName();
}
return topPackageName;
}
//API below 21
#SuppressWarnings("deprecation")
private String getProcessOld() throws Exception {
String topPackageName = null;
ActivityManager activity = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> runningTask = activity.getRunningTasks(1);
if (runningTask != null) {
RunningTaskInfo taskTop = runningTask.get(0);
ComponentName componentTop = taskTop.topActivity;
topPackageName = componentTop.getPackageName();
}
return topPackageName;
}
//required permissions
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<uses-permission android:name="android.permission.GET_TASKS"/>
I Think its not possible to get other app's tasks,
This is what documentation says
With the introduction of the new concurrent documents and activities
tasks feature in the upcoming release (see Concurrent documents and
activities in Recents screen below), the
ActivityManager.getRecentTasks() method is now deprecated to improve
user privacy. For backward compatibility, this method still returns a
small subset of its data, including the calling application’s own
tasks and possibly some other non-sensitive tasks (such as Home). If
your app is using this method to retrieve its own tasks, use
android.app.ActivityManager.getAppTasks() instead to retrieve that
information.
Check out the api overview of Android L here https://developer.android.com/preview/api-overview.html#Behaviors

Any way to check if a running process is a system process in android

I am building an android app in which I need to show the list of currently running apps but It contain all the process including many system or defualt process of android like : launcher,dailer etc.
Now is there any way to check if the currently running process is not a system process (default process) of android.
Thanks a lot.
Here is my code:
First to get a list of running Apps do the following
public void RunningApps() {
final PackageManager pm = getPackageManager();
//Get the Activity Manager Object
ActivityManager aManager =
(ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
//Get the list of running Applications
List<ActivityManager.RunningAppProcessInfo> rapInfoList =
aManager.getRunningAppProcesses();
//Iterate all running apps to get their details
for (ActivityManager.RunningAppProcessInfo rapInfo : rapInfoList) {
//error getting package name for this process so move on
if (rapInfo.pkgList.length == 0)
continue;
try {
PackageInfo pkgInfo = pm.getPackageInfo(rapInfo.pkgList[0],
PackageManager.GET_ACTIVITIES);
if (isSystemPackage(pkgInfo)) {
//do something here
}
else {
//do something here
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
Log.d(TAG, "NameNotFoundException :" + rapInfo.pkgList[0]);
}
}
}
The actual function to check if the running application is system app, and used in the previous method is given below
/**
* Return whether the given PackgeInfo represents a system package or not.
* User-installed packages (Market or otherwise) should not be denoted as
* system packages.
*
* #param pkgInfo The Package info object
* #return Boolean value indicating if the application is system app
*/
private boolean isSystemPackage(PackageInfo pkgInfo) {
return (pkgInfo.applicationInfo.flags &
ApplicationInfo.FLAG_SYSTEM) != 0;
}
I hope it helps.
As per this documentation, it seems you can use FLAG_SYSTEM_PROCESS to identify a process is System process or not. Here is SO discussion on this.
You can use ActivtyManager to get list of all running process in android.See the below link for more information Android process killer .

Disable / Check for Mock Location (prevent gps spoofing)

Looking to find the best way to prevent / detect GPS spoofing on Android. Any suggestions on how this is accomplished, and what can be done to stop it? I am guessing the user has to turn on mock locations to spoof GPS, if this is done, then they can spoof GPS?
I guess I would need to just detect if Mock Locations are enabled? Any other suggestions?
I have done some investigation and sharing my results here,this may be useful for others.
First, we can check whether MockSetting option is turned ON
public static boolean isMockSettingsON(Context context) {
// returns true if mock location enabled, false if not enabled.
if (Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
return false;
else
return true;
}
Second, we can check whether are there other apps in the device, which are using android.permission.ACCESS_MOCK_LOCATION (Location Spoofing Apps)
public static boolean areThereMockPermissionApps(Context context) {
int count = 0;
PackageManager pm = context.getPackageManager();
List<ApplicationInfo> packages =
pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo applicationInfo : packages) {
try {
PackageInfo packageInfo = pm.getPackageInfo(applicationInfo.packageName,
PackageManager.GET_PERMISSIONS);
// Get Permissions
String[] requestedPermissions = packageInfo.requestedPermissions;
if (requestedPermissions != null) {
for (int i = 0; i < requestedPermissions.length; i++) {
if (requestedPermissions[i]
.equals("android.permission.ACCESS_MOCK_LOCATION")
&& !applicationInfo.packageName.equals(context.getPackageName())) {
count++;
}
}
}
} catch (NameNotFoundException e) {
Log.e("Got exception " , e.getMessage());
}
}
if (count > 0)
return true;
return false;
}
If both above methods, first and second are true, then there are good chances that location may be spoofed or fake.
Now, spoofing can be avoided by using Location Manager's API.
We can remove the test provider before requesting the location updates from both the providers (Network and GPS)
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
try {
Log.d(TAG ,"Removing Test providers")
lm.removeTestProvider(LocationManager.GPS_PROVIDER);
} catch (IllegalArgumentException error) {
Log.d(TAG,"Got exception in removing test provider");
}
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, locationListener);
I have seen that removeTestProvider(~) works very well over Jelly Bean and onwards version. This API appeared to be unreliable till Ice Cream Sandwich.
Flutter Update:
Use Geolocator and check Position object's isMocked property.
Since API 18, the object Location has the method .isFromMockProvider() so you can filter out fake locations.
If you want to support versions before 18, it is possible to use something like this:
boolean isMock = false;
if (android.os.Build.VERSION.SDK_INT >= 18) {
isMock = location.isFromMockProvider();
} else {
isMock = !Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0");
}
It seems that the only way to do this is to prevent Location Spoofing preventing MockLocations. The down side is there are some users who use Bluetooth GPS devices to get a better signal, they won't be able to use the app as they are required to use the mock locations.
To do this, I did the following :
// returns true if mock location enabled, false if not enabled.
if (Settings.Secure.getString(getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
return false;
else return true;
Stumbled upon this thread a couple years later. In 2016, most Android devices will have API level >= 18 and should thus rely on Location.isFromMockProvider() as pointed out by Fernando.
I extensively experimented with fake/mock locations on different Android devices and distros. Unfortunately .isFromMockProvider() is not 100% reliable. Every once in a while, a fake location will not be labeled as mock. This seems to be due to some erroneous internal fusion logic in the Google Location API.
I wrote a detailed blog post about this, if you want to learn more. To summarize, if you subscribe to location updates from the Location API, then switch on a fake GPS app and print the result of each Location.toString() to the console, you will see something like this:
Notice how, in the stream of location updates, one location has the same coordinates as the others, but is not flagged as a mock and has a much poorer location accuracy.
To remedy this problem, I wrote a utility class that will reliably suppress Mock locations across all modern Android versions (API level 15 and up):
LocationAssistant - Hassle-free location updates on Android
Basically, it "distrusts" non-mock locations that are within 1km of the last known mock location and also labels them as a mock. It does this until a significant number of non-mock locations have arrived.
The LocationAssistant can not only reject mock locations, but also unburdens you from most of the hassle of setting up and subscribing to location updates.
To receive only real location updates (i.e. suppress mocks), use it as follows:
public class MyActivity extends Activity implements LocationAssistant.Listener {
private LocationAssistant assistant;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
// You can specify a different accuracy and interval here.
// The last parameter (allowMockLocations) must be 'false' to suppress mock locations.
assistant = new LocationAssistant(this, this, LocationAssistant.Accuracy.HIGH, 5000, false);
}
#Override
protected void onResume() {
super.onResume();
assistant.start();
}
#Override
protected void onPause() {
assistant.stop();
super.onPause();
}
#Override
public void onNewLocationAvailable(Location location) {
// No mock locations arriving here
}
...
}
onNewLocationAvailable() will now only be invoked with real location info. There are some more listener methods you need to implement, but in the context of your question (how to prevent GPS spoofing) this is basically it.
Of course, with a rooted OS you can still find ways of spoofing location info that are impossible for normal apps to detect.
If you happened to know the general location of cell towers, you could check to see if the current cell tower matches the location given (within an error margin of something large, like 10 or more miles).
For example, if your app unlocks features only if the user is in a specific location (your store, for example), you could check gps as well as cell towers. Currently, no gps spoofing app also spoofs the cell towers, so you could see if someone across the country is simply trying to spoof their way into your special features (I'm thinking of the Disney Mobile Magic app, for one example).
This is how the Llama app manages location by default, since checking cell tower ids are much less battery intensive than gps. It isn't useful for very specific locations, but if home and work are several miles away, it can distinguish between the two general locations very easily.
Of course, this would require the user to have a cell signal at all. And you would have to know all the cell towers ids in the area --on all network providers-- or you would run the risk of a false negative.
try this code its very simple and usefull
public boolean isMockLocationEnabled() {
boolean isMockLocation = false;
try {
//if marshmallow
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
AppOpsManager opsManager = (AppOpsManager) getApplicationContext().getSystemService(Context.APP_OPS_SERVICE);
isMockLocation = (opsManager.checkOp(AppOpsManager.OPSTR_MOCK_LOCATION, android.os.Process.myUid(), BuildConfig.APPLICATION_ID)== AppOpsManager.MODE_ALLOWED);
} else {
// in marshmallow this will always return true
isMockLocation = !android.provider.Settings.Secure.getString(getApplicationContext().getContentResolver(), "mock_location").equals("0");
}
} catch (Exception e) {
return isMockLocation;
}
return isMockLocation;
}
This scrip is working for all version of android and i find it after many search
LocationManager locMan;
String[] mockProviders = {LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER};
try {
locMan = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
for (String p : mockProviders) {
if (p.contentEquals(LocationManager.GPS_PROVIDER))
locMan.addTestProvider(p, false, false, false, false, true, true, true, 1,
android.hardware.SensorManager.SENSOR_STATUS_ACCURACY_HIGH);
else
locMan.addTestProvider(p, false, false, false, false, true, true, true, 1,
android.hardware.SensorManager.SENSOR_STATUS_ACCURACY_LOW);
locMan.setTestProviderEnabled(p, true);
locMan.setTestProviderStatus(p, android.location.LocationProvider.AVAILABLE, Bundle.EMPTY,
java.lang.System.currentTimeMillis());
}
} catch (Exception ignored) {
// here you should show dialog which is mean the mock location is not enable
}
You can add additional check based on cell tower triangulation or Wifi Access Points info using Google Maps Geolocation API
The simplest way to get info about CellTowers
final TelephonyManager telephonyManager = (TelephonyManager) appContext.getSystemService(Context.TELEPHONY_SERVICE);
String networkOperator = telephonyManager.getNetworkOperator();
int mcc = Integer.parseInt(networkOperator.substring(0, 3));
int mnc = Integer.parseInt(networkOperator.substring(3));
String operatorName = telephonyManager.getNetworkOperatorName();
final GsmCellLocation cellLocation = (GsmCellLocation) telephonyManager.getCellLocation();
int cid = cellLocation.getCid();
int lac = cellLocation.getLac();
You can compare your results with site
To get info about Wifi Access Points
final WifiManager mWifiManager = (WifiManager) appContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
if (mWifiManager != null && mWifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
// register WiFi scan results receiver
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
List<ScanResult> results = mWifiManager.getScanResults();//<-result list
}
};
appContext.registerReceiver(broadcastReceiver, filter);
// start WiFi Scan
mWifiManager.startScan();
}
Below approach is working for me getting proper detection of mock location
#Override
public void onLocationChanged (Location location){
boolean isMockLocation = location.isFromMockProvider();
}
Paste this in your activity/where you want to validate fake/mock gps
try {
if (areThereMockPermissionApps(mContext)) {
Log.e(TAG, " - " + "Yup its use fake gps");
List<String> mFakeList = new ArrayList<>();
mFakeList = getListOfFakeLocationAppsInstalled(mContext); // this will return the fake app list
for (int a = 0; a < mFakeList.size(); a++) {
Log.e(TAG, mFakeList.size() + " - " + "NameList ----- " + mFakeList.get(a));
}
} else
Log.e(TAG, " - " + "Nope its not use fake gps");
} catch (Exception w) {
w.printStackTrace();
}
Here you can get the list of installed fake/mock app in your device.
private List<String> getListOfFakeLocationAppsInstalled(Context context) {
List<String> fakeApps = new ArrayList<>();
try {
List<String> runningApps = new ArrayList<>();
final PackageManager pm = getPackageManager();
List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo packageInfo : packages) {
runningApps.add(packageInfo.packageName);
} // the getLaunchIntentForPackage returns an intent that you can use with startActivity()
for (String app : runningApps) {
if (!isSystemPackage(context, app) && hasAppPermission(context, app, "android.permission.ACCESS_MOCK_LOCATION")) {
fakeApps.add(getApplicationName(context, app));
}
}
} catch (Exception w) {
w.printStackTrace();
}
return fakeApps;
}
Paste this method in your Helper/same class
public static boolean areThereMockPermissionApps(Context context) {
int count = 0;
try {
PackageManager pm = context.getPackageManager();
List<ApplicationInfo> packages =
pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo applicationInfo : packages) {
try {
PackageInfo packageInfo = pm.getPackageInfo(applicationInfo.packageName,
PackageManager.GET_PERMISSIONS);
// Get Permissions
String[] requestedPermissions = packageInfo.requestedPermissions;
if (requestedPermissions != null) {
for (int i = 0; i < requestedPermissions.length; i++) {
if (requestedPermissions[i]
.equals("android.permission.ACCESS_MOCK_LOCATION")
&& !applicationInfo.packageName.equals(context.getPackageName())) {
count++;
}
}
}
} catch (PackageManager.NameNotFoundException e) {
Log.e("MockDeductionAgilanbu", "Got exception --- " + e.getMessage());
}
}
} catch (Exception w) {
w.printStackTrace();
}
if (count > 0)
return true;
return false;
}

Categories

Resources