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 }