Home | History | Annotate | Download | only in location
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.cts.verifier.location;
     18 
     19 import android.location.Location;
     20 import android.location.LocationListener;
     21 import android.location.LocationManager;
     22 import android.os.Bundle;
     23 import android.os.Handler;
     24 import android.os.Message;
     25 
     26 public class LocationVerifier implements Handler.Callback {
     27     public static final String TAG = "CtsVerifierLocation";
     28 
     29     private static final int MSG_TIMEOUT = 1;
     30 
     31     private final LocationManager mLocationManager;
     32     private final PassFailLog mCb;
     33     private final String mProvider;
     34     private final long mInterval;
     35     private final long mMinActiveInterval;
     36     private final long mMinPassiveInterval;
     37     private final long mTimeout;
     38     private final Handler mHandler;
     39     private final int mRequestedUpdates;
     40     private final ActiveListener mActiveListener;
     41     private final PassiveListener mPassiveListener;
     42 
     43     private long mLastActiveTimestamp = -1;
     44     private long mLastPassiveTimestamp = -1;
     45     private int mNumActiveUpdates = 0;
     46     private int mNumPassiveUpdates = 0;
     47     private boolean mRunning = false;
     48 
     49     private class ActiveListener implements LocationListener {
     50         @Override
     51         public void onLocationChanged(Location location) {
     52             if (!mRunning) return;
     53 
     54             mNumActiveUpdates++;
     55             scheduleTimeout();
     56 
     57             long timestamp = location.getTime();
     58             long delta = timestamp - mLastActiveTimestamp;
     59             mLastActiveTimestamp = timestamp;
     60 
     61             if (mNumActiveUpdates != 1 && delta < mMinActiveInterval) {
     62                 fail(mProvider + " location updated too fast: " + delta + "ms < " +
     63                         mMinActiveInterval + "ms");
     64                 return;
     65             }
     66 
     67             mCb.log("active " + mProvider + " update (" + delta + "ms)");
     68 
     69             if (!mProvider.equals(location.getProvider())) {
     70                 fail("wrong provider in callback, actual: " + location.getProvider() +
     71                         " expected: " + mProvider);
     72                 return;
     73             }
     74 
     75             if (mNumActiveUpdates >= mRequestedUpdates) {
     76                 if (mNumPassiveUpdates < mRequestedUpdates - 1) {
     77                     fail("passive location updates not working (expected: " + mRequestedUpdates +
     78                             " received: " + mNumPassiveUpdates + ")");
     79                 }
     80                 pass();
     81             }
     82         }
     83 
     84         @Override
     85         public void onStatusChanged(String provider, int status, Bundle extras) { }
     86         @Override
     87         public void onProviderEnabled(String provider) { }
     88         @Override
     89         public void onProviderDisabled(String provider) { }
     90     }
     91 
     92     private class PassiveListener implements LocationListener {
     93         @Override
     94         public void onLocationChanged(Location location) {
     95             if (!mRunning) return;
     96             if (!location.getProvider().equals(mProvider)) return;
     97 
     98             mNumPassiveUpdates++;
     99             long timestamp = location.getTime();
    100             long delta = timestamp - mLastPassiveTimestamp;
    101             mLastPassiveTimestamp = timestamp;
    102 
    103             if (mNumPassiveUpdates != 1 && delta < mMinPassiveInterval) {
    104                 fail("passive " + mProvider + " location updated too fast: " + delta + "ms < " +
    105                         mMinPassiveInterval + "ms");
    106                 mCb.log("when passive updates are much much faster than active updates it " +
    107                         "suggests the location provider implementation is not power efficient");
    108                 if (LocationManager.GPS_PROVIDER.equals(mProvider)) {
    109                     mCb.log("check GPS_CAPABILITY_SCHEDULING in GPS driver");
    110                 }
    111                 return;
    112             }
    113 
    114             mCb.log("passive " + mProvider + " update (" + delta + "ms)");
    115         }
    116 
    117         @Override
    118         public void onStatusChanged(String provider, int status, Bundle extras) { }
    119         @Override
    120         public void onProviderEnabled(String provider) { }
    121         @Override
    122         public void onProviderDisabled(String provider) { }
    123     }
    124 
    125     public LocationVerifier(PassFailLog cb, LocationManager locationManager,
    126             String provider, long requestedInterval, int numUpdates) {
    127         mProvider = provider;
    128         mInterval = requestedInterval;
    129         // Updates can be up to 100ms ahead of schedule
    130         mMinActiveInterval = Math.max(0, requestedInterval - 100);
    131         // Allow passive updates to be up to 10x faster than active updates,
    132         // beyond that it is very likely the implementation is not taking
    133         // advantage of the interval to be power efficient
    134         mMinPassiveInterval = mMinActiveInterval / 10;
    135         // timeout at 60 seconds after interval time
    136         mTimeout = requestedInterval + 60 * 1000;
    137         mRequestedUpdates = numUpdates;
    138         mLocationManager = locationManager;
    139         mCb = cb;
    140         mHandler = new Handler(this);
    141         mActiveListener = new ActiveListener();
    142         mPassiveListener = new PassiveListener();
    143     }
    144 
    145     public void start() {
    146         mRunning = true;
    147         scheduleTimeout();
    148         mLastActiveTimestamp = System.currentTimeMillis();
    149         mLastPassiveTimestamp = mLastActiveTimestamp;
    150         mCb.log("enabling passive listener");
    151         mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0,
    152                 mPassiveListener);
    153         mCb.log("enabling " + mProvider + " (minTime=" + mInterval + "ms)");
    154         mLocationManager.requestLocationUpdates(mProvider, mInterval, 0,
    155                 mActiveListener);
    156     }
    157 
    158     public void stop() {
    159         mRunning = false;
    160         mCb.log("disabling " + mProvider);
    161         mLocationManager.removeUpdates(mActiveListener);
    162         mCb.log("disabling passive listener");
    163         mLocationManager.removeUpdates(mPassiveListener);
    164         mHandler.removeMessages(MSG_TIMEOUT);
    165     }
    166 
    167     private void pass() {
    168         stop();
    169         mCb.pass();
    170     }
    171 
    172     private void fail(String s) {
    173         stop();
    174         mCb.fail(s);
    175     }
    176 
    177     private void scheduleTimeout() {
    178         mHandler.removeMessages(MSG_TIMEOUT);
    179         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), mTimeout);
    180     }
    181 
    182     @Override
    183     public boolean handleMessage(Message msg) {
    184         if (!mRunning) return true;
    185         fail("timeout (" + mTimeout + "ms) waiting for " +
    186                 mProvider + " location change");
    187         return true;
    188     }
    189 }