diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 197787e5b6e62..1c10edb112934 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -33,6 +33,9 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.app.AlarmManager; import android.app.PendingIntent; +import android.compat.Compatibility; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; @@ -50,7 +53,6 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; -import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.location.ProviderProperties; @@ -81,6 +83,36 @@ public class LocationManager { private static final String TAG = "LocationManager"; + /** + * For apps targeting Android K and above, supplied {@link PendingIntent}s must be targeted to a + * specific package. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN) + public static final long TARGETED_PENDING_INTENT = 148963590L; + + /** + * For apps targeting Android K and above, incomplete locations may not be passed to + * {@link #setTestProviderLocation}. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN) + private static final long INCOMPLETE_LOCATION = 148964793L; + + /** + * For apps targeting Android S and above, all {@link GpsStatus} API usage must be replaced with + * {@link GnssStatus} APIs. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) + private static final long GPS_STATUS_USAGE = 144027538L; + /** * Name of the network location provider. * @@ -771,7 +803,6 @@ public class LocationManager { public void requestSingleUpdate(@NonNull String provider, @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(provider != null, "invalid null provider"); - checkPendingIntent(pendingIntent); LocationRequest request = LocationRequest.createFromDeprecatedProvider( provider, 0, 0, true); @@ -800,7 +831,6 @@ public class LocationManager { public void requestSingleUpdate(@NonNull Criteria criteria, @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(criteria != null, "invalid null criteria"); - checkPendingIntent(pendingIntent); LocationRequest request = LocationRequest.createFromDeprecatedCriteria( criteria, 0, 0, true); @@ -1021,7 +1051,6 @@ public class LocationManager { public void requestLocationUpdates(@NonNull String provider, long minTimeMs, float minDistanceM, @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(provider != null, "invalid null provider"); - checkPendingIntent(pendingIntent); LocationRequest request = LocationRequest.createFromDeprecatedProvider( provider, minTimeMs, minDistanceM, false); @@ -1048,7 +1077,6 @@ public class LocationManager { public void requestLocationUpdates(long minTimeMs, float minDistanceM, @NonNull Criteria criteria, @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(criteria != null, "invalid null criteria"); - checkPendingIntent(pendingIntent); LocationRequest request = LocationRequest.createFromDeprecatedCriteria( criteria, minTimeMs, minDistanceM, false); @@ -1164,9 +1192,9 @@ public class LocationManager { @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(locationRequest != null, "invalid null location request"); Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent"); - if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) { + if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) { Preconditions.checkArgument(pendingIntent.isTargetedToPackage(), - "pending intent must be targeted to package"); + "pending intent must be targeted to a package"); } try { @@ -1198,15 +1226,9 @@ public class LocationManager { */ @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION}) public boolean injectLocation(@NonNull Location location) { - if (location == null) { - IllegalArgumentException e = new IllegalArgumentException("invalid null location"); - if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R) { - throw e; - } else { - Log.w(TAG, e); - return false; - } - } + Preconditions.checkArgument(location != null, "invalid null location"); + Preconditions.checkArgument(location.isComplete(), + "incomplete location object, missing timestamp or accuracy?"); try { return mService.injectLocation(location); @@ -1487,15 +1509,11 @@ public class LocationManager { Preconditions.checkArgument(provider != null, "invalid null provider"); Preconditions.checkArgument(location != null, "invalid null location"); - if (!location.isComplete()) { - IllegalArgumentException e = new IllegalArgumentException( - "Incomplete location object, missing timestamp or accuracy? " + location); - if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) { - Log.w(TAG, e); - location.makeComplete(); - } else { - throw e; - } + if (Compatibility.isChangeEnabled(INCOMPLETE_LOCATION)) { + Preconditions.checkArgument(location.isComplete(), + "incomplete location object, missing timestamp or accuracy?"); + } else { + location.makeComplete(); } try { @@ -1629,7 +1647,11 @@ public class LocationManager { @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public void addProximityAlert(double latitude, double longitude, float radius, long expiration, @NonNull PendingIntent intent) { - checkPendingIntent(intent); + Preconditions.checkArgument(intent != null, "invalid null pending intent"); + if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) { + Preconditions.checkArgument(intent.isTargetedToPackage(), + "pending intent must be targeted to a package"); + } if (expiration < 0) expiration = Long.MAX_VALUE; Geofence fence = Geofence.createCircle(latitude, longitude, radius); @@ -1659,7 +1681,11 @@ public class LocationManager { * permission is not present */ public void removeProximityAlert(@NonNull PendingIntent intent) { - checkPendingIntent(intent); + Preconditions.checkArgument(intent != null, "invalid null pending intent"); + if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) { + Preconditions.checkArgument(intent.isTargetedToPackage(), + "pending intent must be targeted to a package"); + } try { mService.removeGeofence(null, intent, mContext.getPackageName()); @@ -1709,8 +1735,13 @@ public class LocationManager { @NonNull LocationRequest request, @NonNull Geofence fence, @NonNull PendingIntent intent) { - checkPendingIntent(intent); + Preconditions.checkArgument(request != null, "invalid null location request"); Preconditions.checkArgument(fence != null, "invalid null geofence"); + Preconditions.checkArgument(intent != null, "invalid null pending intent"); + if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) { + Preconditions.checkArgument(intent.isTargetedToPackage(), + "pending intent must be targeted to a package"); + } try { mService.requestGeofence(request, fence, intent, mContext.getPackageName(), @@ -1737,8 +1768,12 @@ public class LocationManager { * @hide */ public void removeGeofence(@NonNull Geofence fence, @NonNull PendingIntent intent) { - checkPendingIntent(intent); Preconditions.checkArgument(fence != null, "invalid null geofence"); + Preconditions.checkArgument(intent != null, "invalid null pending intent"); + if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) { + Preconditions.checkArgument(intent.isTargetedToPackage(), + "pending intent must be targeted to a package"); + } try { mService.removeGeofence(fence, intent, mContext.getPackageName()); @@ -1759,7 +1794,11 @@ public class LocationManager { * @hide */ public void removeAllGeofences(@NonNull PendingIntent intent) { - checkPendingIntent(intent); + Preconditions.checkArgument(intent != null, "invalid null pending intent"); + if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) { + Preconditions.checkArgument(intent.isTargetedToPackage(), + "pending intent must be targeted to a package"); + } try { mService.removeGeofence(null, intent, mContext.getPackageName()); @@ -1833,14 +1872,15 @@ public class LocationManager { * @param status object containing GPS status details, or null. * @return status object containing updated GPS status. * - * @deprecated GpsStatus APIs are deprecated, use {@link GnssStatus} APIs instead. + * @deprecated GpsStatus APIs are deprecated, use {@link GnssStatus} APIs instead. No longer + * supported in apps targeting S and above. */ @Deprecated @RequiresPermission(ACCESS_FINE_LOCATION) public @Nullable GpsStatus getGpsStatus(@Nullable GpsStatus status) { - if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.R) { + if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) { throw new UnsupportedOperationException( - "GpsStatus APIs not supported in S and above, use GnssStatus APIs instead"); + "GpsStatus APIs not supported, please use GnssStatus APIs instead"); } GnssStatus gnssStatus = mGnssStatusListenerManager.getGnssStatus(); @@ -1863,17 +1903,14 @@ public class LocationManager { * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present * * @deprecated use {@link #registerGnssStatusCallback(GnssStatus.Callback)} instead. No longer - * supported in apps targeting R and above. + * supported in apps targeting S and above. */ @Deprecated @RequiresPermission(ACCESS_FINE_LOCATION) public boolean addGpsStatusListener(GpsStatus.Listener listener) { - UnsupportedOperationException ex = new UnsupportedOperationException( - "GpsStatus APIs not supported in S and above, use GnssStatus APIs instead"); - if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.R) { - throw ex; - } else { - Log.w(TAG, ex); + if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) { + throw new UnsupportedOperationException( + "GpsStatus APIs not supported, please use GnssStatus APIs instead"); } try { @@ -1889,16 +1926,13 @@ public class LocationManager { * @param listener GPS status listener object to remove * * @deprecated use {@link #unregisterGnssStatusCallback(GnssStatus.Callback)} instead. No longer - * supported in apps targeting R and above. + * supported in apps targeting S and above. */ @Deprecated public void removeGpsStatusListener(GpsStatus.Listener listener) { - UnsupportedOperationException ex = new UnsupportedOperationException( - "GpsStatus APIs not supported in S and above, use GnssStatus APIs instead"); - if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.R) { - throw ex; - } else { - Log.w(TAG, ex); + if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) { + throw new UnsupportedOperationException( + "GpsStatus APIs not supported, please use GnssStatus APIs instead"); } try { @@ -2397,19 +2431,6 @@ public class LocationManager { } } - private void checkPendingIntent(PendingIntent pendingIntent) { - Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent"); - if (!pendingIntent.isTargetedToPackage()) { - IllegalArgumentException e = new IllegalArgumentException( - "invalid pending intent - must be targeted to package"); - if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) { - throw e; - } else { - Log.w(TAG, e); - } - } - } - private static class GetCurrentLocationTransport extends ILocationListener.Stub implements AlarmManager.OnAlarmListener { diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 0ab8af6ad027d..63cddac0bfba5 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -30,6 +30,7 @@ import static android.os.PowerManager.locationPowerSaveModeToString; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -2170,10 +2171,10 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean injectLocation(Location location) { - mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to inject location"); - mContext.enforceCallingPermission(ACCESS_FINE_LOCATION, - "Access Fine Location permission not granted to inject Location"); + mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, null); + mContext.enforceCallingPermission(ACCESS_FINE_LOCATION, null); + + Preconditions.checkArgument(location.isComplete()); synchronized (mLock) { LocationProviderManager manager = getLocationProviderManager(location.getProvider()); @@ -2419,20 +2420,15 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean isLocationEnabledForUser(int userId) { - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS, - null); - } - + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false, false, "isLocationEnabledForUser", null); return mSettingsHelper.isLocationEnabled(userId); } @Override public boolean isProviderEnabledForUser(String providerName, int userId) { - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS, - null); - } + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false, false, "isProviderEnabledForUser", null); // Fused provider is accessed indirectly via criteria rather than the provider-based APIs, // so we discourage its use @@ -2741,6 +2737,9 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void setTestProviderLocation(String provider, Location location, String packageName) { + Preconditions.checkArgument(location.isComplete(), + "incomplete location object, missing timestamp or accuracy?"); + if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName) != AppOpsManager.MODE_ALLOWED) { return;