Merge "Adds LastLocationRequest SystemApi"

This commit is contained in:
TreeHugger Robot
2020-12-07 18:22:20 +00:00
committed by Android (Google) Code Review
8 changed files with 361 additions and 51 deletions

View File

@@ -4116,6 +4116,22 @@ package android.location {
method @Deprecated public void onStatusChanged(int);
}
public final class LastLocationRequest implements android.os.Parcelable {
method public int describeContents();
method public boolean isHiddenFromAppOps();
method public boolean isLocationSettingsIgnored();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.LastLocationRequest> CREATOR;
}
public static final class LastLocationRequest.Builder {
ctor public LastLocationRequest.Builder();
ctor public LastLocationRequest.Builder(@NonNull android.location.LastLocationRequest);
method @NonNull public android.location.LastLocationRequest build();
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LastLocationRequest.Builder setHiddenFromAppOps(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LastLocationRequest.Builder setLocationSettingsIgnored(boolean);
}
public class Location implements android.os.Parcelable {
method public boolean isComplete();
method public void makeComplete();
@@ -4128,6 +4144,7 @@ package android.location {
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
method @Nullable public String getExtraLocationControllerPackage();
method @Deprecated public int getGnssBatchSize();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.location.Location getLastKnownLocation(@NonNull String, @NonNull android.location.LastLocationRequest);
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void injectGnssMeasurementCorrections(@NonNull android.location.GnssMeasurementCorrections);
method public boolean isExtraLocationControllerPackageEnabled();
method public boolean isLocationEnabledForUser(@NonNull android.os.UserHandle);

View File

@@ -30,6 +30,7 @@ import android.location.IGnssStatusListener;
import android.location.IGnssNavigationMessageListener;
import android.location.ILocationCallback;
import android.location.ILocationListener;
import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationRequest;
import android.location.LocationTime;
@@ -45,7 +46,7 @@ import com.android.internal.location.ProviderProperties;
*/
interface ILocationManager
{
@nullable Location getLastLocation(String provider, String packageName, String attributionTag);
@nullable Location getLastLocation(String provider, in LastLocationRequest request, String packageName, String attributionTag);
@nullable ICancellationSignal getCurrentLocation(String provider, in LocationRequest request, in ILocationCallback callback, String packageName, String attributionTag, String listenerId);
void registerLocationListener(String provider, in LocationRequest request, in ILocationListener listener, String packageName, String attributionTag, String listenerId);

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2012, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.location;
parcelable LastLocationRequest;

View File

@@ -0,0 +1,192 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.location;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* An encapsulation of various parameters for requesting last location via {@link LocationManager}.
*
* @hide
*/
@SystemApi
public final class LastLocationRequest implements Parcelable {
private final boolean mHiddenFromAppOps;
private final boolean mLocationSettingsIgnored;
private LastLocationRequest(
boolean hiddenFromAppOps,
boolean locationSettingsIgnored) {
mHiddenFromAppOps = hiddenFromAppOps;
mLocationSettingsIgnored = locationSettingsIgnored;
}
/**
* Returns true if this last location request should be ignored while updating app ops with
* location usage. This implies that someone else (usually the creator of the last location
* request) is responsible for updating app ops.
*
* @return true if this request should be ignored while updating app ops with location usage
*
*/
public boolean isHiddenFromAppOps() {
return mHiddenFromAppOps;
}
/**
* 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.
*
* @return true if all limiting factors will be ignored to satisfy this request
*/
public boolean isLocationSettingsIgnored() {
return 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(),
/* locationSettingsIgnored= */ in.readBoolean());
}
@Override
public LastLocationRequest[] newArray(int size) {
return new LastLocationRequest[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeBoolean(mHiddenFromAppOps);
parcel.writeBoolean(mLocationSettingsIgnored);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LastLocationRequest that = (LastLocationRequest) o;
return mHiddenFromAppOps == that.mHiddenFromAppOps
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored;
}
@Override
public int hashCode() {
return Objects.hash(mHiddenFromAppOps, mLocationSettingsIgnored);
}
@NonNull
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("LastLocationRequest[");
if (mHiddenFromAppOps) {
s.append("hiddenFromAppOps, ");
}
if (mLocationSettingsIgnored) {
s.append("locationSettingsIgnored, ");
}
if (s.length() > "LastLocationRequest[".length()) {
s.setLength(s.length() - 2);
}
s.append(']');
return s.toString();
}
/**
* A builder class for {@link LastLocationRequest}.
*/
public static final class Builder {
private boolean mHiddenFromAppOps;
private boolean mLocationSettingsIgnored;
/**
* Creates a new Builder.
*/
public Builder() {
mHiddenFromAppOps = false;
mLocationSettingsIgnored = false;
}
/**
* Creates a new Builder with all parameters copied from the given last location request.
*/
public Builder(@NonNull LastLocationRequest lastLocationRequest) {
mHiddenFromAppOps = lastLocationRequest.mHiddenFromAppOps;
mLocationSettingsIgnored = lastLocationRequest.mLocationSettingsIgnored;
}
/**
* If set to true, indicates that app ops should not be updated with location usage due to
* this request. This implies that someone else (usually the creator of the last location
* request) is responsible for updating app ops as appropriate. Defaults to false.
*
* <p>Permissions enforcement occurs when resulting last location request is actually used,
* not when this method is invoked.
*/
@RequiresPermission(Manifest.permission.UPDATE_APP_OPS_STATS)
public @NonNull Builder setHiddenFromAppOps(boolean hiddenFromAppOps) {
mHiddenFromAppOps = hiddenFromAppOps;
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
* last location request. This is only intended for use in user initiated emergency
* situations, and should be used extremely cautiously. Defaults to false.
*
* <p>Permissions enforcement occurs when resulting last location request is actually used,
* not when this method is invoked.
*/
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
mLocationSettingsIgnored = locationSettingsIgnored;
return this;
}
/**
* Builds a last location request from this builder.
*
* @return a new last location request
*/
public @NonNull LastLocationRequest build() {
return new LastLocationRequest(
mHiddenFromAppOps,
mLocationSettingsIgnored);
}
}
}

View File

@@ -683,6 +683,7 @@ public class LocationManager {
* location should always be checked.
*
* @return the last known location, or null if not available
*
* @throws SecurityException if no suitable location permission is present
*
* @hide
@@ -706,18 +707,50 @@ public class LocationManager {
* in the course of the attempt as compared to this method.
*
* @param provider a provider listed by {@link #getAllProviders()}
*
* @return the last known location for the given provider, or null if not available
*
* @throws SecurityException if no suitable permission is present
* @throws IllegalArgumentException if provider is null or doesn't exist
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
@Nullable
public Location getLastKnownLocation(@NonNull String provider) {
return getLastKnownLocation(provider, new LastLocationRequest.Builder().build());
}
/**
* Gets the last known location from the given provider, or null if there is no last known
* location.
*
* <p>See {@link LastLocationRequest} documentation for an explanation of various request
* parameters and how they can affect the returned location.
*
* <p>See {@link #getLastKnownLocation(String)} for more detail on how this method works.
*
* @param provider a provider listed by {@link #getAllProviders()}
* @param lastLocationRequest the last location request containing location parameters
*
* @return the last known location for the given provider, or null if not available
*
* @throws SecurityException if no suitable permission is present
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if lastLocationRequest is null
*
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
@Nullable
public Location getLastKnownLocation(@NonNull String provider,
@NonNull LastLocationRequest lastLocationRequest) {
Preconditions.checkArgument(provider != null, "invalid null provider");
Preconditions.checkArgument(lastLocationRequest != null,
"invalid null last location request");
try {
return mService.getLastLocation(provider, mContext.getPackageName(),
mContext.getAttributionTag());
return mService.getLastLocation(provider, lastLocationRequest,
mContext.getPackageName(), mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@@ -57,6 +57,7 @@ import android.location.IGpsGeofenceHardware;
import android.location.ILocationCallback;
import android.location.ILocationListener;
import android.location.ILocationManager;
import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationManagerInternal;
@@ -588,6 +589,30 @@ public class LocationManagerService extends ILocationManager.Stub {
new String[0]);
}
@Nullable
@Override
public ICancellationSignal getCurrentLocation(String provider, LocationRequest request,
ILocationCallback consumer, String packageName, String attributionTag,
String listenerId) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
identity.getPid());
LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
PERMISSION_COARSE);
// clients in the system process must have an attribution tag set
Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);
request = validateLocationRequest(request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
"provider \"" + provider + "\" does not exist");
return manager.getCurrentLocation(request, identity, permissionLevel, consumer);
}
@Override
public void registerLocationListener(String provider, LocationRequest request,
ILocationListener listener, String packageName, @Nullable String attributionTag,
@@ -741,7 +766,8 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
public Location getLastLocation(String provider, String packageName, String attributionTag) {
public Location getLastLocation(String provider, LastLocationRequest request,
String packageName, String attributionTag) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
identity.getPid());
@@ -751,36 +777,29 @@ public class LocationManagerService extends ILocationManager.Stub {
// clients in the system process must have an attribution tag set
Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
request = validateLastLocationRequest(request);
LocationProviderManager manager = getLocationProviderManager(provider);
if (manager == null) {
return null;
}
return manager.getLastLocation(identity, permissionLevel, false);
return manager.getLastLocation(request, identity, permissionLevel);
}
@Nullable
@Override
public ICancellationSignal getCurrentLocation(String provider, LocationRequest request,
ILocationCallback consumer, String packageName, String attributionTag,
String listenerId) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
identity.getPid());
LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel,
PERMISSION_COARSE);
private LastLocationRequest validateLastLocationRequest(LastLocationRequest request) {
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);
}
// clients in the system process must have an attribution tag set
Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);
request = validateLocationRequest(request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
"provider \"" + provider + "\" does not exist");
return manager.getCurrentLocation(request, identity, permissionLevel, consumer);
return request;
}
@Override

View File

@@ -49,6 +49,7 @@ import android.content.Intent;
import android.location.Criteria;
import android.location.ILocationCallback;
import android.location.ILocationListener;
import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationManagerInternal;
@@ -1467,9 +1468,9 @@ public class LocationProviderManager extends
}
}
public @Nullable Location getLastLocation(CallerIdentity identity,
@PermissionLevel int permissionLevel, boolean ignoreLocationSettings) {
if (!isActive(ignoreLocationSettings, identity)) {
public @Nullable Location getLastLocation(LastLocationRequest request,
CallerIdentity identity, @PermissionLevel int permissionLevel) {
if (!isActive(request.isLocationSettingsIgnored(), identity)) {
return null;
}
@@ -1483,7 +1484,7 @@ public class LocationProviderManager extends
getLastLocationUnsafe(
identity.getUserId(),
permissionLevel,
ignoreLocationSettings,
request.isLocationSettingsIgnored(),
Long.MAX_VALUE),
permissionLevel);

View File

@@ -60,6 +60,7 @@ import static org.testng.Assert.assertThrows;
import android.content.Context;
import android.location.ILocationCallback;
import android.location.ILocationListener;
import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationManagerInternal;
import android.location.LocationManagerInternal.ProviderEnabledListener;
@@ -260,55 +261,77 @@ public class LocationProviderManagerTest {
@Test
public void testGetLastLocation_Fine() {
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
Location loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc);
}
@Test
public void testGetLastLocation_Coarse() {
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
Location loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
Location coarse = mManager.getLastLocation(IDENTITY, PERMISSION_COARSE, false);
Location coarse = mManager.getLastLocation(new LastLocationRequest.Builder().build(),
IDENTITY, PERMISSION_COARSE);
assertThat(coarse).isNotEqualTo(loc);
assertThat(coarse).isNearby(loc, 5000);
}
@Test
public void testGetLastLocation_Bypass() {
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isNull();
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
assertThat(mManager.getLastLocation(
new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
IDENTITY, PERMISSION_FINE)).isNull();
Location loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc);
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo(
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc);
assertThat(mManager.getLastLocation(
new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
IDENTITY, PERMISSION_FINE)).isEqualTo(
loc);
mProvider.setProviderAllowed(false);
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo(
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
assertThat(mManager.getLastLocation(
new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
IDENTITY, PERMISSION_FINE)).isEqualTo(
loc);
loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo(
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
assertThat(mManager.getLastLocation(
new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
IDENTITY, PERMISSION_FINE)).isEqualTo(
loc);
mProvider.setProviderAllowed(true);
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo(
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
assertThat(mManager.getLastLocation(
new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
IDENTITY, PERMISSION_FINE)).isEqualTo(
loc);
loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc);
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo(
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc);
assertThat(mManager.getLastLocation(
new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
IDENTITY, PERMISSION_FINE)).isEqualTo(
loc);
}
@@ -320,10 +343,12 @@ public class LocationProviderManagerTest {
Location loc = createLocation(NAME, mRandom);
mockProvider.setProviderLocation(loc);
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc);
mManager.setMockProvider(null);
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isNull();
}
@Test
@@ -331,12 +356,14 @@ public class LocationProviderManagerTest {
Location loc1 = createLocation(NAME, mRandom);
mManager.injectLastLocation(loc1, CURRENT_USER);
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc1);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc1);
Location loc2 = createLocation(NAME, mRandom);
mManager.injectLastLocation(loc2, CURRENT_USER);
assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc1);
assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc1);
}
@Test
@@ -355,7 +382,8 @@ public class LocationProviderManagerTest {
Location loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
assertThat(mPassive.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc);
assertThat(mPassive.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
PERMISSION_FINE)).isEqualTo(loc);
}
@Test