/* * 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 android.location.provider; import static android.location.LocationRequest.QUALITY_BALANCED_POWER_ACCURACY; import static android.location.LocationRequest.QUALITY_HIGH_ACCURACY; import static android.location.LocationRequest.QUALITY_LOW_POWER; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; import android.location.LocationRequest; import android.location.LocationRequest.Quality; import android.os.Parcel; import android.os.Parcelable; import android.os.WorkSource; import android.util.TimeUtils; import com.android.internal.util.Preconditions; import java.util.Objects; /** * Location provider request. * @hide */ @SystemApi public final class ProviderRequest implements Parcelable { public static final long INTERVAL_DISABLED = Long.MAX_VALUE; public static final @NonNull ProviderRequest EMPTY_REQUEST = new ProviderRequest( INTERVAL_DISABLED, QUALITY_BALANCED_POWER_ACCURACY, 0, false, false, false, new WorkSource()); private final long mIntervalMillis; private final @Quality int mQuality; private final long mMaxUpdateDelayMillis; private final boolean mLowPower; private final boolean mAdasGnssBypass; private final boolean mLocationSettingsIgnored; private final WorkSource mWorkSource; /** * Listener to be invoked when a new request is set to the provider. */ public interface ChangedListener { /** * Invoked when a new request is set. * * @param provider the location provider associated with the request * @param request the new {@link ProviderRequest} */ void onProviderRequestChanged(@NonNull String provider, @NonNull ProviderRequest request); } private ProviderRequest( long intervalMillis, @Quality int quality, long maxUpdateDelayMillis, boolean lowPower, boolean adasGnssBypass, boolean locationSettingsIgnored, @NonNull WorkSource workSource) { mIntervalMillis = intervalMillis; mQuality = quality; mMaxUpdateDelayMillis = maxUpdateDelayMillis; mLowPower = lowPower; mAdasGnssBypass = adasGnssBypass; mLocationSettingsIgnored = locationSettingsIgnored; mWorkSource = Objects.requireNonNull(workSource); } /** * True if this is an active request with a valid location reporting interval, false if this * request is inactive and does not require any locations to be reported. */ public boolean isActive() { return mIntervalMillis != INTERVAL_DISABLED; } /** * The interval at which a provider should report location. Will return * {@link #INTERVAL_DISABLED} for an inactive request. */ public @IntRange(from = 0) long getIntervalMillis() { return mIntervalMillis; } /** * The quality hint for this location request. The quality hint informs the provider how it * should attempt to manage any accuracy vs power tradeoffs while attempting to satisfy this * provider request. */ public @Quality int getQuality() { return mQuality; } /** * The maximum time any location update may be delayed, and thus grouped with following updates * to enable location batching. If the maximum update delay is equal to or greater than * twice the interval, then the provider may provide batched results if possible. The maximum * batch size a provider is allowed to return is the maximum update delay divided by the * interval. */ public @IntRange(from = 0) long getMaxUpdateDelayMillis() { return mMaxUpdateDelayMillis; } /** * Whether any applicable hardware low power modes should be used to satisfy this request. */ public boolean isLowPower() { return mLowPower; } /** * Returns true if this request may access GNSS even if location settings would normally deny * this, in order to enable automotive safety features. This field is only respected on * automotive devices, and only if the client is recognized as a legitimate ADAS (Advanced * Driving Assistance Systems) application. * * @hide */ public boolean isAdasGnssBypass() { return mAdasGnssBypass; } /** * Whether the provider should ignore all location settings, user consents, power restrictions * or any other restricting factors and always satisfy this request to the best of their * ability. This should only be used in case of a user initiated emergency. */ public boolean isLocationSettingsIgnored() { return mLocationSettingsIgnored; } /** * Returns true if any bypass flag is set on this request. * * @hide */ public boolean isBypass() { return mAdasGnssBypass || mLocationSettingsIgnored; } /** * The power blame for this provider request. */ public @NonNull WorkSource getWorkSource() { return mWorkSource; } public static final @NonNull Creator CREATOR = new Creator() { @Override public ProviderRequest createFromParcel(Parcel in) { long intervalMillis = in.readLong(); if (intervalMillis == INTERVAL_DISABLED) { return EMPTY_REQUEST; } else { return new ProviderRequest( intervalMillis, /* quality= */ in.readInt(), /* maxUpdateDelayMillis= */ in.readLong(), /* lowPower= */ in.readBoolean(), /* adasGnssBypass= */ in.readBoolean(), /* locationSettingsIgnored= */ in.readBoolean(), /* workSource= */ in.readTypedObject(WorkSource.CREATOR)); } } @Override public ProviderRequest[] newArray(int size) { return new ProviderRequest[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel parcel, int flags) { parcel.writeLong(mIntervalMillis); if (mIntervalMillis != INTERVAL_DISABLED) { parcel.writeInt(mQuality); parcel.writeLong(mMaxUpdateDelayMillis); parcel.writeBoolean(mLowPower); parcel.writeBoolean(mAdasGnssBypass); parcel.writeBoolean(mLocationSettingsIgnored); parcel.writeTypedObject(mWorkSource, flags); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ProviderRequest that = (ProviderRequest) o; if (mIntervalMillis == INTERVAL_DISABLED) { return that.mIntervalMillis == INTERVAL_DISABLED; } else { return mIntervalMillis == that.mIntervalMillis && mQuality == that.mQuality && mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis && mLowPower == that.mLowPower && mAdasGnssBypass == that.mAdasGnssBypass && mLocationSettingsIgnored == that.mLocationSettingsIgnored && mWorkSource.equals(that.mWorkSource); } } @Override public int hashCode() { return Objects.hash(mIntervalMillis, mQuality, mWorkSource); } @Override public String toString() { StringBuilder s = new StringBuilder(); s.append("ProviderRequest["); if (mIntervalMillis != INTERVAL_DISABLED) { s.append("@"); TimeUtils.formatDuration(mIntervalMillis, s); if (mQuality != QUALITY_BALANCED_POWER_ACCURACY) { if (mQuality == QUALITY_HIGH_ACCURACY) { s.append(", HIGH_ACCURACY"); } else if (mQuality == QUALITY_LOW_POWER) { s.append(", LOW_POWER"); } } if (mMaxUpdateDelayMillis / 2 > mIntervalMillis) { s.append(", maxUpdateDelay="); TimeUtils.formatDuration(mMaxUpdateDelayMillis, s); } if (mLowPower) { s.append(", lowPower"); } if (mAdasGnssBypass) { s.append(", adasGnssBypass"); } if (mLocationSettingsIgnored) { s.append(", settingsBypass"); } if (!mWorkSource.isEmpty()) { s.append(", ").append(mWorkSource); } } else { s.append("OFF"); } s.append(']'); return s.toString(); } /** * A Builder for {@link ProviderRequest}s. */ public static final class Builder { private long mIntervalMillis = INTERVAL_DISABLED; private int mQuality = QUALITY_BALANCED_POWER_ACCURACY; private long mMaxUpdateDelayMillis = 0; private boolean mLowPower; private boolean mAdasGnssBypass; private boolean mLocationSettingsIgnored; private WorkSource mWorkSource = new WorkSource(); /** * Sets the request interval. Use {@link #INTERVAL_DISABLED} for an inactive request. * Defaults to {@link #INTERVAL_DISABLED}. */ public @NonNull Builder setIntervalMillis(@IntRange(from = 0) long intervalMillis) { mIntervalMillis = Preconditions.checkArgumentInRange(intervalMillis, 0, Long.MAX_VALUE, "intervalMillis"); return this; } /** * Sets the request quality. The quality is a hint to providers on how they should weigh * power vs accuracy tradeoffs. High accuracy locations may cost more power to produce, and * lower accuracy locations may cost less power to produce. Defaults to * {@link LocationRequest#QUALITY_BALANCED_POWER_ACCURACY}. */ public @NonNull Builder setQuality(@Quality int quality) { Preconditions.checkArgument( quality == QUALITY_LOW_POWER || quality == QUALITY_BALANCED_POWER_ACCURACY || quality == QUALITY_HIGH_ACCURACY); mQuality = quality; return this; } /** * Sets the maximum time any location update may be delayed, and thus grouped with following * updates to enable location batching. If the maximum update delay is equal to or greater * than twice the interval, then location providers may provide batched results. Defaults to * 0. */ public @NonNull Builder setMaxUpdateDelayMillis( @IntRange(from = 0) long maxUpdateDelayMillis) { mMaxUpdateDelayMillis = Preconditions.checkArgumentInRange(maxUpdateDelayMillis, 0, Long.MAX_VALUE, "maxUpdateDelayMillis"); return this; } /** * Sets whether hardware low power mode should be used. False by default. */ public @NonNull Builder setLowPower(boolean lowPower) { mLowPower = lowPower; return this; } /** * Sets whether this ADAS request should bypass GNSS settings. False by default. * * @hide */ public @NonNull Builder setAdasGnssBypass(boolean adasGnssBypass) { this.mAdasGnssBypass = adasGnssBypass; return this; } /** * Sets whether location settings should be ignored. False by default. */ public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) { this.mLocationSettingsIgnored = locationSettingsIgnored; return this; } /** * Sets the work source for power blame. Empty by default. */ public @NonNull Builder setWorkSource(@NonNull WorkSource workSource) { mWorkSource = Objects.requireNonNull(workSource); return this; } /** * Builds a ProviderRequest. */ public @NonNull ProviderRequest build() { if (mIntervalMillis == INTERVAL_DISABLED) { return EMPTY_REQUEST; } else { return new ProviderRequest( mIntervalMillis, mQuality, mMaxUpdateDelayMillis, mLowPower, mAdasGnssBypass, mLocationSettingsIgnored, mWorkSource); } } } }