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>
+ *   &lt;uses-permission
+ *       android:name="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE"/&gt;
+ *
+ * ...
+ *
  *     &lt;service android:name=".FooTimeZoneProviderService"
  *             android:exported="true"
- *             android:permission="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER"&gt;
+ *             android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE"&gt;
  *         &lt;intent-filter&gt;
  *             &lt;action
  *             android:name="android.service.timezone.SecondaryLocationTimeZoneProviderService"
@@ -88,7 +99,6 @@
  *     &lt;/service&gt;
  * </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