Merge "Implement stationary location throttling" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2727a9130f
@@ -10779,6 +10779,13 @@ public final class Settings {
|
||||
public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST =
|
||||
"location_ignore_settings_package_whitelist";
|
||||
|
||||
/**
|
||||
* Whether to throttle location when the device is in doze and still.
|
||||
* @hide
|
||||
*/
|
||||
public static final String LOCATION_ENABLE_STATIONARY_THROTTLE =
|
||||
"location_enable_stationary_throttle";
|
||||
|
||||
/**
|
||||
* Whether TV will switch to MHL port when a mobile device is plugged in.
|
||||
* (0 = false, 1 = true)
|
||||
|
||||
@@ -569,7 +569,12 @@ public class Location implements Parcelable {
|
||||
|
||||
/** @hide */
|
||||
public long getElapsedRealtimeAgeMillis() {
|
||||
return NANOSECONDS.toMillis(getElapsedRealtimeAgeNanos());
|
||||
return getElapsedRealtimeAgeMillis(SystemClock.elapsedRealtime());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public long getElapsedRealtimeAgeMillis(long referenceRealtimeMs) {
|
||||
return referenceRealtimeMs - NANOSECONDS.toMillis(mElapsedRealtimeNanos);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -323,6 +323,7 @@ public class SettingsBackupTest {
|
||||
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
|
||||
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
|
||||
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
|
||||
Settings.Global.LOCATION_ENABLE_STATIONARY_THROTTLE,
|
||||
Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST,
|
||||
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
|
||||
Settings.Global.LOCK_SOUND,
|
||||
|
||||
@@ -78,6 +78,7 @@ import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.WorkSource;
|
||||
import android.os.WorkSource.WorkChain;
|
||||
import android.provider.Settings;
|
||||
import android.stats.location.LocationStatsEnums;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.IndentingPrintWriter;
|
||||
@@ -97,6 +98,8 @@ import com.android.server.location.gnss.hal.GnssNative;
|
||||
import com.android.server.location.injector.AlarmHelper;
|
||||
import com.android.server.location.injector.AppForegroundHelper;
|
||||
import com.android.server.location.injector.AppOpsHelper;
|
||||
import com.android.server.location.injector.DeviceIdleHelper;
|
||||
import com.android.server.location.injector.DeviceStationaryHelper;
|
||||
import com.android.server.location.injector.EmergencyHelper;
|
||||
import com.android.server.location.injector.Injector;
|
||||
import com.android.server.location.injector.LocationAttributionHelper;
|
||||
@@ -108,6 +111,8 @@ import com.android.server.location.injector.SettingsHelper;
|
||||
import com.android.server.location.injector.SystemAlarmHelper;
|
||||
import com.android.server.location.injector.SystemAppForegroundHelper;
|
||||
import com.android.server.location.injector.SystemAppOpsHelper;
|
||||
import com.android.server.location.injector.SystemDeviceIdleHelper;
|
||||
import com.android.server.location.injector.SystemDeviceStationaryHelper;
|
||||
import com.android.server.location.injector.SystemEmergencyHelper;
|
||||
import com.android.server.location.injector.SystemLocationPermissionsHelper;
|
||||
import com.android.server.location.injector.SystemLocationPowerSaveModeHelper;
|
||||
@@ -120,6 +125,7 @@ import com.android.server.location.provider.LocationProviderManager;
|
||||
import com.android.server.location.provider.MockLocationProvider;
|
||||
import com.android.server.location.provider.PassiveLocationProvider;
|
||||
import com.android.server.location.provider.PassiveLocationProviderManager;
|
||||
import com.android.server.location.provider.StationaryThrottlingLocationProvider;
|
||||
import com.android.server.location.provider.proxy.ProxyLocationProvider;
|
||||
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
|
||||
|
||||
@@ -313,6 +319,18 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
|
||||
manager.startManager();
|
||||
if (realProvider != null) {
|
||||
|
||||
// custom logic wrapping all non-passive providers
|
||||
if (manager != mPassiveManager) {
|
||||
boolean enableStationaryThrottling = Settings.Global.getInt(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Global.LOCATION_ENABLE_STATIONARY_THROTTLE, 1) != 0;
|
||||
if (enableStationaryThrottling) {
|
||||
realProvider = new StationaryThrottlingLocationProvider(manager.getName(),
|
||||
mInjector, realProvider, mEventLog);
|
||||
}
|
||||
}
|
||||
|
||||
manager.setRealProvider(realProvider);
|
||||
}
|
||||
mProviderManagers.add(manager);
|
||||
@@ -1368,6 +1386,8 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
private final SystemAppForegroundHelper mAppForegroundHelper;
|
||||
private final SystemLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
|
||||
private final SystemScreenInteractiveHelper mScreenInteractiveHelper;
|
||||
private final SystemDeviceStationaryHelper mDeviceStationaryHelper;
|
||||
private final SystemDeviceIdleHelper mDeviceIdleHelper;
|
||||
private final LocationAttributionHelper mLocationAttributionHelper;
|
||||
private final LocationUsageLogger mLocationUsageLogger;
|
||||
|
||||
@@ -1391,6 +1411,8 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
mAppForegroundHelper = new SystemAppForegroundHelper(context);
|
||||
mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context, eventLog);
|
||||
mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
|
||||
mDeviceStationaryHelper = new SystemDeviceStationaryHelper();
|
||||
mDeviceIdleHelper = new SystemDeviceIdleHelper(context);
|
||||
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
|
||||
mLocationUsageLogger = new LocationUsageLogger();
|
||||
}
|
||||
@@ -1450,6 +1472,16 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
return mScreenInteractiveHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceStationaryHelper getDeviceStationaryHelper() {
|
||||
return mDeviceStationaryHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceIdleHelper getDeviceIdleHelper() {
|
||||
return mDeviceIdleHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationAttributionHelper getLocationAttributionHelper() {
|
||||
return mLocationAttributionHelper;
|
||||
|
||||
@@ -60,7 +60,8 @@ public class LocationEventLog extends LocalEventLog {
|
||||
private static final int EVENT_PROVIDER_UPDATE_REQUEST = 6;
|
||||
private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 7;
|
||||
private static final int EVENT_PROVIDER_DELIVER_LOCATION = 8;
|
||||
private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 9;
|
||||
private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 9;
|
||||
private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 10;
|
||||
|
||||
@GuardedBy("mAggregateStats")
|
||||
private final ArrayMap<String, ArrayMap<String, AggregateStats>> mAggregateStats;
|
||||
@@ -167,6 +168,11 @@ public class LocationEventLog extends LocalEventLog {
|
||||
getAggregateStats(provider, identity.getPackageName()).markLocationDelivered();
|
||||
}
|
||||
|
||||
/** Logs that a provider has entered or exited stationary throttling. */
|
||||
public void logProviderStationaryThrottled(String provider, boolean throttled) {
|
||||
addLogEvent(EVENT_PROVIDER_STATIONARY_THROTTLED, provider, throttled);
|
||||
}
|
||||
|
||||
/** Logs that the location power save mode has changed. */
|
||||
public void logLocationPowerSaveMode(
|
||||
@LocationPowerSaveMode int locationPowerSaveMode) {
|
||||
@@ -198,6 +204,9 @@ public class LocationEventLog extends LocalEventLog {
|
||||
case EVENT_PROVIDER_DELIVER_LOCATION:
|
||||
return new ProviderDeliverLocationEvent(timeDelta, (String) args[0],
|
||||
(Integer) args[1], (CallerIdentity) args[2]);
|
||||
case EVENT_PROVIDER_STATIONARY_THROTTLED:
|
||||
return new ProviderStationaryThrottledEvent(timeDelta, (String) args[0],
|
||||
(Boolean) args[1]);
|
||||
case EVENT_LOCATION_POWER_SAVE_MODE_CHANGE:
|
||||
return new LocationPowerSaveModeEvent(timeDelta, (Integer) args[0]);
|
||||
default:
|
||||
@@ -332,6 +341,23 @@ public class LocationEventLog extends LocalEventLog {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ProviderStationaryThrottledEvent extends ProviderEvent {
|
||||
|
||||
private final boolean mStationaryThrottled;
|
||||
|
||||
private ProviderStationaryThrottledEvent(long timeDelta, String provider,
|
||||
boolean stationaryThrottled) {
|
||||
super(timeDelta, provider);
|
||||
mStationaryThrottled = stationaryThrottled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLogString() {
|
||||
return mProvider + " provider stationary/idle " + (mStationaryThrottled ? "throttled"
|
||||
: "unthrottled");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LocationPowerSaveModeEvent extends LogEvent {
|
||||
|
||||
@LocationPowerSaveMode
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 com.android.server.location.injector;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Provides accessors and listeners for device idle status.
|
||||
*/
|
||||
public abstract class DeviceIdleHelper {
|
||||
|
||||
/**
|
||||
* Listener for device stationary status.
|
||||
*/
|
||||
public interface DeviceIdleListener {
|
||||
/**
|
||||
* Called when device idle state has changed.
|
||||
*/
|
||||
void onDeviceIdleChanged(boolean deviceIdle);
|
||||
}
|
||||
|
||||
private final CopyOnWriteArrayList<DeviceIdleListener> mListeners;
|
||||
|
||||
protected DeviceIdleHelper() {
|
||||
mListeners = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for device idle status.
|
||||
*/
|
||||
public final synchronized void addListener(DeviceIdleListener listener) {
|
||||
if (mListeners.add(listener) && mListeners.size() == 1) {
|
||||
registerInternal();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener for device idle status.
|
||||
*/
|
||||
public final synchronized void removeListener(DeviceIdleListener listener) {
|
||||
if (mListeners.remove(listener) && mListeners.isEmpty()) {
|
||||
unregisterInternal();
|
||||
}
|
||||
}
|
||||
|
||||
protected final void notifyDeviceIdleChanged() {
|
||||
boolean deviceIdle = isDeviceIdle();
|
||||
|
||||
for (DeviceIdleListener listener : mListeners) {
|
||||
listener.onDeviceIdleChanged(deviceIdle);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void registerInternal();
|
||||
|
||||
protected abstract void unregisterInternal();
|
||||
|
||||
/**
|
||||
* Returns true if the device is currently idle.
|
||||
*/
|
||||
public abstract boolean isDeviceIdle();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 com.android.server.location.injector;
|
||||
|
||||
import com.android.server.DeviceIdleInternal;
|
||||
|
||||
/**
|
||||
* Provides accessors and listeners for device stationary status.
|
||||
*/
|
||||
public abstract class DeviceStationaryHelper {
|
||||
|
||||
/**
|
||||
* Adds a listener for stationary status. The listener will be immediately invoked with the
|
||||
* current stationary status.
|
||||
*/
|
||||
public abstract void addListener(DeviceIdleInternal.StationaryListener listener);
|
||||
|
||||
/**
|
||||
* Removes a listener for stationary status.
|
||||
*/
|
||||
public abstract void removeListener(DeviceIdleInternal.StationaryListener listener);
|
||||
}
|
||||
@@ -48,6 +48,12 @@ public interface Injector {
|
||||
/** Returns a ScreenInteractiveHelper. */
|
||||
ScreenInteractiveHelper getScreenInteractiveHelper();
|
||||
|
||||
/** Returns a DeviceStationaryHelper. */
|
||||
DeviceStationaryHelper getDeviceStationaryHelper();
|
||||
|
||||
/** Returns a DeviceIdleHelper. */
|
||||
DeviceIdleHelper getDeviceIdleHelper();
|
||||
|
||||
/** Returns a LocationAttributionHelper. */
|
||||
LocationAttributionHelper getLocationAttributionHelper();
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 com.android.server.location.injector;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.PowerManager;
|
||||
|
||||
import com.android.server.FgThread;
|
||||
|
||||
/**
|
||||
* Provides accessors and listeners for device stationary state.
|
||||
*/
|
||||
public class SystemDeviceIdleHelper extends DeviceIdleHelper {
|
||||
|
||||
private final Context mContext;
|
||||
private final PowerManager mPowerManager;
|
||||
|
||||
private @Nullable BroadcastReceiver mReceiver;
|
||||
|
||||
public SystemDeviceIdleHelper(Context context) {
|
||||
mContext = context;
|
||||
mPowerManager = context.getSystemService(PowerManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerInternal() {
|
||||
if (mReceiver == null) {
|
||||
mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
notifyDeviceIdleChanged();
|
||||
}
|
||||
};
|
||||
|
||||
mContext.registerReceiver(mReceiver,
|
||||
new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED), null,
|
||||
FgThread.getHandler());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterInternal() {
|
||||
if (mReceiver != null) {
|
||||
mContext.unregisterReceiver(mReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeviceIdle() {
|
||||
return mPowerManager.isDeviceIdleMode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 com.android.server.location.injector;
|
||||
|
||||
import com.android.server.DeviceIdleInternal;
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Provides accessors and listeners for device stationary state.
|
||||
*/
|
||||
public class SystemDeviceStationaryHelper extends DeviceStationaryHelper {
|
||||
|
||||
private final DeviceIdleInternal mDeviceIdle;
|
||||
|
||||
public SystemDeviceStationaryHelper() {
|
||||
mDeviceIdle = Objects.requireNonNull(LocalServices.getService(DeviceIdleInternal.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(DeviceIdleInternal.StationaryListener listener) {
|
||||
mDeviceIdle.registerStationaryListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(DeviceIdleInternal.StationaryListener listener) {
|
||||
mDeviceIdle.unregisterStationaryListener(listener);
|
||||
}
|
||||
}
|
||||
@@ -337,8 +337,10 @@ public abstract class AbstractLocationProvider {
|
||||
|
||||
@Override
|
||||
public State setListener(@Nullable Listener listener) {
|
||||
return mInternalState.updateAndGet(
|
||||
internalState -> internalState.withListener(listener)).state;
|
||||
InternalState oldInternalState = mInternalState.getAndUpdate(
|
||||
internalState -> internalState.withListener(listener));
|
||||
Preconditions.checkState(listener == null || oldInternalState.listener == null);
|
||||
return oldInternalState.state;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 com.android.server.location.provider;
|
||||
|
||||
import android.location.LocationResult;
|
||||
import android.location.provider.ProviderRequest;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Helper class for wrapping location providers. Subclasses MUST ensure that
|
||||
* {@link #initializeDelegate()} is invoked before the delegate is used.
|
||||
*/
|
||||
class DelegateLocationProvider extends AbstractLocationProvider
|
||||
implements AbstractLocationProvider.Listener {
|
||||
|
||||
private final Object mInitializationLock = new Object();
|
||||
|
||||
protected final AbstractLocationProvider mDelegate;
|
||||
|
||||
private boolean mInitialized = false;
|
||||
|
||||
DelegateLocationProvider(Executor executor, AbstractLocationProvider delegate) {
|
||||
super(executor, null, null);
|
||||
|
||||
mDelegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function initializes state from the delegate and allows all other callbacks to be
|
||||
* immediately invoked. If this method is invoked from a subclass constructor, it should be the
|
||||
* last statement in the constructor since it allows the subclass' reference to escape.
|
||||
*/
|
||||
protected void initializeDelegate() {
|
||||
synchronized (mInitializationLock) {
|
||||
Preconditions.checkState(!mInitialized);
|
||||
setState(previousState -> mDelegate.getController().setListener(this));
|
||||
mInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
// must be invoked in every listener callback to ensure they don't run until initialized
|
||||
protected final void waitForInitialization() {
|
||||
// callbacks can start coming as soon as the setListener() call in initializeDelegate
|
||||
// completes - but we can't allow any to proceed until the setState call afterwards
|
||||
// completes. acquiring the initialization lock here blocks until initialization is
|
||||
// complete, and we verify this wasn't called before initializeDelegate for some reason.
|
||||
synchronized (mInitializationLock) {
|
||||
Preconditions.checkState(mInitialized);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChanged(State oldState, State newState) {
|
||||
waitForInitialization();
|
||||
setState(previousState -> newState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReportLocation(LocationResult locationResult) {
|
||||
waitForInitialization();
|
||||
reportLocation(locationResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
Preconditions.checkState(mInitialized);
|
||||
mDelegate.getController().start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
Preconditions.checkState(mInitialized);
|
||||
mDelegate.getController().stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetRequest(ProviderRequest request) {
|
||||
Preconditions.checkState(mInitialized);
|
||||
mDelegate.getController().setRequest(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFlush(Runnable callback) {
|
||||
Preconditions.checkState(mInitialized);
|
||||
mDelegate.getController().flush(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {
|
||||
Preconditions.checkState(mInitialized);
|
||||
mDelegate.getController().sendExtraCommand(uid, pid, command, extras);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
Preconditions.checkState(mInitialized);
|
||||
mDelegate.dump(fd, pw, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 com.android.server.location.provider;
|
||||
|
||||
import static android.location.provider.ProviderRequest.INTERVAL_DISABLED;
|
||||
|
||||
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
|
||||
import static com.android.server.location.LocationManagerService.D;
|
||||
import static com.android.server.location.LocationManagerService.TAG;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.location.Location;
|
||||
import android.location.LocationResult;
|
||||
import android.location.provider.ProviderRequest;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.DeviceIdleInternal;
|
||||
import com.android.server.FgThread;
|
||||
import com.android.server.location.eventlog.LocationEventLog;
|
||||
import com.android.server.location.injector.DeviceIdleHelper;
|
||||
import com.android.server.location.injector.DeviceStationaryHelper;
|
||||
import com.android.server.location.injector.Injector;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* Throttles location providers completely while the device is in doze and stationary, and returns
|
||||
* the last known location as a new location at the appropriate interval instead. Hypothetically,
|
||||
* this throttling could be applied only when the device is stationary - however we don't trust the
|
||||
* accuracy of the on-device SMD (which could allow for 100s of feet of movement without triggering
|
||||
* in some use cases) enough to rely just on this. Instead we require the device to be in doze mode
|
||||
* and stationary to narrow down the effect of false positives/negatives.
|
||||
*/
|
||||
public final class StationaryThrottlingLocationProvider extends DelegateLocationProvider
|
||||
implements DeviceIdleHelper.DeviceIdleListener, DeviceIdleInternal.StationaryListener {
|
||||
|
||||
private static final long MAX_STATIONARY_LOCATION_AGE_MS = 30000;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
private final String mName;
|
||||
private final DeviceIdleHelper mDeviceIdleHelper;
|
||||
private final DeviceStationaryHelper mDeviceStationaryHelper;
|
||||
private final LocationEventLog mEventLog;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private boolean mDeviceIdle = false;
|
||||
@GuardedBy("mLock")
|
||||
private boolean mDeviceStationary = false;
|
||||
@GuardedBy("mLock")
|
||||
private long mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
|
||||
@GuardedBy("mLock")
|
||||
private ProviderRequest mIncomingRequest = ProviderRequest.EMPTY_REQUEST;
|
||||
@GuardedBy("mLock")
|
||||
private ProviderRequest mOutgoingRequest = ProviderRequest.EMPTY_REQUEST;
|
||||
@GuardedBy("mLock")
|
||||
private long mThrottlingIntervalMs = INTERVAL_DISABLED;
|
||||
@GuardedBy("mLock")
|
||||
private @Nullable DeliverLastLocationRunnable mDeliverLastLocationCallback = null;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private @Nullable Location mLastLocation;
|
||||
|
||||
public StationaryThrottlingLocationProvider(String name, Injector injector,
|
||||
AbstractLocationProvider delegate, LocationEventLog eventLog) {
|
||||
super(DIRECT_EXECUTOR, delegate);
|
||||
|
||||
mName = name;
|
||||
mDeviceIdleHelper = injector.getDeviceIdleHelper();
|
||||
mDeviceStationaryHelper = injector.getDeviceStationaryHelper();
|
||||
mEventLog = eventLog;
|
||||
|
||||
// must be last statement in the constructor because reference is escaping
|
||||
initializeDelegate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReportLocation(LocationResult locationResult) {
|
||||
super.onReportLocation(locationResult);
|
||||
|
||||
synchronized (mLock) {
|
||||
mLastLocation = locationResult.getLastLocation();
|
||||
onThrottlingChangedLocked(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
mDelegate.getController().start();
|
||||
|
||||
synchronized (mLock) {
|
||||
mDeviceIdleHelper.addListener(this);
|
||||
mDeviceIdle = mDeviceIdleHelper.isDeviceIdle();
|
||||
mDeviceStationaryHelper.addListener(this);
|
||||
mDeviceStationary = false;
|
||||
mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
|
||||
|
||||
onThrottlingChangedLocked(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
synchronized (mLock) {
|
||||
mDeviceStationaryHelper.removeListener(this);
|
||||
mDeviceIdleHelper.removeListener(this);
|
||||
|
||||
mIncomingRequest = ProviderRequest.EMPTY_REQUEST;
|
||||
mOutgoingRequest = ProviderRequest.EMPTY_REQUEST;
|
||||
mThrottlingIntervalMs = INTERVAL_DISABLED;
|
||||
|
||||
if (mDeliverLastLocationCallback != null) {
|
||||
FgThread.getHandler().removeCallbacks(mDeliverLastLocationCallback);
|
||||
mDeliverLastLocationCallback = null;
|
||||
}
|
||||
|
||||
mLastLocation = null;
|
||||
}
|
||||
|
||||
mDelegate.getController().stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetRequest(ProviderRequest request) {
|
||||
synchronized (mLock) {
|
||||
mIncomingRequest = request;
|
||||
onThrottlingChangedLocked(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceIdleChanged(boolean deviceIdle) {
|
||||
synchronized (mLock) {
|
||||
if (deviceIdle == mDeviceIdle) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDeviceIdle = deviceIdle;
|
||||
onThrottlingChangedLocked(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceStationaryChanged(boolean deviceStationary) {
|
||||
synchronized (mLock) {
|
||||
if (mDeviceStationary == deviceStationary) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDeviceStationary = deviceStationary;
|
||||
if (mDeviceStationary) {
|
||||
mDeviceStationaryRealtimeMs = SystemClock.elapsedRealtime();
|
||||
} else {
|
||||
mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
|
||||
}
|
||||
onThrottlingChangedLocked(false);
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void onThrottlingChangedLocked(boolean deliverImmediate) {
|
||||
long throttlingIntervalMs = INTERVAL_DISABLED;
|
||||
if (mDeviceStationary && mDeviceIdle && !mIncomingRequest.isLocationSettingsIgnored()
|
||||
&& mLastLocation != null
|
||||
&& mLastLocation.getElapsedRealtimeAgeMillis(mDeviceStationaryRealtimeMs)
|
||||
<= MAX_STATIONARY_LOCATION_AGE_MS) {
|
||||
throttlingIntervalMs = mIncomingRequest.getIntervalMillis();
|
||||
}
|
||||
|
||||
ProviderRequest newRequest;
|
||||
if (throttlingIntervalMs != INTERVAL_DISABLED) {
|
||||
newRequest = ProviderRequest.EMPTY_REQUEST;
|
||||
} else {
|
||||
newRequest = mIncomingRequest;
|
||||
}
|
||||
|
||||
if (!newRequest.equals(mOutgoingRequest)) {
|
||||
mOutgoingRequest = newRequest;
|
||||
mDelegate.getController().setRequest(mOutgoingRequest);
|
||||
}
|
||||
|
||||
if (throttlingIntervalMs == mThrottlingIntervalMs) {
|
||||
return;
|
||||
}
|
||||
|
||||
long oldThrottlingIntervalMs = mThrottlingIntervalMs;
|
||||
mThrottlingIntervalMs = throttlingIntervalMs;
|
||||
|
||||
if (mThrottlingIntervalMs != INTERVAL_DISABLED) {
|
||||
if (oldThrottlingIntervalMs == INTERVAL_DISABLED) {
|
||||
if (D) {
|
||||
Log.d(TAG, mName + " provider stationary throttled");
|
||||
}
|
||||
mEventLog.logProviderStationaryThrottled(mName, true);
|
||||
}
|
||||
|
||||
if (mDeliverLastLocationCallback != null) {
|
||||
FgThread.getHandler().removeCallbacks(mDeliverLastLocationCallback);
|
||||
}
|
||||
mDeliverLastLocationCallback = new DeliverLastLocationRunnable();
|
||||
|
||||
Preconditions.checkState(mLastLocation != null);
|
||||
|
||||
if (deliverImmediate) {
|
||||
FgThread.getHandler().post(mDeliverLastLocationCallback);
|
||||
} else {
|
||||
long delayMs = mThrottlingIntervalMs - mLastLocation.getElapsedRealtimeAgeMillis();
|
||||
FgThread.getHandler().postDelayed(mDeliverLastLocationCallback, delayMs);
|
||||
}
|
||||
} else {
|
||||
if (oldThrottlingIntervalMs != INTERVAL_DISABLED) {
|
||||
mEventLog.logProviderStationaryThrottled(mName, false);
|
||||
if (D) {
|
||||
Log.d(TAG, mName + " provider stationary unthrottled");
|
||||
}
|
||||
}
|
||||
|
||||
FgThread.getHandler().removeCallbacks(mDeliverLastLocationCallback);
|
||||
mDeliverLastLocationCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
if (mThrottlingIntervalMs != INTERVAL_DISABLED) {
|
||||
pw.println("stationary throttled=" + mLastLocation);
|
||||
} else {
|
||||
pw.print("stationary throttled=false");
|
||||
if (!mDeviceIdle) {
|
||||
pw.print(" (not idle)");
|
||||
}
|
||||
if (!mDeviceStationary) {
|
||||
pw.print(" (not stationary)");
|
||||
}
|
||||
pw.println();
|
||||
}
|
||||
|
||||
mDelegate.dump(fd, pw, args);
|
||||
}
|
||||
|
||||
private class DeliverLastLocationRunnable implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
Location location;
|
||||
synchronized (mLock) {
|
||||
if (mDeliverLastLocationCallback != this) {
|
||||
return;
|
||||
}
|
||||
if (mLastLocation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
location = new Location(mLastLocation);
|
||||
location.setTime(System.currentTimeMillis());
|
||||
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
|
||||
if (location.hasSpeed()) {
|
||||
location.removeSpeed();
|
||||
if (location.hasSpeedAccuracy()) {
|
||||
location.removeSpeedAccuracy();
|
||||
}
|
||||
}
|
||||
if (location.hasBearing()) {
|
||||
location.removeBearing();
|
||||
if (location.hasBearingAccuracy()) {
|
||||
location.removeBearingAccuracy();
|
||||
}
|
||||
}
|
||||
|
||||
mLastLocation = location;
|
||||
FgThread.getHandler().postDelayed(this, mThrottlingIntervalMs);
|
||||
}
|
||||
|
||||
reportLocation(LocationResult.wrap(location));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ public class ProxyLocationProvider extends AbstractLocationProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetRequest(ProviderRequest request) {
|
||||
protected void onSetRequest(ProviderRequest request) {
|
||||
mRequest = request;
|
||||
mServiceWatcher.runOnBinder(binder -> {
|
||||
ILocationProvider provider = ILocationProvider.Stub.asInterface(binder);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 com.android.server.location.injector;
|
||||
|
||||
public class FakeDeviceIdleHelper extends DeviceIdleHelper {
|
||||
|
||||
private boolean mDeviceIdle = false;
|
||||
|
||||
public void setIdle(boolean deviceIdle) {
|
||||
mDeviceIdle = deviceIdle;
|
||||
notifyDeviceIdleChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerInternal() {}
|
||||
|
||||
@Override
|
||||
protected void unregisterInternal() {}
|
||||
|
||||
@Override
|
||||
public boolean isDeviceIdle() {
|
||||
return mDeviceIdle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 com.android.server.location.injector;
|
||||
|
||||
import com.android.server.DeviceIdleInternal;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class FakeDeviceStationaryHelper extends DeviceStationaryHelper {
|
||||
|
||||
private final CopyOnWriteArrayList<DeviceIdleInternal.StationaryListener> mListeners =
|
||||
new CopyOnWriteArrayList<>();
|
||||
|
||||
private boolean mStationary = false;
|
||||
|
||||
@Override
|
||||
public void addListener(DeviceIdleInternal.StationaryListener listener) {
|
||||
synchronized (mListeners) {
|
||||
mListeners.add(listener);
|
||||
listener.onDeviceStationaryChanged(mStationary);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(DeviceIdleInternal.StationaryListener listener) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
public void setStationary(boolean stationary) {
|
||||
synchronized (mListeners) {
|
||||
mStationary = stationary;
|
||||
}
|
||||
|
||||
for (DeviceIdleInternal.StationaryListener listener : mListeners) {
|
||||
listener.onDeviceStationaryChanged(stationary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,8 @@ public class TestInjector implements Injector {
|
||||
private final FakeAppForegroundHelper mAppForegroundHelper;
|
||||
private final FakeLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
|
||||
private final FakeScreenInteractiveHelper mScreenInteractiveHelper;
|
||||
private final FakeDeviceStationaryHelper mDeviceStationaryHelper;
|
||||
private final FakeDeviceIdleHelper mDeviceIdleHelper;
|
||||
private final LocationAttributionHelper mLocationAttributionHelper;
|
||||
private final FakeEmergencyHelper mEmergencyHelper;
|
||||
private final LocationUsageLogger mLocationUsageLogger;
|
||||
@@ -45,6 +47,8 @@ public class TestInjector implements Injector {
|
||||
mAppForegroundHelper = new FakeAppForegroundHelper();
|
||||
mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper(eventLog);
|
||||
mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
|
||||
mDeviceStationaryHelper = new FakeDeviceStationaryHelper();
|
||||
mDeviceIdleHelper = new FakeDeviceIdleHelper();
|
||||
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
|
||||
mEmergencyHelper = new FakeEmergencyHelper();
|
||||
mLocationUsageLogger = new LocationUsageLogger();
|
||||
@@ -90,6 +94,16 @@ public class TestInjector implements Injector {
|
||||
return mScreenInteractiveHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FakeDeviceStationaryHelper getDeviceStationaryHelper() {
|
||||
return mDeviceStationaryHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FakeDeviceIdleHelper getDeviceIdleHelper() {
|
||||
return mDeviceIdleHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationAttributionHelper getLocationAttributionHelper() {
|
||||
return mLocationAttributionHelper;
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 com.android.server.location.provider;
|
||||
|
||||
import static com.android.server.location.LocationUtils.createLocation;
|
||||
import static com.android.server.location.LocationUtils.createLocationResult;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.after;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
import android.location.Location;
|
||||
import android.location.LocationResult;
|
||||
import android.location.provider.ProviderRequest;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.location.eventlog.LocationEventLog;
|
||||
import com.android.server.location.injector.TestInjector;
|
||||
import com.android.server.location.test.FakeProvider;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Presubmit
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class StationaryThrottlingLocationProviderTest {
|
||||
|
||||
private static final String TAG = "StationaryThrottlingLocationProviderTest";
|
||||
|
||||
private Random mRandom;
|
||||
private TestInjector mInjector;
|
||||
private FakeProvider mDelegateProvider;
|
||||
|
||||
private @Mock AbstractLocationProvider.Listener mListener;
|
||||
private @Mock FakeProvider.FakeProviderInterface mDelegate;
|
||||
|
||||
private StationaryThrottlingLocationProvider mProvider;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
initMocks(this);
|
||||
|
||||
long seed = System.currentTimeMillis();
|
||||
Log.i(TAG, "location random seed: " + seed);
|
||||
|
||||
mRandom = new Random(seed);
|
||||
|
||||
mInjector = new TestInjector();
|
||||
mDelegateProvider = new FakeProvider(mDelegate);
|
||||
|
||||
mProvider = new StationaryThrottlingLocationProvider("test_provider", mInjector,
|
||||
mDelegateProvider, new LocationEventLog());
|
||||
mProvider.getController().setListener(mListener);
|
||||
mProvider.getController().start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mProvider.getController().setRequest(ProviderRequest.EMPTY_REQUEST);
|
||||
mProvider.getController().stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrottle() {
|
||||
ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
|
||||
|
||||
mProvider.getController().setRequest(request);
|
||||
verify(mDelegate).onSetRequest(request);
|
||||
|
||||
mDelegateProvider.reportLocation(createLocationResult("test_provider", mRandom));
|
||||
verify(mListener, times(1)).onReportLocation(any(LocationResult.class));
|
||||
|
||||
mInjector.getDeviceStationaryHelper().setStationary(true);
|
||||
verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
|
||||
|
||||
mInjector.getDeviceIdleHelper().setIdle(true);
|
||||
verify(mDelegate).onSetRequest(ProviderRequest.EMPTY_REQUEST);
|
||||
verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
|
||||
verify(mListener, timeout(75).times(3)).onReportLocation(any(LocationResult.class));
|
||||
|
||||
mInjector.getDeviceStationaryHelper().setStationary(false);
|
||||
verify(mDelegate, times(2)).onSetRequest(request);
|
||||
verify(mListener, after(75).times(3)).onReportLocation(any(LocationResult.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrottle_NoInitialLocation() {
|
||||
ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
|
||||
|
||||
mProvider.getController().setRequest(request);
|
||||
verify(mDelegate).onSetRequest(request);
|
||||
|
||||
mInjector.getDeviceStationaryHelper().setStationary(true);
|
||||
mInjector.getDeviceIdleHelper().setIdle(true);
|
||||
verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
|
||||
|
||||
mDelegateProvider.reportLocation(createLocationResult("test_provider", mRandom));
|
||||
verify(mListener, times(1)).onReportLocation(any(LocationResult.class));
|
||||
verify(mDelegate, times(1)).onSetRequest(ProviderRequest.EMPTY_REQUEST);
|
||||
verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
|
||||
|
||||
mInjector.getDeviceStationaryHelper().setStationary(false);
|
||||
verify(mDelegate, times(2)).onSetRequest(request);
|
||||
verify(mListener, after(75).times(2)).onReportLocation(any(LocationResult.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoThrottle_noLocation() {
|
||||
ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
|
||||
|
||||
mProvider.getController().setRequest(request);
|
||||
verify(mDelegate).onSetRequest(request);
|
||||
verify(mListener, never()).onReportLocation(any(LocationResult.class));
|
||||
|
||||
mInjector.getDeviceStationaryHelper().setStationary(true);
|
||||
mInjector.getDeviceIdleHelper().setIdle(true);
|
||||
verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
|
||||
verify(mListener, after(75).times(0)).onReportLocation(any(LocationResult.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoThrottle_oldLocation() {
|
||||
ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
|
||||
|
||||
mProvider.getController().setRequest(request);
|
||||
verify(mDelegate).onSetRequest(request);
|
||||
|
||||
Location l = createLocation("test_provider", mRandom);
|
||||
l.setElapsedRealtimeNanos(0);
|
||||
|
||||
LocationResult loc = LocationResult.wrap(l);
|
||||
mDelegateProvider.reportLocation(loc);
|
||||
verify(mListener, times(1)).onReportLocation(loc);
|
||||
|
||||
mInjector.getDeviceStationaryHelper().setStationary(true);
|
||||
mInjector.getDeviceIdleHelper().setIdle(true);
|
||||
verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
|
||||
verify(mListener, after(75).times(1)).onReportLocation(any(LocationResult.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoThrottle_locationSettingsIgnored() {
|
||||
ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(
|
||||
50).setLocationSettingsIgnored(true).build();
|
||||
|
||||
mProvider.getController().setRequest(request);
|
||||
verify(mDelegate).onSetRequest(request);
|
||||
|
||||
LocationResult loc = createLocationResult("test_provider", mRandom);
|
||||
mDelegateProvider.reportLocation(loc);
|
||||
verify(mListener, times(1)).onReportLocation(loc);
|
||||
|
||||
mInjector.getDeviceStationaryHelper().setStationary(true);
|
||||
mInjector.getDeviceIdleHelper().setIdle(true);
|
||||
verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
|
||||
verify(mListener, after(75).times(1)).onReportLocation(any(LocationResult.class));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user