Adjust time zone provider permissions
Modify ther permissions system around TimeZoneProviderService discovery.
Bug: 175718639
Test: Confirmed witholding the INSTALL_ permission led to a boot failure
Test: build / boot / treehugger
Change-Id: Id91fa7716e3ccc53cd2801aa4d7a469c01c990b6
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 576963d..5eaaee3 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -51,6 +51,7 @@
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 @@
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 @@
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 9533a8f..f2bf176 100644
--- a/core/java/android/service/timezone/TimeZoneProviderService.java
+++ b/core/java/android/service/timezone/TimeZoneProviderService.java
@@ -53,7 +53,7 @@
* <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 @@
*
* <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
- * #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.
+ * <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.
*
- * For example:
+ * <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} 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.
+ *
+ * <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 @@
* </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 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 60e0ae8..8682fea 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 3ccb6e5..c2d8fa2 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_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.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 @@
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 @@
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 @@
@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 @@
* 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 @@
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 6cc148a..231136bc 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.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 @@
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