LocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) is not reliable, why? - android

One user of my app reported that app tells network for location is off even he did turn it on. He sent me few screen shots and they made me think;
LocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
is not working properly. His phone is running Android 4.1.2 and first I thought this is the cause of this issue. But it was not the case. He sent me a screen shot of that setting too.
Then I googled and found this. The question seems to help me but unfortunately answer was not helpful for this case and questioner did not pursue farther.
My app is related to location and have been using LocationManager.isProviderEnabled to know GPS and Network for location is on or off. I have never been told my app is not properly knowing those settings until recently. He is the first user who reported the issue. I learned there are another method to know GPS and Network for location settings, by seeing Secure.LOCATION_PROVIDERS_ALLOWED. To see how this method work on his phone, I wrote simple app and asked him to run. This app does simple task and shows text on screen.
LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
if(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
{
string = "GPS=on\n";
}
else
{
string = "GPS=off\n";
}
if(locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))
{
string += "Network=on\n";
}
else
{
string += "Network=off\n";
}
String status = android.provider.Settings.Secure.getString(getContentResolver(), Secure.LOCATION_PROVIDERS_ALLOWED);
if(status.contains("gps"))
{
string += "GPS=on\n";
}
else
{
string += "GPS=off\n";
}
if(status.contains("network"))
{
string += "Network=on\n";
}
else
{
string += "Network=off\n";
}
He sent back screen shot again. It looks;
GPS=on
Network=off
GPS=on
Network=on
This result did not make me happy. There could be some possibilities for this.
As other person questioned before, this issue has been there on some phones.
Google broke this with 4.1.2. isProviderEnabled does not work on this version.
Although not documented, starting 4.1.2, isProviderEnabled won't work as it did before.
No, Google changed anything. This is a bug for this particular phone.
Now my questions are;
Is LocationManager.isProviderEnabled still valid for Android 4.1.2 and later?
Does seeing Secure.LOCATION_PROVIDERS_ALLOWED have some drawbacks/pit holes (when I gave up using LocationManager.isProviderEnabled?
Thanks in advance.
EDIT1:
Here you can download test app from Google Play to try or ask someone to try.
EDIT6:
I removed test app since this question is answered.
EDIT2:
I released my app which checks network provider is usable by seeing Secure.LOCATION_PROVIDERS_ALLOWED and got exception on limited phones.
These are ACRA's report.
Some phone running OS 4.1.1.
java.lang.IllegalArgumentException: requested provider network doesn't exisit
at android.os.Parcel.readException(Parcel.java:1434)
at android.os.Parcel.readException(Parcel.java:1384)
at android.location.ILocationManager$Stub$Proxy.requestLocationUpdates(ILocationManager.java:675)
at android.location.LocationManager._requestLocationUpdates(LocationManager.java:686)
at android.location.LocationManager.requestLocationUpdates(LocationManager.java:508)
Some phone running OS 4.1.2.
java.lang.IllegalArgumentException: provider=network
at android.os.Parcel.readException(Parcel.java:1439)
at android.os.Parcel.readException(Parcel.java:1389)
at android.location.ILocationManager$Stub$Proxy.requestLocationUpdates(ILocationManager.java:659)
at android.location.LocationManager._requestLocationUpdates(LocationManager.java:690)
at android.location.LocationManager.requestLocationUpdates(LocationManager.java:512)
I have never seen those exceptions until I changed a method to check network provider for location is usable or not. So I think LocationManager.isProviderEnabled is safe and seeing Secure.LOCATION_PROVIDERS_ALLOWED is risky. But this will put me back to original issue. Why LocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) returns false (and there is not really) when Secure.LOCATION_PROVIDERS_ALLOWED tells there IS. Is Android OS poorly designed? Or I have just seeing issues tied only to specific (but there are at least 2 of them) phones?
EDIT3:
I updated test app to show GPS/Network location provider seems really usable or not by accessing with requestLocationUpdates().
And I disclose 2 phones name.
1) SBM200SH, OS4.1.2, Softbank mobile, Sharp Corporation
2) HTX21 (INFOBAR A02), OS4.1.1, KDDI, HTC
EDIT4:
I found 3rd phone.
3) SBM203SH, OS4.1.2, Softbank mobile, Sharp Corporation
EDIT5:
Sharp Corporation is running discussion space for mobile developers. I posted topic by presenting this SO's question. I hope someone at Sharp Corporation takes action for this. I will keep this updated.

Developer support provided by Sharp corporation is excellent and they answered to my question in less than 48 hours.
This is what I got from them.
There are 2 conditions must be met that LocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) returns true.
Some internal state is ready for network location.
Network location is enabled on setting screen.
Second one is obvious. But first one is not. They told how to simulate first one is negative. You can confirm the issue with steps shown below and running my test app (please see my question for link to download).
Open settings of you phone.
Tap Applications.
Tap All tab.
Find "Network Location", tap it.
Tap "Disable".
Reboot your phone.
Run test app.
For reason I can't understand the user's phone failed to do something related to first condition shown above and exhibits the issue.
Conclusion:
LocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) is reliable. And be aware, Secure.LOCATION_PROVIDERS_ALLOWED is less reliable.

The modern way to check the users Location settings is through LOCATION_MODE in Settings.Secure
For example if you simply want to know if the user has disabled them or not, you can do:
public static boolean isLocationEnabled(Context context) {
return getLocationMode(context) != Settings.Secure.LOCATION_MODE_OFF;
}
private static int getLocationMode(Context context) {
return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
}
This will return true if Location is enabled. If you need finer granularity see the docs for details.
This method is better suited when using the Google Services Location APIs than the old NETWORK_PROVIDER and GPS_PROVIDER ways. Note: Requires KitKat / API19

Not directly an answer to your question(s), but check out the new Location API that Google launched last week. It's really easy to implement and it will check for the best possible location without wasting battery.
http://developer.android.com/google/play-services/location.html
and here's a session at Google I/O about this new API and how to use it.
http://www.youtube.com/watch?v=Bte_GHuxUGc
This way you don't need to worry about checking if the GPS is on or not and stuff like that

Location Manager is not reliable on some phones. You may notice that if you launch google maps all of a sudden your app works. That is because Google Maps kicked the LocationManager. Which also means that there is programmatic way to kick that dude alive. So I used
HomeScreen.getLocationManager().requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 0, 0, new LocationListener() {
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onLocationChanged(final Location location) {
}
});
After the above code, I called what ever I needed from LocationManager and it kinda worked. If not try out the new API's LocationClient. This is suppose to be much better, battery, accuracy and reliability.

Related

is there any way to read "select mock location app" from Android developer settings?

I know the recomended way to know in Android if a location is a fake point is something like this:
boolean isMock = false;
if (android.os.Build.VERSION.SDK_INT >= 18) {
isMock = location.isFromMockProvider();
} else { // Old Android versions (<6)
isMock = !Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0");
}
So, for current Androids I have to request location permission to know if this is a mock location. But I think it is not necessary if I can read "select mock location app".
Also, if you want to show an alert message you can custom this message. e.g.: You could say something like this: "Please remove com.lexa.fakegps app". And when user clicks on "Accept", you could redirect to next link: https://play.google.com/store/apps/details?id=com.lexa.fakegps
Also, there are fake gps apps that return false in the isFromMockProvider method.
Also, you can do stats with apps more use for yours users.
And finally, I don't understood why you can read every setting of developer settings except "select mock location app":
https://developer.android.com/reference/android/provider/Settings
Is there any solution? Or simply is it a stupid impossible thing?
I haven't tested this but this looks legit. I spent an hour or so trying to come up with my own solution and it was right there on GitHub.
https://github.com/GantMan/jail-monkey/blob/master/android/src/main/java/com/gantix/JailMonkey/MockLocation/MockLocationCheck.java
So first of all... by using the following code
boolean isMock = false;
if (android.os.Build.VERSION.SDK_INT >= 18) {
isMock = location.isFromMockProvider();
} else { // Old Android versions (<6)
isMock = !Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0");
}
you can't ensure the device location is faked all the time.
For older versions of android
isMock = !Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0");
is being used but what it does basically it just gives whether the user turned on the settings for allowing mock location... so it just acts as a filter to check
So if I am using an old phone and turn this setting on even if I am not using fake location it still returns true
For latest versions of android
isMock = location.isFromMockProvider();
it is buggy ... may not always return correct answer i.e even if you set the location using mock location app sometimes it may return false.
Conclusion:
To conclude reading the "select mock location app" setting and making a decision on its value is just a stupid thing. and you can't write software that is always bug-free.
I personally tried and tested with some of the fake GPS apps like
Lexa Fake GPS Location
Fake GPS
for me during my tests isFromMockProvider() always returned me the correct output So I found it the probability of finding the bug is rare.
And in order to make sure it's a buggy value I found this remedy on this post : Location on Android: Stop Mocking Me! I quote remedy from this article
Since most location readings are correctly labeled, it’s not too
difficult to identify and reject the false negatives. I chose the
following strategy:
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.
And I also recommend you to go through FauChristian Answer for Detect or prevent if the user uses fake location
Thank you...

How to get device location

So, I'm trying to build a weather app for practice and I've been running into the problem of trying to get the location. I've read people suggest to use getLastLocation through the fused location API, problem with that is if they don't already have a location registered on the device it comes up null. I've noticed using the emulator times that this come up is rare, but I'd still like my app to handle it properly. One instance where you might run into this is if they just turned GPS off and back on, or if the phone was just turned on. One thing I did was if getLastLocation does come back null, is to request an update, but then you still have to wait for the device to register an updated location, which with a weather app all of the data is based off of and you're still kind of running into the same problem. I've noticed with other apps this isn't a problem, like sometimes I actually have to load up Google Maps to get it to register a location. How does Google Maps force it to update the location? Here's the example from my getLocation method:
public void getLocation() throws SecurityException {
boolean gps_enabled;
LocationManager lm = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (gps_enabled) {
Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (location != null) {
startGetForecast(location);
} else {
LocationRequest request = LocationRequest.create();
request.setNumUpdates(1);
request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, request, this);
}
}
else {
AlertDialogFragment alertDialogFragment = new AlertDialogFragment();
alertDialogFragment.setErrorTexts("Location Unavailable", "Could not retrieve location information.", "Exit");
alertDialogFragment.show(getSupportFragmentManager(), "Location Unavailable");
}
}
Note: This answer could sound like not answering to the question directly but after chatting (comments in question) with OP as his code was ok in general but the condition around the problem, this answer has satisfied OP.
Fortunately, the standard library Is not the only way Google can get
code into your hands. In addition to the standard library, Google
provider Play services. This is a set of common services that are
installed alongside the Google Play store application. To fix this
location mess, Google shipped a new locations services in Play
Services called the Fused Location Provider.
Since these libraries
live in another application, you must actually have that application
installed. This means that only devices with the Play Store app
installed and up to date will be able to use your application.
So the conclusion is:
You need to test the app on your device as mentioned above.
Since you are using Fused Location Provider Api, that means the Api will automatically determine last location from one of the following sources:
GPS radio
Coarse points from cell towers
WiFi connections
So you could easily remove the GPS if condition from your code
Be a ware of that if you must use GPS signal, you need to be out side the building, lab, home or office.
If you want to dig more find more in the mentioned resources and there is a lot of online resources.
Resources: The first part of the answer, the source of it is from:
Android Programming: The Big Nerd Ranch Guide 2nd edition chapter 31 page 552 under Google Play Services

How turn on GPS programmatically in my android application?

If we use this code we see a message: searching for GPS. However, the GPS symbol is merely shown; GPS doesn't actually work:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent("android.location.GPS_ENABLED_CHANGE");
intent.putExtra("enabled", true);
this.sendBroadcast(intent);
}
Why isn't it working ? And how to make it work correctly ?
That's not allowed anymore. If you take a look at this bug report, this hack was subverted in Android 4.4. It still works on older OS versions, though you shouldn't be using it anywhere now. To quote that report:
This doesn't work ... however, it can cause stuff to react as if the GPS status changed -- for example, the HTC One S will show the GPS icon in the status bar, even though the GPS is still disabled.
That explains why you can see the GPS icon even though it isn't actually ON. Now as for why you can't do that ...
Android's GPS technology periodically sends location data to Google even when no third-party apps are actually using the GPS function. In many Western countries this is seen as a major violation of privacy. That's why Google made it mandatory to get the user's consent before using the GPS function. The following dialog is seen whenever the user turns GPS on:
And hence it is no longer possible to programmatically change the GPS settings, as by necessity it requires the user's permission. What the programmer can do is direct the user to the GPS settings by calling
startActivity(context, new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
and let the user make a choice.
As an interesting point, if you try sending the GPS_ENABLED_CHANGE broadcast on the new OS versions, you get a
java.lang.SecurityException: Permission Denial:
not allowed to send broadcast android.location.GPS_ENABLED_CHANGE
error. As you can see, its a SecurityException with a permission denial message.

Android Force Location of Apps

I work in QA for an company that helps market applications. Currently I have been tasked to ensure location dependent apps function if correctly (ie if im in russia the app displays the russian content and if i change to us the app updates to the us content)
I have tried numerous items to accomplish this but get no where.
What i have tried:
Proxy. Setting this up via wifi to several locations. Both the app and the device still show my current location.
Apps: I have tried all of the following apps and they all give the same result as above.
Fake Location
AutoProxy
Fake GPS
I have enabled developer mode on the devices, and have ensured that "Mock Locations" is checked.
Use case 1:
A developer whats us to test his app which is only available in england. Google Play still sees me in US
Use case 2:
A developer releases a game where the background changes based on your location if i cant force the location change I cant verify this feature works.
Im guessing the fails so far are due to the fact that the location being called in our test apps is network location not GPS?
Any help would be awesome. Even if you could just point me in a direction.
Thank you Greatly
J
Update:
Ok, Thanks to the answers about google play and mock locations. So with those in mind lets think about it from another perspective. If the app is not calling for mock location, Is there anyway to force a mock location override? im trying to cover all the bases here, One app for sure does not use this method, so I still need to find a way around.
depending on how they are calling for the location, I would think (remember im knew here) that using a proxy would work, however as stated above, i'm not getting anywhere there either. Did try a few free proxy's from hidemyass but even the browser wouldn't work with them.
Yes, network location does not get faked by mock location providers. I don't know why not, but it only fakes GPS. In addition, your app has to request the MOCK permission to get it. If it doesn't have this permission, setting a mock location will not actually fake anything to the app. This is for security purposes, so a malicious app can't start broadcasting the wrong location to the phone.

Geofences not available and how to handle it

I'm working with geofences on android, it's all working fine on most phones,
but on some of them, it's just not working (showing "geofences not available" in my error logs).
Some users don't have their location tracking enabled for Google Play Services.
I think that is the reason why geofencing is not working on their phones. (right?)
Now I want a way to detect this. (inform the user and eventually show them how to fix it)
I know there is a way to know wether google play services is available or not (and I already do this), but that doesn't say anything about the location services being enabled or not.
I know that I can check it while adding or removing geofence, there is an error code for it (geofence not available Location Status Codes), but that's not enough.
Some users disable the location services after they added the geofences. Then I have no way of knowing this, and my app won't work. (complaining users etc.).
At least I have to inform the user when they open the app to check what's wrong.
Does someone have an idea on how to do this?
The best I can do now is adding and removing a dummy geofence on app boot, but there has to be a better way?
EDIT:
I tested it with a device that had the issue, removing the app and reinstalling seems to fix the problem.
I'm not doing anything special on the first boot, so this is really weird.
It looks as if there is a problem with the google play services connection, and reinstalling the app does something special with these services.
Is this possible? It gives no errors while connecting to them, but it did when I tried to set geofences (see above).
The geofences will get disabled (and unregistered) if the GPS Provider or the Network Provider are disabled for whatever reason. That will cause your onAddGeofencesResult() to get called with an status code of error (1000). You can detect it right there.
Or, you detect this by implementing a Receiver that gets called in response to android.location.PROVIDERS_CHANGED. Remember to add the receiver to the AndroidManifest
<receiver android:name=".service.GeofenceProvidersChangedBroadcastReceiver">
<intent-filter>
<action android:name="android.location.PROVIDERS_CHANGED"/>
</intent-filter>
</receiver>
On the receiver implementation, on its onReceive(), get the location manager and test for the providers status:
public class ProvidersChangedBroadcastReceiver extends WakefulBroadcastReceiver {
:
:
#Override
public void onReceive(Context context, Intent intent) {
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
// GPS_PROVIDER IS enabled...
} else {
// GPS_PROVIDER is NOT enabled...
}
if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
// NETWORK_PROVIDER IS enabled...
} else {
// NETWORK_PROVIDER is NOT enabled...
}
:
:
:
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) &&
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
// We are good for geofencing as both GPS and Network providers are enabled....
}
:
:
:
}

Categories

Resources