/* * Copyright (C) 2008 Google Inc. * * 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.location; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.location.Criteria; import android.location.Location; import android.location.LocationManager; import android.provider.Settings; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; /** * Tests for LocationManager.addProximityAlert * * TODO: add tests for more scenarios * * To run: * adb shell am instrument -e class android.location.LocationManagerProximityTest \ * -w android.core/android.test.InstrumentationTestRunner * * This test requires that the "Allow mock locations" setting be enabled * */ @MediumTest public class LocationManagerProximityTest extends AndroidTestCase { private static final int UPDATE_LOCATION_WAIT_TIME = 1000; private static final int PROXIMITY_WAIT_TIME = 2000; private LocationManager mLocationManager; private PendingIntent mPendingIntent; private TestIntentReceiver mIntentReceiver; private static final String LOG_TAG = "LocationProximityTest"; // use network provider as mock location provider, because: // - proximity alert is hardcoded to listen to only network or gps // - 'network' provider is not installed in emulator, so can mock it // using test provider APIs private static final String PROVIDER_NAME = LocationManager.NETWORK_PROVIDER; @Override protected void setUp() throws Exception { super.setUp(); // test that mock locations are allowed so a more descriptive error message can be logged if (Settings.Secure.getInt(getContext().getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 0) { fail("Mock locations are currently disabled in Settings - this test requires " + "mock locations"); } mLocationManager = (LocationManager) getContext(). getSystemService(Context.LOCATION_SERVICE); if (mLocationManager.getProvider(PROVIDER_NAME) != null) { mLocationManager.removeTestProvider(PROVIDER_NAME); } mLocationManager.addTestProvider(PROVIDER_NAME, true, //requiresNetwork, false, // requiresSatellite, true, // requiresCell, false, // hasMonetaryCost, false, // supportsAltitude, false, // supportsSpeed, s false, // upportsBearing, Criteria.POWER_MEDIUM, // powerRequirement Criteria.ACCURACY_FINE); // accuracy } @Override protected void tearDown() throws Exception { mLocationManager.removeTestProvider(PROVIDER_NAME); if (mPendingIntent != null) { mLocationManager.removeProximityAlert(mPendingIntent); } if (mIntentReceiver != null) { getContext().unregisterReceiver(mIntentReceiver); } } /** * Tests basic proximity alert when entering proximity */ public void testEnterProximity() throws Exception { doTestEnterProximity(10000); } /** * Tests proximity alert when entering proximity, with no expiration */ public void testEnterProximity_noexpire() throws Exception { doTestEnterProximity(-1); } /** * Helper variant for testing enter proximity scenario * TODO: add additional parameters as more scenarios are added * * @param expiration - expiry of proximity alert */ private void doTestEnterProximity(long expiration) throws Exception { // update location to outside proximity range synchronousSendLocation(30, 30); registerProximityListener(0, 0, 1000, expiration); sendLocation(0, 0); waitForAlert(); assertProximityType(true); } /** * Tests basic proximity alert when exiting proximity */ public void testExitProximity() throws Exception { // first do enter proximity scenario doTestEnterProximity(-1); // now update to trigger exit proximity proximity mIntentReceiver.clearReceivedIntents(); sendLocation(20, 20); waitForAlert(); assertProximityType(false); } /** * Registers the proximity intent receiver */ private void registerProximityListener(double latitude, double longitude, float radius, long expiration) { String intentKey = "testProximity"; Intent proximityIntent = new Intent(intentKey); mPendingIntent = PendingIntent.getBroadcast(getContext(), 0, proximityIntent, PendingIntent.FLAG_CANCEL_CURRENT); mIntentReceiver = new TestIntentReceiver(intentKey); mLocationManager.addProximityAlert(latitude, longitude, radius, expiration, mPendingIntent); getContext().registerReceiver(mIntentReceiver, mIntentReceiver.getFilter()); } /** * Blocks until proximity intent notification is received * @throws InterruptedException */ private void waitForAlert() throws InterruptedException { Log.d(LOG_TAG, "Waiting for proximity update"); synchronized (mIntentReceiver) { mIntentReceiver.wait(PROXIMITY_WAIT_TIME); } assertNotNull("Did not receive proximity alert", mIntentReceiver.getLastReceivedIntent()); } /** * Asserts that the received intent had the enter proximity property set as * expected * @param expectedEnterProximity - true if enter proximity expected, false if * exit expected */ private void assertProximityType(boolean expectedEnterProximity) throws Exception { boolean proximityTest = mIntentReceiver.getLastReceivedIntent(). getBooleanExtra(LocationManager.KEY_PROXIMITY_ENTERING, !expectedEnterProximity); assertEquals("proximity alert not set to expected enter proximity value", expectedEnterProximity, proximityTest); } /** * Synchronous variant of sendLocation */ private void synchronousSendLocation(final double latitude, final double longitude) throws InterruptedException { sendLocation(latitude, longitude, this); // wait for location to be set synchronized (this) { wait(UPDATE_LOCATION_WAIT_TIME); } } /** * Asynchronously update the mock location provider without notification */ private void sendLocation(final double latitude, final double longitude) { sendLocation(latitude, longitude, null); } /** * Asynchronously update the mock location provider with given latitude and * longitude * * @param latitude - update location * @param longitude - update location * @param observer - optionally, object to notify when update is sent.If * null, no update will be sent */ private void sendLocation(final double latitude, final double longitude, final Object observer) { Thread locationUpdater = new Thread() { @Override public void run() { Location loc = new Location(PROVIDER_NAME); loc.setLatitude(latitude); loc.setLongitude(longitude); loc.setTime(java.lang.System.currentTimeMillis()); Log.d(LOG_TAG, "Sending update for " + PROVIDER_NAME); mLocationManager.setTestProviderLocation(PROVIDER_NAME, loc); if (observer != null) { synchronized (observer) { observer.notify(); } } } }; locationUpdater.start(); } /** * 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())) { Log.d(LOG_TAG, "Intent Received: " + intent.toString()); mLastReceivedIntent = intent; synchronized (this) { notify(); } } } public Intent getLastReceivedIntent() { return mLastReceivedIntent; } public void clearReceivedIntents() { mLastReceivedIntent = null; } } }