Add ADAS GNSS bypass functionality
ADAS applications on automotive device require a way to access GNSS sensors even when the master location toggle is off, so that they can provide automotive safety services. Bug: 156688086 Test: atest LocationProviderManagerTest Change-Id: I438a781b2202c488da97f7ea732f87403d1068e4
This commit is contained in:
@@ -1706,6 +1706,10 @@
|
||||
config_enableFusedLocationOverlay is false. -->
|
||||
<string name="config_fusedLocationProviderPackageName" translatable="false">com.android.location.fused</string>
|
||||
|
||||
<!-- Default value for the ADAS GNSS Location Enabled setting if this setting has never been
|
||||
set before. -->
|
||||
<bool name="config_defaultAdasGnssLocationEnabled" translatable="false">false</bool>
|
||||
|
||||
<string-array name="config_locationExtraPackageNames" translatable="false"></string-array>
|
||||
|
||||
<!-- The package name of the default network recommendation app.
|
||||
|
||||
@@ -1918,6 +1918,7 @@
|
||||
<java-symbol type="bool" name="config_tintNotificationActionButtons" />
|
||||
<java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" />
|
||||
<java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" />
|
||||
<java-symbol type="bool" name="config_defaultAdasGnssLocationEnabled" />
|
||||
<java-symbol type="bool" name="config_enableFusedLocationOverlay" />
|
||||
<java-symbol type="bool" name="config_enableGeocoderOverlay" />
|
||||
<java-symbol type="bool" name="config_enableGeofenceOverlay" />
|
||||
|
||||
@@ -122,6 +122,9 @@ interface ILocationManager
|
||||
boolean isLocationEnabledForUser(int userId);
|
||||
void setLocationEnabledForUser(boolean enabled, int userId);
|
||||
|
||||
boolean isAdasGnssLocationEnabledForUser(int userId);
|
||||
void setAdasGnssLocationEnabledForUser(boolean enabled, int userId);
|
||||
|
||||
void addTestProvider(String name, in ProviderProperties properties,
|
||||
in List<String> locationTags, String packageName, @nullable String attributionTag);
|
||||
void removeTestProvider(String provider, String packageName, @nullable String attributionTag);
|
||||
|
||||
@@ -34,12 +34,15 @@ import java.util.Objects;
|
||||
public final class LastLocationRequest implements Parcelable {
|
||||
|
||||
private final boolean mHiddenFromAppOps;
|
||||
private final boolean mAdasGnssBypass;
|
||||
private final boolean mLocationSettingsIgnored;
|
||||
|
||||
private LastLocationRequest(
|
||||
boolean hiddenFromAppOps,
|
||||
boolean adasGnssBypass,
|
||||
boolean locationSettingsIgnored) {
|
||||
mHiddenFromAppOps = hiddenFromAppOps;
|
||||
mAdasGnssBypass = adasGnssBypass;
|
||||
mLocationSettingsIgnored = locationSettingsIgnored;
|
||||
}
|
||||
|
||||
@@ -55,6 +58,21 @@ public final class LastLocationRequest implements Parcelable {
|
||||
return mHiddenFromAppOps;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return true if all limiting factors will be ignored to satisfy GNSS request
|
||||
* @hide
|
||||
*/
|
||||
// TODO: make this system api
|
||||
public boolean isAdasGnssBypass() {
|
||||
return mAdasGnssBypass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if location settings, throttling, background location limits, and any other
|
||||
* possible limiting factors will be ignored in order to satisfy this last location request.
|
||||
@@ -65,12 +83,22 @@ public final class LastLocationRequest implements Parcelable {
|
||||
return mLocationSettingsIgnored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any bypass flag is set on this request. For internal use only.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public boolean isBypass() {
|
||||
return mAdasGnssBypass || mLocationSettingsIgnored;
|
||||
}
|
||||
|
||||
public static final @NonNull Parcelable.Creator<LastLocationRequest> CREATOR =
|
||||
new Parcelable.Creator<LastLocationRequest>() {
|
||||
@Override
|
||||
public LastLocationRequest createFromParcel(Parcel in) {
|
||||
return new LastLocationRequest(
|
||||
/* hiddenFromAppOps= */ in.readBoolean(),
|
||||
/* adasGnssBypass= */ in.readBoolean(),
|
||||
/* locationSettingsIgnored= */ in.readBoolean());
|
||||
}
|
||||
@Override
|
||||
@@ -86,6 +114,7 @@ public final class LastLocationRequest implements Parcelable {
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel parcel, int flags) {
|
||||
parcel.writeBoolean(mHiddenFromAppOps);
|
||||
parcel.writeBoolean(mAdasGnssBypass);
|
||||
parcel.writeBoolean(mLocationSettingsIgnored);
|
||||
}
|
||||
|
||||
@@ -99,12 +128,13 @@ public final class LastLocationRequest implements Parcelable {
|
||||
}
|
||||
LastLocationRequest that = (LastLocationRequest) o;
|
||||
return mHiddenFromAppOps == that.mHiddenFromAppOps
|
||||
&& mAdasGnssBypass == that.mAdasGnssBypass
|
||||
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mHiddenFromAppOps, mLocationSettingsIgnored);
|
||||
return Objects.hash(mHiddenFromAppOps, mAdasGnssBypass, mLocationSettingsIgnored);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -115,8 +145,11 @@ public final class LastLocationRequest implements Parcelable {
|
||||
if (mHiddenFromAppOps) {
|
||||
s.append("hiddenFromAppOps, ");
|
||||
}
|
||||
if (mAdasGnssBypass) {
|
||||
s.append("adasGnssBypass, ");
|
||||
}
|
||||
if (mLocationSettingsIgnored) {
|
||||
s.append("locationSettingsIgnored, ");
|
||||
s.append("settingsBypass, ");
|
||||
}
|
||||
if (s.length() > "LastLocationRequest[".length()) {
|
||||
s.setLength(s.length() - 2);
|
||||
@@ -131,6 +164,7 @@ public final class LastLocationRequest implements Parcelable {
|
||||
public static final class Builder {
|
||||
|
||||
private boolean mHiddenFromAppOps;
|
||||
private boolean mAdasGnssBypass;
|
||||
private boolean mLocationSettingsIgnored;
|
||||
|
||||
/**
|
||||
@@ -138,6 +172,7 @@ public final class LastLocationRequest implements Parcelable {
|
||||
*/
|
||||
public Builder() {
|
||||
mHiddenFromAppOps = false;
|
||||
mAdasGnssBypass = false;
|
||||
mLocationSettingsIgnored = false;
|
||||
}
|
||||
|
||||
@@ -146,6 +181,7 @@ public final class LastLocationRequest implements Parcelable {
|
||||
*/
|
||||
public Builder(@NonNull LastLocationRequest lastLocationRequest) {
|
||||
mHiddenFromAppOps = lastLocationRequest.mHiddenFromAppOps;
|
||||
mAdasGnssBypass = lastLocationRequest.mAdasGnssBypass;
|
||||
mLocationSettingsIgnored = lastLocationRequest.mLocationSettingsIgnored;
|
||||
}
|
||||
|
||||
@@ -163,6 +199,25 @@ public final class LastLocationRequest implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to true, indicates that the client is an ADAS (Advanced Driving Assistance
|
||||
* Systems) client, which requires access to GNSS even if location settings would normally
|
||||
* deny this, in order to enable auto safety features. This field is only respected on
|
||||
* automotive devices, and only if the client is recognized as a legitimate ADAS
|
||||
* application. Defaults to false.
|
||||
*
|
||||
* <p>Permissions enforcement occurs when resulting location request is actually used, not
|
||||
* when this method is invoked.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// TODO: make this system api
|
||||
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
|
||||
public @NonNull LastLocationRequest.Builder setAdasGnssBypass(boolean adasGnssBypass) {
|
||||
mAdasGnssBypass = adasGnssBypass;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to true, indicates that location settings, throttling, background location limits,
|
||||
* and any other possible limiting factors should be ignored in order to satisfy this
|
||||
@@ -186,6 +241,7 @@ public final class LastLocationRequest implements Parcelable {
|
||||
public @NonNull LastLocationRequest build() {
|
||||
return new LastLocationRequest(
|
||||
mHiddenFromAppOps,
|
||||
mAdasGnssBypass,
|
||||
mLocationSettingsIgnored);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,6 +314,33 @@ public class LocationManager {
|
||||
*/
|
||||
public static final String EXTRA_LOCATION_ENABLED = "android.location.extra.LOCATION_ENABLED";
|
||||
|
||||
/**
|
||||
* Broadcast intent action when the ADAS (Advanced Driving Assistance Systems) GNSS location
|
||||
* enabled state changes. Includes a boolean intent extra, {@link #EXTRA_ADAS_GNSS_ENABLED},
|
||||
* with the enabled state of ADAS GNSS location. This broadcast only has meaning on automotive
|
||||
* devices.
|
||||
*
|
||||
* @see #EXTRA_ADAS_GNSS_ENABLED
|
||||
* @see #isAdasGnssLocationEnabled()
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// TODO: @SystemApi
|
||||
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
|
||||
public static final String ACTION_ADAS_GNSS_ENABLED_CHANGED =
|
||||
"android.location.action.ADAS_GNSS_ENABLED_CHANGED";
|
||||
|
||||
/**
|
||||
* Intent extra included with {@link #ACTION_ADAS_GNSS_ENABLED_CHANGED} broadcasts, containing
|
||||
* the boolean enabled state of ADAS GNSS location.
|
||||
*
|
||||
* @see #ACTION_ADAS_GNSS_ENABLED_CHANGED
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// TODO: @SystemApi
|
||||
public static final String EXTRA_ADAS_GNSS_ENABLED = "android.location.extra.ADAS_GNSS_ENABLED";
|
||||
|
||||
/**
|
||||
* Broadcast intent action indicating that a high power location requests
|
||||
* has either started or stopped being active. The current state of
|
||||
@@ -620,6 +647,42 @@ public class LocationManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current enabled/disabled state of ADAS (Advanced Driving Assistance Systems)
|
||||
* GNSS location access for the given user. This controls safety critical automotive access to
|
||||
* GNSS location. This only has meaning on automotive devices.
|
||||
*
|
||||
* @return true if ADAS location is enabled and false if ADAS location is disabled.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
//TODO: @SystemApi
|
||||
public boolean isAdasGnssLocationEnabled() {
|
||||
try {
|
||||
return mService.isAdasGnssLocationEnabledForUser(mContext.getUser().getIdentifier());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables ADAS (Advanced Driving Assistance Systems) GNSS location access for the
|
||||
* given user. This only has meaning on automotive devices.
|
||||
*
|
||||
* @param enabled true to enable ADAS location and false to disable ADAS location.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// TODO: @SystemApi
|
||||
@RequiresPermission(WRITE_SECURE_SETTINGS)
|
||||
public void setAdasGnssLocationEnabled(boolean enabled) {
|
||||
try {
|
||||
mService.setAdasGnssLocationEnabledForUser(enabled, mContext.getUser().getIdentifier());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current enabled/disabled status of the given provider. To listen for changes, see
|
||||
* {@link #PROVIDERS_CHANGED_ACTION}.
|
||||
|
||||
@@ -194,6 +194,7 @@ public final class LocationRequest implements Parcelable {
|
||||
private float mMinUpdateDistanceMeters;
|
||||
private final long mMaxUpdateDelayMillis;
|
||||
private boolean mHideFromAppOps;
|
||||
private final boolean mAdasGnssBypass;
|
||||
private boolean mLocationSettingsIgnored;
|
||||
private boolean mLowPower;
|
||||
private @Nullable WorkSource mWorkSource;
|
||||
@@ -236,7 +237,7 @@ public final class LocationRequest implements Parcelable {
|
||||
if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
|
||||
quality = POWER_NONE;
|
||||
} else if (LocationManager.GPS_PROVIDER.equals(provider)) {
|
||||
quality = ACCURACY_FINE;
|
||||
quality = QUALITY_HIGH_ACCURACY;
|
||||
} else {
|
||||
quality = POWER_LOW;
|
||||
}
|
||||
@@ -289,6 +290,7 @@ public final class LocationRequest implements Parcelable {
|
||||
float minUpdateDistanceMeters,
|
||||
long maxUpdateDelayMillis,
|
||||
boolean hiddenFromAppOps,
|
||||
boolean adasGnssBypass,
|
||||
boolean locationSettingsIgnored,
|
||||
boolean lowPower,
|
||||
WorkSource workSource) {
|
||||
@@ -302,8 +304,9 @@ public final class LocationRequest implements Parcelable {
|
||||
mMinUpdateDistanceMeters = minUpdateDistanceMeters;
|
||||
mMaxUpdateDelayMillis = maxUpdateDelayMillis;
|
||||
mHideFromAppOps = hiddenFromAppOps;
|
||||
mLowPower = lowPower;
|
||||
mAdasGnssBypass = adasGnssBypass;
|
||||
mLocationSettingsIgnored = locationSettingsIgnored;
|
||||
mLowPower = lowPower;
|
||||
mWorkSource = Objects.requireNonNull(workSource);
|
||||
}
|
||||
|
||||
@@ -339,15 +342,15 @@ public final class LocationRequest implements Parcelable {
|
||||
switch (quality) {
|
||||
case POWER_HIGH:
|
||||
// fall through
|
||||
case ACCURACY_FINE:
|
||||
case QUALITY_HIGH_ACCURACY:
|
||||
mQuality = QUALITY_HIGH_ACCURACY;
|
||||
break;
|
||||
case ACCURACY_BLOCK:
|
||||
case QUALITY_BALANCED_POWER_ACCURACY:
|
||||
mQuality = QUALITY_BALANCED_POWER_ACCURACY;
|
||||
break;
|
||||
case POWER_LOW:
|
||||
// fall through
|
||||
case ACCURACY_CITY:
|
||||
case QUALITY_LOW_POWER:
|
||||
mQuality = QUALITY_LOW_POWER;
|
||||
break;
|
||||
case POWER_NONE:
|
||||
@@ -647,6 +650,21 @@ public final class LocationRequest implements Parcelable {
|
||||
return mHideFromAppOps;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return true if all limiting factors will be ignored to satisfy GNSS request
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// TODO: @SystemApi
|
||||
public boolean isAdasGnssBypass() {
|
||||
return mAdasGnssBypass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated LocationRequests should be treated as immutable.
|
||||
@@ -672,6 +690,15 @@ public final class LocationRequest implements Parcelable {
|
||||
return mLocationSettingsIgnored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any bypass flag is set on this request. For internal use only.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public boolean isBypass() {
|
||||
return mAdasGnssBypass || mLocationSettingsIgnored;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated LocationRequests should be treated as immutable.
|
||||
@@ -749,6 +776,7 @@ public final class LocationRequest implements Parcelable {
|
||||
/* minUpdateDistanceMeters= */ in.readFloat(),
|
||||
/* maxUpdateDelayMillis= */ in.readLong(),
|
||||
/* hiddenFromAppOps= */ in.readBoolean(),
|
||||
/* adasGnssBypass= */ in.readBoolean(),
|
||||
/* locationSettingsIgnored= */ in.readBoolean(),
|
||||
/* lowPower= */ in.readBoolean(),
|
||||
/* workSource= */ in.readTypedObject(WorkSource.CREATOR));
|
||||
@@ -777,6 +805,7 @@ public final class LocationRequest implements Parcelable {
|
||||
parcel.writeFloat(mMinUpdateDistanceMeters);
|
||||
parcel.writeLong(mMaxUpdateDelayMillis);
|
||||
parcel.writeBoolean(mHideFromAppOps);
|
||||
parcel.writeBoolean(mAdasGnssBypass);
|
||||
parcel.writeBoolean(mLocationSettingsIgnored);
|
||||
parcel.writeBoolean(mLowPower);
|
||||
parcel.writeTypedObject(mWorkSource, 0);
|
||||
@@ -801,6 +830,7 @@ public final class LocationRequest implements Parcelable {
|
||||
&& Float.compare(that.mMinUpdateDistanceMeters, mMinUpdateDistanceMeters) == 0
|
||||
&& mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis
|
||||
&& mHideFromAppOps == that.mHideFromAppOps
|
||||
&& mAdasGnssBypass == that.mAdasGnssBypass
|
||||
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored
|
||||
&& mLowPower == that.mLowPower
|
||||
&& Objects.equals(mProvider, that.mProvider)
|
||||
@@ -866,8 +896,11 @@ public final class LocationRequest implements Parcelable {
|
||||
if (mHideFromAppOps) {
|
||||
s.append(", hiddenFromAppOps");
|
||||
}
|
||||
if (mAdasGnssBypass) {
|
||||
s.append(", adasGnssBypass");
|
||||
}
|
||||
if (mLocationSettingsIgnored) {
|
||||
s.append(", locationSettingsIgnored");
|
||||
s.append(", settingsBypass");
|
||||
}
|
||||
if (mWorkSource != null && !mWorkSource.isEmpty()) {
|
||||
s.append(", ").append(mWorkSource);
|
||||
@@ -889,6 +922,7 @@ public final class LocationRequest implements Parcelable {
|
||||
private float mMinUpdateDistanceMeters;
|
||||
private long mMaxUpdateDelayMillis;
|
||||
private boolean mHiddenFromAppOps;
|
||||
private boolean mAdasGnssBypass;
|
||||
private boolean mLocationSettingsIgnored;
|
||||
private boolean mLowPower;
|
||||
@Nullable private WorkSource mWorkSource;
|
||||
@@ -908,6 +942,7 @@ public final class LocationRequest implements Parcelable {
|
||||
mMinUpdateDistanceMeters = 0;
|
||||
mMaxUpdateDelayMillis = 0;
|
||||
mHiddenFromAppOps = false;
|
||||
mAdasGnssBypass = false;
|
||||
mLocationSettingsIgnored = false;
|
||||
mLowPower = false;
|
||||
mWorkSource = null;
|
||||
@@ -925,6 +960,7 @@ public final class LocationRequest implements Parcelable {
|
||||
mMinUpdateDistanceMeters = locationRequest.mMinUpdateDistanceMeters;
|
||||
mMaxUpdateDelayMillis = locationRequest.mMaxUpdateDelayMillis;
|
||||
mHiddenFromAppOps = locationRequest.mHideFromAppOps;
|
||||
mAdasGnssBypass = locationRequest.mAdasGnssBypass;
|
||||
mLocationSettingsIgnored = locationRequest.mLocationSettingsIgnored;
|
||||
mLowPower = locationRequest.mLowPower;
|
||||
mWorkSource = locationRequest.mWorkSource;
|
||||
@@ -977,10 +1013,10 @@ public final class LocationRequest implements Parcelable {
|
||||
public @NonNull Builder setQuality(@NonNull Criteria criteria) {
|
||||
switch (criteria.getAccuracy()) {
|
||||
case Criteria.ACCURACY_COARSE:
|
||||
mQuality = ACCURACY_BLOCK;
|
||||
mQuality = QUALITY_BALANCED_POWER_ACCURACY;
|
||||
break;
|
||||
case Criteria.ACCURACY_FINE:
|
||||
mQuality = ACCURACY_FINE;
|
||||
mQuality = QUALITY_HIGH_ACCURACY;
|
||||
break;
|
||||
default: {
|
||||
if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) {
|
||||
@@ -1091,6 +1127,25 @@ public final class LocationRequest implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to true, indicates that the client is an ADAS (Advanced Driving Assistance
|
||||
* Systems) client, which requires access to GNSS even if location settings would normally
|
||||
* deny this, in order to enable auto safety features. This field is only respected on
|
||||
* automotive devices, and only if the client is recognized as a legitimate ADAS
|
||||
* application. Defaults to false.
|
||||
*
|
||||
* <p>Permissions enforcement occurs when resulting location request is actually used, not
|
||||
* when this method is invoked.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// TODO: @SystemApi
|
||||
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
|
||||
public @NonNull Builder setAdasGnssBypass(boolean adasGnssBypass) {
|
||||
mAdasGnssBypass = adasGnssBypass;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to true, indicates that location settings, throttling, background location limits,
|
||||
* and any other possible limiting factors should be ignored in order to satisfy this
|
||||
@@ -1171,6 +1226,7 @@ public final class LocationRequest implements Parcelable {
|
||||
mMinUpdateDistanceMeters,
|
||||
mMaxUpdateDelayMillis,
|
||||
mHiddenFromAppOps,
|
||||
mAdasGnssBypass,
|
||||
mLocationSettingsIgnored,
|
||||
mLowPower,
|
||||
new WorkSource(mWorkSource));
|
||||
|
||||
@@ -44,12 +44,19 @@ 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, new WorkSource());
|
||||
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;
|
||||
|
||||
@@ -72,12 +79,14 @@ public final class ProviderRequest implements Parcelable {
|
||||
@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);
|
||||
}
|
||||
@@ -125,6 +134,18 @@ public final class ProviderRequest implements Parcelable {
|
||||
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
|
||||
@@ -134,6 +155,15 @@ public final class ProviderRequest implements Parcelable {
|
||||
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.
|
||||
*/
|
||||
@@ -153,6 +183,7 @@ public final class ProviderRequest implements Parcelable {
|
||||
/* quality= */ in.readInt(),
|
||||
/* maxUpdateDelayMillis= */ in.readLong(),
|
||||
/* lowPower= */ in.readBoolean(),
|
||||
/* adasGnssBypass= */ in.readBoolean(),
|
||||
/* locationSettingsIgnored= */ in.readBoolean(),
|
||||
/* workSource= */ in.readTypedObject(WorkSource.CREATOR));
|
||||
}
|
||||
@@ -176,6 +207,7 @@ public final class ProviderRequest implements Parcelable {
|
||||
parcel.writeInt(mQuality);
|
||||
parcel.writeLong(mMaxUpdateDelayMillis);
|
||||
parcel.writeBoolean(mLowPower);
|
||||
parcel.writeBoolean(mAdasGnssBypass);
|
||||
parcel.writeBoolean(mLocationSettingsIgnored);
|
||||
parcel.writeTypedObject(mWorkSource, flags);
|
||||
}
|
||||
@@ -198,6 +230,7 @@ public final class ProviderRequest implements Parcelable {
|
||||
&& mQuality == that.mQuality
|
||||
&& mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis
|
||||
&& mLowPower == that.mLowPower
|
||||
&& mAdasGnssBypass == that.mAdasGnssBypass
|
||||
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored
|
||||
&& mWorkSource.equals(that.mWorkSource);
|
||||
}
|
||||
@@ -229,8 +262,11 @@ public final class ProviderRequest implements Parcelable {
|
||||
if (mLowPower) {
|
||||
s.append(", lowPower");
|
||||
}
|
||||
if (mAdasGnssBypass) {
|
||||
s.append(", adasGnssBypass");
|
||||
}
|
||||
if (mLocationSettingsIgnored) {
|
||||
s.append(", locationSettingsIgnored");
|
||||
s.append(", settingsBypass");
|
||||
}
|
||||
if (!mWorkSource.isEmpty()) {
|
||||
s.append(", ").append(mWorkSource);
|
||||
@@ -246,10 +282,12 @@ public final class ProviderRequest implements Parcelable {
|
||||
* 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();
|
||||
|
||||
@@ -298,6 +336,16 @@ public final class ProviderRequest implements Parcelable {
|
||||
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.
|
||||
*/
|
||||
@@ -326,6 +374,7 @@ public final class ProviderRequest implements Parcelable {
|
||||
mQuality,
|
||||
mMaxUpdateDelayMillis,
|
||||
mLowPower,
|
||||
mAdasGnssBypass,
|
||||
mLocationSettingsIgnored,
|
||||
mWorkSource);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import android.app.PendingIntent;
|
||||
import android.app.compat.CompatChanges;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Criteria;
|
||||
import android.location.GeocoderParams;
|
||||
import android.location.Geofence;
|
||||
@@ -91,6 +92,7 @@ import android.util.IndentingPrintWriter;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.FgThread;
|
||||
@@ -134,6 +136,8 @@ 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.location.settings.LocationSettings;
|
||||
import com.android.server.location.settings.LocationUserSettings;
|
||||
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
@@ -270,6 +274,8 @@ public class LocationManagerService extends ILocationManager.Stub implements
|
||||
|
||||
mGeofenceManager = new GeofenceManager(mContext, injector);
|
||||
|
||||
mInjector.getLocationSettings().registerLocationUserSettingsListener(
|
||||
this::onLocationUserSettingsChanged);
|
||||
mInjector.getSettingsHelper().addOnLocationEnabledChangedListener(
|
||||
this::onLocationModeChanged);
|
||||
mInjector.getSettingsHelper().addIgnoreSettingsAllowlistChangedListener(
|
||||
@@ -476,6 +482,25 @@ public class LocationManagerService extends ILocationManager.Stub implements
|
||||
}
|
||||
}
|
||||
|
||||
private void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
|
||||
LocationUserSettings newSettings) {
|
||||
if (oldSettings.isAdasGnssLocationEnabled() != newSettings.isAdasGnssLocationEnabled()) {
|
||||
boolean enabled = newSettings.isAdasGnssLocationEnabled();
|
||||
|
||||
if (D) {
|
||||
Log.d(TAG, "[u" + userId + "] adas gnss location enabled = " + enabled);
|
||||
}
|
||||
|
||||
EVENT_LOG.logAdasLocationEnabled(userId, enabled);
|
||||
|
||||
Intent intent = new Intent(LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED)
|
||||
.putExtra(LocationManager.EXTRA_ADAS_GNSS_ENABLED, enabled)
|
||||
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
|
||||
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
||||
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
|
||||
}
|
||||
}
|
||||
|
||||
private void onLocationModeChanged(int userId) {
|
||||
boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
|
||||
LocationManager.invalidateLocalLocationEnabledCaches();
|
||||
@@ -661,7 +686,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
|
||||
// clients in the system process must have an attribution tag set
|
||||
Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);
|
||||
|
||||
request = validateLocationRequest(request, identity);
|
||||
request = validateLocationRequest(provider, request, identity);
|
||||
|
||||
LocationProviderManager manager = getLocationProviderManager(provider);
|
||||
Preconditions.checkArgument(manager != null,
|
||||
@@ -687,7 +712,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
|
||||
new IllegalArgumentException());
|
||||
}
|
||||
|
||||
request = validateLocationRequest(request, identity);
|
||||
request = validateLocationRequest(provider, request, identity);
|
||||
|
||||
LocationProviderManager manager = getLocationProviderManager(provider);
|
||||
Preconditions.checkArgument(manager != null,
|
||||
@@ -725,7 +750,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
|
||||
}
|
||||
}
|
||||
|
||||
request = validateLocationRequest(request, identity);
|
||||
request = validateLocationRequest(provider, request, identity);
|
||||
|
||||
LocationProviderManager manager = getLocationProviderManager(provider);
|
||||
Preconditions.checkArgument(manager != null,
|
||||
@@ -734,33 +759,27 @@ public class LocationManagerService extends ILocationManager.Stub implements
|
||||
manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent);
|
||||
}
|
||||
|
||||
private LocationRequest validateLocationRequest(LocationRequest request,
|
||||
private LocationRequest validateLocationRequest(String provider, LocationRequest request,
|
||||
CallerIdentity identity) {
|
||||
// validate unsanitized request
|
||||
if (!request.getWorkSource().isEmpty()) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
permission.UPDATE_DEVICE_STATS,
|
||||
"setting a work source requires " + permission.UPDATE_DEVICE_STATS);
|
||||
}
|
||||
if (request.isHiddenFromAppOps()) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
permission.UPDATE_APP_OPS_STATS,
|
||||
"hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
|
||||
}
|
||||
if (request.isLocationSettingsIgnored()) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
permission.WRITE_SECURE_SETTINGS,
|
||||
"ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
|
||||
}
|
||||
|
||||
// sanitize request
|
||||
LocationRequest.Builder sanitized = new LocationRequest.Builder(request);
|
||||
|
||||
if (CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
|
||||
if (request.isLowPower()) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
permission.LOCATION_HARDWARE,
|
||||
"low power request requires " + permission.LOCATION_HARDWARE);
|
||||
}
|
||||
} else {
|
||||
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
|
||||
&& GPS_PROVIDER.equals(provider)
|
||||
&& ArrayUtils.contains(mContext.getResources().getStringArray(
|
||||
com.android.internal.R.array.config_locationDriverAssistancePackageNames),
|
||||
identity.getPackageName())) {
|
||||
sanitized.setAdasGnssBypass(true);
|
||||
}
|
||||
|
||||
if (!CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
|
||||
if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE)
|
||||
!= PERMISSION_GRANTED) {
|
||||
sanitized.setLowPower(false);
|
||||
@@ -786,7 +805,52 @@ public class LocationManagerService extends ILocationManager.Stub implements
|
||||
}
|
||||
sanitized.setWorkSource(workSource);
|
||||
|
||||
return sanitized.build();
|
||||
request = sanitized.build();
|
||||
|
||||
// validate sanitized request
|
||||
boolean isLocationProvider = mLocalService.isProvider(null, identity);
|
||||
|
||||
if (request.isLowPower() && CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS,
|
||||
identity.getUid())) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
permission.LOCATION_HARDWARE,
|
||||
"low power request requires " + permission.LOCATION_HARDWARE);
|
||||
}
|
||||
if (request.isHiddenFromAppOps()) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
permission.UPDATE_APP_OPS_STATS,
|
||||
"hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
|
||||
}
|
||||
if (request.isAdasGnssBypass()) {
|
||||
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
|
||||
throw new IllegalArgumentException(
|
||||
"adas gnss bypass requests are only allowed on automotive devices");
|
||||
}
|
||||
if (!GPS_PROVIDER.equals(provider)) {
|
||||
throw new IllegalArgumentException(
|
||||
"adas gnss bypass requests are only allowed on the \"gps\" provider");
|
||||
}
|
||||
if (!ArrayUtils.contains(mContext.getResources().getStringArray(
|
||||
com.android.internal.R.array.config_locationDriverAssistancePackageNames),
|
||||
identity.getPackageName())) {
|
||||
throw new SecurityException(
|
||||
"only verified adas packages may use adas gnss bypass requests");
|
||||
}
|
||||
if (!isLocationProvider) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
permission.WRITE_SECURE_SETTINGS,
|
||||
"adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
|
||||
}
|
||||
}
|
||||
if (request.isLocationSettingsIgnored()) {
|
||||
if (!isLocationProvider) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
permission.WRITE_SECURE_SETTINGS,
|
||||
"ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
|
||||
}
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -834,7 +898,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
|
||||
// clients in the system process must have an attribution tag set
|
||||
Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
|
||||
|
||||
request = validateLastLocationRequest(request);
|
||||
request = validateLastLocationRequest(provider, request, identity);
|
||||
|
||||
LocationProviderManager manager = getLocationProviderManager(provider);
|
||||
if (manager == null) {
|
||||
@@ -844,16 +908,58 @@ public class LocationManagerService extends ILocationManager.Stub implements
|
||||
return manager.getLastLocation(request, identity, permissionLevel);
|
||||
}
|
||||
|
||||
private LastLocationRequest validateLastLocationRequest(LastLocationRequest request) {
|
||||
private LastLocationRequest validateLastLocationRequest(String provider,
|
||||
LastLocationRequest request,
|
||||
CallerIdentity identity) {
|
||||
// sanitize request
|
||||
LastLocationRequest.Builder sanitized = new LastLocationRequest.Builder(request);
|
||||
|
||||
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
|
||||
&& GPS_PROVIDER.equals(provider)
|
||||
&& ArrayUtils.contains(mContext.getResources().getStringArray(
|
||||
com.android.internal.R.array.config_locationDriverAssistancePackageNames),
|
||||
identity.getPackageName())) {
|
||||
sanitized.setAdasGnssBypass(true);
|
||||
}
|
||||
|
||||
request = sanitized.build();
|
||||
|
||||
// validate request
|
||||
boolean isLocationProvider = mLocalService.isProvider(null, identity);
|
||||
|
||||
if (request.isHiddenFromAppOps()) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
permission.UPDATE_APP_OPS_STATS,
|
||||
"hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
|
||||
}
|
||||
|
||||
if (request.isAdasGnssBypass()) {
|
||||
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
|
||||
throw new IllegalArgumentException(
|
||||
"adas gnss bypass requests are only allowed on automotive devices");
|
||||
}
|
||||
if (!GPS_PROVIDER.equals(provider)) {
|
||||
throw new IllegalArgumentException(
|
||||
"adas gnss bypass requests are only allowed on the \"gps\" provider");
|
||||
}
|
||||
if (!ArrayUtils.contains(mContext.getResources().getStringArray(
|
||||
com.android.internal.R.array.config_locationDriverAssistancePackageNames),
|
||||
identity.getPackageName())) {
|
||||
throw new SecurityException(
|
||||
"only verified adas packages may use adas gnss bypass requests");
|
||||
}
|
||||
if (!isLocationProvider) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
permission.WRITE_SECURE_SETTINGS,
|
||||
"adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
|
||||
}
|
||||
}
|
||||
if (request.isLocationSettingsIgnored()) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
permission.WRITE_SECURE_SETTINGS,
|
||||
"ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
|
||||
if (!isLocationProvider) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
permission.WRITE_SECURE_SETTINGS,
|
||||
"ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
|
||||
}
|
||||
}
|
||||
|
||||
return request;
|
||||
@@ -1125,6 +1231,24 @@ public class LocationManagerService extends ILocationManager.Stub implements
|
||||
return mInjector.getSettingsHelper().isLocationEnabled(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdasGnssLocationEnabledForUser(boolean enabled, int userId) {
|
||||
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
|
||||
userId, false, false, "setAdasGnssLocationEnabledForUser", null);
|
||||
|
||||
mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null);
|
||||
|
||||
mInjector.getLocationSettings().updateUserSettings(userId,
|
||||
settings -> settings.withAdasGnssLocationEnabled(enabled));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdasGnssLocationEnabledForUser(int userId) {
|
||||
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
|
||||
userId, false, false, "isAdasGnssLocationEnabledForUser", null);
|
||||
return mInjector.getLocationSettings().getUserSettings(userId).isAdasGnssLocationEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProviderEnabledForUser(String provider, int userId) {
|
||||
return mLocalService.isProviderEnabledForUser(provider, userId);
|
||||
@@ -1555,11 +1679,12 @@ public class LocationManagerService extends ILocationManager.Stub implements
|
||||
}
|
||||
}
|
||||
|
||||
private static class SystemInjector implements Injector {
|
||||
private static final class SystemInjector implements Injector {
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
private final UserInfoHelper mUserInfoHelper;
|
||||
private final LocationSettings mLocationSettings;
|
||||
private final AlarmHelper mAlarmHelper;
|
||||
private final SystemAppOpsHelper mAppOpsHelper;
|
||||
private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
|
||||
@@ -1584,6 +1709,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
|
||||
mContext = context;
|
||||
|
||||
mUserInfoHelper = userInfoHelper;
|
||||
mLocationSettings = new LocationSettings(context);
|
||||
mAlarmHelper = new SystemAlarmHelper(context);
|
||||
mAppOpsHelper = new SystemAppOpsHelper(context);
|
||||
mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
|
||||
@@ -1620,6 +1746,11 @@ public class LocationManagerService extends ILocationManager.Stub implements
|
||||
return mUserInfoHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationSettings getLocationSettings() {
|
||||
return mLocationSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlarmHelper getAlarmHelper() {
|
||||
return mAlarmHelper;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Criteria;
|
||||
import android.location.Location;
|
||||
import android.location.provider.ProviderProperties;
|
||||
@@ -60,6 +61,14 @@ class LocationShellCommand extends BasicShellCommandHandler {
|
||||
handleSetLocationEnabled();
|
||||
return 0;
|
||||
}
|
||||
case "is-adas-gnss-location-enabled": {
|
||||
handleIsAdasGnssLocationEnabled();
|
||||
return 0;
|
||||
}
|
||||
case "set-adas-gnss-location-enabled": {
|
||||
handleSetAdasGnssLocationEnabled();
|
||||
return 0;
|
||||
}
|
||||
case "providers": {
|
||||
String command = getNextArgRequired();
|
||||
return parseProvidersCommand(command);
|
||||
@@ -134,6 +143,52 @@ class LocationShellCommand extends BasicShellCommandHandler {
|
||||
mService.setLocationEnabledForUser(enabled, userId);
|
||||
}
|
||||
|
||||
private void handleIsAdasGnssLocationEnabled() {
|
||||
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
|
||||
throw new IllegalStateException("command only recognized on automotive devices");
|
||||
}
|
||||
|
||||
int userId = UserHandle.USER_CURRENT_OR_SELF;
|
||||
|
||||
do {
|
||||
String option = getNextOption();
|
||||
if (option == null) {
|
||||
break;
|
||||
}
|
||||
if ("--user".equals(option)) {
|
||||
userId = UserHandle.parseUserArg(getNextArgRequired());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown option: " + option);
|
||||
}
|
||||
} while (true);
|
||||
|
||||
getOutPrintWriter().println(mService.isAdasGnssLocationEnabledForUser(userId));
|
||||
}
|
||||
|
||||
private void handleSetAdasGnssLocationEnabled() {
|
||||
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
|
||||
throw new IllegalStateException("command only recognized on automotive devices");
|
||||
}
|
||||
|
||||
boolean enabled = Boolean.parseBoolean(getNextArgRequired());
|
||||
|
||||
int userId = UserHandle.USER_CURRENT_OR_SELF;
|
||||
|
||||
do {
|
||||
String option = getNextOption();
|
||||
if (option == null) {
|
||||
break;
|
||||
}
|
||||
if ("--user".equals(option)) {
|
||||
userId = UserHandle.parseUserArg(getNextArgRequired());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown option: " + option);
|
||||
}
|
||||
} while (true);
|
||||
|
||||
mService.setAdasGnssLocationEnabledForUser(enabled, userId);
|
||||
}
|
||||
|
||||
private void handleAddTestProvider() {
|
||||
String provider = getNextArgRequired();
|
||||
|
||||
@@ -297,6 +352,14 @@ class LocationShellCommand extends BasicShellCommandHandler {
|
||||
pw.println(" set-location-enabled true|false [--user <USER_ID>]");
|
||||
pw.println(" Sets the master location switch enabled state. If no user is specified,");
|
||||
pw.println(" the current user is assumed.");
|
||||
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
|
||||
pw.println(" is-adas-gnss-location-enabled [--user <USER_ID>]");
|
||||
pw.println(" Gets the ADAS GNSS location enabled state. If no user is specified,");
|
||||
pw.println(" the current user is assumed.");
|
||||
pw.println(" set-adas-gnss-location-enabled true|false [--user <USER_ID>]");
|
||||
pw.println(" Sets the ADAS GNSS location enabled state. If no user is specified,");
|
||||
pw.println(" the current user is assumed.");
|
||||
}
|
||||
pw.println(" providers");
|
||||
pw.println(" The providers command is followed by a subcommand, as listed below:");
|
||||
pw.println();
|
||||
@@ -323,9 +386,8 @@ class LocationShellCommand extends BasicShellCommandHandler {
|
||||
pw.println(" Common commands that may be supported by the gps provider, depending on");
|
||||
pw.println(" hardware and software configurations:");
|
||||
pw.println(" delete_aiding_data - requests deletion of any predictive aiding data");
|
||||
pw.println(" force_time_injection - requests NTP time injection to chipset");
|
||||
pw.println(" force_psds_injection - "
|
||||
+ "requests predictive aiding data injection to chipset");
|
||||
pw.println(" request_power_stats - requests GNSS power stats update from chipset");
|
||||
pw.println(" force_time_injection - requests NTP time injection");
|
||||
pw.println(" force_psds_injection - requests predictive aiding data injection");
|
||||
pw.println(" request_power_stats - requests GNSS power stats update");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,19 +55,20 @@ public class LocationEventLog extends LocalEventLog {
|
||||
|
||||
private static final int EVENT_USER_SWITCHED = 1;
|
||||
private static final int EVENT_LOCATION_ENABLED = 2;
|
||||
private static final int EVENT_PROVIDER_ENABLED = 3;
|
||||
private static final int EVENT_PROVIDER_MOCKED = 4;
|
||||
private static final int EVENT_PROVIDER_CLIENT_REGISTER = 5;
|
||||
private static final int EVENT_PROVIDER_CLIENT_UNREGISTER = 6;
|
||||
private static final int EVENT_PROVIDER_CLIENT_FOREGROUND = 7;
|
||||
private static final int EVENT_PROVIDER_CLIENT_BACKGROUND = 8;
|
||||
private static final int EVENT_PROVIDER_CLIENT_PERMITTED = 9;
|
||||
private static final int EVENT_PROVIDER_CLIENT_UNPERMITTED = 10;
|
||||
private static final int EVENT_PROVIDER_UPDATE_REQUEST = 11;
|
||||
private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 12;
|
||||
private static final int EVENT_PROVIDER_DELIVER_LOCATION = 13;
|
||||
private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 14;
|
||||
private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 15;
|
||||
private static final int EVENT_ADAS_LOCATION_ENABLED = 3;
|
||||
private static final int EVENT_PROVIDER_ENABLED = 4;
|
||||
private static final int EVENT_PROVIDER_MOCKED = 5;
|
||||
private static final int EVENT_PROVIDER_CLIENT_REGISTER = 6;
|
||||
private static final int EVENT_PROVIDER_CLIENT_UNREGISTER = 7;
|
||||
private static final int EVENT_PROVIDER_CLIENT_FOREGROUND = 8;
|
||||
private static final int EVENT_PROVIDER_CLIENT_BACKGROUND = 9;
|
||||
private static final int EVENT_PROVIDER_CLIENT_PERMITTED = 10;
|
||||
private static final int EVENT_PROVIDER_CLIENT_UNPERMITTED = 11;
|
||||
private static final int EVENT_PROVIDER_UPDATE_REQUEST = 12;
|
||||
private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 13;
|
||||
private static final int EVENT_PROVIDER_DELIVER_LOCATION = 14;
|
||||
private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 15;
|
||||
private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 16;
|
||||
|
||||
@GuardedBy("mAggregateStats")
|
||||
private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats;
|
||||
@@ -116,6 +117,11 @@ public class LocationEventLog extends LocalEventLog {
|
||||
addLogEvent(EVENT_LOCATION_ENABLED, userId, enabled);
|
||||
}
|
||||
|
||||
/** Logs a location enabled/disabled event. */
|
||||
public void logAdasLocationEnabled(int userId, boolean enabled) {
|
||||
addLogEvent(EVENT_ADAS_LOCATION_ENABLED, userId, enabled);
|
||||
}
|
||||
|
||||
/** Logs a location provider enabled/disabled event. */
|
||||
public void logProviderEnabled(String provider, int userId, boolean enabled) {
|
||||
addLogEvent(EVENT_PROVIDER_ENABLED, provider, userId, enabled);
|
||||
@@ -219,6 +225,9 @@ public class LocationEventLog extends LocalEventLog {
|
||||
return new UserSwitchedEvent(timeDelta, (Integer) args[0], (Integer) args[1]);
|
||||
case EVENT_LOCATION_ENABLED:
|
||||
return new LocationEnabledEvent(timeDelta, (Integer) args[0], (Boolean) args[1]);
|
||||
case EVENT_ADAS_LOCATION_ENABLED:
|
||||
return new LocationAdasEnabledEvent(timeDelta, (Integer) args[0],
|
||||
(Boolean) args[1]);
|
||||
case EVENT_PROVIDER_ENABLED:
|
||||
return new ProviderEnabledEvent(timeDelta, (String) args[0], (Integer) args[1],
|
||||
(Boolean) args[2]);
|
||||
@@ -517,6 +526,23 @@ public class LocationEventLog extends LocalEventLog {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LocationAdasEnabledEvent extends LogEvent {
|
||||
|
||||
private final int mUserId;
|
||||
private final boolean mEnabled;
|
||||
|
||||
LocationAdasEnabledEvent(long timeDelta, int userId, boolean enabled) {
|
||||
super(timeDelta);
|
||||
mUserId = userId;
|
||||
mEnabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLogString() {
|
||||
return "adas location [u" + mUserId + "] " + (mEnabled ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregate statistics for a single package under a single provider.
|
||||
*/
|
||||
|
||||
@@ -771,10 +771,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
boolean enabled = mContext.getSystemService(LocationManager.class)
|
||||
.isLocationEnabledForUser(UserHandle.CURRENT);
|
||||
|
||||
// .. but enable anyway, if there's an active settings-ignored request (e.g. ELS)
|
||||
// .. but enable anyway, if there's an active bypass request (e.g. ELS or ADAS)
|
||||
enabled |= (mProviderRequest != null
|
||||
&& mProviderRequest.isActive()
|
||||
&& mProviderRequest.isLocationSettingsIgnored());
|
||||
&& mProviderRequest.isBypass());
|
||||
|
||||
// ... and, finally, disable anyway, if device is being shut down
|
||||
enabled &= !mShutdown;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.location.injector;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.location.settings.LocationSettings;
|
||||
|
||||
/**
|
||||
* Injects various location dependencies so that they may be controlled by tests.
|
||||
@@ -27,6 +28,9 @@ public interface Injector {
|
||||
/** Returns a UserInfoHelper. */
|
||||
UserInfoHelper getUserInfoHelper();
|
||||
|
||||
/** Returns a LocationSettings. */
|
||||
LocationSettings getLocationSettings();
|
||||
|
||||
/** Returns an AlarmHelper. */
|
||||
AlarmHelper getAlarmHelper();
|
||||
|
||||
|
||||
@@ -670,8 +670,6 @@ public class SystemSettingsHelper extends SettingsHelper {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static class PackageTagsListSetting extends DeviceConfigSetting {
|
||||
|
||||
private final Supplier<ArrayMap<String, ArraySet<String>>> mBaseValuesSupplier;
|
||||
|
||||
@@ -113,6 +113,8 @@ import com.android.server.location.injector.UserInfoHelper;
|
||||
import com.android.server.location.injector.UserInfoHelper.UserListener;
|
||||
import com.android.server.location.listeners.ListenerMultiplexer;
|
||||
import com.android.server.location.listeners.RemoteListenerRegistration;
|
||||
import com.android.server.location.settings.LocationSettings;
|
||||
import com.android.server.location.settings.LocationUserSettings;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -548,6 +550,19 @@ public class LocationProviderManager extends
|
||||
return false;
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
final boolean onAdasGnssLocationEnabledChanged(int userId) {
|
||||
if (Build.IS_DEBUGGABLE) {
|
||||
Preconditions.checkState(Thread.holdsLock(mLock));
|
||||
}
|
||||
|
||||
if (getIdentity().getUserId() == userId) {
|
||||
return onProviderLocationRequestChanged();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
final boolean onForegroundChanged(int uid, boolean foreground) {
|
||||
if (Build.IS_DEBUGGABLE) {
|
||||
@@ -592,8 +607,8 @@ public class LocationProviderManager extends
|
||||
onHighPowerUsageChanged();
|
||||
updateService();
|
||||
|
||||
// if location settings ignored has changed then the active state may have changed
|
||||
return oldRequest.isLocationSettingsIgnored() != newRequest.isLocationSettingsIgnored();
|
||||
// if bypass state has changed then the active state may have changed
|
||||
return oldRequest.isBypass() != newRequest.isBypass();
|
||||
}
|
||||
|
||||
private LocationRequest calculateProviderLocationRequest() {
|
||||
@@ -616,9 +631,24 @@ public class LocationProviderManager extends
|
||||
if (!mSettingsHelper.getIgnoreSettingsAllowlist().contains(
|
||||
getIdentity().getPackageName(), getIdentity().getAttributionTag())
|
||||
&& !mLocationManagerInternal.isProvider(null, getIdentity())) {
|
||||
builder.setLocationSettingsIgnored(false);
|
||||
locationSettingsIgnored = false;
|
||||
}
|
||||
|
||||
builder.setLocationSettingsIgnored(locationSettingsIgnored);
|
||||
}
|
||||
|
||||
boolean adasGnssBypass = baseRequest.isAdasGnssBypass();
|
||||
if (adasGnssBypass) {
|
||||
// if we are not currently allowed use adas gnss bypass, disable it
|
||||
if (!GPS_PROVIDER.equals(mName)) {
|
||||
Log.e(TAG, "adas gnss bypass request received in non-gps provider");
|
||||
adasGnssBypass = false;
|
||||
} else if (!mLocationSettings.getUserSettings(
|
||||
getIdentity().getUserId()).isAdasGnssLocationEnabled()) {
|
||||
adasGnssBypass = false;
|
||||
}
|
||||
|
||||
builder.setAdasGnssBypass(adasGnssBypass);
|
||||
}
|
||||
|
||||
if (!locationSettingsIgnored && !isThrottlingExempt()) {
|
||||
@@ -769,7 +799,7 @@ public class LocationProviderManager extends
|
||||
Location lastLocation = getLastLocationUnsafe(
|
||||
getIdentity().getUserId(),
|
||||
getPermissionLevel(),
|
||||
getRequest().isLocationSettingsIgnored(),
|
||||
getRequest().isBypass(),
|
||||
maxLocationAgeMs);
|
||||
if (lastLocation != null) {
|
||||
executeOperation(acceptLocationChange(LocationResult.wrap(lastLocation)));
|
||||
@@ -1114,7 +1144,7 @@ public class LocationProviderManager extends
|
||||
Location lastLocation = getLastLocationUnsafe(
|
||||
getIdentity().getUserId(),
|
||||
getPermissionLevel(),
|
||||
getRequest().isLocationSettingsIgnored(),
|
||||
getRequest().isBypass(),
|
||||
MAX_CURRENT_LOCATION_AGE_MS);
|
||||
if (lastLocation != null) {
|
||||
executeOperation(acceptLocationChange(LocationResult.wrap(lastLocation)));
|
||||
@@ -1267,6 +1297,7 @@ public class LocationProviderManager extends
|
||||
private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners;
|
||||
|
||||
protected final LocationManagerInternal mLocationManagerInternal;
|
||||
protected final LocationSettings mLocationSettings;
|
||||
protected final SettingsHelper mSettingsHelper;
|
||||
protected final UserInfoHelper mUserHelper;
|
||||
protected final AlarmHelper mAlarmHelper;
|
||||
@@ -1280,6 +1311,8 @@ public class LocationProviderManager extends
|
||||
protected final LocationFudger mLocationFudger;
|
||||
|
||||
private final UserListener mUserChangedListener = this::onUserChanged;
|
||||
private final LocationSettings.LocationUserSettingsListener mLocationUserSettingsListener =
|
||||
this::onLocationUserSettingsChanged;
|
||||
private final UserSettingChangedListener mLocationEnabledChangedListener =
|
||||
this::onLocationEnabledChanged;
|
||||
private final GlobalSettingChangedListener mBackgroundThrottlePackageWhitelistChangedListener =
|
||||
@@ -1332,6 +1365,7 @@ public class LocationProviderManager extends
|
||||
|
||||
mLocationManagerInternal = Objects.requireNonNull(
|
||||
LocalServices.getService(LocationManagerInternal.class));
|
||||
mLocationSettings = injector.getLocationSettings();
|
||||
mSettingsHelper = injector.getSettingsHelper();
|
||||
mUserHelper = injector.getUserInfoHelper();
|
||||
mAlarmHelper = injector.getAlarmHelper();
|
||||
@@ -1362,6 +1396,7 @@ public class LocationProviderManager extends
|
||||
mStateChangedListener = listener;
|
||||
|
||||
mUserHelper.addListener(mUserChangedListener);
|
||||
mLocationSettings.registerLocationUserSettingsListener(mLocationUserSettingsListener);
|
||||
mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
|
||||
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
@@ -1389,6 +1424,7 @@ public class LocationProviderManager extends
|
||||
}
|
||||
|
||||
mUserHelper.removeListener(mUserChangedListener);
|
||||
mLocationSettings.unregisterLocationUserSettingsListener(mLocationUserSettingsListener);
|
||||
mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
|
||||
|
||||
// if external entities are registering listeners it's their responsibility to
|
||||
@@ -1550,7 +1586,7 @@ public class LocationProviderManager extends
|
||||
|
||||
public @Nullable Location getLastLocation(LastLocationRequest request,
|
||||
CallerIdentity identity, @PermissionLevel int permissionLevel) {
|
||||
if (!isActive(request.isLocationSettingsIgnored(), identity)) {
|
||||
if (!isActive(request.isBypass(), identity)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1564,7 +1600,7 @@ public class LocationProviderManager extends
|
||||
getLastLocationUnsafe(
|
||||
identity.getUserId(),
|
||||
permissionLevel,
|
||||
request.isLocationSettingsIgnored(),
|
||||
request.isBypass(),
|
||||
Long.MAX_VALUE),
|
||||
permissionLevel);
|
||||
|
||||
@@ -1584,7 +1620,7 @@ public class LocationProviderManager extends
|
||||
* location if necessary.
|
||||
*/
|
||||
public @Nullable Location getLastLocationUnsafe(int userId,
|
||||
@PermissionLevel int permissionLevel, boolean ignoreLocationSettings,
|
||||
@PermissionLevel int permissionLevel, boolean isBypass,
|
||||
long maximumAgeMs) {
|
||||
if (userId == UserHandle.USER_ALL) {
|
||||
// find the most recent location across all users
|
||||
@@ -1592,7 +1628,7 @@ public class LocationProviderManager extends
|
||||
final int[] runningUserIds = mUserHelper.getRunningUserIds();
|
||||
for (int i = 0; i < runningUserIds.length; i++) {
|
||||
Location next = getLastLocationUnsafe(runningUserIds[i], permissionLevel,
|
||||
ignoreLocationSettings, maximumAgeMs);
|
||||
isBypass, maximumAgeMs);
|
||||
if (lastLocation == null || (next != null && next.getElapsedRealtimeNanos()
|
||||
> lastLocation.getElapsedRealtimeNanos())) {
|
||||
lastLocation = next;
|
||||
@@ -1601,7 +1637,7 @@ public class LocationProviderManager extends
|
||||
return lastLocation;
|
||||
} else if (userId == UserHandle.USER_CURRENT) {
|
||||
return getLastLocationUnsafe(mUserHelper.getCurrentUserId(), permissionLevel,
|
||||
ignoreLocationSettings, maximumAgeMs);
|
||||
isBypass, maximumAgeMs);
|
||||
}
|
||||
|
||||
Preconditions.checkArgument(userId >= 0);
|
||||
@@ -1613,7 +1649,7 @@ public class LocationProviderManager extends
|
||||
if (lastLocation == null) {
|
||||
location = null;
|
||||
} else {
|
||||
location = lastLocation.get(permissionLevel, ignoreLocationSettings);
|
||||
location = lastLocation.get(permissionLevel, isBypass);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1925,7 +1961,7 @@ public class LocationProviderManager extends
|
||||
// provider, under the assumption that once we send the request off, the provider will
|
||||
// immediately attempt to deliver a new location satisfying that request.
|
||||
long delayMs;
|
||||
if (!oldRequest.isLocationSettingsIgnored() && newRequest.isLocationSettingsIgnored()) {
|
||||
if (!oldRequest.isBypass() && newRequest.isBypass()) {
|
||||
delayMs = 0;
|
||||
} else if (newRequest.getIntervalMillis() > oldRequest.getIntervalMillis()) {
|
||||
// if the interval has increased, tell the provider immediately, so it can save power
|
||||
@@ -2002,12 +2038,12 @@ public class LocationProviderManager extends
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean locationSettingsIgnored = registration.getRequest().isLocationSettingsIgnored();
|
||||
if (!isActive(locationSettingsIgnored, registration.getIdentity())) {
|
||||
boolean isBypass = registration.getRequest().isBypass();
|
||||
if (!isActive(isBypass, registration.getIdentity())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!locationSettingsIgnored) {
|
||||
if (!isBypass) {
|
||||
switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) {
|
||||
case LOCATION_MODE_FOREGROUND_ONLY:
|
||||
if (!registration.isForeground()) {
|
||||
@@ -2036,15 +2072,15 @@ public class LocationProviderManager extends
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isActive(boolean locationSettingsIgnored, CallerIdentity identity) {
|
||||
private boolean isActive(boolean isBypass, CallerIdentity identity) {
|
||||
if (identity.isSystemServer()) {
|
||||
if (!locationSettingsIgnored) {
|
||||
if (!isBypass) {
|
||||
if (!isEnabled(mUserHelper.getCurrentUserId())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!locationSettingsIgnored) {
|
||||
if (!isBypass) {
|
||||
if (!isEnabled(identity.getUserId())) {
|
||||
return false;
|
||||
}
|
||||
@@ -2071,6 +2107,7 @@ public class LocationProviderManager extends
|
||||
long intervalMs = ProviderRequest.INTERVAL_DISABLED;
|
||||
int quality = LocationRequest.QUALITY_LOW_POWER;
|
||||
long maxUpdateDelayMs = Long.MAX_VALUE;
|
||||
boolean adasGnssBypass = false;
|
||||
boolean locationSettingsIgnored = false;
|
||||
boolean lowPower = true;
|
||||
|
||||
@@ -2086,6 +2123,7 @@ public class LocationProviderManager extends
|
||||
intervalMs = min(request.getIntervalMillis(), intervalMs);
|
||||
quality = min(request.getQuality(), quality);
|
||||
maxUpdateDelayMs = min(request.getMaxUpdateDelayMillis(), maxUpdateDelayMs);
|
||||
adasGnssBypass |= request.isAdasGnssBypass();
|
||||
locationSettingsIgnored |= request.isLocationSettingsIgnored();
|
||||
lowPower &= request.isLowPower();
|
||||
}
|
||||
@@ -2123,6 +2161,7 @@ public class LocationProviderManager extends
|
||||
.setIntervalMillis(intervalMs)
|
||||
.setQuality(quality)
|
||||
.setMaxUpdateDelayMillis(maxUpdateDelayMs)
|
||||
.setAdasGnssBypass(adasGnssBypass)
|
||||
.setLocationSettingsIgnored(locationSettingsIgnored)
|
||||
.setLowPower(lowPower)
|
||||
.setWorkSource(workSource)
|
||||
@@ -2191,6 +2230,16 @@ public class LocationProviderManager extends
|
||||
}
|
||||
}
|
||||
|
||||
private void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
|
||||
LocationUserSettings newSettings) {
|
||||
if (oldSettings.isAdasGnssLocationEnabled() != newSettings.isAdasGnssLocationEnabled()) {
|
||||
synchronized (mLock) {
|
||||
updateRegistrations(
|
||||
registration -> registration.onAdasGnssLocationEnabledChanged(userId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onLocationEnabledChanged(int userId) {
|
||||
synchronized (mLock) {
|
||||
if (mState == STATE_STOPPED) {
|
||||
@@ -2560,16 +2609,16 @@ public class LocationProviderManager extends
|
||||
}
|
||||
|
||||
public @Nullable Location get(@PermissionLevel int permissionLevel,
|
||||
boolean ignoreLocationSettings) {
|
||||
boolean isBypass) {
|
||||
switch (permissionLevel) {
|
||||
case PERMISSION_FINE:
|
||||
if (ignoreLocationSettings) {
|
||||
if (isBypass) {
|
||||
return mFineBypassLocation;
|
||||
} else {
|
||||
return mFineLocation;
|
||||
}
|
||||
case PERMISSION_COARSE:
|
||||
if (ignoreLocationSettings) {
|
||||
if (isBypass) {
|
||||
return mCoarseBypassLocation;
|
||||
} else {
|
||||
return mCoarseLocation;
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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.settings;
|
||||
|
||||
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.FgThread;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Accessor for location user settings. Ensure there is only ever one instance as multiple instances
|
||||
* don't play nicely with each other.
|
||||
*/
|
||||
public class LocationSettings {
|
||||
|
||||
/** Listens for changes to location user settings. */
|
||||
public interface LocationUserSettingsListener {
|
||||
/** Invoked when location user settings have changed for the given user. */
|
||||
void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
|
||||
LocationUserSettings newSettings);
|
||||
}
|
||||
|
||||
private static final String LOCATION_DIRNAME = "location";
|
||||
private static final String LOCATION_SETTINGS_FILENAME = "settings";
|
||||
|
||||
final Context mContext;
|
||||
|
||||
@GuardedBy("mUserSettings")
|
||||
private final SparseArray<LocationUserSettingsStore> mUserSettings;
|
||||
private final CopyOnWriteArrayList<LocationUserSettingsListener> mUserSettingsListeners;
|
||||
|
||||
public LocationSettings(Context context) {
|
||||
mContext = context;
|
||||
mUserSettings = new SparseArray<>(1);
|
||||
mUserSettingsListeners = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
/** Registers a listener for changes to location user settings. */
|
||||
public final void registerLocationUserSettingsListener(LocationUserSettingsListener listener) {
|
||||
mUserSettingsListeners.add(listener);
|
||||
}
|
||||
|
||||
/** Unregisters a listener for changes to location user settings. */
|
||||
public final void unregisterLocationUserSettingsListener(
|
||||
LocationUserSettingsListener listener) {
|
||||
mUserSettingsListeners.remove(listener);
|
||||
}
|
||||
|
||||
protected File getUserSettingsDir(int userId) {
|
||||
return Environment.getDataSystemDeDirectory(userId);
|
||||
}
|
||||
|
||||
protected LocationUserSettingsStore createUserSettingsStore(int userId, File file) {
|
||||
return new LocationUserSettingsStore(userId, file);
|
||||
}
|
||||
|
||||
private LocationUserSettingsStore getUserSettingsStore(int userId) {
|
||||
synchronized (mUserSettings) {
|
||||
LocationUserSettingsStore settingsStore = mUserSettings.get(userId);
|
||||
if (settingsStore == null) {
|
||||
File file = new File(new File(getUserSettingsDir(userId), LOCATION_DIRNAME),
|
||||
LOCATION_SETTINGS_FILENAME);
|
||||
settingsStore = createUserSettingsStore(userId, file);
|
||||
mUserSettings.put(userId, settingsStore);
|
||||
}
|
||||
return settingsStore;
|
||||
}
|
||||
}
|
||||
|
||||
/** Retrieves the current state of location user settings. */
|
||||
public final LocationUserSettings getUserSettings(int userId) {
|
||||
return getUserSettingsStore(userId).get();
|
||||
}
|
||||
|
||||
/** Updates the current state of location user settings for the given user. */
|
||||
public final void updateUserSettings(int userId,
|
||||
Function<LocationUserSettings, LocationUserSettings> updater) {
|
||||
getUserSettingsStore(userId).update(updater);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
final void flushFiles() throws InterruptedException {
|
||||
synchronized (mUserSettings) {
|
||||
int size = mUserSettings.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
mUserSettings.valueAt(i).flushFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
final void deleteFiles() throws InterruptedException {
|
||||
synchronized (mUserSettings) {
|
||||
int size = mUserSettings.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
mUserSettings.valueAt(i).deleteFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final void fireListeners(int userId, LocationUserSettings oldSettings,
|
||||
LocationUserSettings newSettings) {
|
||||
for (LocationUserSettingsListener listener : mUserSettingsListeners) {
|
||||
listener.onLocationUserSettingsChanged(userId, oldSettings, newSettings);
|
||||
}
|
||||
}
|
||||
|
||||
class LocationUserSettingsStore extends SettingsStore<LocationUserSettings> {
|
||||
|
||||
protected final int mUserId;
|
||||
|
||||
LocationUserSettingsStore(int userId, File file) {
|
||||
super(file);
|
||||
mUserId = userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LocationUserSettings read(int version, DataInput in) throws IOException {
|
||||
return filterSettings(LocationUserSettings.read(mContext.getResources(), version, in));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void write(DataOutput out, LocationUserSettings settings) throws IOException {
|
||||
settings.write(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Function<LocationUserSettings, LocationUserSettings> updater) {
|
||||
super.update(settings -> filterSettings(updater.apply(settings)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChange(LocationUserSettings oldSettings,
|
||||
LocationUserSettings newSettings) {
|
||||
FgThread.getExecutor().execute(() -> fireListeners(mUserId, oldSettings, newSettings));
|
||||
}
|
||||
|
||||
private LocationUserSettings filterSettings(LocationUserSettings settings) {
|
||||
if (settings.isAdasGnssLocationEnabled()
|
||||
&& !mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) {
|
||||
// prevent non-automotive devices from ever enabling this
|
||||
settings = settings.withAdasGnssLocationEnabled(false);
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.settings;
|
||||
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/** Holds the state of location user settings. */
|
||||
public final class LocationUserSettings implements SettingsStore.VersionedSettings {
|
||||
|
||||
// remember to bump this version code and add the appropriate upgrade logic whenever the format
|
||||
// is changed.
|
||||
private static final int VERSION = 1;
|
||||
|
||||
private final boolean mAdasGnssLocationEnabled;
|
||||
|
||||
private LocationUserSettings(boolean adasGnssLocationEnabled) {
|
||||
mAdasGnssLocationEnabled = adasGnssLocationEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
public boolean isAdasGnssLocationEnabled() {
|
||||
return mAdasGnssLocationEnabled;
|
||||
}
|
||||
|
||||
/** Returns an instance with ADAS GNSS location enabled state set as given. */
|
||||
public LocationUserSettings withAdasGnssLocationEnabled(boolean adasEnabled) {
|
||||
if (adasEnabled == mAdasGnssLocationEnabled) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new LocationUserSettings(adasEnabled);
|
||||
}
|
||||
|
||||
void write(DataOutput out) throws IOException {
|
||||
out.writeBoolean(mAdasGnssLocationEnabled);
|
||||
}
|
||||
|
||||
static LocationUserSettings read(Resources resources, int version, DataInput in)
|
||||
throws IOException {
|
||||
boolean adasGnssLocationEnabled;
|
||||
|
||||
// upgrade code goes here. remember to bump the version field when changing the format
|
||||
switch (version) {
|
||||
default:
|
||||
// set all fields to defaults
|
||||
adasGnssLocationEnabled = resources.getBoolean(
|
||||
R.bool.config_defaultAdasGnssLocationEnabled);
|
||||
break;
|
||||
case 1:
|
||||
adasGnssLocationEnabled = in.readBoolean();
|
||||
// fall through
|
||||
}
|
||||
|
||||
return new LocationUserSettings(adasGnssLocationEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof LocationUserSettings)) {
|
||||
return false;
|
||||
}
|
||||
LocationUserSettings that = (LocationUserSettings) o;
|
||||
return mAdasGnssLocationEnabled == that.mAdasGnssLocationEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mAdasGnssLocationEnabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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.settings;
|
||||
|
||||
import static com.android.server.location.LocationManagerService.TAG;
|
||||
import static com.android.server.location.settings.SettingsStore.VersionedSettings.VERSION_DOES_NOT_EXIST;
|
||||
|
||||
import android.util.AtomicFile;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.os.BackgroundThread;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.function.Function;
|
||||
|
||||
/** Base class for read/write/versioning functionality for storing persistent settings to a file. */
|
||||
abstract class SettingsStore<T extends SettingsStore.VersionedSettings> {
|
||||
|
||||
interface VersionedSettings {
|
||||
/** Represents that the settings do not exist. */
|
||||
int VERSION_DOES_NOT_EXIST = Integer.MAX_VALUE;
|
||||
|
||||
/** Must always return a version number less than {@link #VERSION_DOES_NOT_EXIST}. */
|
||||
int getVersion();
|
||||
}
|
||||
|
||||
private final AtomicFile mFile;
|
||||
|
||||
@GuardedBy("this")
|
||||
private boolean mInitialized;
|
||||
@GuardedBy("this")
|
||||
private T mCache;
|
||||
|
||||
protected SettingsStore(File file) {
|
||||
mFile = new AtomicFile(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be implemented to read in a settings instance, and upgrade to the appropriate version
|
||||
* where necessary. If the provided version is {@link VersionedSettings#VERSION_DOES_NOT_EXIST}
|
||||
* then the DataInput will be empty, and the method should return a settings instance with all
|
||||
* settings set to the default value.
|
||||
*/
|
||||
protected abstract T read(int version, DataInput in) throws IOException;
|
||||
|
||||
/**
|
||||
* Must be implemented to write the given settings to the given DataOutput.
|
||||
*/
|
||||
protected abstract void write(DataOutput out, T settings) throws IOException;
|
||||
|
||||
/**
|
||||
* Invoked when settings change, and while holding the internal lock. If used to invoke
|
||||
* listeners, ensure they are not invoked while holding the lock (ie, asynchronously).
|
||||
*/
|
||||
protected abstract void onChange(T oldSettings, T newSettings);
|
||||
|
||||
public final synchronized void initializeCache() {
|
||||
if (!mInitialized) {
|
||||
if (mFile.exists()) {
|
||||
try (DataInputStream is = new DataInputStream(mFile.openRead())) {
|
||||
mCache = read(is.readInt(), is);
|
||||
Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "error reading location settings (" + mFile
|
||||
+ "), falling back to defaults", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (mCache == null) {
|
||||
try {
|
||||
mCache = read(VERSION_DOES_NOT_EXIST,
|
||||
new DataInputStream(new ByteArrayInputStream(new byte[0])));
|
||||
Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
mInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public final synchronized T get() {
|
||||
initializeCache();
|
||||
return mCache;
|
||||
}
|
||||
|
||||
public synchronized void update(Function<T, T> updater) {
|
||||
initializeCache();
|
||||
|
||||
T oldSettings = mCache;
|
||||
T newSettings = Objects.requireNonNull(updater.apply(oldSettings));
|
||||
if (oldSettings.equals(newSettings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mCache = newSettings;
|
||||
Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
|
||||
|
||||
writeLazily(newSettings);
|
||||
|
||||
onChange(oldSettings, newSettings);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
synchronized void flushFile() throws InterruptedException {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
BackgroundThread.getExecutor().execute(latch::countDown);
|
||||
latch.await();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
synchronized void deleteFile() throws InterruptedException {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
BackgroundThread.getExecutor().execute(() -> {
|
||||
mFile.delete();
|
||||
latch.countDown();
|
||||
});
|
||||
latch.await();
|
||||
}
|
||||
|
||||
private void writeLazily(T settings) {
|
||||
BackgroundThread.getExecutor().execute(() -> {
|
||||
FileOutputStream os = null;
|
||||
try {
|
||||
os = mFile.startWrite();
|
||||
DataOutputStream out = new DataOutputStream(os);
|
||||
out.writeInt(settings.getVersion());
|
||||
write(out, settings);
|
||||
mFile.finishWrite(os);
|
||||
} catch (IOException e) {
|
||||
mFile.failWrite(os);
|
||||
Log.e(TAG, "failure serializing location settings", e);
|
||||
} catch (Throwable e) {
|
||||
mFile.failWrite(os);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.android.server.location.gnss;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -49,6 +50,7 @@ public class GnssGeofenceProxyTest {
|
||||
private static final int NOTIFICATION_RESPONSIVENESS = 0;
|
||||
private static final int UNKNOWN_TIMER = 0;
|
||||
|
||||
private @Mock Context mContext;
|
||||
private @Mock GnssConfiguration mMockConfiguration;
|
||||
private @Mock GnssNative.GeofenceCallbacks mGeofenceCallbacks;
|
||||
|
||||
@@ -63,7 +65,7 @@ public class GnssGeofenceProxyTest {
|
||||
GnssNative.setGnssHalForTest(mFakeHal);
|
||||
|
||||
GnssNative gnssNative = Objects.requireNonNull(
|
||||
GnssNative.create(new TestInjector(), mMockConfiguration));
|
||||
GnssNative.create(new TestInjector(mContext), mMockConfiguration));
|
||||
gnssNative.setGeofenceCallbacks(mGeofenceCallbacks);
|
||||
mTestProvider = new GnssGeofenceProxy(gnssNative);
|
||||
gnssNative.register();
|
||||
|
||||
@@ -29,9 +29,10 @@ import java.util.HashMap;
|
||||
public class FakeAppOpsHelper extends AppOpsHelper {
|
||||
|
||||
private static class AppOp {
|
||||
private boolean mAllowed = true;
|
||||
private boolean mStarted = false;
|
||||
private int mNoteCount = 0;
|
||||
AppOp() {}
|
||||
boolean mAllowed = true;
|
||||
boolean mStarted = false;
|
||||
int mNoteCount = 0;
|
||||
}
|
||||
|
||||
private final HashMap<String, SparseArray<AppOp>> mAppOps;
|
||||
|
||||
@@ -29,8 +29,8 @@ import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Version of AppOpsHelper for testing. Settings are initialized to reasonable defaults (location is
|
||||
* enabled by default).
|
||||
* Version of SettingsHelper for testing. Settings are initialized to reasonable defaults (location
|
||||
* is enabled by default).
|
||||
*/
|
||||
public class FakeSettingsHelper extends SettingsHelper {
|
||||
|
||||
|
||||
@@ -16,9 +16,14 @@
|
||||
|
||||
package com.android.server.location.injector;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.server.location.settings.FakeLocationSettings;
|
||||
|
||||
public class TestInjector implements Injector {
|
||||
|
||||
private final FakeUserInfoHelper mUserInfoHelper;
|
||||
private final FakeLocationSettings mLocationSettings;
|
||||
private final FakeAlarmHelper mAlarmHelper;
|
||||
private final FakeAppOpsHelper mAppOpsHelper;
|
||||
private final FakeLocationPermissionsHelper mLocationPermissionsHelper;
|
||||
@@ -32,8 +37,9 @@ public class TestInjector implements Injector {
|
||||
private final FakeEmergencyHelper mEmergencyHelper;
|
||||
private final LocationUsageLogger mLocationUsageLogger;
|
||||
|
||||
public TestInjector() {
|
||||
public TestInjector(Context context) {
|
||||
mUserInfoHelper = new FakeUserInfoHelper();
|
||||
mLocationSettings = new FakeLocationSettings(context);
|
||||
mAlarmHelper = new FakeAlarmHelper();
|
||||
mAppOpsHelper = new FakeAppOpsHelper();
|
||||
mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
|
||||
@@ -53,6 +59,11 @@ public class TestInjector implements Injector {
|
||||
return mUserInfoHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FakeLocationSettings getLocationSettings() {
|
||||
return mLocationSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FakeAlarmHelper getAlarmHelper() {
|
||||
return mAlarmHelper;
|
||||
|
||||
@@ -19,6 +19,8 @@ package com.android.server.location.provider;
|
||||
import static android.app.AppOpsManager.OP_FINE_LOCATION;
|
||||
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
|
||||
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
|
||||
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
|
||||
import static android.location.LocationManager.GPS_PROVIDER;
|
||||
import static android.location.LocationRequest.PASSIVE_INTERVAL;
|
||||
import static android.location.provider.ProviderProperties.POWER_USAGE_HIGH;
|
||||
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
|
||||
@@ -56,6 +58,8 @@ import static org.mockito.MockitoAnnotations.initMocks;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.location.ILocationCallback;
|
||||
import android.location.ILocationListener;
|
||||
import android.location.LastLocationRequest;
|
||||
@@ -82,6 +86,7 @@ import android.util.Log;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.server.FgThread;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.location.injector.FakeUserInfoHelper;
|
||||
@@ -139,6 +144,10 @@ public class LocationProviderManagerTest {
|
||||
@Mock
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private Resources mResources;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private PowerManager mPowerManager;
|
||||
@Mock
|
||||
private PowerManager.WakeLock mWakeLock;
|
||||
@@ -161,20 +170,28 @@ public class LocationProviderManagerTest {
|
||||
LocalServices.addService(LocationManagerInternal.class, mInternal);
|
||||
|
||||
doReturn("android").when(mContext).getPackageName();
|
||||
doReturn(mResources).when(mContext).getResources();
|
||||
doReturn(mPackageManager).when(mContext).getPackageManager();
|
||||
doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
|
||||
doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
|
||||
|
||||
mInjector = new TestInjector();
|
||||
mInjector = new TestInjector(mContext);
|
||||
mInjector.getUserInfoHelper().startUser(OTHER_USER);
|
||||
|
||||
mPassive = new PassiveLocationProviderManager(mContext, mInjector);
|
||||
mPassive.startManager(null);
|
||||
mPassive.setRealProvider(new PassiveLocationProvider(mContext));
|
||||
|
||||
createManager(NAME);
|
||||
}
|
||||
|
||||
private void createManager(String name) {
|
||||
mStateChangedListener = mock(LocationProviderManager.StateChangedListener.class);
|
||||
|
||||
mProvider = new TestProvider(PROPERTIES, PROVIDER_IDENTITY);
|
||||
mProvider.setProviderAllowed(true);
|
||||
|
||||
mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive);
|
||||
mManager = new LocationProviderManager(mContext, mInjector, name, mPassive);
|
||||
mManager.startManager(mStateChangedListener);
|
||||
mManager.setRealProvider(mProvider);
|
||||
}
|
||||
@@ -1016,6 +1033,95 @@ public class LocationProviderManagerTest {
|
||||
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProviderRequest_AdasGnssBypass() {
|
||||
doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
|
||||
doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
|
||||
|
||||
createManager(GPS_PROVIDER);
|
||||
|
||||
ILocationListener listener1 = createMockLocationListener();
|
||||
LocationRequest request1 = new LocationRequest.Builder(5)
|
||||
.setWorkSource(WORK_SOURCE)
|
||||
.build();
|
||||
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
|
||||
|
||||
assertThat(mProvider.getRequest().isActive()).isTrue();
|
||||
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
|
||||
assertThat(mProvider.getRequest().isAdasGnssBypass()).isFalse();
|
||||
|
||||
ILocationListener listener2 = createMockLocationListener();
|
||||
LocationRequest request2 = new LocationRequest.Builder(1)
|
||||
.setAdasGnssBypass(true)
|
||||
.setWorkSource(WORK_SOURCE)
|
||||
.build();
|
||||
mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
|
||||
|
||||
assertThat(mProvider.getRequest().isActive()).isTrue();
|
||||
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
|
||||
assertThat(mProvider.getRequest().isAdasGnssBypass()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProviderRequest_AdasGnssBypass_ProviderDisabled() {
|
||||
doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
|
||||
doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
|
||||
|
||||
createManager(GPS_PROVIDER);
|
||||
|
||||
ILocationListener listener1 = createMockLocationListener();
|
||||
LocationRequest request1 = new LocationRequest.Builder(1)
|
||||
.setWorkSource(WORK_SOURCE)
|
||||
.build();
|
||||
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
|
||||
|
||||
ILocationListener listener2 = createMockLocationListener();
|
||||
LocationRequest request2 = new LocationRequest.Builder(5)
|
||||
.setAdasGnssBypass(true)
|
||||
.setWorkSource(WORK_SOURCE)
|
||||
.build();
|
||||
mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
|
||||
|
||||
mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
|
||||
|
||||
assertThat(mProvider.getRequest().isActive()).isTrue();
|
||||
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
|
||||
assertThat(mProvider.getRequest().isAdasGnssBypass()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProviderRequest_AdasGnssBypass_ProviderDisabled_AdasDisabled() {
|
||||
mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
|
||||
new PackageTagsList.Builder().add(
|
||||
IDENTITY.getPackageName()).build());
|
||||
doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
|
||||
doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
|
||||
|
||||
createManager(GPS_PROVIDER);
|
||||
|
||||
ILocationListener listener1 = createMockLocationListener();
|
||||
LocationRequest request1 = new LocationRequest.Builder(5)
|
||||
.setLocationSettingsIgnored(true)
|
||||
.setWorkSource(WORK_SOURCE)
|
||||
.build();
|
||||
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
|
||||
|
||||
ILocationListener listener2 = createMockLocationListener();
|
||||
LocationRequest request2 = new LocationRequest.Builder(1)
|
||||
.setAdasGnssBypass(true)
|
||||
.setWorkSource(WORK_SOURCE)
|
||||
.build();
|
||||
mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
|
||||
|
||||
mInjector.getLocationSettings().updateUserSettings(IDENTITY.getUserId(),
|
||||
settings -> settings.withAdasGnssLocationEnabled(false));
|
||||
mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
|
||||
|
||||
assertThat(mProvider.getRequest().isActive()).isTrue();
|
||||
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
|
||||
assertThat(mProvider.getRequest().isAdasGnssBypass()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProviderRequest_BatterySaver_ScreenOnOff() {
|
||||
mInjector.getLocationPowerSaveModeHelper().setLocationPowerSaveMode(
|
||||
|
||||
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.location.LocationResult;
|
||||
import android.location.provider.ProviderRequest;
|
||||
@@ -58,6 +59,7 @@ public class StationaryThrottlingLocationProviderTest {
|
||||
private TestInjector mInjector;
|
||||
private FakeProvider mDelegateProvider;
|
||||
|
||||
private @Mock Context mContext;
|
||||
private @Mock AbstractLocationProvider.Listener mListener;
|
||||
private @Mock FakeProvider.FakeProviderInterface mDelegate;
|
||||
|
||||
@@ -72,7 +74,7 @@ public class StationaryThrottlingLocationProviderTest {
|
||||
|
||||
mRandom = new Random(seed);
|
||||
|
||||
mInjector = new TestInjector();
|
||||
mInjector = new TestInjector(mContext);
|
||||
mDelegateProvider = new FakeProvider(mDelegate);
|
||||
|
||||
mProvider = new StationaryThrottlingLocationProvider("test_provider", mInjector,
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.settings;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class FakeLocationSettings extends LocationSettings {
|
||||
|
||||
public FakeLocationSettings(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getUserSettingsDir(int userId) {
|
||||
return ApplicationProvider.getApplicationContext().getCacheDir();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LocationUserSettingsStore createUserSettingsStore(int userId, File file) {
|
||||
return new FakeLocationUserSettingsStore(userId, file);
|
||||
}
|
||||
|
||||
private class FakeLocationUserSettingsStore extends LocationUserSettingsStore {
|
||||
|
||||
FakeLocationUserSettingsStore(int userId, File file) {
|
||||
super(userId, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChange(LocationUserSettings oldSettings,
|
||||
LocationUserSettings newSettings) {
|
||||
fireListeners(mUserId, oldSettings, newSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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.settings;
|
||||
|
||||
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.after;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@Presubmit
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class LocationSettingsTest {
|
||||
|
||||
private @Mock Context mContext;
|
||||
private @Mock Resources mResources;
|
||||
private @Mock PackageManager mPackageManager;
|
||||
|
||||
private LocationSettings mLocationSettings;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
initMocks(this);
|
||||
|
||||
doReturn(mResources).when(mContext).getResources();
|
||||
doReturn(mPackageManager).when(mContext).getPackageManager();
|
||||
doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
|
||||
|
||||
resetLocationSettings();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
mLocationSettings.deleteFiles();
|
||||
}
|
||||
|
||||
private void resetLocationSettings() {
|
||||
mLocationSettings = new LocationSettings(mContext) {
|
||||
@Override
|
||||
protected File getUserSettingsDir(int userId) {
|
||||
return ApplicationProvider.getApplicationContext().getCacheDir();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadDefaults() {
|
||||
doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
|
||||
assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
|
||||
|
||||
doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
|
||||
assertThat(mLocationSettings.getUserSettings(2).isAdasGnssLocationEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() {
|
||||
doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
|
||||
mLocationSettings.updateUserSettings(1,
|
||||
settings -> settings.withAdasGnssLocationEnabled(true));
|
||||
assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
|
||||
|
||||
mLocationSettings.updateUserSettings(1,
|
||||
settings -> settings.withAdasGnssLocationEnabled(false));
|
||||
assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSerialization() throws Exception {
|
||||
doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
|
||||
mLocationSettings.updateUserSettings(1,
|
||||
settings -> settings.withAdasGnssLocationEnabled(true));
|
||||
assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
|
||||
|
||||
mLocationSettings.flushFiles();
|
||||
resetLocationSettings();
|
||||
assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListeners() {
|
||||
doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
|
||||
LocationSettings.LocationUserSettingsListener listener = mock(
|
||||
LocationSettings.LocationUserSettingsListener.class);
|
||||
|
||||
mLocationSettings.registerLocationUserSettingsListener(listener);
|
||||
|
||||
ArgumentCaptor<LocationUserSettings> oldCaptor = ArgumentCaptor.forClass(
|
||||
LocationUserSettings.class);
|
||||
ArgumentCaptor<LocationUserSettings> newCaptor = ArgumentCaptor.forClass(
|
||||
LocationUserSettings.class);
|
||||
mLocationSettings.updateUserSettings(1,
|
||||
settings -> settings.withAdasGnssLocationEnabled(true));
|
||||
verify(listener, timeout(500).times(1)).onLocationUserSettingsChanged(eq(1),
|
||||
oldCaptor.capture(), newCaptor.capture());
|
||||
assertThat(oldCaptor.getValue().isAdasGnssLocationEnabled()).isFalse();
|
||||
assertThat(newCaptor.getValue().isAdasGnssLocationEnabled()).isTrue();
|
||||
|
||||
oldCaptor = ArgumentCaptor.forClass(LocationUserSettings.class);
|
||||
newCaptor = ArgumentCaptor.forClass(LocationUserSettings.class);
|
||||
mLocationSettings.updateUserSettings(1,
|
||||
settings -> settings.withAdasGnssLocationEnabled(false));
|
||||
verify(listener, timeout(500).times(2)).onLocationUserSettingsChanged(eq(1),
|
||||
oldCaptor.capture(), newCaptor.capture());
|
||||
assertThat(oldCaptor.getValue().isAdasGnssLocationEnabled()).isTrue();
|
||||
assertThat(newCaptor.getValue().isAdasGnssLocationEnabled()).isFalse();
|
||||
|
||||
mLocationSettings.unregisterLocationUserSettingsListener(listener);
|
||||
mLocationSettings.updateUserSettings(1,
|
||||
settings -> settings.withAdasGnssLocationEnabled(true));
|
||||
verify(listener, after(500).times(2)).onLocationUserSettingsChanged(anyInt(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonAutomotive() {
|
||||
doReturn(false).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
|
||||
doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
|
||||
|
||||
LocationSettings.LocationUserSettingsListener listener = mock(
|
||||
LocationSettings.LocationUserSettingsListener.class);
|
||||
mLocationSettings.registerLocationUserSettingsListener(listener);
|
||||
|
||||
assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
|
||||
mLocationSettings.updateUserSettings(1,
|
||||
settings -> settings.withAdasGnssLocationEnabled(true));
|
||||
assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
|
||||
verify(listener, after(500).never()).onLocationUserSettingsChanged(anyInt(), any(), any());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user