Switch to android.service APIs

Switch the OfflineTimeZoneProvider to new, cleaner System APIs.

Bug: 175633818
Test: build / treehugger
Change-Id: If4a772e6063a464bd7166e6cd08653ea7485c330
diff --git a/geolocation/apex/com.android.geotz/Android.bp b/geolocation/apex/com.android.geotz/Android.bp
index aec1a84..6fad0fa 100644
--- a/geolocation/apex/com.android.geotz/Android.bp
+++ b/geolocation/apex/com.android.geotz/Android.bp
@@ -62,7 +62,7 @@
     static_libs: [
         "offlinelocationtimezoneprovider",
     ],
-    sdk_version: "current",
+    sdk_version: "system_current",
     apex_available: [
         "com.android.geotz",
     ],
diff --git a/geolocation/locationtzprovider/Android.bp b/geolocation/locationtzprovider/Android.bp
index c25e887..9c3c4b5 100644
--- a/geolocation/locationtzprovider/Android.bp
+++ b/geolocation/locationtzprovider/Android.bp
@@ -17,10 +17,9 @@
 java_library {
     name: "offlinelocationtimezoneprovider",
     srcs: ["src/main/java/**/*.java"],
-    sdk_version: "current",
+    sdk_version: "system_current",
     libs: [
         "androidx.annotation_annotation",
-        "com.android.location.provider",
     ],
     static_libs: [
         "geotz_lookup",
@@ -35,10 +34,7 @@
     name: "OfflineLocationTimeZoneProviderTests",
     srcs: ["src/test/java/**/*.java"],
     manifest: "src/test/AndroidManifest.xml",
-    sdk_version: "current",
-    libs: [
-        "com.android.location.provider",
-    ],
+    sdk_version: "system_current",
     static_libs: [
         "androidx.test.runner",
         "junit",
diff --git a/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/EnvironmentImpl.java b/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/EnvironmentImpl.java
index 60fc294..a29078d 100644
--- a/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/EnvironmentImpl.java
+++ b/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/EnvironmentImpl.java
@@ -32,11 +32,11 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.location.timezone.provider.LocationTimeZoneEventUnbundled;
 import com.android.timezone.geotz.lookup.GeoTimeZonesFinder;
 import com.android.timezone.geotz.provider.core.Cancellable;
 import com.android.timezone.geotz.provider.core.OfflineLocationTimeZoneDelegate;
 import com.android.timezone.geotz.provider.core.OfflineLocationTimeZoneDelegate.ListenModeEnum;
+import com.android.timezone.geotz.provider.core.TimeZoneProviderResult;
 
 import java.io.File;
 import java.io.IOException;
@@ -63,15 +63,14 @@
     @NonNull
     private final Handler mHandler;
     @NonNull
-    private final Consumer<LocationTimeZoneEventUnbundled> mEventConsumer;
+    private final Consumer<TimeZoneProviderResult> mResultConsumer;
     @NonNull
     private final File mGeoDataFile;
 
-    EnvironmentImpl(
-            @NonNull Context context,
-            @NonNull Consumer<LocationTimeZoneEventUnbundled> eventConsumer) {
+    EnvironmentImpl(@NonNull Context context,
+            @NonNull Consumer<TimeZoneProviderResult> resultConsumer) {
         mLocationManager = context.getSystemService(LocationManager.class);
-        mEventConsumer = Objects.requireNonNull(eventConsumer);
+        mResultConsumer = Objects.requireNonNull(resultConsumer);
         mHandler = new Handler(Looper.getMainLooper());
 
         Properties configProperties = loadConfigProperties(getClass().getClassLoader());
@@ -97,7 +96,7 @@
     @Override
     @NonNull
     public <T> Cancellable startTimeout(@Nullable Consumer<T> callback, @NonNull T callbackToken,
-            @NonNull  long delayMillis) {
+            long delayMillis) {
 
         // Deliberate use of an anonymous class as the equality of lambdas is not well defined but
         // instance equality is required for the remove call.
@@ -213,8 +212,8 @@
     }
 
     @Override
-    public void reportLocationTimeZoneEvent(@NonNull LocationTimeZoneEventUnbundled event) {
-        mEventConsumer.accept(event);
+    public void reportTimeZoneProviderResult(@NonNull TimeZoneProviderResult result) {
+        mResultConsumer.accept(result);
     }
 
     @Override
diff --git a/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/OfflineLocationTimeZoneProvider.java b/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/OfflineLocationTimeZoneProvider.java
deleted file mode 100644
index a11e667..0000000
--- a/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/OfflineLocationTimeZoneProvider.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.timezone.geotz.provider;
-
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-
-import com.android.location.timezone.provider.LocationTimeZoneProviderBase;
-import com.android.location.timezone.provider.LocationTimeZoneProviderRequestUnbundled;
-import com.android.timezone.geotz.provider.core.LogUtils;
-import com.android.timezone.geotz.provider.core.OfflineLocationTimeZoneDelegate;
-import com.android.timezone.geotz.provider.core.OfflineLocationTimeZoneDelegate.Environment;
-
-import java.io.PrintWriter;
-
-/**
- * A Location Time Zone Provider implementation that uses only Android public SDK APIs and on-device
- * data to determine the current time zone(s) for a location.
- *
- * <p>This implementation can be deployed to run as either the current user (e.g. it could run in a
- * user-specific process like GMS core), or always as the system user (e.g. it can run in a single
- * user process like system server). It relies on being disabled by the system server when the user
- * changes. No location state is retained when the provider is disabled.
- *
- * <p>See {@link OfflineLocationTimeZoneDelegate} for implementation details.
- *
- * <p>The provider is configured via a "offlineltzprovider.properties" resource file. See
- * {@link EnvironmentImpl} for details.
- */
-final class OfflineLocationTimeZoneProvider extends LocationTimeZoneProviderBase {
-
-    private static final String ATTRIBUTION_TAG = "OfflineLocationTimeZoneProvider";
-
-    @NonNull
-    private final OfflineLocationTimeZoneDelegate mDelegate;
-
-    OfflineLocationTimeZoneProvider(@NonNull Context context) {
-        super(context, LogUtils.LOG_TAG);
-        Context attributionContext = context.createAttributionContext(ATTRIBUTION_TAG);
-        Environment environment = new EnvironmentImpl(
-                attributionContext, this::reportLocationTimeZoneEvent);
-        mDelegate = new OfflineLocationTimeZoneDelegate(environment);
-    }
-
-    public void onBind() {
-        mDelegate.onBind();
-    }
-
-    public void onDestroy() {
-        mDelegate.onDestroy();
-    }
-
-    @Override
-    protected void onSetRequest(@NonNull LocationTimeZoneProviderRequestUnbundled request) {
-        if (request.getReportLocationTimeZone()) {
-            mDelegate.onEnable(request.getInitializationTimeoutMillis());
-        } else {
-            mDelegate.onDisable();
-        }
-    }
-
-    public void dump(@NonNull PrintWriter pw) {
-        mDelegate.dump(pw);
-    }
-
-}
\ No newline at end of file
diff --git a/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/OfflineLocationTimeZoneProviderService.java b/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/OfflineLocationTimeZoneProviderService.java
new file mode 100644
index 0000000..d108f3c
--- /dev/null
+++ b/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/OfflineLocationTimeZoneProviderService.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.timezone.geotz.provider;
+
+import android.content.Context;
+import android.service.timezone.TimeZoneProviderService;
+
+import androidx.annotation.NonNull;
+
+import com.android.timezone.geotz.provider.core.OfflineLocationTimeZoneDelegate;
+import com.android.timezone.geotz.provider.core.OfflineLocationTimeZoneDelegate.Environment;
+import com.android.timezone.geotz.provider.core.TimeZoneProviderResult;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * A Location Time Zone Provider implementation that uses only Android public SDK APIs and on-device
+ * data to determine the current time zone(s) for a location.
+ *
+ * <p>This implementation can be deployed to run as either the current user (e.g. it could run in a
+ * user-specific process like GMS core), or always as the system user (e.g. it can run in a single
+ * user process like system server). It relies on being stopped by the system server when the user
+ * changes. No location state is retained when the provider is stopped.
+ *
+ * <p>See {@link OfflineLocationTimeZoneDelegate} for implementation details.
+ *
+ * <p>The provider is configured via a "offlineltzprovider.properties" resource file. See
+ * {@link EnvironmentImpl} for details.
+ */
+public final class OfflineLocationTimeZoneProviderService extends TimeZoneProviderService {
+
+    private static final String ATTRIBUTION_TAG = "OfflineLocationTimeZoneProviderService";
+
+    // NonNull after onCreate()
+    private OfflineLocationTimeZoneDelegate mDelegate;
+
+    @Override
+    public void onCreate() {
+        Context attributionContext = createAttributionContext(ATTRIBUTION_TAG);
+        Environment environment = new EnvironmentImpl(
+                attributionContext, this::reportTimeZoneProviderEvent);
+        mDelegate = new OfflineLocationTimeZoneDelegate(environment);
+    }
+
+    @Override
+    public void onStartUpdates(long initializationTimeoutMillis) {
+        mDelegate.onStartUpdates(initializationTimeoutMillis);
+    }
+
+    private void reportTimeZoneProviderEvent(
+            @NonNull TimeZoneProviderResult timeZoneProviderResult) {
+        switch (timeZoneProviderResult.getType()) {
+            case TimeZoneProviderResult.RESULT_TYPE_SUGGESTION: {
+                reportSuggestion(timeZoneProviderResult.getSuggestion());
+                break;
+            }
+            case TimeZoneProviderResult.RESULT_TYPE_UNCERTAIN: {
+                reportUncertain();
+                break;
+            }
+            case TimeZoneProviderResult.RESULT_TYPE_PERMANENT_FAILURE: {
+                reportPermanentFailure(timeZoneProviderResult.getFailureCause());
+                break;
+            }
+            default: {
+                throw new IllegalArgumentException("Unknown result type=" + timeZoneProviderResult);
+            }
+        }
+    }
+
+    @Override
+    public void onStopUpdates() {
+        mDelegate.onStopUpdates();
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        mDelegate.dump(writer);
+    }
+}
\ No newline at end of file
diff --git a/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/OfflineLocationTimeZoneService.java b/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/OfflineLocationTimeZoneService.java
deleted file mode 100644
index fe4f1e6..0000000
--- a/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/OfflineLocationTimeZoneService.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.timezone.geotz.provider;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-import androidx.annotation.Nullable;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * The service that provides the {@link OfflineLocationTimeZoneProvider}. An instance of this
- * service is discovered via configuration in an {@code AndroidManifest.xml}.
- *
- * <p>See {@link com.android.server.location.timezone.LocationTimeZoneManagerService} for the server
- * component that binds to it and how it is discovered.
- *
- * <p>See {@link com.android.server.ServiceWatcher} for how to control how the service is treated
- * and how the server resolves to a single service if there are multiple available.
- */
-public final class OfflineLocationTimeZoneService extends Service {
-
-    private final Object mLock = new Object();
-    @Nullable
-    private OfflineLocationTimeZoneProvider mProvider;
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        synchronized (mLock) {
-            if (mProvider == null) {
-                mProvider = new OfflineLocationTimeZoneProvider(this);
-                mProvider.onBind();
-            }
-            return mProvider.getBinder();
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        synchronized (mLock) {
-            if (mProvider != null) {
-                mProvider.onDestroy();
-                mProvider = null;
-            }
-        }
-    }
-
-    @Override
-    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-        synchronized (mLock) {
-            if (mProvider != null) {
-                mProvider.dump(writer);
-            }
-        }
-    }
-}
diff --git a/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/core/Mode.java b/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/core/Mode.java
index ad0f02b..dcf4621 100644
--- a/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/core/Mode.java
+++ b/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/core/Mode.java
@@ -42,31 +42,31 @@
  * <p>See the docs for each {@code MODE_} constant for an explanation of each mode.
  *
  * <pre>
- * The initial mode is {@link #MODE_DISABLED}.
+ * The initial mode is {@link #MODE_STOPPED}.
  *
  * Valid transitions:
  *
- * {@link #MODE_DISABLED}
- *   -> {@link #MODE_ENABLED}(1)
- *       - when the LTZP first receives an enabled request it starts listening for the current
+ * {@link #MODE_STOPPED}
+ *   -> {@link #MODE_STARTED}(1)
+ *       - when the LTZP first receives an started request it starts listening for the current
  *         location with {@link OfflineLocationTimeZoneDelegate#LOCATION_LISTEN_MODE_HIGH}.
- * {@link #MODE_ENABLED}(1)
- *   -> {@link #MODE_ENABLED}(2)
+ * {@link #MODE_STARTED}(1)
+ *   -> {@link #MODE_STARTED}(2)
  *       - when the LTZP receives a valid location, or if it is unable to determine the current
  *         location within the mode timeout, it moves to {@link
  *         OfflineLocationTimeZoneDelegate#LOCATION_LISTEN_MODE_LOW}
- *   -> {@link #MODE_DISABLED}
- *       - when the system server sends a "disabled" request the LTZP is disabled.
- * {@link #MODE_ENABLED}(2)
- *   -> {@link #MODE_ENABLED}(1)
+ *   -> {@link #MODE_STOPPED}
+ *       - when the system server sends a "stopped" request the LTZP is stopped.
+ * {@link #MODE_STARTED}(2)
+ *   -> {@link #MODE_STARTED}(1)
  *       - when no valid location has been received within the mode timeout, the LTZP will start
  *         listening for the current location using {@link
  *         OfflineLocationTimeZoneDelegate#LOCATION_LISTEN_MODE_HIGH}.
- *   -> {@link #MODE_ENABLED}(2)
+ *   -> {@link #MODE_STARTED}(2)
  *       - when the LTZP receives a valid location, it stays in {@link
  *         OfflineLocationTimeZoneDelegate#LOCATION_LISTEN_MODE_LOW}
- *   -> {@link #MODE_DISABLED}
- *       - when the system server sends a "disabled" request the LTZP is disabled.
+ *   -> {@link #MODE_STOPPED}
+ *       - when the system server sends a "stopped" request the LTZP is stopped.
  *
  * {All states}
  *   -> {@link #MODE_FAILED} (terminal state)
@@ -78,21 +78,21 @@
  */
 class Mode {
 
-    @IntDef({ MODE_DISABLED, MODE_ENABLED, MODE_FAILED, MODE_DESTROYED })
+    @IntDef({ MODE_STOPPED, MODE_STARTED, MODE_FAILED, MODE_DESTROYED })
     @interface ModeEnum {}
 
     /**
      * An inactive state. The LTZP may not have received a request yet, or it has and the LTZP has
-     * been explicitly disabled.
+     * been explicitly stopped.
      */
     @ModeEnum
-    static final int MODE_DISABLED = 1;
+    static final int MODE_STOPPED = 1;
 
     /**
-     * The LTZP has been enabled by the system server, and is listening for the current location.
+     * The LTZP has been started by the system server, and is listening for the current location.
      */
     @ModeEnum
-    static final int MODE_ENABLED = 2;
+    static final int MODE_STARTED = 2;
 
     /**
      * The LTZP's service has been destroyed.
@@ -111,7 +111,7 @@
     final int mModeEnum;
 
     /**
-     * The current location listen mode. Only used when mModeEnum == {@link #MODE_ENABLED}.
+     * The current location listen mode. Only used when mModeEnum == {@link #MODE_STARTED}.
      */
     final @ListenModeEnum int mListenMode;
 
@@ -134,21 +134,21 @@
     private final String mEntryCause;
 
     /**
-     * Used when mModeEnum == {@link #MODE_ENABLED}. The {@link Cancellable} that can be
+     * Used when mModeEnum == {@link #MODE_STARTED}. The {@link Cancellable} that can be
      * used to stop listening for the current location.
      */
     @Nullable
     private Cancellable mLocationListenerCancellable;
 
     /**
-     * Used when mModeEnum == {@link #MODE_ENABLED}. The {@link Cancellable} that can be
+     * Used when mModeEnum == {@link #MODE_STARTED}. The {@link Cancellable} that can be
      * used to stop listening for the current location.
      */
     @Nullable
     private Cancellable mTimeoutCancellable;
 
     /**
-     * Used when mModeEnum == {@link #MODE_ENABLED} to record the token associated with the
+     * Used when mModeEnum == {@link #MODE_STARTED} to record the token associated with the
      * mode timeout.
      */
     @Nullable
@@ -169,15 +169,15 @@
         mEntryCause = entryCause;
     }
 
-    /** Returns the disabled mode which is the starting state for a provider. */
+    /** Returns the stopped mode which is the starting state for a provider. */
     @NonNull
-    static Mode createDisabledMode() {
-        return new Mode(MODE_DISABLED, "init" /* entryCause */);
+    static Mode createStoppedMode() {
+        return new Mode(MODE_STOPPED, "init" /* entryCause */);
     }
 
     /**
      * Associates the supplied {@link Cancellable} with the mode to enable location listening to
-     * be cancelled. Used when mModeEnum == {@link #MODE_ENABLED}. See
+     * be cancelled. Used when mModeEnum == {@link #MODE_STARTED}. See
      * {@link #cancelLocationListening()}.
      */
     void setLocationListenerCancellable(@NonNull Cancellable locationListenerCancellable) {
@@ -206,7 +206,7 @@
 
     /**
      * Associates the {@code timeoutToken} with the mode for later retrieval. Used for
-     * {@link #MODE_ENABLED}.
+     * {@link #MODE_STARTED}.
      */
     void setTimeoutInfo(@NonNull Cancellable timeoutCancellable,
             @NonNull String timeoutToken) {
@@ -252,10 +252,10 @@
     /** Returns a string representation of the {@link ModeEnum} value provided. */
     static String prettyPrintModeEnum(@ModeEnum int modeEnum) {
         switch (modeEnum) {
-            case MODE_DISABLED:
-                return "MODE_DISABLED";
-            case MODE_ENABLED:
-                return "MODE_ENABLED";
+            case MODE_STOPPED:
+                return "MODE_STOPPED";
+            case MODE_STARTED:
+                return "MODE_STARTED";
             case MODE_DESTROYED:
                 return "MODE_DESTROYED";
             case MODE_FAILED:
@@ -280,7 +280,7 @@
     }
 
     private static @ModeEnum int validateModeEnum(@ModeEnum int modeEnum) {
-        if (modeEnum < MODE_DISABLED || modeEnum > MODE_FAILED) {
+        if (modeEnum < MODE_STOPPED || modeEnum > MODE_FAILED) {
             throw new IllegalArgumentException("modeEnum=" + modeEnum);
         }
         return modeEnum;
@@ -288,7 +288,7 @@
 
     private static @ListenModeEnum int validateListenModeEnum(
             @ModeEnum int modeEnum, @ListenModeEnum int listenMode) {
-        if (modeEnum == MODE_ENABLED) {
+        if (modeEnum == MODE_STARTED) {
             if (listenMode != LOCATION_LISTEN_MODE_HIGH && listenMode != LOCATION_LISTEN_MODE_LOW) {
                 throw new IllegalArgumentException();
             }
diff --git a/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/core/OfflineLocationTimeZoneDelegate.java b/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/core/OfflineLocationTimeZoneDelegate.java
index fef030a..f127343 100644
--- a/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/core/OfflineLocationTimeZoneDelegate.java
+++ b/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/core/OfflineLocationTimeZoneDelegate.java
@@ -22,19 +22,19 @@
 import static com.android.timezone.geotz.provider.core.LogUtils.logWarn;
 import static com.android.timezone.geotz.provider.core.Mode.MODE_DESTROYED;
 import static com.android.timezone.geotz.provider.core.Mode.MODE_FAILED;
-import static com.android.timezone.geotz.provider.core.Mode.MODE_DISABLED;
-import static com.android.timezone.geotz.provider.core.Mode.MODE_ENABLED;
+import static com.android.timezone.geotz.provider.core.Mode.MODE_STARTED;
+import static com.android.timezone.geotz.provider.core.Mode.MODE_STOPPED;
 import static com.android.timezone.geotz.provider.core.Mode.prettyPrintListenModeEnum;
 
 import android.location.Location;
 import android.os.SystemClock;
+import android.service.timezone.TimeZoneProviderSuggestion;
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.location.timezone.provider.LocationTimeZoneEventUnbundled;
 import com.android.timezone.geotz.lookup.GeoTimeZonesFinder;
 import com.android.timezone.geotz.lookup.GeoTimeZonesFinder.LocationToken;
 
@@ -46,9 +46,9 @@
 import java.util.function.Consumer;
 
 /**
- * A class encapsulating the time zone detection logic for an Offline LocationTimeZoneProvider.
- * It has been decoupled from the Android environment and many API via the {@link Environment}
- * interface to enable easier unit testing.
+ * A class encapsulating the time zone detection logic for an Offline location-based
+ * {@link android.service.timezone.TimeZoneProviderService}. It has been decoupled from the Android
+ * environment and many API via the {@link Environment} interface to enable easier unit testing.
  *
  * <p>The overall goal of this class is to balance power consumption with responsiveness.
  *
@@ -57,19 +57,19 @@
  * <p>The instance interacts with multiple threads, but state changes occur in a single-threaded
  * manner through the use of a lock object, {@link #mLock}.
  *
- * <p>When first enabled, the current location is requested using {@link
+ * <p>When first started, the current location is requested using {@link
  * Environment#startLocationListening(int, Consumer)} with {@link #LOCATION_LISTEN_MODE_HIGH} and
  * a timeout is requested using {@link Environment#startTimeout(Consumer, Object, long)}.
  *
  * <p>If a valid location is found within the timeout, the time zones for the location are looked up
- * and an {@link LocationTimeZoneEventUnbundled event} is sent via {@link
- * Environment#reportLocationTimeZoneEvent(LocationTimeZoneEventUnbundled)}.
+ * and an {@link TimeZoneProviderResult result} is recorded via {@link
+ * Environment#reportTimeZoneProviderResult(TimeZoneProviderResult)}.
  *
  * <p>If a valid location cannot be found within the timeout, a {@link
- * LocationTimeZoneEventUnbundled#EVENT_TYPE_UNCERTAIN} {@link
- * LocationTimeZoneEventUnbundled event} is sent to the system server.
+ * TimeZoneProviderResult#RESULT_TYPE_UNCERTAIN} {@link TimeZoneProviderResult result} is recorded to the
+ * system server.
  *
- * <p>After an {@link LocationTimeZoneEventUnbundled event} has been sent, the provider restarts
+ * <p>After an {@link TimeZoneProviderResult result} has been sent, the provider restarts
  * location listening with a new timeout but in {@link #LOCATION_LISTEN_MODE_LOW}. If the current
  * location continues to be available it will stay in this mode, extending the timeout, otherwise it
  * will switch to {@link #LOCATION_LISTEN_MODE_HIGH} with a shorter timeout.
@@ -113,7 +113,7 @@
          */
         @NonNull
         <T> Cancellable startTimeout(@NonNull Consumer<T> callback,
-                @Nullable T callbackToken, @NonNull long delayMillis);
+                @Nullable T callbackToken, long delayMillis);
 
         /**
          * Starts an async location lookup. The location passed to {@code locationListener} will not
@@ -134,7 +134,7 @@
         /**
          * Used to report location time zone information.
          */
-        void reportLocationTimeZoneEvent(@NonNull LocationTimeZoneEventUnbundled event);
+        void reportTimeZoneProviderResult(@NonNull TimeZoneProviderResult result);
 
         /** See {@link SystemClock#elapsedRealtime()}. */
         long elapsedRealtimeMillis();
@@ -157,16 +157,17 @@
     private LocationToken mLastLocationToken;
 
     /**
-     * The last location time zone event sent by the provider. Currently used for debugging only.
+     * The last time zone provider result determined by the provider. Currently used for debugging
+     * only.
      */
     @GuardedBy("mLock")
-    private final ReferenceWithHistory<LocationTimeZoneEventUnbundled> mLastLocationTimeZoneEvent =
+    private final ReferenceWithHistory<TimeZoneProviderResult> mLastTimeZoneProviderResult =
             new ReferenceWithHistory<>(10);
 
     public OfflineLocationTimeZoneDelegate(@NonNull Environment environment) {
         mEnvironment = Objects.requireNonNull(environment);
 
-        mCurrentMode.set(Mode.createDisabledMode());
+        mCurrentMode.set(Mode.createStoppedMode());
     }
 
     public void onBind() {
@@ -175,13 +176,13 @@
 
         synchronized (mLock) {
             Mode currentMode = mCurrentMode.get();
-            if (currentMode.mModeEnum != MODE_DISABLED) {
+            if (currentMode.mModeEnum != MODE_STOPPED) {
                 handleUnexpectedStateTransition(
                         "onBind() called when in unexpected mode=" + currentMode);
                 return;
             }
 
-            Mode newMode = new Mode(MODE_DISABLED, entryCause);
+            Mode newMode = new Mode(MODE_STOPPED, entryCause);
             mCurrentMode.set(newMode);
         }
     }
@@ -194,35 +195,35 @@
             cancelTimeoutAndLocationCallbacks();
 
             Mode currentMode = mCurrentMode.get();
-            if (currentMode.mModeEnum == MODE_ENABLED) {
-                sendTimeZoneUncertainEventIfNeeded();
+            if (currentMode.mModeEnum == MODE_STARTED) {
+                sendTimeZoneUncertainResultIfNeeded();
             }
             Mode newMode = new Mode(MODE_DESTROYED, entryCause);
             mCurrentMode.set(newMode);
         }
     }
 
-    public void onDisable() {
-        String debugInfo = "onDisable()";
+    public void onStopUpdates() {
+        String debugInfo = "onStopUpdates()";
         logDebug(debugInfo);
 
         synchronized (mLock) {
             Mode currentMode = mCurrentMode.get();
             switch (currentMode.mModeEnum) {
-                case MODE_DISABLED: {
-                    // No-op - the provider is already disabled.
-                    logWarn("Unexpected onDisable() when currentMode=" + currentMode);
+                case MODE_STOPPED: {
+                    // No-op - the provider is already stopped.
+                    logWarn("Unexpected onStopUpdates() when currentMode=" + currentMode);
                     break;
                 }
-                case MODE_ENABLED: {
-                    enterDisabledMode(debugInfo);
+                case MODE_STARTED: {
+                    enterStoppedMode(debugInfo);
                     break;
                 }
                 case MODE_FAILED:
                 case MODE_DESTROYED:
                 default: {
                     handleUnexpectedStateTransition(
-                            "Unexpected onDisable() when currentMode=" + currentMode);
+                            "Unexpected onStopUpdates() when currentMode=" + currentMode);
                     break;
                 }
             }
@@ -230,31 +231,32 @@
 
     }
 
-    public void onEnable(@NonNull long initializationTimeoutMillis) {
-        String debugInfo = "onEnable(), initializationTimeoutMillis=" + initializationTimeoutMillis;
+    public void onStartUpdates(long initializationTimeoutMillis) {
+        String debugInfo = "onStartUpdates(),"
+                + " initializationTimeoutMillis=" + initializationTimeoutMillis;
         logDebug(debugInfo);
 
         synchronized (mLock) {
             Mode currentMode = mCurrentMode.get();
             switch (currentMode.mModeEnum) {
-                case MODE_DISABLED: {
+                case MODE_STOPPED: {
                     // Always start in the most aggressive location listening mode. The request
                     // contains the time in which the LTZP is given to provide the first
-                    // event, so this is used for the first timeout.
+                    // result, so this is used for the first timeout.
                     enterLocationListeningMode(LOCATION_LISTEN_MODE_HIGH, debugInfo,
                             initializationTimeoutMillis);
                     break;
                 }
-                case MODE_ENABLED: {
-                    // No-op - the provider is already enabled.
-                    logWarn("Unexpected onEnabled() received when in currentMode=" + currentMode);
+                case MODE_STARTED: {
+                    // No-op - the provider is already started.
+                    logWarn("Unexpected onStarted() received when in currentMode=" + currentMode);
                     break;
                 }
                 case MODE_FAILED:
                 case MODE_DESTROYED:
                 default: {
                     handleUnexpectedStateTransition(
-                            "Unexpected onEnabled() received when in currentMode=" + currentMode);
+                            "Unexpected onStarted() received when in currentMode=" + currentMode);
                     break;
                 }
             }
@@ -268,17 +270,13 @@
                     + formatElapsedRealtimeMillis(mEnvironment.elapsedRealtimeMillis()));
             pw.println("mCurrentMode=" + mCurrentMode);
             pw.println("mLastLocationToken=" + mLastLocationToken);
-            pw.println("mLastLocationTimeZoneEvent=" + mLastLocationTimeZoneEvent);
+            pw.println("mLastTimeZoneProviderResult=" + mLastTimeZoneProviderResult);
             pw.println();
             pw.println("Mode history:");
-            // pw.increaseIndent();
             mCurrentMode.dump(pw);
-            // pw.decreaseIndent();
             pw.println();
-            pw.println("LocationTimeZoneEvent history:");
-            // pw.increaseIndent();
-            mLastLocationTimeZoneEvent.dump(pw);
-            // pw.decreaseIndent();
+            pw.println("TimeZoneProviderResult history:");
+            mLastTimeZoneProviderResult.dump(pw);
         }
     }
 
@@ -289,7 +287,7 @@
     }
 
     /**
-     * Accepts the current location when in {@link Mode#MODE_ENABLED}.
+     * Accepts the current location when in {@link Mode#MODE_STARTED}.
      */
     private void onLocationReceived(@NonNull Location location) {
         Objects.requireNonNull(location);
@@ -297,7 +295,7 @@
         synchronized (mLock) {
             Mode currentMode = mCurrentMode.get();
 
-            if (currentMode.mModeEnum != MODE_ENABLED) {
+            if (currentMode.mModeEnum != MODE_STARTED) {
                 // This is not expected to happen.
                 String unexpectedStateDebugInfo = "Unexpected call to onLocationReceived(),"
                         + " location=" + location
@@ -313,7 +311,7 @@
 
             // A good location has been received.
             try {
-                sendTimeZoneCertainEventIfNeeded(location);
+                sendTimeZoneCertainResultIfNeeded(location);
 
                 // Move to the least aggressive location listening mode.
                 enterLocationListeningMode(LOCATION_LISTEN_MODE_LOW, debugInfo,
@@ -324,7 +322,7 @@
                         + " previous debugInfo=" + debugInfo;
                 logWarn(lookupFailureDebugInfo, e);
 
-                enterFailedMode(lookupFailureDebugInfo);
+                enterFailedMode(new IOException(lookupFailureDebugInfo, e));
             }
         }
     }
@@ -339,7 +337,7 @@
 
         synchronized (mLock) {
             Mode currentMode = mCurrentMode.get();
-            if (currentMode.mModeEnum != MODE_ENABLED) {
+            if (currentMode.mModeEnum != MODE_STARTED) {
                 handleUnexpectedCallback("Unexpected timeout for mode=" + currentMode);
                 return;
             }
@@ -350,7 +348,7 @@
             }
 
             if (currentMode.mListenMode == LOCATION_LISTEN_MODE_HIGH) {
-                sendTimeZoneUncertainEventIfNeeded();
+                sendTimeZoneUncertainResultIfNeeded();
                 enterLocationListeningMode(LOCATION_LISTEN_MODE_LOW, debugInfo,
                         LOCATION_LISTEN_MODE_LOW_TIMEOUT_MILLIS);
             } else {
@@ -361,7 +359,7 @@
     }
 
     @GuardedBy("mLock")
-    private void sendTimeZoneCertainEventIfNeeded(@NonNull Location location)
+    private void sendTimeZoneCertainResultIfNeeded(@NonNull Location location)
             throws IOException {
         try (GeoTimeZonesFinder geoTimeZonesFinder = mEnvironment.createGeoTimeZoneFinder()) {
             // Convert the location to a LocationToken.
@@ -369,61 +367,59 @@
                     location.getLatitude(), location.getLongitude());
 
             // If the location token is the same as the last lookup, there is no need to do the
-            // lookup / send another event.
+            // lookup / send another suggestion.
             if (locationToken.equals(mLastLocationToken)) {
                 logDebug("Location token=" + locationToken + " has not changed.");
             } else {
                 List<String> tzIds =
                         geoTimeZonesFinder.findTimeZonesForLocationToken(locationToken);
                 logDebug("tzIds found for location=" + location + ", tzIds=" + tzIds);
-                LocationTimeZoneEventUnbundled event =
-                        new LocationTimeZoneEventUnbundled.Builder()
-                                .setEventType(LocationTimeZoneEventUnbundled.EVENT_TYPE_SUCCESS)
-                                .setTimeZoneIds(tzIds)
-                                .build();
-                reportLocationTimeZoneEventInternal(event, locationToken);
+                TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
+                        .setTimeZoneIds(tzIds)
+                        .setElapsedRealtimeMillis(mEnvironment.elapsedRealtimeMillis())
+                        .build();
+
+                TimeZoneProviderResult result =
+                        TimeZoneProviderResult.createSuggestion(suggestion);
+                reportTimeZoneProviderResultInternal(result, locationToken);
             }
         }
     }
 
     @GuardedBy("mLock")
-    private void sendTimeZoneUncertainEventIfNeeded() {
-        LocationTimeZoneEventUnbundled lastEvent = mLastLocationTimeZoneEvent.get();
+    private void sendTimeZoneUncertainResultIfNeeded() {
+        TimeZoneProviderResult lastResult = mLastTimeZoneProviderResult.get();
 
-        // If the last event was uncertain, there is no need to send another.
-        if (lastEvent == null ||
-                lastEvent.getEventType() != LocationTimeZoneEventUnbundled.EVENT_TYPE_UNCERTAIN) {
-            LocationTimeZoneEventUnbundled event = new LocationTimeZoneEventUnbundled.Builder()
-                    .setEventType(LocationTimeZoneEventUnbundled.EVENT_TYPE_UNCERTAIN)
-                    .build();
-            reportLocationTimeZoneEventInternal(event, null /* locationToken */);
+        // If the last result was uncertain, there is no need to send another.
+        if (lastResult == null ||
+                lastResult.getType() != TimeZoneProviderResult.RESULT_TYPE_UNCERTAIN) {
+            TimeZoneProviderResult result = TimeZoneProviderResult.createUncertain();
+            reportTimeZoneProviderResultInternal(result, null /* locationToken */);
         } else {
-            logDebug("sendTimeZoneUncertainEventIfNeeded(): Last event=" + lastEvent
+            logDebug("sendTimeZoneUncertainResultIfNeeded(): Last result=" + lastResult
                     + ", no need to sent another.");
         }
     }
 
     @GuardedBy("mLock")
-    private void sendPermanentFailureEvent() {
-        LocationTimeZoneEventUnbundled event = new LocationTimeZoneEventUnbundled.Builder()
-                .setEventType(LocationTimeZoneEventUnbundled.EVENT_TYPE_PERMANENT_FAILURE)
-                .build();
-        reportLocationTimeZoneEventInternal(event, null /* locationToken */);
+    private void sendPermanentFailureResult(@NonNull Throwable cause) {
+        TimeZoneProviderResult result = TimeZoneProviderResult.createPermanentFailure(cause);
+        reportTimeZoneProviderResultInternal(result, null /* locationToken */);
     }
 
     @GuardedBy("mLock")
-    private void reportLocationTimeZoneEventInternal(
-            @NonNull LocationTimeZoneEventUnbundled event,
+    private void reportTimeZoneProviderResultInternal(
+            @NonNull TimeZoneProviderResult result,
             @Nullable LocationToken locationToken) {
-        mLastLocationTimeZoneEvent.set(event);
+        mLastTimeZoneProviderResult.set(result);
         mLastLocationToken = locationToken;
-        mEnvironment.reportLocationTimeZoneEvent(event);
+        mEnvironment.reportTimeZoneProviderResult(result);
     }
 
     @GuardedBy("mLock")
     private void clearLocationState() {
         mLastLocationToken = null;
-        mLastLocationTimeZoneEvent.set(null);
+        mLastTimeZoneProviderResult.set(null);
     }
 
     /** Called when leaving the current mode to cancel all pending asynchronous operations. */
@@ -449,35 +445,36 @@
     }
 
     @GuardedBy("mLock")
-    private void enterFailedMode(@NonNull String entryCause) {
+    private void enterFailedMode(@NonNull Throwable entryCause) {
         logDebug("Provider entering failed mode, entryCause=" + entryCause);
 
         cancelTimeoutAndLocationCallbacks();
 
-        sendPermanentFailureEvent();
+        sendPermanentFailureResult(entryCause);
 
-        Mode newMode = new Mode(MODE_FAILED, entryCause);
+        String failureReason = entryCause.getMessage();
+        Mode newMode = new Mode(MODE_FAILED, failureReason);
         mCurrentMode.set(newMode);
     }
 
     @GuardedBy("mLock")
-    private void enterDisabledMode(@NonNull String entryCause) {
-        logDebug("Provider entering disabled mode, entryCause=" + entryCause);
+    private void enterStoppedMode(@NonNull String entryCause) {
+        logDebug("Provider entering stopped mode, entryCause=" + entryCause);
 
         cancelTimeoutAndLocationCallbacks();
 
-        // Clear all location-derived state. The provider may be disabled due to the current user
+        // Clear all location-derived state. The provider may be stopped due to the current user
         // changing.
         clearLocationState();
 
-        Mode newMode = new Mode(MODE_DISABLED, entryCause);
+        Mode newMode = new Mode(MODE_STOPPED, entryCause);
         mCurrentMode.set(newMode);
     }
 
     @GuardedBy("mLock")
     private void enterLocationListeningMode(
             @ListenModeEnum int listenMode,
-            @NonNull String entryCause, @NonNull long timeoutMillis) {
+            @NonNull String entryCause, long timeoutMillis) {
         logDebug("Provider entering location listening mode"
                 + ", listenMode=" + prettyPrintListenModeEnum(listenMode)
                 + ", entryCause=" + entryCause);
@@ -485,8 +482,8 @@
         Mode currentMode = mCurrentMode.get();
         currentMode.cancelTimeout();
 
-        Mode newMode = new Mode(MODE_ENABLED, entryCause, listenMode);
-        if (currentMode.mModeEnum != MODE_ENABLED
+        Mode newMode = new Mode(MODE_STARTED, entryCause, listenMode);
+        if (currentMode.mModeEnum != MODE_STARTED
                 || currentMode.mListenMode != listenMode) {
             currentMode.cancelLocationListening();
             Cancellable locationListenerCancellable =
diff --git a/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/core/TimeZoneProviderResult.java b/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/core/TimeZoneProviderResult.java
new file mode 100644
index 0000000..7633412
--- /dev/null
+++ b/geolocation/locationtzprovider/src/main/java/com/android/timezone/geotz/provider/core/TimeZoneProviderResult.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.timezone.geotz.provider.core;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import android.service.timezone.TimeZoneProviderService;
+import android.service.timezone.TimeZoneProviderSuggestion;
+
+import java.util.Objects;
+
+/**
+ * A result of a period of time zone detection.
+ */
+public final class TimeZoneProviderResult {
+
+    @IntDef({ RESULT_TYPE_PERMANENT_FAILURE, RESULT_TYPE_SUGGESTION, RESULT_TYPE_UNCERTAIN })
+    public @interface ResultType {}
+
+    /**
+     * The provider failed permanently. See {@link
+     * TimeZoneProviderService#reportPermanentFailure(Throwable)}
+     */
+    public static final int RESULT_TYPE_PERMANENT_FAILURE = 1;
+
+    /**
+     * The provider made a suggestion. See {@link
+     * TimeZoneProviderService#reportSuggestion(TimeZoneProviderSuggestion)}
+     */
+    public static final int RESULT_TYPE_SUGGESTION = 2;
+
+    /**
+     * The provider was uncertain about the time zone. See {@link
+     * TimeZoneProviderService#reportUncertain()}
+     */
+    public static final int RESULT_TYPE_UNCERTAIN = 3;
+
+    private static final TimeZoneProviderResult UNCERTAIN_RESULT =
+            new TimeZoneProviderResult(RESULT_TYPE_UNCERTAIN, null, null);
+
+    private static final int RESULT_TYPE_MIN = RESULT_TYPE_PERMANENT_FAILURE;
+    private static final int RESULT_TYPE_MAX = RESULT_TYPE_UNCERTAIN;
+
+    @ResultType
+    private final int mType;
+
+    @Nullable
+    private final TimeZoneProviderSuggestion mSuggestion;
+
+    @Nullable
+    private final Throwable mFailureCause;
+
+    private TimeZoneProviderResult(@ResultType int type,
+            @Nullable TimeZoneProviderSuggestion suggestion,
+            @Nullable Throwable failureCause) {
+        mType = type;
+        mSuggestion = suggestion;
+        mFailureCause = failureCause;
+    }
+
+    /** Returns a result of type {@link #RESULT_TYPE_SUGGESTION}. */
+    public static TimeZoneProviderResult createSuggestion(
+            @NonNull TimeZoneProviderSuggestion suggestion) {
+        return new TimeZoneProviderResult(RESULT_TYPE_SUGGESTION,
+                Objects.requireNonNull(suggestion), null);
+    }
+
+    /** Returns a result of type {@link #RESULT_TYPE_UNCERTAIN}. */
+    public static TimeZoneProviderResult createUncertain() {
+        return UNCERTAIN_RESULT;
+    }
+
+    /** Returns a result of type {@link #RESULT_TYPE_PERMANENT_FAILURE}. */
+    public static TimeZoneProviderResult createPermanentFailure(@NonNull Throwable cause) {
+        return new TimeZoneProviderResult(RESULT_TYPE_PERMANENT_FAILURE, null,
+                Objects.requireNonNull(cause));
+    }
+
+    /**
+     * Returns the result type.
+     */
+    public @ResultType int getType() {
+        return mType;
+    }
+
+    /**
+     * Returns the suggestion. Populated for {@link #RESULT_TYPE_SUGGESTION}.
+     */
+    @NonNull
+    public TimeZoneProviderSuggestion getSuggestion() {
+        return mSuggestion;
+    }
+
+    /**
+     * Returns the failure cause. Populated for {@link #RESULT_TYPE_PERMANENT_FAILURE}.
+     */
+    @Nullable
+    public Throwable getFailureCause() {
+        return mFailureCause;
+    }
+
+    @Override
+    public String toString() {
+        return "TimeZoneProviderResult{"
+                + "mResultType=" + mType
+                + ", mSuggestion=" + mSuggestion;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        TimeZoneProviderResult that = (TimeZoneProviderResult) o;
+        return mType == that.mType
+                && Objects.equals(mSuggestion, that.mSuggestion);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mType, mSuggestion);
+    }
+}
diff --git a/geolocation/locationtzprovider/src/test/java/com/android/timezone/geotz/provider/core/OfflineLocationTimeZoneDelegateTest.java b/geolocation/locationtzprovider/src/test/java/com/android/timezone/geotz/provider/core/OfflineLocationTimeZoneDelegateTest.java
index 602eeb9..17aad97 100644
--- a/geolocation/locationtzprovider/src/test/java/com/android/timezone/geotz/provider/core/OfflineLocationTimeZoneDelegateTest.java
+++ b/geolocation/locationtzprovider/src/test/java/com/android/timezone/geotz/provider/core/OfflineLocationTimeZoneDelegateTest.java
@@ -26,7 +26,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.location.timezone.provider.LocationTimeZoneEventUnbundled;
 import com.android.timezone.geotz.lookup.GeoTimeZonesFinder;
 
 import org.junit.Before;
@@ -62,18 +61,18 @@
         List<String> timeZoneIds = Arrays.asList("Europe/London");
         mTestGeoTimeZoneFinder.setTimeZonesForLocation(latDegrees, lngDegrees, timeZoneIds);
 
-        assertEquals(Mode.MODE_DISABLED, mDelegate.getCurrentModeEnumForTests());
+        assertEquals(Mode.MODE_STOPPED, mDelegate.getCurrentModeEnumForTests());
         mTestEnvironment.assertIsNotListening();
         mTestEnvironment.assertNoTimeoutSet();
 
         mDelegate.onBind();
-        assertEquals(Mode.MODE_DISABLED, mDelegate.getCurrentModeEnumForTests());
+        assertEquals(Mode.MODE_STOPPED, mDelegate.getCurrentModeEnumForTests());
         mTestEnvironment.assertIsNotListening();
         mTestEnvironment.assertNoTimeoutSet();
 
         final int initializationTimeoutMillis = 20000;
-        mDelegate.onEnable(initializationTimeoutMillis);
-        assertEquals(Mode.MODE_ENABLED, mDelegate.getCurrentModeEnumForTests());
+        mDelegate.onStartUpdates(initializationTimeoutMillis);
+        assertEquals(Mode.MODE_STARTED, mDelegate.getCurrentModeEnumForTests());
         mTestEnvironment.assertIsListening(
                 OfflineLocationTimeZoneDelegate.LOCATION_LISTEN_MODE_HIGH);
         mTestEnvironment.assertTimeoutSet(initializationTimeoutMillis);
@@ -83,8 +82,8 @@
         location.setLongitude(1.0);
         mTestEnvironment.simulateCurrentLocationDetected(location);
 
-        mTestEnvironment.assertLocationEventReported(timeZoneIds);
-        assertEquals(Mode.MODE_ENABLED, mDelegate.getCurrentModeEnumForTests());
+        mTestEnvironment.assertSuggestionResult(timeZoneIds);
+        assertEquals(Mode.MODE_STARTED, mDelegate.getCurrentModeEnumForTests());
         mTestEnvironment.assertIsListening(
                 OfflineLocationTimeZoneDelegate.LOCATION_LISTEN_MODE_LOW);
         mTestEnvironment.assertTimeoutSet(
@@ -97,7 +96,7 @@
         private long mElapsedRealtimeMillis;
         private TestLocationListenerState mLocationListeningState;
         private TestTimeoutState<?> mTimeoutState;
-        private LocationTimeZoneEventUnbundled mLastEvent;
+        private TimeZoneProviderResult mLastResult;
 
         @NonNull
         @Override
@@ -124,9 +123,9 @@
         }
 
         @Override
-        public void reportLocationTimeZoneEvent(@NonNull LocationTimeZoneEventUnbundled event) {
-            assertNotNull(event);
-            mLastEvent = event;
+        public void reportTimeZoneProviderResult(@NonNull TimeZoneProviderResult result) {
+            assertNotNull(result);
+            mLastResult = result;
         }
 
         @Override
@@ -157,11 +156,10 @@
             assertEquals(expectedTimeoutMillis, mTimeoutState.mDelayMillis);
         }
 
-        public void assertLocationEventReported(List<String> expectedTimeZoneIds) {
-            assertNotNull(mLastEvent);
-            assertEquals(LocationTimeZoneEventUnbundled.EVENT_TYPE_SUCCESS,
-                    mLastEvent.getEventType());
-            assertEquals(expectedTimeZoneIds, mLastEvent.getTimeZoneIds());
+        public void assertSuggestionResult(List<String> expectedTimeZoneIds) {
+            assertNotNull(mLastResult);
+            assertEquals(TimeZoneProviderResult.RESULT_TYPE_SUGGESTION, mLastResult.getType());
+            assertEquals(expectedTimeZoneIds, mLastResult.getSuggestion().getTimeZoneIds());
         }
 
         private class TestLocationListenerState extends TestCancellable {