Loading services/core/java/com/android/server/timezonedetector/Dumpable.java 0 → 100644 +38 −0 Original line number Diff line number Diff line /* * 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.server.timezonedetector; import java.io.PrintWriter; /** An interface for components that can write their internal state to dumpsys logs. */ public interface Dumpable { /** Dump internal state. */ void dump(PrintWriter pw, String[] args); /** * An interface that can be used expose when one component allows another to be registered so * that it is dumped at the same time. */ interface Dumpee { /** * Registers the supplied {@link Dumpable}. When the implementation is dumped * {@link Dumpable#dump(PrintWriter, String[])} should be called on the {@code dumpable}. */ void addDumpable(Dumpable dumpable); } } services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java 0 → 100644 +173 −0 Original line number Diff line number Diff line /* * 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.server.timezonedetector; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.ShellCommand; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.StringTokenizer; /** * A time zone suggestion from a geolocation source. * * <p> Geolocation-based suggestions have the following properties: * * <ul> * <li>{@code zoneIds}. When not {@code null}, {@code zoneIds} contains a list of suggested time * zone IDs, e.g. ["America/Phoenix", "America/Denver"]. Usually there will be a single zoneId. * When there are multiple, this indicates multiple answers are possible for the current * location / accuracy, i.e. if there is a nearby time zone border. The detection logic * receiving the suggestion is expected to use the first element in the absence of other * information, but one of the others may be used if there is supporting evidence / preferences * such as a device setting or corroborating signals from another source. * <br />{@code zoneIds} can be empty if the current location has been determined to have no * time zone. For example, oceans or disputed areas. This is considered a strong signal and the * received need not look for time zone from other sources. * <br />{@code zoneIds} can be {@code null} to indicate that the geolocation source has entered * an "un-opinionated" state and any previous suggestion is being withdrawn. This indicates the * source cannot provide a valid suggestion due to technical limitations. For example, a * geolocation source may become un-opinionated if the device's location is no longer known with * sufficient accuracy, or if the location is known but no time zone can be determined because * no time zone mapping information is available.</li> * <li>{@code debugInfo} contains debugging metadata associated with the suggestion. This is * used to record why the suggestion exists and how it was obtained. This information exists * only to aid in debugging and therefore is used by {@link #toString()}, but it is not for use * in detection logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}. * </li> * </ul> * * @hide */ public final class GeolocationTimeZoneSuggestion { @NonNull private final List<String> mZoneIds; @Nullable private ArrayList<String> mDebugInfo; public GeolocationTimeZoneSuggestion(@Nullable List<String> zoneIds) { if (zoneIds == null) { // Unopinionated mZoneIds = null; } else { mZoneIds = Collections.unmodifiableList(new ArrayList<>(zoneIds)); } } /** * Returns the zone Ids being suggested. See {@link GeolocationTimeZoneSuggestion} for details. */ @Nullable public List<String> getZoneIds() { return mZoneIds; } /** Returns debug information. See {@link GeolocationTimeZoneSuggestion} for details. */ @NonNull public List<String> getDebugInfo() { return mDebugInfo == null ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo); } /** * Associates information with the instance that can be useful for debugging / logging. The * information is present in {@link #toString()} but is not considered for * {@link #equals(Object)} and {@link #hashCode()}. */ public void addDebugInfo(String... debugInfos) { if (mDebugInfo == null) { mDebugInfo = new ArrayList<>(); } mDebugInfo.addAll(Arrays.asList(debugInfos)); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } GeolocationTimeZoneSuggestion that = (GeolocationTimeZoneSuggestion) o; return Objects.equals(mZoneIds, that.mZoneIds); } @Override public int hashCode() { return Objects.hash(mZoneIds); } @Override public String toString() { return "GeolocationTimeZoneSuggestion{" + "mZoneIds=" + mZoneIds + ", mDebugInfo=" + mDebugInfo + '}'; } /** @hide */ public static GeolocationTimeZoneSuggestion parseCommandLineArg(@NonNull ShellCommand cmd) { String zoneIdsString = null; String opt; while ((opt = cmd.getNextArg()) != null) { switch (opt) { case "--zone_ids": { zoneIdsString = cmd.getNextArgRequired(); break; } default: { throw new IllegalArgumentException("Unknown option: " + opt); } } } List<String> zoneIds = parseZoneIdsArg(zoneIdsString); GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(zoneIds); suggestion.addDebugInfo("Command line injection"); return suggestion; } private static List<String> parseZoneIdsArg(String zoneIdsString) { if ("UNCERTAIN".equals(zoneIdsString)) { return null; } else if ("EMPTY".equals(zoneIdsString)) { return Collections.emptyList(); } else { ArrayList<String> zoneIds = new ArrayList<>(); StringTokenizer tokenizer = new StringTokenizer(zoneIdsString, ","); while (tokenizer.hasMoreTokens()) { zoneIds.add(tokenizer.nextToken()); } return zoneIds; } } /** @hide */ public static void printCommandLineOpts(@NonNull PrintWriter pw) { pw.println("Geolocation suggestion options:"); pw.println(" --zone_ids {UNCERTAIN|EMPTY|<Olson ID>+}"); pw.println(); pw.println("See " + GeolocationTimeZoneSuggestion.class.getName() + " for more information"); } } services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java 0 → 100644 +35 −0 Original line number Diff line number Diff line /* * 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.server.timezonedetector; import android.annotation.NonNull; /** * The internal (in-process) system server API for the {@link * com.android.server.timezonedetector.TimeZoneDetectorService}. * * @hide */ public interface TimeZoneDetectorInternal extends Dumpable.Dumpee { /** * Suggests the current time zone, determined using geolocation, to the detector. The * detector may ignore the signal based on system settings, whether better information is * available, and so on. */ void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion); } services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java 0 → 100644 +64 −0 Original line number Diff line number Diff line /* * 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.server.timezonedetector; import android.annotation.NonNull; import android.content.Context; import android.os.Handler; import com.android.internal.annotations.VisibleForTesting; import java.util.Objects; /** * The real {@link TimeZoneDetectorInternal} local service implementation. * * @hide */ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInternal { @NonNull private final Context mContext; @NonNull private final Handler mHandler; @NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy; static TimeZoneDetectorInternalImpl create(@NonNull Context context, @NonNull Handler handler, @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) { return new TimeZoneDetectorInternalImpl(context, handler, timeZoneDetectorStrategy); } @VisibleForTesting public TimeZoneDetectorInternalImpl(@NonNull Context context, @NonNull Handler handler, @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) { mContext = Objects.requireNonNull(context); mHandler = Objects.requireNonNull(handler); mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy); } @Override public void addDumpable(Dumpable dumpable) { mTimeZoneDetectorStrategy.addDumpable(dumpable); } @Override public void suggestGeolocationTimeZone( @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) { Objects.requireNonNull(timeZoneSuggestion); mHandler.post( () -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion)); } } services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +36 −7 Original line number Diff line number Diff line Loading @@ -63,9 +63,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub private static final String TAG = "TimeZoneDetectorService"; /** * Handles the lifecycle for {@link TimeZoneDetectorService}. * Handles the service lifecycle for {@link TimeZoneDetectorService} and * {@link TimeZoneDetectorInternalImpl}. */ public static class Lifecycle extends SystemService { public static final class Lifecycle extends SystemService { public Lifecycle(@NonNull Context context) { super(context); Loading @@ -73,10 +74,21 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub @Override public void onStart() { TimeZoneDetectorService service = TimeZoneDetectorService.create(getContext()); // Obtain / create the shared dependencies. Context context = getContext(); Handler handler = FgThread.getHandler(); TimeZoneDetectorStrategy timeZoneDetectorStrategy = TimeZoneDetectorStrategyImpl.create(context); // Create and publish the local service for use by internal callers. TimeZoneDetectorInternal internal = TimeZoneDetectorInternalImpl.create(context, handler, timeZoneDetectorStrategy); publishLocalService(TimeZoneDetectorInternal.class, internal); // Publish the binder service so it can be accessed from other (appropriately // permissioned) processes. TimeZoneDetectorService service = TimeZoneDetectorService.create(context, handler, timeZoneDetectorStrategy); publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service); } } Loading @@ -94,11 +106,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub @NonNull private final ArrayList<ConfigListenerInfo> mConfigurationListeners = new ArrayList<>(); private static TimeZoneDetectorService create(@NonNull Context context) { final TimeZoneDetectorStrategy timeZoneDetectorStrategy = TimeZoneDetectorStrategyImpl.create(context); private static TimeZoneDetectorService create( @NonNull Context context, @NonNull Handler handler, @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) { Handler handler = FgThread.getHandler(); TimeZoneDetectorService service = new TimeZoneDetectorService(context, handler, timeZoneDetectorStrategy); Loading Loading @@ -224,6 +235,16 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub } } /** Provided for command-line access. This is not exposed as a binder API. */ void suggestGeolocationTimeZone( @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) { enforceSuggestGeolocationTimeZonePermission(); Objects.requireNonNull(timeZoneSuggestion); mHandler.post( () -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion)); } @Override public boolean suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) { enforceSuggestManualTimeZonePermission(); Loading Loading @@ -267,6 +288,14 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub "manage time and time zone configuration"); } private void enforceSuggestGeolocationTimeZonePermission() { // The associated method is only used for the shell command interface, it's not possible to // call it via Binder, and Shell currently can set the time zone directly anyway. mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_TIME_ZONE, "suggest geolocation time zone"); } private void enforceSuggestTelephonyTimeZonePermission() { mContext.enforceCallingPermission( android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE, Loading Loading
services/core/java/com/android/server/timezonedetector/Dumpable.java 0 → 100644 +38 −0 Original line number Diff line number Diff line /* * 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.server.timezonedetector; import java.io.PrintWriter; /** An interface for components that can write their internal state to dumpsys logs. */ public interface Dumpable { /** Dump internal state. */ void dump(PrintWriter pw, String[] args); /** * An interface that can be used expose when one component allows another to be registered so * that it is dumped at the same time. */ interface Dumpee { /** * Registers the supplied {@link Dumpable}. When the implementation is dumped * {@link Dumpable#dump(PrintWriter, String[])} should be called on the {@code dumpable}. */ void addDumpable(Dumpable dumpable); } }
services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java 0 → 100644 +173 −0 Original line number Diff line number Diff line /* * 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.server.timezonedetector; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.ShellCommand; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.StringTokenizer; /** * A time zone suggestion from a geolocation source. * * <p> Geolocation-based suggestions have the following properties: * * <ul> * <li>{@code zoneIds}. When not {@code null}, {@code zoneIds} contains a list of suggested time * zone IDs, e.g. ["America/Phoenix", "America/Denver"]. Usually there will be a single zoneId. * When there are multiple, this indicates multiple answers are possible for the current * location / accuracy, i.e. if there is a nearby time zone border. The detection logic * receiving the suggestion is expected to use the first element in the absence of other * information, but one of the others may be used if there is supporting evidence / preferences * such as a device setting or corroborating signals from another source. * <br />{@code zoneIds} can be empty if the current location has been determined to have no * time zone. For example, oceans or disputed areas. This is considered a strong signal and the * received need not look for time zone from other sources. * <br />{@code zoneIds} can be {@code null} to indicate that the geolocation source has entered * an "un-opinionated" state and any previous suggestion is being withdrawn. This indicates the * source cannot provide a valid suggestion due to technical limitations. For example, a * geolocation source may become un-opinionated if the device's location is no longer known with * sufficient accuracy, or if the location is known but no time zone can be determined because * no time zone mapping information is available.</li> * <li>{@code debugInfo} contains debugging metadata associated with the suggestion. This is * used to record why the suggestion exists and how it was obtained. This information exists * only to aid in debugging and therefore is used by {@link #toString()}, but it is not for use * in detection logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}. * </li> * </ul> * * @hide */ public final class GeolocationTimeZoneSuggestion { @NonNull private final List<String> mZoneIds; @Nullable private ArrayList<String> mDebugInfo; public GeolocationTimeZoneSuggestion(@Nullable List<String> zoneIds) { if (zoneIds == null) { // Unopinionated mZoneIds = null; } else { mZoneIds = Collections.unmodifiableList(new ArrayList<>(zoneIds)); } } /** * Returns the zone Ids being suggested. See {@link GeolocationTimeZoneSuggestion} for details. */ @Nullable public List<String> getZoneIds() { return mZoneIds; } /** Returns debug information. See {@link GeolocationTimeZoneSuggestion} for details. */ @NonNull public List<String> getDebugInfo() { return mDebugInfo == null ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo); } /** * Associates information with the instance that can be useful for debugging / logging. The * information is present in {@link #toString()} but is not considered for * {@link #equals(Object)} and {@link #hashCode()}. */ public void addDebugInfo(String... debugInfos) { if (mDebugInfo == null) { mDebugInfo = new ArrayList<>(); } mDebugInfo.addAll(Arrays.asList(debugInfos)); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } GeolocationTimeZoneSuggestion that = (GeolocationTimeZoneSuggestion) o; return Objects.equals(mZoneIds, that.mZoneIds); } @Override public int hashCode() { return Objects.hash(mZoneIds); } @Override public String toString() { return "GeolocationTimeZoneSuggestion{" + "mZoneIds=" + mZoneIds + ", mDebugInfo=" + mDebugInfo + '}'; } /** @hide */ public static GeolocationTimeZoneSuggestion parseCommandLineArg(@NonNull ShellCommand cmd) { String zoneIdsString = null; String opt; while ((opt = cmd.getNextArg()) != null) { switch (opt) { case "--zone_ids": { zoneIdsString = cmd.getNextArgRequired(); break; } default: { throw new IllegalArgumentException("Unknown option: " + opt); } } } List<String> zoneIds = parseZoneIdsArg(zoneIdsString); GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(zoneIds); suggestion.addDebugInfo("Command line injection"); return suggestion; } private static List<String> parseZoneIdsArg(String zoneIdsString) { if ("UNCERTAIN".equals(zoneIdsString)) { return null; } else if ("EMPTY".equals(zoneIdsString)) { return Collections.emptyList(); } else { ArrayList<String> zoneIds = new ArrayList<>(); StringTokenizer tokenizer = new StringTokenizer(zoneIdsString, ","); while (tokenizer.hasMoreTokens()) { zoneIds.add(tokenizer.nextToken()); } return zoneIds; } } /** @hide */ public static void printCommandLineOpts(@NonNull PrintWriter pw) { pw.println("Geolocation suggestion options:"); pw.println(" --zone_ids {UNCERTAIN|EMPTY|<Olson ID>+}"); pw.println(); pw.println("See " + GeolocationTimeZoneSuggestion.class.getName() + " for more information"); } }
services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java 0 → 100644 +35 −0 Original line number Diff line number Diff line /* * 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.server.timezonedetector; import android.annotation.NonNull; /** * The internal (in-process) system server API for the {@link * com.android.server.timezonedetector.TimeZoneDetectorService}. * * @hide */ public interface TimeZoneDetectorInternal extends Dumpable.Dumpee { /** * Suggests the current time zone, determined using geolocation, to the detector. The * detector may ignore the signal based on system settings, whether better information is * available, and so on. */ void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion); }
services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java 0 → 100644 +64 −0 Original line number Diff line number Diff line /* * 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.server.timezonedetector; import android.annotation.NonNull; import android.content.Context; import android.os.Handler; import com.android.internal.annotations.VisibleForTesting; import java.util.Objects; /** * The real {@link TimeZoneDetectorInternal} local service implementation. * * @hide */ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInternal { @NonNull private final Context mContext; @NonNull private final Handler mHandler; @NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy; static TimeZoneDetectorInternalImpl create(@NonNull Context context, @NonNull Handler handler, @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) { return new TimeZoneDetectorInternalImpl(context, handler, timeZoneDetectorStrategy); } @VisibleForTesting public TimeZoneDetectorInternalImpl(@NonNull Context context, @NonNull Handler handler, @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) { mContext = Objects.requireNonNull(context); mHandler = Objects.requireNonNull(handler); mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy); } @Override public void addDumpable(Dumpable dumpable) { mTimeZoneDetectorStrategy.addDumpable(dumpable); } @Override public void suggestGeolocationTimeZone( @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) { Objects.requireNonNull(timeZoneSuggestion); mHandler.post( () -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion)); } }
services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +36 −7 Original line number Diff line number Diff line Loading @@ -63,9 +63,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub private static final String TAG = "TimeZoneDetectorService"; /** * Handles the lifecycle for {@link TimeZoneDetectorService}. * Handles the service lifecycle for {@link TimeZoneDetectorService} and * {@link TimeZoneDetectorInternalImpl}. */ public static class Lifecycle extends SystemService { public static final class Lifecycle extends SystemService { public Lifecycle(@NonNull Context context) { super(context); Loading @@ -73,10 +74,21 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub @Override public void onStart() { TimeZoneDetectorService service = TimeZoneDetectorService.create(getContext()); // Obtain / create the shared dependencies. Context context = getContext(); Handler handler = FgThread.getHandler(); TimeZoneDetectorStrategy timeZoneDetectorStrategy = TimeZoneDetectorStrategyImpl.create(context); // Create and publish the local service for use by internal callers. TimeZoneDetectorInternal internal = TimeZoneDetectorInternalImpl.create(context, handler, timeZoneDetectorStrategy); publishLocalService(TimeZoneDetectorInternal.class, internal); // Publish the binder service so it can be accessed from other (appropriately // permissioned) processes. TimeZoneDetectorService service = TimeZoneDetectorService.create(context, handler, timeZoneDetectorStrategy); publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service); } } Loading @@ -94,11 +106,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub @NonNull private final ArrayList<ConfigListenerInfo> mConfigurationListeners = new ArrayList<>(); private static TimeZoneDetectorService create(@NonNull Context context) { final TimeZoneDetectorStrategy timeZoneDetectorStrategy = TimeZoneDetectorStrategyImpl.create(context); private static TimeZoneDetectorService create( @NonNull Context context, @NonNull Handler handler, @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) { Handler handler = FgThread.getHandler(); TimeZoneDetectorService service = new TimeZoneDetectorService(context, handler, timeZoneDetectorStrategy); Loading Loading @@ -224,6 +235,16 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub } } /** Provided for command-line access. This is not exposed as a binder API. */ void suggestGeolocationTimeZone( @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) { enforceSuggestGeolocationTimeZonePermission(); Objects.requireNonNull(timeZoneSuggestion); mHandler.post( () -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion)); } @Override public boolean suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) { enforceSuggestManualTimeZonePermission(); Loading Loading @@ -267,6 +288,14 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub "manage time and time zone configuration"); } private void enforceSuggestGeolocationTimeZonePermission() { // The associated method is only used for the shell command interface, it's not possible to // call it via Binder, and Shell currently can set the time zone directly anyway. mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_TIME_ZONE, "suggest geolocation time zone"); } private void enforceSuggestTelephonyTimeZonePermission() { mContext.enforceCallingPermission( android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE, Loading