diff options
5 files changed, 111 insertions, 27 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 576963d9fb5d..5eaaee348536 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -51,6 +51,7 @@ package android { field public static final String BIND_TELEPHONY_DATA_SERVICE = "android.permission.BIND_TELEPHONY_DATA_SERVICE"; field public static final String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE"; field public static final String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE"; + field public static final String BIND_TIME_ZONE_PROVIDER_SERVICE = "android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE"; field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT"; field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE"; field public static final String BRICK = "android.permission.BRICK"; @@ -99,7 +100,7 @@ package android { field public static final String INJECT_EVENTS = "android.permission.INJECT_EVENTS"; field public static final String INSTALL_DYNAMIC_SYSTEM = "android.permission.INSTALL_DYNAMIC_SYSTEM"; field public static final String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS"; - field public static final String INSTALL_LOCATION_TIME_ZONE_PROVIDER = "android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER"; + field public static final String INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE = "android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE"; field public static final String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES"; field public static final String INSTALL_SELF_UPDATES = "android.permission.INSTALL_SELF_UPDATES"; field public static final String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT"; @@ -9776,7 +9777,6 @@ package android.service.timezone { method public final void reportPermanentFailure(@NonNull Throwable); method public final void reportSuggestion(@NonNull android.service.timezone.TimeZoneProviderSuggestion); method public final void reportUncertain(); - field public static final String BIND_PERMISSION = "android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER"; field public static final String PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE = "android.service.timezone.PrimaryLocationTimeZoneProviderService"; field public static final String SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE = "android.service.timezone.SecondaryLocationTimeZoneProviderService"; } diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java index 9533a8f9f13b..f2bf176620bd 100644 --- a/core/java/android/service/timezone/TimeZoneProviderService.java +++ b/core/java/android/service/timezone/TimeZoneProviderService.java @@ -53,7 +53,7 @@ import java.util.Objects; * <p>Provider discovery: * * <p>You must declare the service in your manifest file with the - * {@link android.Manifest.permission#INSTALL_LOCATION_TIME_ZONE_PROVIDER} permission, + * {@link android.Manifest.permission#BIND_TIME_ZONE_PROVIDER_SERVICE} permission, * and include an intent filter with the necessary action indicating what type of provider it is. * * <p>Device configuration can influence how {@link TimeZoneProviderService}s are discovered. @@ -66,18 +66,29 @@ import java.util.Objects; * * <p>Provider types: * - * <p>Android currently supports up to two location-derived time zone providers. These are called - * the "primary" and "secondary" location time zone provider, configured using {@link + * <p>Android supports up to two <em>location-derived</em> time zone providers. These are called the + * "primary" and "secondary" location time zone provider. The primary location time zone provider is + * started first and will be used until it becomes uncertain or fails, at which point the secondary + * provider will be started. + * + * <p>Location-derived time zone providers are configured using {@link * #PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE} and {@link - * #SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE} respectively. The primary location time - * zone provider is started first and will be used until becomes uncertain or fails, at which point - * the secondary provider will be started. + * #SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE} intent-filter actions respectively. + * Besides declaring the android:permission attribute mentioned above, the application supplying a + * location provider must be granted the {@link + * android.Manifest.permission#INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE} permission to be + * accepted by the system server. * - * For example: + * <p>For example: * <pre> + * <uses-permission + * android:name="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE"/> + * + * ... + * * <service android:name=".FooTimeZoneProviderService" * android:exported="true" - * android:permission="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER"> + * android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE"> * <intent-filter> * <action * android:name="android.service.timezone.SecondaryLocationTimeZoneProviderService" @@ -88,7 +99,6 @@ import java.util.Objects; * </service> * </pre> * - * * <p>Threading: * * <p>Calls to {@code report} methods can be made on on any thread and will be passed asynchronously @@ -120,14 +130,6 @@ public abstract class TimeZoneProviderService extends Service { public static final String SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE = "android.service.timezone.SecondaryLocationTimeZoneProviderService"; - /** - * The permission that a service must require to ensure that only Android system can bind to it. - * If this permission is not enforced in the AndroidManifest of the service, the system will - * skip that service. - */ - public static final String BIND_PERMISSION = - "android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER"; - private final TimeZoneProviderServiceWrapper mWrapper = new TimeZoneProviderServiceWrapper(); /** Set by {@link #mHandler} thread. */ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 60e0ae813d18..8682fea1f8dc 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1591,13 +1591,31 @@ <permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" android:protectionLevel="signature|privileged" /> - <!-- @SystemApi @hide Allows an application to install a LocationTimeZoneProvider into the - LocationTimeZoneProviderManager. + <!-- @SystemApi @hide Allows an application to provide location-based time zone suggestions to + the system server. This is needed because the system server discovers time zone providers + by exposed intent actions and metadata, without it any app could potentially register + itself as time zone provider. The system server checks for this permission. <p>Not for use by third-party applications. --> - <permission android:name="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER" + <permission android:name="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE" android:protectionLevel="signature|privileged" /> + <!-- The system server uses this permission to install a default secondary location time zone + provider. + --> + <uses-permission android:name="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE"/> + + <!-- @SystemApi @hide Allows an application to bind to a android.service.TimeZoneProviderService + for the purpose of detecting the device's time zone. This prevents arbitrary clients + connecting to the time zone provider service. The system server checks that the provider's + intent service explicitly sets this permission via the android:permission attribute of the + service. + This is only expected to be possessed by the system server outside of tests. + <p>Not for use by third-party applications. + --> + <permission android:name="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE" + android:protectionLevel="signature" /> + <!-- @SystemApi @hide Allows HDMI-CEC service to access device and configuration files. This should only be used by HDMI-CEC service. --> @@ -5786,6 +5804,7 @@ data set from the com.android.geotz APEX. --> <service android:name="com.android.timezone.geotz.provider.OfflineLocationTimeZoneProviderService" android:enabled="@bool/config_enableSecondaryLocationTimeZoneProvider" + android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE" android:exported="false"> <intent-filter> <action android:name="android.service.timezone.SecondaryLocationTimeZoneProviderService" /> diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java index 3ccb6e5b730b..c2d8fa24157a 100644 --- a/services/core/java/com/android/server/ServiceWatcher.java +++ b/services/core/java/com/android/server/ServiceWatcher.java @@ -26,6 +26,7 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import android.annotation.BoolRes; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; import android.annotation.UserIdInt; @@ -53,6 +54,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; import java.util.Objects; +import java.util.function.Predicate; /** * Maintains a binding to the best service that matches the given intent information. Bind and @@ -68,6 +70,8 @@ public class ServiceWatcher implements ServiceConnection { private static final long RETRY_DELAY_MS = 15 * 1000; + private static final Predicate<ResolveInfo> DEFAULT_SERVICE_CHECK_PREDICATE = x -> true; + /** Function to run on binder interface. */ public interface BinderRunner { /** Called to run client code with the binder. */ @@ -184,6 +188,7 @@ public class ServiceWatcher implements ServiceConnection { private final Context mContext; private final Handler mHandler; private final Intent mIntent; + private final Predicate<ResolveInfo> mServiceCheckPredicate; private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override @@ -239,15 +244,24 @@ public class ServiceWatcher implements ServiceConnection { @Nullable OnBindRunner onBind, @Nullable Runnable onUnbind, @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) { this(context, FgThread.getHandler(), action, onBind, onUnbind, enableOverlayResId, - nonOverlayPackageResId); + nonOverlayPackageResId, DEFAULT_SERVICE_CHECK_PREDICATE); } public ServiceWatcher(Context context, Handler handler, String action, @Nullable OnBindRunner onBind, @Nullable Runnable onUnbind, @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) { + this(context, handler, action, onBind, onUnbind, enableOverlayResId, nonOverlayPackageResId, + DEFAULT_SERVICE_CHECK_PREDICATE); + } + + public ServiceWatcher(Context context, Handler handler, String action, + @Nullable OnBindRunner onBind, @Nullable Runnable onUnbind, + @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId, + @NonNull Predicate<ResolveInfo> serviceCheckPredicate) { mContext = context; mHandler = handler; mIntent = new Intent(Objects.requireNonNull(action)); + mServiceCheckPredicate = Objects.requireNonNull(serviceCheckPredicate); Resources resources = context.getResources(); boolean enableOverlay = resources.getBoolean(enableOverlayResId); @@ -269,9 +283,16 @@ public class ServiceWatcher implements ServiceConnection { * constraints. */ public boolean checkServiceResolves() { - return !mContext.getPackageManager().queryIntentServicesAsUser(mIntent, - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_SYSTEM_ONLY, - UserHandle.USER_SYSTEM).isEmpty(); + List<ResolveInfo> resolveInfos = mContext.getPackageManager() + .queryIntentServicesAsUser(mIntent, + MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_SYSTEM_ONLY, + UserHandle.USER_SYSTEM); + for (ResolveInfo resolveInfo : resolveInfos) { + if (mServiceCheckPredicate.test(resolveInfo)) { + return true; + } + } + return false; } /** @@ -320,6 +341,9 @@ public class ServiceWatcher implements ServiceConnection { GET_META_DATA | MATCH_DIRECT_BOOT_AUTO | MATCH_SYSTEM_ONLY, mCurrentUserId); for (ResolveInfo resolveInfo : resolveInfos) { + if (!mServiceCheckPredicate.test(resolveInfo)) { + continue; + } ServiceInfo serviceInfo = new ServiceInfo(resolveInfo, mCurrentUserId); if (serviceInfo.compareTo(bestServiceInfo) > 0) { bestServiceInfo = serviceInfo; diff --git a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java index 6cc148ad2dee..231136bc91f9 100644 --- a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java @@ -16,10 +16,18 @@ package com.android.server.location.timezone; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog; + +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -32,6 +40,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.server.ServiceWatcher; import java.util.Objects; +import java.util.function.Predicate; /** * System server-side proxy for ITimeZoneProvider implementations, i.e. this provides the @@ -57,8 +66,38 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy { super(context, threadingDomain); mManagerProxy = null; mRequest = TimeZoneProviderRequest.createStopUpdatesRequest(); + + // A predicate that is used to confirm that an intent service can be used as a + // location-based TimeZoneProvider. The service must: + // 1) Declare android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE" - this + // ensures that the provider will only communicate with the system server. + // 2) Be in an application that has been granted the + // android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE permission. This + // ensures only trusted time zone providers will be discovered. + final String requiredClientPermission = Manifest.permission.BIND_TIME_ZONE_PROVIDER_SERVICE; + final String requiredPermission = + Manifest.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE; + Predicate<ResolveInfo> intentServiceCheckPredicate = resolveInfo -> { + ServiceInfo serviceInfo = resolveInfo.serviceInfo; + + boolean hasClientPermissionRequirement = + requiredClientPermission.equals(serviceInfo.permission); + + String packageName = serviceInfo.packageName; + PackageManager packageManager = context.getPackageManager(); + int checkResult = packageManager.checkPermission(requiredPermission, packageName); + boolean hasRequiredPermission = checkResult == PERMISSION_GRANTED; + + boolean result = hasClientPermissionRequirement && hasRequiredPermission; + if (!result) { + warnLog("resolveInfo=" + resolveInfo + " does not meet requirements:" + + " hasClientPermissionRequirement=" + hasClientPermissionRequirement + + ", hasRequiredPermission=" + hasRequiredPermission); + } + return result; + }; mServiceWatcher = new ServiceWatcher(context, handler, action, this::onBind, this::onUnbind, - enableOverlayResId, nonOverlayPackageResId); + enableOverlayResId, nonOverlayPackageResId, intentServiceCheckPredicate); } @Override |