diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 576963d9fb5d2..5eaaee3485361 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 9533a8f9f13ba..f2bf176620bd5 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;
*
Provider discovery:
*
*
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.
*
*
Device configuration can influence how {@link TimeZoneProviderService}s are discovered.
@@ -66,18 +66,29 @@ import java.util.Objects;
*
*
Provider types:
*
- *
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
- * #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.
+ *
Android supports up to two location-derived 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.
*
- * For example:
+ *
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} 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:
*
+ * <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>
*
*
- *
* Threading:
*
*
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 60e0ae813d181..8682fea1f8dc6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1591,13 +1591,31 @@
-
-
+
+
+
+
+
+
@@ -5786,6 +5804,7 @@
data set from the com.android.geotz APEX. -->
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 3ccb6e5b730ba..c2d8fa24157a2 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 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 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 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 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 6cc148ad2dee9..231136bc91f91 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 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