Refactor providers/managers
Refactor how providers/managers are implemented in LMS, substantially simplifying code, and opening up further simplification around permissions in the next change. Also, limit the number of additional packages providers can have, and merge WorkSource into ProviderRequest so that providers can pass just ProviderRequests around, not both. Test: presubmits Change-Id: Ic6fab46883cf53eb26368b54a824feac39c57a32
This commit is contained in:
@@ -16,9 +16,16 @@
|
||||
|
||||
package android.location;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* A class indicating the application criteria for selecting a
|
||||
* location provider. Providers may be ordered according to accuracy,
|
||||
@@ -26,6 +33,25 @@ import android.os.Parcelable;
|
||||
* cost.
|
||||
*/
|
||||
public class Criteria implements Parcelable {
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({NO_REQUIREMENT, POWER_LOW, POWER_MEDIUM, POWER_HIGH})
|
||||
public @interface PowerRequirement {
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({NO_REQUIREMENT, ACCURACY_LOW, ACCURACY_MEDIUM, ACCURACY_HIGH})
|
||||
public @interface AccuracyRequirement {
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({NO_REQUIREMENT, ACCURACY_FINE, ACCURACY_COARSE})
|
||||
public @interface LocationAccuracyRequirement {
|
||||
}
|
||||
|
||||
/**
|
||||
* A constant indicating that the application does not choose to
|
||||
* place requirement on a particular feature.
|
||||
@@ -81,15 +107,15 @@ public class Criteria implements Parcelable {
|
||||
*/
|
||||
public static final int ACCURACY_HIGH = 3;
|
||||
|
||||
private int mHorizontalAccuracy = NO_REQUIREMENT;
|
||||
private int mVerticalAccuracy = NO_REQUIREMENT;
|
||||
private int mSpeedAccuracy = NO_REQUIREMENT;
|
||||
private int mBearingAccuracy = NO_REQUIREMENT;
|
||||
private int mPowerRequirement = NO_REQUIREMENT;
|
||||
private boolean mAltitudeRequired = false;
|
||||
private boolean mBearingRequired = false;
|
||||
private boolean mSpeedRequired = false;
|
||||
private boolean mCostAllowed = false;
|
||||
private int mHorizontalAccuracy = NO_REQUIREMENT;
|
||||
private int mVerticalAccuracy = NO_REQUIREMENT;
|
||||
private int mSpeedAccuracy = NO_REQUIREMENT;
|
||||
private int mBearingAccuracy = NO_REQUIREMENT;
|
||||
private int mPowerRequirement = NO_REQUIREMENT;
|
||||
private boolean mAltitudeRequired = false;
|
||||
private boolean mBearingRequired = false;
|
||||
private boolean mSpeedRequired = false;
|
||||
private boolean mCostAllowed = false;
|
||||
|
||||
/**
|
||||
* Constructs a new Criteria object. The new object will have no
|
||||
@@ -97,7 +123,8 @@ public class Criteria implements Parcelable {
|
||||
* require altitude, speed, or bearing; and will not allow monetary
|
||||
* cost.
|
||||
*/
|
||||
public Criteria() {}
|
||||
public Criteria() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new Criteria object that is a copy of the given criteria.
|
||||
@@ -115,125 +142,121 @@ public class Criteria implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the desired horizontal accuracy (latitude and longitude).
|
||||
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
|
||||
* {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}.
|
||||
* More accurate location may consume more power and may take longer.
|
||||
* Indicates the desired horizontal accuracy (latitude and longitude). Accuracy may be
|
||||
* {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM}, {@link #ACCURACY_HIGH} or
|
||||
* {@link #NO_REQUIREMENT}. More accurate location may consume more power and may take longer.
|
||||
*
|
||||
* @throws IllegalArgumentException if accuracy is not one of the supported constants
|
||||
*/
|
||||
public void setHorizontalAccuracy(int accuracy) {
|
||||
if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) {
|
||||
throw new IllegalArgumentException("accuracy=" + accuracy);
|
||||
}
|
||||
mHorizontalAccuracy = accuracy;
|
||||
public void setHorizontalAccuracy(@AccuracyRequirement int accuracy) {
|
||||
mHorizontalAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT,
|
||||
ACCURACY_HIGH, "accuracy");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a constant indicating the desired horizontal accuracy (latitude and longitude).
|
||||
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
|
||||
* {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}.
|
||||
*
|
||||
* @see #setHorizontalAccuracy(int)
|
||||
*/
|
||||
@AccuracyRequirement
|
||||
public int getHorizontalAccuracy() {
|
||||
return mHorizontalAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the desired vertical accuracy (altitude).
|
||||
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
|
||||
* {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}.
|
||||
* More accurate location may consume more power and may take longer.
|
||||
* Indicates the desired vertical accuracy (altitude). Accuracy may be {@link #ACCURACY_LOW},
|
||||
* {@link #ACCURACY_MEDIUM}, {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}. More accurate
|
||||
* location may consume more power and may take longer.
|
||||
*
|
||||
* @throws IllegalArgumentException if accuracy is not one of the supported constants
|
||||
*/
|
||||
public void setVerticalAccuracy(int accuracy) {
|
||||
if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) {
|
||||
throw new IllegalArgumentException("accuracy=" + accuracy);
|
||||
}
|
||||
mVerticalAccuracy = accuracy;
|
||||
public void setVerticalAccuracy(@AccuracyRequirement int accuracy) {
|
||||
mVerticalAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT,
|
||||
ACCURACY_HIGH, "accuracy");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a constant indicating the desired vertical accuracy (altitude).
|
||||
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
|
||||
* or {@link #NO_REQUIREMENT}.
|
||||
*
|
||||
* @see #setVerticalAccuracy(int)
|
||||
*/
|
||||
@AccuracyRequirement
|
||||
public int getVerticalAccuracy() {
|
||||
return mVerticalAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the desired speed accuracy.
|
||||
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
|
||||
* or {@link #NO_REQUIREMENT}.
|
||||
* More accurate location may consume more power and may take longer.
|
||||
* Indicates the desired speed accuracy. Accuracy may be {@link #ACCURACY_LOW},
|
||||
* {@link #ACCURACY_MEDIUM}, {@link #ACCURACY_HIGH}, or {@link #NO_REQUIREMENT}. More accurate
|
||||
* location may consume more power and may take longer.
|
||||
*
|
||||
* @throws IllegalArgumentException if accuracy is not one of the supported constants
|
||||
*/
|
||||
public void setSpeedAccuracy(int accuracy) {
|
||||
if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) {
|
||||
throw new IllegalArgumentException("accuracy=" + accuracy);
|
||||
}
|
||||
mSpeedAccuracy = accuracy;
|
||||
public void setSpeedAccuracy(@AccuracyRequirement int accuracy) {
|
||||
mSpeedAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT, ACCURACY_HIGH,
|
||||
"accuracy");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a constant indicating the desired speed accuracy
|
||||
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
|
||||
* or {@link #NO_REQUIREMENT}.
|
||||
* Returns a constant indicating the desired speed accuracy.
|
||||
*
|
||||
* @see #setSpeedAccuracy(int)
|
||||
*/
|
||||
@AccuracyRequirement
|
||||
public int getSpeedAccuracy() {
|
||||
return mSpeedAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the desired bearing accuracy.
|
||||
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
|
||||
* or {@link #NO_REQUIREMENT}.
|
||||
* More accurate location may consume more power and may take longer.
|
||||
* Indicates the desired bearing accuracy. Accuracy may be {@link #ACCURACY_LOW},
|
||||
* {@link #ACCURACY_MEDIUM}, {@link #ACCURACY_HIGH}, or {@link #NO_REQUIREMENT}. More accurate
|
||||
* location may consume more power and may take longer.
|
||||
*
|
||||
* @throws IllegalArgumentException if accuracy is not one of the supported constants
|
||||
*/
|
||||
public void setBearingAccuracy(int accuracy) {
|
||||
if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) {
|
||||
throw new IllegalArgumentException("accuracy=" + accuracy);
|
||||
}
|
||||
mBearingAccuracy = accuracy;
|
||||
public void setBearingAccuracy(@AccuracyRequirement int accuracy) {
|
||||
mBearingAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT,
|
||||
ACCURACY_HIGH, "accuracy");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a constant indicating the desired bearing accuracy.
|
||||
* Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
|
||||
* or {@link #NO_REQUIREMENT}.
|
||||
*
|
||||
* @see #setBearingAccuracy(int)
|
||||
*/
|
||||
@AccuracyRequirement
|
||||
public int getBearingAccuracy() {
|
||||
return mBearingAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the desired accuracy for latitude and longitude. Accuracy
|
||||
* may be {@link #ACCURACY_FINE} if desired location
|
||||
* is fine, else it can be {@link #ACCURACY_COARSE}.
|
||||
* More accurate location may consume more power and may take longer.
|
||||
* Indicates the desired accuracy for latitude and longitude. Accuracy may be
|
||||
* {@link #ACCURACY_FINE} or {@link #ACCURACY_COARSE}. More accurate location may consume more
|
||||
* power and may take longer.
|
||||
*
|
||||
* @throws IllegalArgumentException if accuracy is not one of the supported constants
|
||||
*/
|
||||
public void setAccuracy(int accuracy) {
|
||||
if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_COARSE) {
|
||||
throw new IllegalArgumentException("accuracy=" + accuracy);
|
||||
}
|
||||
if (accuracy == ACCURACY_FINE) {
|
||||
mHorizontalAccuracy = ACCURACY_HIGH;
|
||||
} else {
|
||||
mHorizontalAccuracy = ACCURACY_LOW;
|
||||
public void setAccuracy(@LocationAccuracyRequirement int accuracy) {
|
||||
Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT, ACCURACY_COARSE, "accuracy");
|
||||
switch (accuracy) {
|
||||
case NO_REQUIREMENT:
|
||||
setHorizontalAccuracy(NO_REQUIREMENT);
|
||||
break;
|
||||
case ACCURACY_FINE:
|
||||
setHorizontalAccuracy(ACCURACY_HIGH);
|
||||
break;
|
||||
case ACCURACY_COARSE:
|
||||
setHorizontalAccuracy(ACCURACY_LOW);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a constant indicating desired accuracy of location
|
||||
* Accuracy may be {@link #ACCURACY_FINE} if desired location
|
||||
* is fine, else it can be {@link #ACCURACY_COARSE}.
|
||||
* Returns a constant indicating desired accuracy of location.
|
||||
*
|
||||
* @see #setAccuracy(int)
|
||||
*/
|
||||
@LocationAccuracyRequirement
|
||||
public int getAccuracy() {
|
||||
if (mHorizontalAccuracy >= ACCURACY_HIGH) {
|
||||
return ACCURACY_FINE;
|
||||
@@ -243,21 +266,20 @@ public class Criteria implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the desired maximum power level. The level parameter
|
||||
* must be one of NO_REQUIREMENT, POWER_LOW, POWER_MEDIUM, or
|
||||
* POWER_HIGH.
|
||||
* Indicates the desired maximum power requirement. The power requirement parameter may be
|
||||
* {@link #NO_REQUIREMENT}, {@link #POWER_LOW}, {@link #POWER_MEDIUM}, or {@link #POWER_HIGH}.
|
||||
*/
|
||||
public void setPowerRequirement(int level) {
|
||||
if (level < NO_REQUIREMENT || level > POWER_HIGH) {
|
||||
throw new IllegalArgumentException("level=" + level);
|
||||
}
|
||||
mPowerRequirement = level;
|
||||
public void setPowerRequirement(@PowerRequirement int powerRequirement) {
|
||||
mPowerRequirement = Preconditions.checkArgumentInRange(powerRequirement, NO_REQUIREMENT,
|
||||
POWER_HIGH, "powerRequirement");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a constant indicating the desired power requirement. The
|
||||
* returned
|
||||
* Returns a constant indicating the desired maximum power requirement.
|
||||
*
|
||||
* @see #setPowerRequirement(int)
|
||||
*/
|
||||
@PowerRequirement
|
||||
public int getPowerRequirement() {
|
||||
return mPowerRequirement;
|
||||
}
|
||||
@@ -277,8 +299,8 @@ public class Criteria implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the provider must provide altitude information.
|
||||
* Not all fixes are guaranteed to contain such information.
|
||||
* Indicates whether the provider must provide altitude information. Not all fixes are
|
||||
* guaranteed to contain such information.
|
||||
*/
|
||||
public void setAltitudeRequired(boolean altitudeRequired) {
|
||||
mAltitudeRequired = altitudeRequired;
|
||||
@@ -286,15 +308,16 @@ public class Criteria implements Parcelable {
|
||||
|
||||
/**
|
||||
* Returns whether the provider must provide altitude information.
|
||||
* Not all fixes are guaranteed to contain such information.
|
||||
*
|
||||
* @see #setAltitudeRequired(boolean)
|
||||
*/
|
||||
public boolean isAltitudeRequired() {
|
||||
return mAltitudeRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the provider must provide speed information.
|
||||
* Not all fixes are guaranteed to contain such information.
|
||||
* Indicates whether the provider must provide speed information. Not all fixes are guaranteed
|
||||
* to contain such information.
|
||||
*/
|
||||
public void setSpeedRequired(boolean speedRequired) {
|
||||
mSpeedRequired = speedRequired;
|
||||
@@ -302,15 +325,16 @@ public class Criteria implements Parcelable {
|
||||
|
||||
/**
|
||||
* Returns whether the provider must provide speed information.
|
||||
* Not all fixes are guaranteed to contain such information.
|
||||
*
|
||||
* @see #setSpeedRequired(boolean)
|
||||
*/
|
||||
public boolean isSpeedRequired() {
|
||||
return mSpeedRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the provider must provide bearing information.
|
||||
* Not all fixes are guaranteed to contain such information.
|
||||
* Indicates whether the provider must provide bearing information. Not all fixes are guaranteed
|
||||
* to contain such information.
|
||||
*/
|
||||
public void setBearingRequired(boolean bearingRequired) {
|
||||
mBearingRequired = bearingRequired;
|
||||
@@ -318,34 +342,36 @@ public class Criteria implements Parcelable {
|
||||
|
||||
/**
|
||||
* Returns whether the provider must provide bearing information.
|
||||
* Not all fixes are guaranteed to contain such information.
|
||||
*
|
||||
* @see #setBearingRequired(boolean)
|
||||
*/
|
||||
public boolean isBearingRequired() {
|
||||
return mBearingRequired;
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<Criteria> CREATOR =
|
||||
new Parcelable.Creator<Criteria>() {
|
||||
@Override
|
||||
public Criteria createFromParcel(Parcel in) {
|
||||
Criteria c = new Criteria();
|
||||
c.mHorizontalAccuracy = in.readInt();
|
||||
c.mVerticalAccuracy = in.readInt();
|
||||
c.mSpeedAccuracy = in.readInt();
|
||||
c.mBearingAccuracy = in.readInt();
|
||||
c.mPowerRequirement = in.readInt();
|
||||
c.mAltitudeRequired = in.readInt() != 0;
|
||||
c.mBearingRequired = in.readInt() != 0;
|
||||
c.mSpeedRequired = in.readInt() != 0;
|
||||
c.mCostAllowed = in.readInt() != 0;
|
||||
return c;
|
||||
}
|
||||
@NonNull
|
||||
public static final Parcelable.Creator<Criteria> CREATOR =
|
||||
new Parcelable.Creator<Criteria>() {
|
||||
@Override
|
||||
public Criteria createFromParcel(Parcel in) {
|
||||
Criteria c = new Criteria();
|
||||
c.mHorizontalAccuracy = in.readInt();
|
||||
c.mVerticalAccuracy = in.readInt();
|
||||
c.mSpeedAccuracy = in.readInt();
|
||||
c.mBearingAccuracy = in.readInt();
|
||||
c.mPowerRequirement = in.readInt();
|
||||
c.mAltitudeRequired = in.readInt() != 0;
|
||||
c.mBearingRequired = in.readInt() != 0;
|
||||
c.mSpeedRequired = in.readInt() != 0;
|
||||
c.mCostAllowed = in.readInt() != 0;
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Criteria[] newArray(int size) {
|
||||
return new Criteria[size];
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public Criteria[] newArray(int size) {
|
||||
return new Criteria[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
@@ -365,42 +391,57 @@ public class Criteria implements Parcelable {
|
||||
parcel.writeInt(mCostAllowed ? 1 : 0);
|
||||
}
|
||||
|
||||
private static String powerToString(int power) {
|
||||
switch (power) {
|
||||
case NO_REQUIREMENT:
|
||||
return "NO_REQ";
|
||||
case POWER_LOW:
|
||||
return "LOW";
|
||||
case POWER_MEDIUM:
|
||||
return "MEDIUM";
|
||||
case POWER_HIGH:
|
||||
return "HIGH";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
private static String accuracyToString(int accuracy) {
|
||||
switch (accuracy) {
|
||||
case NO_REQUIREMENT:
|
||||
return "---";
|
||||
case ACCURACY_HIGH:
|
||||
return "HIGH";
|
||||
case ACCURACY_MEDIUM:
|
||||
return "MEDIUM";
|
||||
case ACCURACY_LOW:
|
||||
return "LOW";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("Criteria[power=").append(powerToString(mPowerRequirement));
|
||||
s.append(" acc=").append(accuracyToString(mHorizontalAccuracy));
|
||||
s.append("Criteria[");
|
||||
s.append("power=").append(requirementToString(mPowerRequirement)).append(", ");
|
||||
s.append("accuracy=").append(requirementToString(mHorizontalAccuracy));
|
||||
if (mVerticalAccuracy != NO_REQUIREMENT) {
|
||||
s.append(", verticalAccuracy=").append(requirementToString(mVerticalAccuracy));
|
||||
}
|
||||
if (mSpeedAccuracy != NO_REQUIREMENT) {
|
||||
s.append(", speedAccuracy=").append(requirementToString(mSpeedAccuracy));
|
||||
}
|
||||
if (mBearingAccuracy != NO_REQUIREMENT) {
|
||||
s.append(", bearingAccuracy=").append(requirementToString(mBearingAccuracy));
|
||||
}
|
||||
if (mAltitudeRequired || mBearingRequired || mSpeedRequired) {
|
||||
s.append(", required=[");
|
||||
if (mAltitudeRequired) {
|
||||
s.append("altitude, ");
|
||||
}
|
||||
if (mBearingRequired) {
|
||||
s.append("bearing, ");
|
||||
}
|
||||
if (mSpeedRequired) {
|
||||
s.append("speed, ");
|
||||
}
|
||||
s.setLength(s.length() - 2);
|
||||
s.append("]");
|
||||
}
|
||||
if (mCostAllowed) {
|
||||
s.append(", costAllowed");
|
||||
}
|
||||
s.append(']');
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
private static String requirementToString(int power) {
|
||||
switch (power) {
|
||||
case NO_REQUIREMENT:
|
||||
return "None";
|
||||
//case ACCURACY_LOW:
|
||||
case POWER_LOW:
|
||||
return "Low";
|
||||
//case ACCURACY_MEDIUM:
|
||||
case POWER_MEDIUM:
|
||||
return "Medium";
|
||||
//case ACCURACY_HIGH:
|
||||
case POWER_HIGH:
|
||||
return "High";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,36 @@
|
||||
|
||||
package com.android.internal.location;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.location.Criteria;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* A Parcelable containing (legacy) location provider properties.
|
||||
* This object is just used inside the framework and system services.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class ProviderProperties implements Parcelable {
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({Criteria.POWER_LOW, Criteria.POWER_MEDIUM, Criteria.POWER_HIGH})
|
||||
public @interface PowerRequirement {
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({Criteria.ACCURACY_FINE, Criteria.ACCURACY_COARSE})
|
||||
public @interface Accuracy {
|
||||
}
|
||||
|
||||
/**
|
||||
* True if provider requires access to a
|
||||
* data network (e.g., the Internet), false otherwise.
|
||||
@@ -79,58 +100,58 @@ public final class ProviderProperties implements Parcelable {
|
||||
|
||||
/**
|
||||
* Power requirement for this provider.
|
||||
*
|
||||
* @return the power requirement for this provider, as one of the
|
||||
* constants Criteria.POWER_*.
|
||||
*/
|
||||
@PowerRequirement
|
||||
public final int mPowerRequirement;
|
||||
|
||||
/**
|
||||
* Constant describing the horizontal accuracy returned
|
||||
* by this provider.
|
||||
*
|
||||
* @return the horizontal accuracy for this provider, as one of the
|
||||
* constants Criteria.ACCURACY_COARSE or Criteria.ACCURACY_FINE
|
||||
*/
|
||||
@Accuracy
|
||||
public final int mAccuracy;
|
||||
|
||||
public ProviderProperties(boolean mRequiresNetwork,
|
||||
boolean mRequiresSatellite, boolean mRequiresCell, boolean mHasMonetaryCost,
|
||||
boolean mSupportsAltitude, boolean mSupportsSpeed, boolean mSupportsBearing,
|
||||
int mPowerRequirement, int mAccuracy) {
|
||||
this.mRequiresNetwork = mRequiresNetwork;
|
||||
this.mRequiresSatellite = mRequiresSatellite;
|
||||
this.mRequiresCell = mRequiresCell;
|
||||
this.mHasMonetaryCost = mHasMonetaryCost;
|
||||
this.mSupportsAltitude = mSupportsAltitude;
|
||||
this.mSupportsSpeed = mSupportsSpeed;
|
||||
this.mSupportsBearing = mSupportsBearing;
|
||||
this.mPowerRequirement = mPowerRequirement;
|
||||
this.mAccuracy = mAccuracy;
|
||||
public ProviderProperties(boolean requiresNetwork, boolean requiresSatellite,
|
||||
boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
|
||||
boolean supportsSpeed, boolean supportsBearing, @PowerRequirement int powerRequirement,
|
||||
@Accuracy int accuracy) {
|
||||
mRequiresNetwork = requiresNetwork;
|
||||
mRequiresSatellite = requiresSatellite;
|
||||
mRequiresCell = requiresCell;
|
||||
mHasMonetaryCost = hasMonetaryCost;
|
||||
mSupportsAltitude = supportsAltitude;
|
||||
mSupportsSpeed = supportsSpeed;
|
||||
mSupportsBearing = supportsBearing;
|
||||
mPowerRequirement = Preconditions.checkArgumentInRange(powerRequirement, Criteria.POWER_LOW,
|
||||
Criteria.POWER_HIGH, "powerRequirement");
|
||||
mAccuracy = Preconditions.checkArgumentInRange(accuracy, Criteria.ACCURACY_FINE,
|
||||
Criteria.ACCURACY_COARSE, "accuracy");
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<ProviderProperties> CREATOR =
|
||||
new Parcelable.Creator<ProviderProperties>() {
|
||||
@Override
|
||||
public ProviderProperties createFromParcel(Parcel in) {
|
||||
boolean requiresNetwork = in.readInt() == 1;
|
||||
boolean requiresSatellite = in.readInt() == 1;
|
||||
boolean requiresCell = in.readInt() == 1;
|
||||
boolean hasMonetaryCost = in.readInt() == 1;
|
||||
boolean supportsAltitude = in.readInt() == 1;
|
||||
boolean supportsSpeed = in.readInt() == 1;
|
||||
boolean supportsBearing = in.readInt() == 1;
|
||||
int powerRequirement = in.readInt();
|
||||
int accuracy = in.readInt();
|
||||
return new ProviderProperties(requiresNetwork, requiresSatellite,
|
||||
requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed, supportsBearing,
|
||||
powerRequirement, accuracy);
|
||||
}
|
||||
@Override
|
||||
public ProviderProperties[] newArray(int size) {
|
||||
return new ProviderProperties[size];
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public ProviderProperties createFromParcel(Parcel in) {
|
||||
boolean requiresNetwork = in.readInt() == 1;
|
||||
boolean requiresSatellite = in.readInt() == 1;
|
||||
boolean requiresCell = in.readInt() == 1;
|
||||
boolean hasMonetaryCost = in.readInt() == 1;
|
||||
boolean supportsAltitude = in.readInt() == 1;
|
||||
boolean supportsSpeed = in.readInt() == 1;
|
||||
boolean supportsBearing = in.readInt() == 1;
|
||||
int powerRequirement = in.readInt();
|
||||
int accuracy = in.readInt();
|
||||
return new ProviderProperties(requiresNetwork, requiresSatellite,
|
||||
requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
|
||||
supportsBearing,
|
||||
powerRequirement, accuracy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProviderProperties[] newArray(int size) {
|
||||
return new ProviderProperties[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
@@ -149,4 +170,67 @@ public final class ProviderProperties implements Parcelable {
|
||||
parcel.writeInt(mPowerRequirement);
|
||||
parcel.writeInt(mAccuracy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder b = new StringBuilder("ProviderProperties[");
|
||||
b.append("power=").append(powerToString(mPowerRequirement)).append(", ");
|
||||
b.append("accuracy=").append(accuracyToString(mAccuracy));
|
||||
if (mRequiresNetwork || mRequiresSatellite || mRequiresCell) {
|
||||
b.append(", requires=");
|
||||
if (mRequiresNetwork) {
|
||||
b.append("network,");
|
||||
}
|
||||
if (mRequiresSatellite) {
|
||||
b.append("satellite,");
|
||||
}
|
||||
if (mRequiresCell) {
|
||||
b.append("cell,");
|
||||
}
|
||||
b.setLength(b.length() - 1);
|
||||
}
|
||||
if (mHasMonetaryCost) {
|
||||
b.append(", hasMonetaryCost");
|
||||
}
|
||||
if (mSupportsBearing || mSupportsSpeed || mSupportsAltitude) {
|
||||
b.append(", supports=[");
|
||||
if (mSupportsBearing) {
|
||||
b.append("bearing, ");
|
||||
}
|
||||
if (mSupportsSpeed) {
|
||||
b.append("speed, ");
|
||||
}
|
||||
if (mSupportsAltitude) {
|
||||
b.append("altitude, ");
|
||||
}
|
||||
b.setLength(b.length() - 2);
|
||||
b.append("]");
|
||||
}
|
||||
b.append("]");
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private static String powerToString(@PowerRequirement int power) {
|
||||
switch (power) {
|
||||
case Criteria.POWER_LOW:
|
||||
return "Low";
|
||||
case Criteria.POWER_MEDIUM:
|
||||
return "Medium";
|
||||
case Criteria.POWER_HIGH:
|
||||
return "High";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
private static String accuracyToString(@Accuracy int accuracy) {
|
||||
switch (accuracy) {
|
||||
case Criteria.ACCURACY_COARSE:
|
||||
return "Coarse";
|
||||
case Criteria.ACCURACY_FINE:
|
||||
return "Fine";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,33 +20,42 @@ import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.location.LocationRequest;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.WorkSource;
|
||||
import android.util.TimeUtils;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** @hide */
|
||||
public final class ProviderRequest implements Parcelable {
|
||||
|
||||
public static final ProviderRequest EMPTY_REQUEST = new ProviderRequest(false, Long.MAX_VALUE,
|
||||
false, false,
|
||||
Collections.emptyList(), new WorkSource());
|
||||
|
||||
/** Location reporting is requested (true) */
|
||||
@UnsupportedAppUsage
|
||||
public boolean reportLocation = false;
|
||||
public final boolean reportLocation;
|
||||
|
||||
/** The smallest requested interval */
|
||||
@UnsupportedAppUsage
|
||||
public long interval = Long.MAX_VALUE;
|
||||
public final long interval;
|
||||
|
||||
/**
|
||||
* Whether provider shall make stronger than normal tradeoffs to substantially restrict power
|
||||
* use.
|
||||
*/
|
||||
public final boolean lowPowerMode;
|
||||
|
||||
/**
|
||||
* When this flag is true, providers should ignore all location settings, user consents, power
|
||||
* restrictions or any other restricting factors and always satisfy this request to the best of
|
||||
* their ability. This flag should only be used in event of an emergency.
|
||||
*/
|
||||
public boolean locationSettingsIgnored = false;
|
||||
|
||||
/**
|
||||
* Whether provider shall make stronger than normal tradeoffs to substantially restrict power
|
||||
* use.
|
||||
*/
|
||||
public boolean lowPowerMode = false;
|
||||
public final boolean locationSettingsIgnored;
|
||||
|
||||
/**
|
||||
* A more detailed set of requests.
|
||||
@@ -56,26 +65,37 @@ public final class ProviderRequest implements Parcelable {
|
||||
* low power fast interval request.
|
||||
*/
|
||||
@UnsupportedAppUsage
|
||||
public final List<LocationRequest> locationRequests = new ArrayList<>();
|
||||
public final List<LocationRequest> locationRequests;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
public ProviderRequest() {
|
||||
public final WorkSource workSource;
|
||||
|
||||
private ProviderRequest(boolean reportLocation, long interval, boolean lowPowerMode,
|
||||
boolean locationSettingsIgnored, List<LocationRequest> locationRequests,
|
||||
WorkSource workSource) {
|
||||
this.reportLocation = reportLocation;
|
||||
this.interval = interval;
|
||||
this.lowPowerMode = lowPowerMode;
|
||||
this.locationSettingsIgnored = locationSettingsIgnored;
|
||||
this.locationRequests = Preconditions.checkNotNull(locationRequests);
|
||||
this.workSource = Preconditions.checkNotNull(workSource);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<ProviderRequest> CREATOR =
|
||||
new Parcelable.Creator<ProviderRequest>() {
|
||||
@Override
|
||||
public ProviderRequest createFromParcel(Parcel in) {
|
||||
ProviderRequest request = new ProviderRequest();
|
||||
request.reportLocation = in.readInt() == 1;
|
||||
request.interval = in.readLong();
|
||||
request.lowPowerMode = in.readBoolean();
|
||||
request.locationSettingsIgnored = in.readBoolean();
|
||||
boolean reportLocation = in.readInt() == 1;
|
||||
long interval = in.readLong();
|
||||
boolean lowPowerMode = in.readBoolean();
|
||||
boolean locationSettingsIgnored = in.readBoolean();
|
||||
int count = in.readInt();
|
||||
ArrayList<LocationRequest> locationRequests = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
request.locationRequests.add(LocationRequest.CREATOR.createFromParcel(in));
|
||||
locationRequests.add(LocationRequest.CREATOR.createFromParcel(in));
|
||||
}
|
||||
return request;
|
||||
WorkSource workSource = in.readParcelable(null);
|
||||
return new ProviderRequest(reportLocation, interval, lowPowerMode,
|
||||
locationSettingsIgnored, locationRequests, workSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -106,14 +126,13 @@ public final class ProviderRequest implements Parcelable {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("ProviderRequest[");
|
||||
if (reportLocation) {
|
||||
s.append("ON");
|
||||
s.append(" interval=");
|
||||
s.append("interval=");
|
||||
TimeUtils.formatDuration(interval, s);
|
||||
if (lowPowerMode) {
|
||||
s.append(" lowPowerMode");
|
||||
s.append(", lowPowerMode");
|
||||
}
|
||||
if (locationSettingsIgnored) {
|
||||
s.append(" locationSettingsIgnored");
|
||||
s.append(", locationSettingsIgnored");
|
||||
}
|
||||
} else {
|
||||
s.append("OFF");
|
||||
@@ -121,4 +140,67 @@ public final class ProviderRequest implements Parcelable {
|
||||
s.append(']');
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A Builder for {@link ProviderRequest}s.
|
||||
*/
|
||||
public static class Builder {
|
||||
private long mInterval = Long.MAX_VALUE;
|
||||
private boolean mLowPowerMode;
|
||||
private boolean mLocationSettingsIgnored;
|
||||
private List<LocationRequest> mLocationRequests = Collections.emptyList();
|
||||
private WorkSource mWorkSource = new WorkSource();
|
||||
|
||||
public long getInterval() {
|
||||
return mInterval;
|
||||
}
|
||||
|
||||
public void setInterval(long interval) {
|
||||
this.mInterval = interval;
|
||||
}
|
||||
|
||||
public boolean isLowPowerMode() {
|
||||
return mLowPowerMode;
|
||||
}
|
||||
|
||||
public void setLowPowerMode(boolean lowPowerMode) {
|
||||
this.mLowPowerMode = lowPowerMode;
|
||||
}
|
||||
|
||||
public boolean isLocationSettingsIgnored() {
|
||||
return mLocationSettingsIgnored;
|
||||
}
|
||||
|
||||
public void setLocationSettingsIgnored(boolean locationSettingsIgnored) {
|
||||
this.mLocationSettingsIgnored = locationSettingsIgnored;
|
||||
}
|
||||
|
||||
public List<LocationRequest> getLocationRequests() {
|
||||
return mLocationRequests;
|
||||
}
|
||||
|
||||
public void setLocationRequests(List<LocationRequest> locationRequests) {
|
||||
this.mLocationRequests = Preconditions.checkNotNull(locationRequests);
|
||||
}
|
||||
|
||||
public WorkSource getWorkSource() {
|
||||
return mWorkSource;
|
||||
}
|
||||
|
||||
public void setWorkSource(WorkSource workSource) {
|
||||
mWorkSource = Preconditions.checkNotNull(workSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a ProviderRequest object with the set information.
|
||||
*/
|
||||
public ProviderRequest build() {
|
||||
if (mInterval == Long.MAX_VALUE) {
|
||||
return EMPTY_REQUEST;
|
||||
} else {
|
||||
return new ProviderRequest(true, mInterval, mLowPowerMode,
|
||||
mLocationSettingsIgnored, mLocationRequests, mWorkSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ import com.android.internal.util.DumpUtils;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.server.LocationManagerServiceUtils.LinkedListener;
|
||||
import com.android.server.LocationManagerServiceUtils.LinkedListenerBase;
|
||||
import com.android.server.location.AbstractLocationProvider;
|
||||
import com.android.server.location.CallerIdentity;
|
||||
import com.android.server.location.GnssBatchingProvider;
|
||||
import com.android.server.location.GnssCapabilitiesProvider;
|
||||
@@ -116,11 +115,9 @@ public class GnssManagerService {
|
||||
private final Handler mHandler;
|
||||
|
||||
public GnssManagerService(LocationManagerService locationManagerService,
|
||||
Context context,
|
||||
AbstractLocationProvider.LocationProviderManager gnssProviderManager,
|
||||
LocationUsageLogger locationUsageLogger) {
|
||||
this(locationManagerService, context, new GnssLocationProvider(context, gnssProviderManager,
|
||||
FgThread.getHandler().getLooper()), locationUsageLogger);
|
||||
Context context, LocationUsageLogger locationUsageLogger) {
|
||||
this(locationManagerService, context,
|
||||
new GnssLocationProvider(context, FgThread.getHandler()), locationUsageLogger);
|
||||
}
|
||||
|
||||
// Can use this constructor to inject GnssLocationProvider for testing
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,11 +16,11 @@
|
||||
|
||||
package com.android.server.location;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.WorkSource;
|
||||
|
||||
import com.android.internal.location.ProviderProperties;
|
||||
import com.android.internal.location.ProviderRequest;
|
||||
@@ -29,127 +29,336 @@ import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* Location Manager's interface for location providers. Always starts as disabled.
|
||||
* Base class for all location providers.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public abstract class AbstractLocationProvider {
|
||||
|
||||
/**
|
||||
* Interface for communicating from a location provider back to the location service.
|
||||
* Interface for listening to location providers.
|
||||
*/
|
||||
public interface LocationProviderManager {
|
||||
public interface Listener {
|
||||
|
||||
/**
|
||||
* May be called to inform the location service of a change in this location provider's
|
||||
* enabled/disabled state.
|
||||
* Called when a provider's state changes. May be invoked from any thread. Will be
|
||||
* invoked with a cleared binder identity.
|
||||
*/
|
||||
void onSetEnabled(boolean enabled);
|
||||
void onStateChanged(State oldState, State newState);
|
||||
|
||||
/**
|
||||
* May be called to inform the location service of a change in this location provider's
|
||||
* properties.
|
||||
*/
|
||||
void onSetProperties(ProviderProperties properties);
|
||||
|
||||
/**
|
||||
* May be called to inform the location service that this provider has a new location
|
||||
* available.
|
||||
* Called when a provider has a new location available. May be invoked from any thread. Will
|
||||
* be invoked with a cleared binder identity.
|
||||
*/
|
||||
void onReportLocation(Location location);
|
||||
|
||||
/**
|
||||
* May be called to inform the location service that this provider has a new location
|
||||
* available.
|
||||
* Called when a provider has a new location available. May be invoked from any thread. Will
|
||||
* be invoked with a cleared binder identity.
|
||||
*/
|
||||
void onReportLocation(List<Location> locations);
|
||||
}
|
||||
|
||||
protected final Context mContext;
|
||||
private final LocationProviderManager mLocationProviderManager;
|
||||
/**
|
||||
* Holds a representation of the public state of a provider.
|
||||
*/
|
||||
public static final class State {
|
||||
|
||||
protected AbstractLocationProvider(
|
||||
Context context, LocationProviderManager locationProviderManager) {
|
||||
/**
|
||||
* Default state value for a location provider that is disabled with no properties and an
|
||||
* empty provider package list.
|
||||
*/
|
||||
public static final State EMPTY_STATE = new State(false, null,
|
||||
Collections.emptySet());
|
||||
|
||||
/**
|
||||
* The provider's enabled state.
|
||||
*/
|
||||
public final boolean enabled;
|
||||
|
||||
/**
|
||||
* The provider's properties.
|
||||
*/
|
||||
@Nullable public final ProviderProperties properties;
|
||||
|
||||
/**
|
||||
* The provider's package name list - provider packages may be afforded special privileges.
|
||||
*/
|
||||
public final Set<String> providerPackageNames;
|
||||
|
||||
private State(boolean enabled, ProviderProperties properties,
|
||||
Set<String> providerPackageNames) {
|
||||
this.enabled = enabled;
|
||||
this.properties = properties;
|
||||
this.providerPackageNames = Objects.requireNonNull(providerPackageNames);
|
||||
}
|
||||
|
||||
private State withEnabled(boolean enabled) {
|
||||
if (enabled == this.enabled) {
|
||||
return this;
|
||||
} else {
|
||||
return new State(enabled, properties, providerPackageNames);
|
||||
}
|
||||
}
|
||||
|
||||
private State withProperties(ProviderProperties properties) {
|
||||
if (properties.equals(this.properties)) {
|
||||
return this;
|
||||
} else {
|
||||
return new State(enabled, properties, providerPackageNames);
|
||||
}
|
||||
}
|
||||
|
||||
private State withProviderPackageNames(Set<String> providerPackageNames) {
|
||||
if (providerPackageNames.equals(this.providerPackageNames)) {
|
||||
return this;
|
||||
} else {
|
||||
return new State(enabled, properties, providerPackageNames);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof State)) {
|
||||
return false;
|
||||
}
|
||||
State state = (State) o;
|
||||
return enabled == state.enabled && properties == state.properties
|
||||
&& providerPackageNames.equals(state.providerPackageNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(enabled, properties, providerPackageNames);
|
||||
}
|
||||
}
|
||||
|
||||
// combines listener and state information so that they can be updated atomically with respect
|
||||
// to each other and an ordering established.
|
||||
private static class InternalState {
|
||||
@Nullable public final Listener listener;
|
||||
public final State state;
|
||||
|
||||
private InternalState(@Nullable Listener listener, State state) {
|
||||
this.listener = listener;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
private InternalState withListener(Listener listener) {
|
||||
if (listener == this.listener) {
|
||||
return this;
|
||||
} else {
|
||||
return new InternalState(listener, state);
|
||||
}
|
||||
}
|
||||
|
||||
private InternalState withState(State state) {
|
||||
if (state.equals(this.state)) {
|
||||
return this;
|
||||
} else {
|
||||
return new InternalState(listener, state);
|
||||
}
|
||||
}
|
||||
|
||||
private InternalState withState(UnaryOperator<State> operator) {
|
||||
return withState(operator.apply(state));
|
||||
}
|
||||
}
|
||||
|
||||
protected final Context mContext;
|
||||
protected final Executor mExecutor;
|
||||
|
||||
// we use a lock-free implementation to update state to ensure atomicity between updating the
|
||||
// provider state and setting the listener, so that the state updates a listener sees are
|
||||
// consistent with when the listener was set (a listener should not see any updates that occur
|
||||
// before it was set, and should not miss any updates that occur after it was set).
|
||||
private final AtomicReference<InternalState> mInternalState;
|
||||
|
||||
protected AbstractLocationProvider(Context context, Executor executor) {
|
||||
this(context, executor, Collections.singleton(context.getPackageName()));
|
||||
}
|
||||
|
||||
protected AbstractLocationProvider(Context context, Executor executor,
|
||||
Set<String> packageNames) {
|
||||
mContext = context;
|
||||
mLocationProviderManager = locationProviderManager;
|
||||
mExecutor = executor;
|
||||
mInternalState = new AtomicReference<>(
|
||||
new InternalState(null, State.EMPTY_STATE.withProviderPackageNames(packageNames)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to report a change in provider enabled/disabled status. May be called from
|
||||
* any thread.
|
||||
* Sets the listener and returns the state at the moment the listener was set. The listener can
|
||||
* expect to receive all state updates from after this point.
|
||||
*/
|
||||
State setListener(@Nullable Listener listener) {
|
||||
return mInternalState.updateAndGet(
|
||||
internalState -> internalState.withListener(listener)).state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the state of the provider.
|
||||
*/
|
||||
State getState() {
|
||||
return mInternalState.get().state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of the provider to the new state.
|
||||
*/
|
||||
void setState(State newState) {
|
||||
InternalState oldInternalState = mInternalState.getAndUpdate(
|
||||
internalState -> internalState.withState(newState));
|
||||
if (newState.equals(oldInternalState.state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we know that we only updated the state, so the listener for the old state is the same as
|
||||
// the listener for the new state.
|
||||
if (oldInternalState.listener != null) {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
oldInternalState.listener.onStateChanged(oldInternalState.state, newState);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setState(UnaryOperator<State> operator) {
|
||||
InternalState oldInternalState = mInternalState.getAndUpdate(
|
||||
internalState -> internalState.withState(operator));
|
||||
|
||||
// recreate the new state from our knowledge of the old state - unfortunately may result in
|
||||
// an extra allocation, but oh well...
|
||||
State newState = operator.apply(oldInternalState.state);
|
||||
|
||||
if (newState.equals(oldInternalState.state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we know that we only updated the state, so the listener for the old state is the same as
|
||||
// the listener for the new state.
|
||||
if (oldInternalState.listener != null) {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
oldInternalState.listener.onStateChanged(oldInternalState.state, newState);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The current enabled state of this provider.
|
||||
*/
|
||||
protected boolean isEnabled() {
|
||||
return mInternalState.get().state.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current provider properties of this provider.
|
||||
*/
|
||||
@Nullable
|
||||
protected ProviderProperties getProperties() {
|
||||
return mInternalState.get().state.properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current package set of this provider.
|
||||
*/
|
||||
protected Set<String> getProviderPackages() {
|
||||
return mInternalState.get().state.providerPackageNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to report a change in provider enabled/disabled status.
|
||||
*/
|
||||
protected void setEnabled(boolean enabled) {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mLocationProviderManager.onSetEnabled(enabled);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
setState(state -> state.withEnabled(enabled));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to report a change in provider properties. May be called from
|
||||
* any thread.
|
||||
* Call this method to report a change in provider properties.
|
||||
*/
|
||||
protected void setProperties(ProviderProperties properties) {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mLocationProviderManager.onSetProperties(properties);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
setState(state -> state.withProperties(properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to report a new location. May be called from any thread.
|
||||
* Call this method to report a change in provider packages.
|
||||
*/
|
||||
protected void setPackageNames(Set<String> packageNames) {
|
||||
setState(state -> state.withProviderPackageNames(packageNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to report a new location.
|
||||
*/
|
||||
protected void reportLocation(Location location) {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mLocationProviderManager.onReportLocation(location);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
Listener listener = mInternalState.get().listener;
|
||||
if (listener != null) {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
listener.onReportLocation(location);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to report a new location. May be called from any thread.
|
||||
* Call this method to report a new location.
|
||||
*/
|
||||
protected void reportLocation(List<Location> locations) {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mLocationProviderManager.onReportLocation(locations);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
Listener listener = mInternalState.get().listener;
|
||||
if (listener != null) {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
listener.onReportLocation(locations);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the location service to return a list of packages currently associated with this
|
||||
* provider. May be called from any thread.
|
||||
* Sets a new request and worksource for the provider.
|
||||
*/
|
||||
public List<String> getProviderPackages() {
|
||||
return Collections.singletonList(mContext.getPackageName());
|
||||
public final void setRequest(ProviderRequest request) {
|
||||
// all calls into the provider must be moved onto the provider thread to prevent deadlock
|
||||
mExecutor.execute(() -> onSetRequest(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the location service to deliver a new request for fulfillment to the provider.
|
||||
* Replaces any previous requests completely. Will always be invoked from the location service
|
||||
* thread with a cleared binder identity.
|
||||
* Always invoked on the provider executor.
|
||||
*/
|
||||
public abstract void onSetRequest(ProviderRequest request, WorkSource source);
|
||||
protected abstract void onSetRequest(ProviderRequest request);
|
||||
|
||||
/**
|
||||
* Invoked by the location service to deliver a custom command to this provider. Will always be
|
||||
* invoked from the location service thread with a cleared binder identity.
|
||||
* Sends an extra command to the provider for it to interpret as it likes.
|
||||
*/
|
||||
public void onSendExtraCommand(int uid, int pid, String command, Bundle extras) {}
|
||||
public final void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
|
||||
// all calls into the provider must be moved onto the provider thread to prevent deadlock
|
||||
mExecutor.execute(() -> onExtraCommand(uid, pid, command, extras));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the location service to dump debug or log information. May be invoked from any
|
||||
* thread.
|
||||
* Always invoked on the provider executor.
|
||||
*/
|
||||
protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
|
||||
|
||||
/**
|
||||
* Dumps debug or log information. May be invoked from any thread.
|
||||
*/
|
||||
public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.os.BatteryStats;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.PersistableBundle;
|
||||
@@ -113,8 +114,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
|
||||
|
||||
private static final ProviderProperties PROPERTIES = new ProviderProperties(
|
||||
true, true, false, false, true, true, true,
|
||||
Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
|
||||
/* requiresNetwork = */false,
|
||||
/* requiresSatellite = */true,
|
||||
/* requiresCell = */false,
|
||||
/* hasMonetaryCost = */false,
|
||||
/* supportAltitude = */true,
|
||||
/* supportsSpeed = */true,
|
||||
/* supportsBearing = */true,
|
||||
Criteria.POWER_HIGH,
|
||||
Criteria.ACCURACY_FINE);
|
||||
|
||||
// these need to match GnssPositionMode enum in IGnss.hal
|
||||
private static final int GPS_POSITION_MODE_STANDALONE = 0;
|
||||
@@ -616,13 +624,12 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
}
|
||||
}
|
||||
|
||||
public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager,
|
||||
Looper looper) {
|
||||
super(context, locationProviderManager);
|
||||
public GnssLocationProvider(Context context, Handler handler) {
|
||||
super(context, new HandlerExecutor(handler));
|
||||
|
||||
ensureInitialized();
|
||||
|
||||
mLooper = looper;
|
||||
mLooper = handler.getLooper();
|
||||
|
||||
// Create a wake lock
|
||||
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
|
||||
@@ -639,7 +646,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
|
||||
|
||||
mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
|
||||
GnssLocationProvider.this::onNetworkAvailable, looper);
|
||||
GnssLocationProvider.this::onNetworkAvailable, mLooper);
|
||||
|
||||
// App ops service to keep track of who is accessing the GPS
|
||||
mAppOps = mContext.getSystemService(AppOpsManager.class);
|
||||
@@ -649,7 +656,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
BatteryStats.SERVICE_NAME));
|
||||
|
||||
// Construct internal handler
|
||||
mHandler = new ProviderHandler(looper);
|
||||
mHandler = new ProviderHandler(mLooper);
|
||||
|
||||
// Load GPS configuration and register listeners in the background:
|
||||
// some operations, such as opening files and registering broadcast receivers, can take a
|
||||
@@ -693,10 +700,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
};
|
||||
|
||||
mGnssMetrics = new GnssMetrics(mBatteryStats);
|
||||
mNtpTimeHelper = new NtpTimeHelper(mContext, looper, this);
|
||||
mNtpTimeHelper = new NtpTimeHelper(mContext, mLooper, this);
|
||||
GnssSatelliteBlacklistHelper gnssSatelliteBlacklistHelper =
|
||||
new GnssSatelliteBlacklistHelper(mContext,
|
||||
looper, this);
|
||||
mLooper, this);
|
||||
mHandler.post(gnssSatelliteBlacklistHelper::updateSatelliteBlacklist);
|
||||
mGnssBatchingProvider = new GnssBatchingProvider();
|
||||
mGnssGeofenceProvider = new GnssGeofenceProvider();
|
||||
@@ -1047,8 +1054,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetRequest(ProviderRequest request, WorkSource source) {
|
||||
sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
|
||||
public void onSetRequest(ProviderRequest request) {
|
||||
sendMessage(SET_REQUEST, 0, new GpsRequest(request, request.workSource));
|
||||
}
|
||||
|
||||
private void handleSetRequest(ProviderRequest request, WorkSource source) {
|
||||
@@ -1185,7 +1192,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendExtraCommand(int uid, int pid, String command, Bundle extras) {
|
||||
public void onExtraCommand(int uid, int pid, String command, Bundle extras) {
|
||||
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
@@ -2064,10 +2071,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is bound to {@link #GnssLocationProvider(Context, LocationProviderManager,
|
||||
* Looper)}.
|
||||
* It is in charge of loading properties and registering for events that will be posted to
|
||||
* this handler.
|
||||
* This method is bound to the constructor. It is in charge of loading properties and
|
||||
* registering for events that will be posted to this handler.
|
||||
*/
|
||||
private void handleInitialize() {
|
||||
// class_init_native() already initializes the GNSS service handle during class loading.
|
||||
|
||||
@@ -23,12 +23,13 @@ import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.WorkSource;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.location.ILocationProvider;
|
||||
import com.android.internal.location.ILocationProviderManager;
|
||||
import com.android.internal.location.ProviderProperties;
|
||||
@@ -39,10 +40,8 @@ import com.android.server.ServiceWatcher;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Proxy for ILocationProvider implementations.
|
||||
@@ -52,59 +51,64 @@ public class LocationProviderProxy extends AbstractLocationProvider {
|
||||
private static final String TAG = "LocationProviderProxy";
|
||||
private static final boolean D = LocationManagerService.D;
|
||||
|
||||
// used to ensure that updates to mProviderPackages are atomic
|
||||
private final Object mProviderPackagesLock = new Object();
|
||||
|
||||
// used to ensure that updates to mRequest and mWorkSource are atomic
|
||||
private final Object mRequestLock = new Object();
|
||||
private static final int MAX_ADDITIONAL_PACKAGES = 2;
|
||||
|
||||
private final ILocationProviderManager.Stub mManager = new ILocationProviderManager.Stub() {
|
||||
// executed on binder thread
|
||||
@Override
|
||||
public void onSetAdditionalProviderPackages(List<String> packageNames) {
|
||||
LocationProviderProxy.this.onSetAdditionalProviderPackages(packageNames);
|
||||
int maxCount = Math.min(MAX_ADDITIONAL_PACKAGES, packageNames.size()) + 1;
|
||||
ArraySet<String> allPackages = new ArraySet<>(maxCount);
|
||||
allPackages.add(mServiceWatcher.getCurrentPackageName());
|
||||
for (String packageName : packageNames) {
|
||||
if (packageNames.size() >= maxCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mContext.getPackageManager().getPackageInfo(packageName, MATCH_SYSTEM_ONLY);
|
||||
allPackages.add(packageName);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, mServiceWatcher + " specified unknown additional provider package: "
|
||||
+ packageName);
|
||||
}
|
||||
}
|
||||
|
||||
setPackageNames(allPackages);
|
||||
}
|
||||
|
||||
// executed on binder thread
|
||||
@Override
|
||||
public void onSetEnabled(boolean enabled) {
|
||||
LocationProviderProxy.this.setEnabled(enabled);
|
||||
setEnabled(enabled);
|
||||
}
|
||||
|
||||
// executed on binder thread
|
||||
@Override
|
||||
public void onSetProperties(ProviderProperties properties) {
|
||||
LocationProviderProxy.this.setProperties(properties);
|
||||
setProperties(properties);
|
||||
}
|
||||
|
||||
// executed on binder thread
|
||||
@Override
|
||||
public void onReportLocation(Location location) {
|
||||
LocationProviderProxy.this.reportLocation(location);
|
||||
reportLocation(location);
|
||||
}
|
||||
};
|
||||
|
||||
private final ServiceWatcher mServiceWatcher;
|
||||
|
||||
@GuardedBy("mProviderPackagesLock")
|
||||
private final CopyOnWriteArrayList<String> mProviderPackages = new CopyOnWriteArrayList<>();
|
||||
|
||||
@GuardedBy("mRequestLock")
|
||||
@Nullable
|
||||
private ProviderRequest mRequest;
|
||||
@GuardedBy("mRequestLock")
|
||||
private WorkSource mWorkSource;
|
||||
@Nullable private ProviderRequest mRequest;
|
||||
|
||||
/**
|
||||
* Creates a new LocationProviderProxy and immediately begins binding to the best applicable
|
||||
* service.
|
||||
*/
|
||||
@Nullable
|
||||
public static LocationProviderProxy createAndBind(
|
||||
Context context, LocationProviderManager locationProviderManager, String action,
|
||||
public static LocationProviderProxy createAndBind(Context context, String action,
|
||||
int overlaySwitchResId, int defaultServicePackageNameResId,
|
||||
int initialPackageNamesResId) {
|
||||
LocationProviderProxy proxy = new LocationProviderProxy(context, locationProviderManager,
|
||||
LocationProviderProxy proxy = new LocationProviderProxy(context, FgThread.getHandler(),
|
||||
action, overlaySwitchResId, defaultServicePackageNameResId,
|
||||
initialPackageNamesResId);
|
||||
if (proxy.bind()) {
|
||||
@@ -114,14 +118,13 @@ public class LocationProviderProxy extends AbstractLocationProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private LocationProviderProxy(Context context, LocationProviderManager locationProviderManager,
|
||||
String action, int overlaySwitchResId, int defaultServicePackageNameResId,
|
||||
private LocationProviderProxy(Context context, Handler handler, String action,
|
||||
int overlaySwitchResId, int defaultServicePackageNameResId,
|
||||
int initialPackageNamesResId) {
|
||||
super(context, locationProviderManager);
|
||||
super(context, new HandlerExecutor(handler), Collections.emptySet());
|
||||
|
||||
mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId,
|
||||
defaultServicePackageNameResId, initialPackageNamesResId,
|
||||
FgThread.getHandler()) {
|
||||
defaultServicePackageNameResId, initialPackageNamesResId, handler) {
|
||||
|
||||
@Override
|
||||
protected void onBind() {
|
||||
@@ -130,14 +133,11 @@ public class LocationProviderProxy extends AbstractLocationProvider {
|
||||
|
||||
@Override
|
||||
protected void onUnbind() {
|
||||
resetProviderPackages(Collections.emptyList());
|
||||
setEnabled(false);
|
||||
setProperties(null);
|
||||
setState(State.EMPTY_STATE);
|
||||
}
|
||||
};
|
||||
|
||||
mRequest = null;
|
||||
mWorkSource = new WorkSource();
|
||||
}
|
||||
|
||||
private boolean bind() {
|
||||
@@ -148,77 +148,34 @@ public class LocationProviderProxy extends AbstractLocationProvider {
|
||||
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
|
||||
if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher);
|
||||
|
||||
resetProviderPackages(Collections.emptyList());
|
||||
setPackageNames(Collections.singleton(mServiceWatcher.getCurrentPackageName()));
|
||||
|
||||
service.setLocationProviderManager(mManager);
|
||||
|
||||
synchronized (mRequestLock) {
|
||||
if (mRequest != null) {
|
||||
service.setRequest(mRequest, mWorkSource);
|
||||
}
|
||||
if (mRequest != null) {
|
||||
service.setRequest(mRequest, mRequest.workSource);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getProviderPackages() {
|
||||
synchronized (mProviderPackagesLock) {
|
||||
return mProviderPackages;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetRequest(ProviderRequest request, WorkSource source) {
|
||||
synchronized (mRequestLock) {
|
||||
mRequest = request;
|
||||
mWorkSource = source;
|
||||
}
|
||||
public void onSetRequest(ProviderRequest request) {
|
||||
mServiceWatcher.runOnBinder(binder -> {
|
||||
mRequest = request;
|
||||
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
|
||||
service.setRequest(request, source);
|
||||
service.setRequest(request, request.workSource);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
pw.println("service=" + mServiceWatcher);
|
||||
synchronized (mProviderPackagesLock) {
|
||||
if (mProviderPackages.size() > 1) {
|
||||
pw.println("additional packages=" + mProviderPackages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendExtraCommand(int uid, int pid, String command, Bundle extras) {
|
||||
public void onExtraCommand(int uid, int pid, String command, Bundle extras) {
|
||||
mServiceWatcher.runOnBinder(binder -> {
|
||||
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
|
||||
service.sendExtraCommand(command, extras);
|
||||
});
|
||||
}
|
||||
|
||||
private void onSetAdditionalProviderPackages(List<String> packageNames) {
|
||||
resetProviderPackages(packageNames);
|
||||
}
|
||||
|
||||
private void resetProviderPackages(List<String> additionalPackageNames) {
|
||||
ArrayList<String> permittedPackages = new ArrayList<>(additionalPackageNames.size());
|
||||
for (String packageName : additionalPackageNames) {
|
||||
try {
|
||||
mContext.getPackageManager().getPackageInfo(packageName, MATCH_SYSTEM_ONLY);
|
||||
permittedPackages.add(packageName);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, mServiceWatcher + " specified unknown additional provider package: "
|
||||
+ packageName);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (mProviderPackagesLock) {
|
||||
mProviderPackages.clear();
|
||||
String myPackage = mServiceWatcher.getCurrentPackageName();
|
||||
if (myPackage != null) {
|
||||
mProviderPackages.add(myPackage);
|
||||
mProviderPackages.addAll(permittedPackages);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
pw.println("service=" + mServiceWatcher);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server.location;
|
||||
|
||||
import static android.location.LocationManager.FUSED_PROVIDER;
|
||||
import static android.location.LocationManager.PASSIVE_PROVIDER;
|
||||
import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS;
|
||||
import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST;
|
||||
import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS;
|
||||
@@ -28,6 +30,7 @@ import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
@@ -248,6 +251,9 @@ public class LocationSettingsStore {
|
||||
DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve maximum age of the last location.
|
||||
*/
|
||||
public long getMaxLastLocationAgeMs() {
|
||||
return Settings.Global.getLong(
|
||||
mContext.getContentResolver(),
|
||||
@@ -255,6 +261,29 @@ public class LocationSettingsStore {
|
||||
DEFAULT_MAX_LAST_LOCATION_AGE_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value for the deprecated LOCATION_PROVIDERS_ALLOWED setting. This is used purely for
|
||||
* backwards compatibility for old clients, and may be removed in the future.
|
||||
*/
|
||||
public void setLocationProviderAllowed(String provider, boolean enabled, int userId) {
|
||||
// fused and passive provider never get public updates for legacy reasons
|
||||
if (FUSED_PROVIDER.equals(provider) || PASSIVE_PROVIDER.equals(provider)) {
|
||||
return;
|
||||
}
|
||||
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
// update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
|
||||
Settings.Secure.putStringForUser(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
|
||||
(enabled ? "+" : "-") + provider,
|
||||
userId);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump info for debugging.
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,6 @@ package com.android.server.location;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.os.WorkSource;
|
||||
|
||||
import com.android.internal.location.ProviderProperties;
|
||||
import com.android.internal.location.ProviderRequest;
|
||||
@@ -34,41 +33,33 @@ import java.io.PrintWriter;
|
||||
*/
|
||||
public class MockProvider extends AbstractLocationProvider {
|
||||
|
||||
private boolean mEnabled;
|
||||
@Nullable private Location mLocation;
|
||||
|
||||
public MockProvider(Context context,
|
||||
LocationProviderManager locationProviderManager, ProviderProperties properties) {
|
||||
super(context, locationProviderManager);
|
||||
|
||||
mEnabled = true;
|
||||
mLocation = null;
|
||||
|
||||
public MockProvider(Context context, ProviderProperties properties) {
|
||||
// using a direct executor is only acceptable because this class is so simple it is trivial
|
||||
// to verify that it does not acquire any locks or re-enter LMS from callbacks
|
||||
super(context, Runnable::run);
|
||||
setProperties(properties);
|
||||
}
|
||||
|
||||
/** Sets the enabled state of this mock provider. */
|
||||
public void setEnabled(boolean enabled) {
|
||||
mEnabled = enabled;
|
||||
super.setEnabled(enabled);
|
||||
public void setProviderEnabled(boolean enabled) {
|
||||
setEnabled(enabled);
|
||||
}
|
||||
|
||||
/** Sets the location to report for this mock provider. */
|
||||
public void setLocation(Location l) {
|
||||
mLocation = new Location(l);
|
||||
if (!mLocation.isFromMockProvider()) {
|
||||
mLocation.setIsFromMockProvider(true);
|
||||
}
|
||||
if (mEnabled) {
|
||||
reportLocation(mLocation);
|
||||
}
|
||||
public void setProviderLocation(Location l) {
|
||||
Location location = new Location(l);
|
||||
location.setIsFromMockProvider(true);
|
||||
mLocation = location;
|
||||
reportLocation(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
pw.println("last location=" + mLocation);
|
||||
pw.println("last mock location=" + mLocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetRequest(ProviderRequest request, WorkSource source) {}
|
||||
public void onSetRequest(ProviderRequest request) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.location.ProviderRequest;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a location provider that may switch between a mock implementation and a real
|
||||
* implementation. Requires owners to provide a lock object that will be used internally and held
|
||||
* for the duration of all listener callbacks. Owners are reponsible for ensuring this cannot lead
|
||||
* to deadlock.
|
||||
*
|
||||
* In order to ensure deadlock does not occur, the owner must validate that the ONLY lock which can
|
||||
* be held BOTH when calling into this class AND when receiving a callback from this class is the
|
||||
* lock given to this class via the constructor. Holding any other lock is ok as long as there is no
|
||||
* possibility that it can be obtained within both codepaths.
|
||||
*
|
||||
* Holding the given lock guarantees atomicity of any operations on this class for the duration.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class MockableLocationProvider extends AbstractLocationProvider {
|
||||
|
||||
private final Object mOwnerLock;
|
||||
|
||||
@GuardedBy("mOwnerLock")
|
||||
@Nullable private AbstractLocationProvider mProvider;
|
||||
@GuardedBy("mOwnerLock")
|
||||
@Nullable private AbstractLocationProvider mRealProvider;
|
||||
@GuardedBy("mOwnerLock")
|
||||
@Nullable private MockProvider mMockProvider;
|
||||
|
||||
@GuardedBy("mOwnerLock")
|
||||
private ProviderRequest mRequest;
|
||||
|
||||
/**
|
||||
* The given lock object will be held any time the listener is invoked, and may also be acquired
|
||||
* and released during the course of invoking any public methods. Holding the given lock ensures
|
||||
* that provider state cannot change except as result of an explicit call by the owner of the
|
||||
* lock into this class. The client is reponsible for ensuring this cannot cause deadlock.
|
||||
*
|
||||
* The client should expect that it may being to receive callbacks as soon as this constructor
|
||||
* is invoked.
|
||||
*/
|
||||
public MockableLocationProvider(Context context, Object ownerLock, Listener listener) {
|
||||
// using a direct executor is acceptable because all inbound calls are delegated to the
|
||||
// actual provider implementations which will use their own executors
|
||||
super(context, Runnable::run, Collections.emptySet());
|
||||
mOwnerLock = ownerLock;
|
||||
mRequest = ProviderRequest.EMPTY_REQUEST;
|
||||
|
||||
setListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current provider implementation. May be null if there is no current
|
||||
* implementation.
|
||||
*/
|
||||
@Nullable
|
||||
public AbstractLocationProvider getProvider() {
|
||||
synchronized (mOwnerLock) {
|
||||
return mProvider;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the real provider implementation, replacing any previous real provider implementation.
|
||||
* May cause an inline invocation of {@link Listener#onStateChanged(State, State)} if this
|
||||
* results in a state change.
|
||||
*/
|
||||
public void setRealProvider(@Nullable AbstractLocationProvider provider) {
|
||||
synchronized (mOwnerLock) {
|
||||
if (mRealProvider == provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
mRealProvider = provider;
|
||||
if (!isMock()) {
|
||||
setProviderLocked(mRealProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mock provider implementation, replacing any previous mock provider implementation.
|
||||
* Mock implementations are always used instead of real implementations if set. May cause an
|
||||
* inline invocation of {@link Listener#onStateChanged(State, State)} if this results in a
|
||||
* state change.
|
||||
*/
|
||||
public void setMockProvider(@Nullable MockProvider provider) {
|
||||
synchronized (mOwnerLock) {
|
||||
if (mMockProvider == provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
mMockProvider = provider;
|
||||
if (mMockProvider != null) {
|
||||
setProviderLocked(mMockProvider);
|
||||
} else {
|
||||
setProviderLocked(mRealProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mOwnerLock")
|
||||
private void setProviderLocked(@Nullable AbstractLocationProvider provider) {
|
||||
if (mProvider == provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
AbstractLocationProvider oldProvider = mProvider;
|
||||
mProvider = provider;
|
||||
|
||||
if (oldProvider != null) {
|
||||
// do this after switching the provider - so even if the old provider is using a direct
|
||||
// executor, if it re-enters this class within setRequest(), it will be ignored
|
||||
oldProvider.setListener(null);
|
||||
oldProvider.setRequest(ProviderRequest.EMPTY_REQUEST);
|
||||
}
|
||||
|
||||
State newState;
|
||||
if (mProvider != null) {
|
||||
newState = mProvider.setListener(new ListenerWrapper(mProvider));
|
||||
} else {
|
||||
newState = State.EMPTY_STATE;
|
||||
}
|
||||
|
||||
ProviderRequest oldRequest = mRequest;
|
||||
setState(newState);
|
||||
|
||||
if (mProvider != null && oldRequest == mRequest) {
|
||||
mProvider.setRequest(mRequest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current active provider implementation is the mock implementation, and
|
||||
* false otherwise.
|
||||
*/
|
||||
public boolean isMock() {
|
||||
synchronized (mOwnerLock) {
|
||||
return mMockProvider != null && mProvider == mMockProvider;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mock provider implementation's enabled state. Will throw an exception if the mock
|
||||
* provider is not currently the active implementation.
|
||||
*/
|
||||
public void setMockProviderEnabled(boolean enabled) {
|
||||
synchronized (mOwnerLock) {
|
||||
Preconditions.checkState(isMock());
|
||||
mMockProvider.setProviderEnabled(enabled);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sets the mock provider implementation's location. Will throw an exception if the mock
|
||||
* provider is not currently the active implementation.
|
||||
*/
|
||||
public void setMockProviderLocation(Location location) {
|
||||
synchronized (mOwnerLock) {
|
||||
Preconditions.checkState(isMock());
|
||||
mMockProvider.setProviderLocation(location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState() {
|
||||
return super.getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current location request.
|
||||
*/
|
||||
public ProviderRequest getCurrentRequest() {
|
||||
synchronized (mOwnerLock) {
|
||||
return mRequest;
|
||||
}
|
||||
}
|
||||
|
||||
protected void onSetRequest(ProviderRequest request) {
|
||||
synchronized (mOwnerLock) {
|
||||
if (request == mRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
mRequest = request;
|
||||
|
||||
if (mProvider != null) {
|
||||
mProvider.setRequest(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {
|
||||
synchronized (mOwnerLock) {
|
||||
if (mProvider != null) {
|
||||
mProvider.sendExtraCommand(uid, pid, command, extras);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the current provider implementation.
|
||||
*/
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
AbstractLocationProvider provider;
|
||||
synchronized (mOwnerLock) {
|
||||
provider = mProvider;
|
||||
pw.println("request=" + mRequest);
|
||||
}
|
||||
|
||||
if (provider != null) {
|
||||
// dump outside the lock in case the provider wants to acquire its own locks, and since
|
||||
// the default provider dump behavior does not move things onto the provider thread...
|
||||
provider.dump(fd, pw, args);
|
||||
}
|
||||
}
|
||||
|
||||
// ensures that callbacks from the incorrect provider are never visible to clients - this
|
||||
// requires holding the owner's lock for the duration of the callback
|
||||
private class ListenerWrapper implements Listener {
|
||||
|
||||
private final AbstractLocationProvider mListenerProvider;
|
||||
|
||||
private ListenerWrapper(AbstractLocationProvider listenerProvider) {
|
||||
mListenerProvider = listenerProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onStateChanged(State oldState, State newState) {
|
||||
synchronized (mOwnerLock) {
|
||||
if (mListenerProvider != mProvider) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(newState);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onReportLocation(Location location) {
|
||||
synchronized (mOwnerLock) {
|
||||
if (mListenerProvider != mProvider) {
|
||||
return;
|
||||
}
|
||||
|
||||
reportLocation(location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onReportLocation(List<Location> locations) {
|
||||
synchronized (mOwnerLock) {
|
||||
if (mListenerProvider != mProvider) {
|
||||
return;
|
||||
}
|
||||
|
||||
reportLocation(locations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package com.android.server.location;
|
||||
import android.content.Context;
|
||||
import android.location.Criteria;
|
||||
import android.location.Location;
|
||||
import android.os.WorkSource;
|
||||
|
||||
import com.android.internal.location.ProviderProperties;
|
||||
import com.android.internal.location.ProviderRequest;
|
||||
@@ -37,13 +36,22 @@ import java.io.PrintWriter;
|
||||
public class PassiveProvider extends AbstractLocationProvider {
|
||||
|
||||
private static final ProviderProperties PROPERTIES = new ProviderProperties(
|
||||
false, false, false, false, false, false, false,
|
||||
Criteria.POWER_LOW, Criteria.ACCURACY_COARSE);
|
||||
/* requiresNetwork = */false,
|
||||
/* requiresSatellite = */false,
|
||||
/* requiresCell = */false,
|
||||
/* hasMonetaryCost = */false,
|
||||
/* supportsAltitude = */false,
|
||||
/* supportsSpeed = */false,
|
||||
/* supportsBearing = */false,
|
||||
Criteria.POWER_LOW,
|
||||
Criteria.ACCURACY_COARSE);
|
||||
|
||||
private boolean mReportLocation;
|
||||
private volatile boolean mReportLocation;
|
||||
|
||||
public PassiveProvider(Context context, LocationProviderManager locationProviderManager) {
|
||||
super(context, locationProviderManager);
|
||||
public PassiveProvider(Context context) {
|
||||
// using a direct executor is only acceptable because this class is so simple it is trivial
|
||||
// to verify that it does not acquire any locks or re-enter LMS from callbacks
|
||||
super(context, Runnable::run);
|
||||
|
||||
mReportLocation = false;
|
||||
|
||||
@@ -52,7 +60,7 @@ public class PassiveProvider extends AbstractLocationProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetRequest(ProviderRequest request, WorkSource source) {
|
||||
public void onSetRequest(ProviderRequest request) {
|
||||
mReportLocation = request.reportLocation;
|
||||
}
|
||||
|
||||
@@ -63,7 +71,5 @@ public class PassiveProvider extends AbstractLocationProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
pw.println("report location=" + mReportLocation);
|
||||
}
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user