blob: e68fea0054a735b26a2affb1377e330d60302c25 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.location2.cts;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.GpsStatus;
import android.location.GpsStatus.Listener;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.SystemClock;
import android.provider.Settings;
import android.test.InstrumentationTestCase;
import java.util.List;
import java.lang.Thread;
/**
* Requires the permissions
* android.permission.ACCESS_MOCK_LOCATION to mock provider
* android.permission.ACCESS_COARSE_LOCATION to access network provider
* android.permission.ACCESS_LOCATION_EXTRA_COMMANDS to send extra commands to provider
*/
public class LocationManagerTest extends InstrumentationTestCase {
private static final String FUSED_PROVIDER_NAME = "fused";
private static final long TEST_TIME_OUT_MS = 10 * 1000;
private static final double LAT = 10.0;
private static final double LNG = 40.0;
private static final double FUDGER_DELTA = 0.2;
private LocationManager mManager;
private Context mContext;
private PendingIntent mPendingIntent;
private TestIntentReceiver mIntentReceiver;
@Override
protected void setUp() throws Exception {
super.setUp();
mContext = getInstrumentation().getTargetContext();
mManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
// test that mock locations are allowed so a more descriptive error message can be logged
if (Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 0) {
fail("Mock locations are currently disabled in Settings - this test requires "
+ "mock locations");
}
}
public void testGetGpsProvider_notAllowed() {
doTestGetFineProvider_notAllowed(LocationManager.GPS_PROVIDER);
}
public void testGetFineProvider_notAllowed() {
doTestGetFineProvider_notAllowed("my fine provider name");
}
private void doTestGetFineProvider_notAllowed(String providerName) {
addTestProvider(providerName, Criteria.ACCURACY_FINE, false, true, false);
try {
mManager.getProvider(providerName);
fail("LocationManager.getProvider() did not throw SecurityException as expected");
} catch (SecurityException e) {
// expected
}
mManager.removeTestProvider(providerName);
}
public void testGetNetworkProvider_allowed() {
doTestGetCoarseProvider_allowed(LocationManager.NETWORK_PROVIDER);
}
public void testGetCoarseProvider_allowed() {
doTestGetCoarseProvider_allowed("my coarse provider name");
}
public void doTestGetCoarseProvider_allowed(String providerName) {
addTestProvider(providerName, Criteria.ACCURACY_COARSE, true, false, true);
assertNotNull(mManager.getProvider(providerName));
mManager.removeTestProvider(providerName);
}
public void testGetNetworkProviderLocationUpdates_withIntent() {
doTestGetLocationUpdates_withIntent(LocationManager.NETWORK_PROVIDER);
}
public void testGetNetworkProviderLocationUpdates_withListener() {
doTestGetLocationUpdates_withListener(LocationManager.NETWORK_PROVIDER);
}
public void testGetCoarseLocationUpdates_withIntent() {
doTestGetLocationUpdates_withIntent("my coarse provider name");
}
public void testGetCoarseLocationUpdates_withListener() {
doTestGetLocationUpdates_withListener("my coarse provider name");
}
private void doTestGetLocationUpdates_withIntent(String providerName) {
addTestProvider(providerName, Criteria.ACCURACY_COARSE, true, false, true);
registerIntentReceiver();
mManager.requestLocationUpdates(providerName, 0, 0, mPendingIntent);
updateLocation(providerName, LAT, LNG);
waitForReceiveBroadcast();
assertNotNull(mIntentReceiver.getLastReceivedIntent());
final Location location = mManager.getLastKnownLocation(providerName);
assertEquals(providerName, location.getProvider());
assertEquals(3000.0f, location.getAccuracy());
assertEquals(LAT, location.getLatitude(), FUDGER_DELTA);
assertEquals(LNG, location.getLongitude(), FUDGER_DELTA);
mManager.removeUpdates(mPendingIntent);
mManager.removeTestProvider(providerName);
}
private void doTestGetLocationUpdates_withListener(String providerName) {
addTestProvider(providerName, Criteria.ACCURACY_COARSE, true, false, true);
MockLocationListener listener = new MockLocationListener();
HandlerThread handlerThread = new HandlerThread("testLocationUpdates for " + providerName);
handlerThread.start();
mManager.requestLocationUpdates(providerName, 0, 0, listener, handlerThread.getLooper());
updateLocation(providerName, LAT, LNG);
assertTrue(listener.hasCalledOnLocationChanged(TEST_TIME_OUT_MS));
Location location = listener.getLocation();
assertEquals(providerName, location.getProvider());
assertEquals(3000.0f, location.getAccuracy());
assertEquals(LAT, location.getLatitude(), FUDGER_DELTA);
assertEquals(LNG, location.getLongitude(), FUDGER_DELTA);
mManager.removeUpdates(listener);
mManager.removeTestProvider(providerName);
}
public void testGetFusedLocationUpdates_withIntent() {
addTestProvider(FUSED_PROVIDER_NAME, Criteria.ACCURACY_COARSE, true, false, true);
registerIntentReceiver();
mManager.requestLocationUpdates(LocationRequest.create(), mPendingIntent);
updateLocation(FUSED_PROVIDER_NAME, LAT, LNG);
waitForReceiveBroadcast();
assertNotNull(mIntentReceiver.getLastReceivedIntent());
final Location location = mManager.getLastKnownLocation(FUSED_PROVIDER_NAME);
assertEquals(FUSED_PROVIDER_NAME, location.getProvider());
assertEquals(3000.0f, location.getAccuracy());
assertEquals(LAT, location.getLatitude(), FUDGER_DELTA);
assertEquals(LNG, location.getLongitude(), FUDGER_DELTA);
mManager.removeUpdates(mPendingIntent);
mManager.removeTestProvider(FUSED_PROVIDER_NAME);
}
public void testGetFusedLocationUpdates_withListener() {
addTestProvider(FUSED_PROVIDER_NAME, Criteria.ACCURACY_COARSE, true, false, true);
MockLocationListener listener = new MockLocationListener();
HandlerThread handlerThread = new HandlerThread(
"testLocationUpdates for " + FUSED_PROVIDER_NAME);
handlerThread.start();
mManager.requestLocationUpdates(LocationRequest.create(),
listener, handlerThread.getLooper());
updateLocation(FUSED_PROVIDER_NAME, LAT, LNG);
assertTrue(listener.hasCalledOnLocationChanged(TEST_TIME_OUT_MS));
Location location = listener.getLocation();
assertEquals(FUSED_PROVIDER_NAME, location.getProvider());
assertEquals(3000.0f, location.getAccuracy());
assertEquals(LAT, location.getLatitude(), FUDGER_DELTA);
assertEquals(LNG, location.getLongitude(), FUDGER_DELTA);
mManager.removeUpdates(listener);
mManager.removeTestProvider(FUSED_PROVIDER_NAME);
}
/**
* Helper method to add a test provider with given name.
*/
private void addTestProvider(final String providerName, int accuracy, boolean requiresNetwork,
boolean requiresSatellite, boolean requiresCell) {
mManager.addTestProvider(providerName,
requiresNetwork,
requiresSatellite,
requiresCell,
false, // hasMonetaryCost,
false, // supportsAltitude,
false, // supportsSpeed,
false, // supportsBearing,
Criteria.POWER_MEDIUM, // powerRequirement
accuracy); // accuracy
mManager.setTestProviderEnabled(providerName, true);
}
public void testGetProviders() {
List<String> providers = mManager.getProviders(false);
assertFalse(hasProvider(providers, LocationManager.PASSIVE_PROVIDER));
assertFalse(hasProvider(providers, LocationManager.GPS_PROVIDER));
}
private boolean hasProvider(List<String> providers, String providerName) {
for (String provider : providers) {
if (provider != null && provider.equals(providerName)) {
return true;
}
}
return false;
}
public void testGpsStatusListener() {
try {
mManager.addGpsStatusListener(new MockGpsStatusListener());
fail("Should have failed to add a gps status listener");
} catch (SecurityException e) {
// expected
}
try {
mManager.addGpsStatusListener(null);
fail("Should have failed to add a gps status listener");
} catch (SecurityException e) {
// expected
}
}
public void testSendExtraCommand() {
addTestProvider(LocationManager.NETWORK_PROVIDER, Criteria.ACCURACY_COARSE, true, false, true);
addTestProvider(LocationManager.GPS_PROVIDER, Criteria.ACCURACY_FINE, false, true, false);
// Unknown command
assertFalse(mManager.sendExtraCommand(LocationManager.NETWORK_PROVIDER, "unknown", new Bundle()));
try {
mManager.sendExtraCommand(LocationManager.GPS_PROVIDER, "unknown", new Bundle());
fail("Should have failed to send a command to the gps provider");
} catch (SecurityException e) {
// expected
}
mManager.removeTestProvider(LocationManager.GPS_PROVIDER);
mManager.removeTestProvider(LocationManager.NETWORK_PROVIDER);
}
private void registerIntentReceiver() {
String intentKey = "LocationManagerTest";
Intent proximityIntent = new Intent(intentKey);
mPendingIntent = PendingIntent.getBroadcast(mContext, 0, proximityIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
mIntentReceiver = new TestIntentReceiver(intentKey);
mContext.registerReceiver(mIntentReceiver, mIntentReceiver.getFilter());
}
/**
* Blocks until receive intent notification or time out.
*/
private void waitForReceiveBroadcast() {
synchronized (mIntentReceiver) {
try {
mIntentReceiver.wait(TEST_TIME_OUT_MS);
} catch (InterruptedException e) {
fail("Interrupted while waiting for intent: " + e);
}
}
}
private void updateLocation(final String providerName, final double latitude,
final double longitude) {
Location nlocation = new Location(providerName);
nlocation.setLatitude(latitude);
nlocation.setLongitude(longitude);
nlocation.setAccuracy(3000.0f);
nlocation.setTime(java.lang.System.currentTimeMillis());
nlocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
Location location = new Location(providerName);
location.setLatitude(latitude);
location.setLongitude(longitude);
location.setAccuracy(1.0f);
location.setTime(java.lang.System.currentTimeMillis());
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, nlocation);
mManager.setTestProviderLocation(providerName, location);
}
/**
* Helper class that receives a proximity intent and notifies the main class
* when received
*/
private static class TestIntentReceiver extends BroadcastReceiver {
private String mExpectedAction;
private Intent mLastReceivedIntent;
public TestIntentReceiver(String expectedAction) {
mExpectedAction = expectedAction;
mLastReceivedIntent = null;
}
public IntentFilter getFilter() {
IntentFilter filter = new IntentFilter(mExpectedAction);
return filter;
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && mExpectedAction.equals(intent.getAction())) {
synchronized (this) {
mLastReceivedIntent = intent;
notify();
}
}
}
public Intent getLastReceivedIntent() {
return mLastReceivedIntent;
}
public void clearReceivedIntents() {
mLastReceivedIntent = null;
}
}
private static class MockLocationListener implements LocationListener {
private String mProvider;
private int mStatus;
private Location mLocation;
private Object mStatusLock = new Object();
private Object mLocationLock = new Object();
private Object mLocationRequestLock = new Object();
private boolean mHasCalledOnLocationChanged;
private boolean mHasCalledOnProviderDisabled;
private boolean mHasCalledOnProviderEnabled;
private boolean mHasCalledOnStatusChanged;
private boolean mHasCalledRequestLocation;
public void reset(){
mHasCalledOnLocationChanged = false;
mHasCalledOnProviderDisabled = false;
mHasCalledOnProviderEnabled = false;
mHasCalledOnStatusChanged = false;
mHasCalledRequestLocation = false;
mProvider = null;
mStatus = 0;
}
/**
* Call to inform listener that location has been updates have been requested
*/
public void setLocationRequested() {
synchronized (mLocationRequestLock) {
mHasCalledRequestLocation = true;
mLocationRequestLock.notify();
}
}
public boolean hasCalledLocationRequested(long timeout) throws InterruptedException {
synchronized (mLocationRequestLock) {
if (timeout > 0 && !mHasCalledRequestLocation) {
mLocationRequestLock.wait(timeout);
}
}
return mHasCalledRequestLocation;
}
/**
* Check whether onLocationChanged() has been called. Wait up to timeout milliseconds
* for the callback.
* @param timeout Maximum time to wait for the callback, 0 to return immediately.
*/
public boolean hasCalledOnLocationChanged(long timeout) {
synchronized (mLocationLock) {
if (timeout > 0 && !mHasCalledOnLocationChanged) {
try {
mLocationLock.wait(timeout);
} catch (InterruptedException e) {
fail("Interrupted while waiting for location change: " + e);
}
}
}
return mHasCalledOnLocationChanged;
}
public boolean hasCalledOnProviderDisabled() {
return mHasCalledOnProviderDisabled;
}
public boolean hasCalledOnProviderEnabled() {
return mHasCalledOnProviderEnabled;
}
public boolean hasCalledOnStatusChanged(long timeout) throws InterruptedException {
synchronized(mStatusLock) {
// wait(0) would wait forever
if (timeout > 0 && !mHasCalledOnStatusChanged) {
mStatusLock.wait(timeout);
}
}
return mHasCalledOnStatusChanged;
}
public void onLocationChanged(Location location) {
mLocation = location;
synchronized (mLocationLock) {
mHasCalledOnLocationChanged = true;
mLocationLock.notify();
}
}
public void onProviderDisabled(String provider) {
mHasCalledOnProviderDisabled = true;
}
public void onProviderEnabled(String provider) {
mHasCalledOnProviderEnabled = true;
}
public void onStatusChanged(String provider, int status, Bundle extras) {
mProvider = provider;
mStatus = status;
synchronized (mStatusLock) {
mHasCalledOnStatusChanged = true;
mStatusLock.notify();
}
}
public String getProvider() {
return mProvider;
}
public int getStatus() {
return mStatus;
}
public Location getLocation() {
return mLocation;
}
}
private static class MockGpsStatusListener implements Listener {
private boolean mHasCallOnGpsStatusChanged;
public boolean hasCallOnGpsStatusChanged() {
return mHasCallOnGpsStatusChanged;
}
public void reset(){
mHasCallOnGpsStatusChanged = false;
}
public void onGpsStatusChanged(int event) {
mHasCallOnGpsStatusChanged = true;
}
}
}