Location CTS tests without fine permission

First CTS tests for the cases where we don't have fine permission.
More to follow.

Bug: 7377632

Change-Id: Ia95864614ddc83641f30c37808e9df7268064579
diff --git a/tests/tests/location2/Android.mk b/tests/tests/location2/Android.mk
new file mode 100644
index 0000000..e89204d
--- /dev/null
+++ b/tests/tests/location2/Android.mk
@@ -0,0 +1,37 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsLocation2TestCases
+
+LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
+
+# uncomment when dalvik.annotation.Test* are removed or part of SDK
+#LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/location2/AndroidManifest.xml b/tests/tests/location2/AndroidManifest.xml
new file mode 100644
index 0000000..118278b
--- /dev/null
+++ b/tests/tests/location2/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.location2">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
+
+    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+                     android:targetPackage="com.android.cts.location2"
+                     android:label="CTS tests of android.location"/>
+</manifest>
+
diff --git a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
new file mode 100755
index 0000000..e68fea0
--- /dev/null
+++ b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
@@ -0,0 +1,506 @@
+/*
+ * 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;
+        }
+    }
+}