summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java83
-rw-r--r--services/core/java/com/android/server/AlarmManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/SystemTimeZone.java160
-rw-r--r--services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java32
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java45
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java9
-rw-r--r--services/java/com/android/server/SystemServer.java11
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java33
12 files changed, 320 insertions, 92 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 5b61f9abc357..8a823e22785c 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -39,6 +39,8 @@ import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROU
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
import static android.os.UserHandle.USER_SYSTEM;
+import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
+import static com.android.server.SystemTimeZone.getTimeZoneId;
import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
import static com.android.server.alarm.Alarm.BATTERY_SAVER_POLICY_INDEX;
import static com.android.server.alarm.Alarm.DEVICE_IDLE_POLICY_INDEX;
@@ -144,6 +146,8 @@ import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
+import com.android.server.SystemTimeZone;
+import com.android.server.SystemTimeZone.TimeZoneConfidence;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
@@ -199,7 +203,6 @@ public class AlarmManagerService extends SystemService {
static final boolean DEBUG_TARE = localLOGV || false;
static final boolean RECORD_ALARMS_IN_HISTORY = true;
static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
- static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
static final int TICK_HISTORY_DEPTH = 10;
static final long INDEFINITE_DELAY = 365 * INTERVAL_DAY;
@@ -1881,9 +1884,10 @@ public class AlarmManagerService extends SystemService {
mNextWakeup = mNextNonWakeup = 0;
- // We have to set current TimeZone info to kernel
- // because kernel doesn't keep this after reboot
- setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
+ // We set the current offset in kernel because the kernel doesn't keep this after a
+ // reboot. Keeping the kernel time zone in sync is "best effort" and can be wrong
+ // for a period after daylight savings transitions.
+ mInjector.syncKernelTimeZoneOffset();
// Ensure that we're booting with a halfway sensible current time. Use the
// most recent of Build.TIME, the root file system's timestamp, and the
@@ -2117,12 +2121,14 @@ public class AlarmManagerService extends SystemService {
synchronized (mLock) {
final long currentTimeMillis = mInjector.getCurrentTimeMillis();
mInjector.setKernelTime(millis);
- final TimeZone timeZone = TimeZone.getDefault();
+
+ // Changing the time may cross a DST transition; sync the kernel offset if needed.
+ final TimeZone timeZone = TimeZone.getTimeZone(SystemTimeZone.getTimeZoneId());
final int currentTzOffset = timeZone.getOffset(currentTimeMillis);
final int newTzOffset = timeZone.getOffset(millis);
if (currentTzOffset != newTzOffset) {
Slog.i(TAG, "Timezone offset has changed, updating kernel timezone");
- mInjector.setKernelTimezone(-(newTzOffset / 60000));
+ mInjector.setKernelTimeZoneOffset(newTzOffset);
}
// The native implementation of setKernelTime can return -1 even when the kernel
// time was set correctly, so assume setting kernel time was successful and always
@@ -2131,31 +2137,28 @@ public class AlarmManagerService extends SystemService {
}
}
- void setTimeZoneImpl(String tz) {
- if (TextUtils.isEmpty(tz)) {
+ void setTimeZoneImpl(String tzId, @TimeZoneConfidence int confidence) {
+ if (TextUtils.isEmpty(tzId)) {
return;
}
- TimeZone zone = TimeZone.getTimeZone(tz);
+ TimeZone newZone = TimeZone.getTimeZone(tzId);
// Prevent reentrant calls from stepping on each other when writing
// the time zone property
- boolean timeZoneWasChanged = false;
+ boolean timeZoneWasChanged;
synchronized (this) {
- String current = SystemProperties.get(TIMEZONE_PROPERTY);
- if (current == null || !current.equals(zone.getID())) {
- if (localLOGV) {
- Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
- }
- timeZoneWasChanged = true;
- SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
- }
+ // TimeZone.getTimeZone() can return a time zone with a different ID (e.g. it can return
+ // "GMT" if the ID is unrecognized). The parameter ID is used here rather than
+ // newZone.getId(). It will be rejected if it is invalid.
+ timeZoneWasChanged = SystemTimeZone.setTimeZoneId(tzId, confidence);
// Update the kernel timezone information
- // Kernel tracks time offsets as 'minutes west of GMT'
- int gmtOffset = zone.getOffset(mInjector.getCurrentTimeMillis());
- mInjector.setKernelTimezone(-(gmtOffset / 60000));
+ int utcOffsetMillis = newZone.getOffset(mInjector.getCurrentTimeMillis());
+ mInjector.setKernelTimeZoneOffset(utcOffsetMillis);
}
+ // Clear the default time zone in the system server process. This forces the next call
+ // to TimeZone.getDefault() to re-read the device settings.
TimeZone.setDefault(null);
if (timeZoneWasChanged) {
@@ -2168,7 +2171,7 @@ public class AlarmManagerService extends SystemService {
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
- intent.putExtra(Intent.EXTRA_TIMEZONE, zone.getID());
+ intent.putExtra(Intent.EXTRA_TIMEZONE, newZone.getID());
mOptsTimeBroadcast.setTemporaryAppAllowlist(
mActivityManagerInternal.getBootTimeTempAllowListDuration(),
TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
@@ -2685,6 +2688,11 @@ public class AlarmManagerService extends SystemService {
}
@Override
+ public void setTimeZone(String tzId, @TimeZoneConfidence int confidence) {
+ setTimeZoneImpl(tzId, confidence);
+ }
+
+ @Override
public void registerInFlightListener(InFlightListener callback) {
synchronized (mLock) {
mInFlightListeners.add(callback);
@@ -2969,7 +2977,11 @@ public class AlarmManagerService extends SystemService {
final long oldId = Binder.clearCallingIdentity();
try {
- setTimeZoneImpl(tz);
+ // The public API (and the shell command that also uses this method) have no concept
+ // of confidence, but since the time zone ID should come either from apps working on
+ // behalf of the user or a developer, confidence is assumed "high".
+ final int timeZoneConfidence = TIME_ZONE_CONFIDENCE_HIGH;
+ setTimeZoneImpl(tz, timeZoneConfidence);
} finally {
Binder.restoreCallingIdentity(oldId);
}
@@ -4541,8 +4553,18 @@ public class AlarmManagerService extends SystemService {
return AlarmManagerService.getNextAlarm(mNativeData, type);
}
- void setKernelTimezone(int minutesWest) {
- AlarmManagerService.setKernelTimezone(mNativeData, minutesWest);
+ void setKernelTimeZoneOffset(int utcOffsetMillis) {
+ // Kernel tracks time offsets as 'minutes west of GMT'
+ AlarmManagerService.setKernelTimezone(mNativeData, -(utcOffsetMillis / 60000));
+ }
+
+ void syncKernelTimeZoneOffset() {
+ long currentTimeMillis = getCurrentTimeMillis();
+ TimeZone currentTimeZone = TimeZone.getTimeZone(getTimeZoneId());
+ // If the time zone ID is invalid, GMT will be returned and this will set a kernel
+ // offset of zero.
+ int utcOffsetMillis = currentTimeZone.getOffset(currentTimeMillis);
+ setKernelTimeZoneOffset(utcOffsetMillis);
}
void setKernelTime(long millis) {
@@ -5009,13 +5031,10 @@ public class AlarmManagerService extends SystemService {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
- // Since the kernel does not keep track of DST, we need to
- // reset the TZ information at the beginning of each day
- // based off of the current Zone gmt offset + userspace tracked
- // daylight savings information.
- TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
- int gmtOffset = zone.getOffset(mInjector.getCurrentTimeMillis());
- mInjector.setKernelTimezone(-(gmtOffset / 60000));
+ // Since the kernel does not keep track of DST, we reset the TZ information at the
+ // beginning of each day. This may miss a DST transition, but it will correct itself
+ // within 24 hours.
+ mInjector.syncKernelTimeZoneOffset();
scheduleDateChangedEvent();
}
}
diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java
index e3c8afabcf6f..b2abdbd144b8 100644
--- a/services/core/java/com/android/server/AlarmManagerInternal.java
+++ b/services/core/java/com/android/server/AlarmManagerInternal.java
@@ -18,6 +18,8 @@ package com.android.server;
import android.app.PendingIntent;
+import com.android.server.SystemTimeZone.TimeZoneConfidence;
+
public interface AlarmManagerInternal {
// Some other components in the system server need to know about
// broadcast alarms currently in flight
@@ -48,4 +50,13 @@ public interface AlarmManagerInternal {
* {@link android.Manifest.permission#USE_EXACT_ALARM}.
*/
boolean hasExactAlarmPermission(String packageName, int uid);
+
+ /**
+ * Sets the device's current time zone and time zone confidence.
+ *
+ * @param tzId the time zone ID
+ * @param confidence the confidence that {@code tzId} is correct, see {@link TimeZoneConfidence}
+ * for details
+ */
+ void setTimeZone(String tzId, @TimeZoneConfidence int confidence);
}
diff --git a/services/core/java/com/android/server/SystemTimeZone.java b/services/core/java/com/android/server/SystemTimeZone.java
new file mode 100644
index 000000000000..7ce0831480dc
--- /dev/null
+++ b/services/core/java/com/android/server/SystemTimeZone.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2022 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;
+
+import static java.lang.annotation.ElementType.TYPE_USE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.i18n.timezone.ZoneInfoDb;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * A set of constants and static methods that encapsulate knowledge of how time zone and associated
+ * metadata are stored on Android.
+ */
+public final class SystemTimeZone {
+
+ private static final String TAG = "SystemTimeZone";
+ private static final boolean DEBUG = false;
+ private static final String TIME_ZONE_SYSTEM_PROPERTY = "persist.sys.timezone";
+ private static final String TIME_ZONE_CONFIDENCE_SYSTEM_PROPERTY =
+ "persist.sys.timezone_confidence";
+
+ /**
+ * The "special" time zone ID used as a low-confidence default when the device's time zone
+ * is empty or invalid during boot.
+ */
+ private static final String DEFAULT_TIME_ZONE_ID = "GMT";
+
+ /**
+ * An annotation that indicates a "time zone confidence" value is expected.
+ *
+ * <p>The confidence indicates whether the time zone is expected to be correct. The confidence
+ * can be upgraded or downgraded over time. It can be used to decide whether a user could /
+ * should be asked to confirm the time zone. For example, during device set up low confidence
+ * would describe a time zone that has been initialized by default or by using low quality
+ * or ambiguous signals. The user may then be asked to confirm the time zone, moving it to a
+ * high confidence.
+ */
+ @Retention(SOURCE)
+ @Target(TYPE_USE)
+ @IntDef(prefix = "TIME_ZONE_CONFIDENCE_",
+ value = { TIME_ZONE_CONFIDENCE_LOW, TIME_ZONE_CONFIDENCE_HIGH })
+ public @interface TimeZoneConfidence {
+ }
+
+ /** Used when confidence is low and would (ideally) be confirmed by a user. */
+ public static final @TimeZoneConfidence int TIME_ZONE_CONFIDENCE_LOW = 0;
+ /**
+ * Used when confidence in the time zone is high and does not need to be confirmed by a user.
+ */
+ public static final @TimeZoneConfidence int TIME_ZONE_CONFIDENCE_HIGH = 100;
+
+ private SystemTimeZone() {}
+
+ /**
+ * Called during device boot to validate and set the time zone ID to a low-confidence default.
+ */
+ public static void initializeTimeZoneSettingsIfRequired() {
+ String timezoneProperty = SystemProperties.get(TIME_ZONE_SYSTEM_PROPERTY);
+ if (!isValidTimeZoneId(timezoneProperty)) {
+ Slog.w(TAG, TIME_ZONE_SYSTEM_PROPERTY + " is not valid (" + timezoneProperty
+ + "); setting to " + DEFAULT_TIME_ZONE_ID);
+ setTimeZoneId(DEFAULT_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW);
+ }
+ }
+
+ /**
+ * Updates the device's time zone system property and associated metadata. Returns {@code true}
+ * if the device's time zone changed, {@code false} if the ID is invalid or the device is
+ * already set to the supplied ID.
+ *
+ * <p>This method ensures the confidence metadata is set to the supplied value if the supplied
+ * time zone ID is considered valid.
+ *
+ * <p>This method is intended only for use by the AlarmManager. When changing the device's time
+ * zone other system service components must use {@link
+ * com.android.server.AlarmManagerInternal#setTimeZone(String, int)} to ensure that important
+ * system-wide side effects occur.
+ */
+ public static boolean setTimeZoneId(String timeZoneId, @TimeZoneConfidence int confidence) {
+ if (TextUtils.isEmpty(timeZoneId) || !isValidTimeZoneId(timeZoneId)) {
+ return false;
+ }
+
+ boolean timeZoneChanged = false;
+ synchronized (SystemTimeZone.class) {
+ String currentTimeZoneId = getTimeZoneId();
+ if (currentTimeZoneId == null || !currentTimeZoneId.equals(timeZoneId)) {
+ SystemProperties.set(TIME_ZONE_SYSTEM_PROPERTY, timeZoneId);
+ if (DEBUG) {
+ Slog.v(TAG, "Time zone changed: " + currentTimeZoneId + ", new=" + timeZoneId);
+ }
+ timeZoneChanged = true;
+ }
+ setTimeZoneConfidence(confidence);
+ }
+
+ return timeZoneChanged;
+ }
+
+ /**
+ * Sets the time zone confidence value if required. See {@link TimeZoneConfidence} for details.
+ */
+ private static void setTimeZoneConfidence(@TimeZoneConfidence int confidence) {
+ int currentConfidence = getTimeZoneConfidence();
+ if (currentConfidence != confidence) {
+ SystemProperties.set(
+ TIME_ZONE_CONFIDENCE_SYSTEM_PROPERTY, Integer.toString(confidence));
+ if (DEBUG) {
+ Slog.v(TAG, "Time zone confidence changed: old=" + currentConfidence
+ + ", new=" + confidence);
+ }
+ }
+ }
+
+ /** Returns the time zone confidence value. See {@link TimeZoneConfidence} for details. */
+ public static @TimeZoneConfidence int getTimeZoneConfidence() {
+ int confidence = SystemProperties.getInt(
+ TIME_ZONE_CONFIDENCE_SYSTEM_PROPERTY, TIME_ZONE_CONFIDENCE_LOW);
+ if (!isValidTimeZoneConfidence(confidence)) {
+ confidence = TIME_ZONE_CONFIDENCE_LOW;
+ }
+ return confidence;
+ }
+
+ /** Returns the device's time zone ID setting. */
+ public static String getTimeZoneId() {
+ return SystemProperties.get(TIME_ZONE_SYSTEM_PROPERTY);
+ }
+
+ private static boolean isValidTimeZoneConfidence(@TimeZoneConfidence int confidence) {
+ return confidence >= TIME_ZONE_CONFIDENCE_LOW && confidence <= TIME_ZONE_CONFIDENCE_HIGH;
+ }
+
+ private static boolean isValidTimeZoneId(String timeZoneId) {
+ return timeZoneId != null
+ && !timeZoneId.isEmpty()
+ && ZoneInfoDb.getInstance().hasTimeZone(timeZoneId);
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index 0ec8826cb7eb..d973ca689600 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -18,13 +18,16 @@ package com.android.server.timezonedetector;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.AlarmManager;
import android.content.Context;
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
+import com.android.server.AlarmManagerInternal;
+import com.android.server.LocalServices;
+import com.android.server.SystemTimeZone;
+import com.android.server.SystemTimeZone.TimeZoneConfidence;
+
import java.util.Objects;
/**
@@ -59,27 +62,22 @@ final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment
}
@Override
- public boolean isDeviceTimeZoneInitialized() {
- // timezone.equals("GMT") will be true and only true if the time zone was
- // set to a default value by the system server (when starting, system server
- // sets the persist.sys.timezone to "GMT" if it's not set). "GMT" is not used by
- // any code that sets it explicitly (in case where something sets GMT explicitly,
- // "Etc/GMT" Olson ID would be used).
-
- String timeZoneId = getDeviceTimeZone();
- return timeZoneId != null && timeZoneId.length() > 0 && !timeZoneId.equals("GMT");
+ @NonNull
+ public String getDeviceTimeZone() {
+ return SystemProperties.get(TIMEZONE_PROPERTY);
}
@Override
- @Nullable
- public String getDeviceTimeZone() {
- return SystemProperties.get(TIMEZONE_PROPERTY);
+ public @TimeZoneConfidence int getDeviceTimeZoneConfidence() {
+ return SystemTimeZone.getTimeZoneConfidence();
}
@Override
- public void setDeviceTimeZone(String zoneId) {
- AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
- alarmManager.setTimeZone(zoneId);
+ public void setDeviceTimeZoneAndConfidence(
+ @NonNull String zoneId, @TimeZoneConfidence int confidence) {
+ AlarmManagerInternal alarmManagerInternal =
+ LocalServices.getService(AlarmManagerInternal.class);
+ alarmManagerInternal.setTimeZone(zoneId, confidence);
}
@Override
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index 66c23f5d01a4..9b35779f6820 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -22,6 +22,8 @@ import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_M
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
+import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
+
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -39,6 +41,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemTimeZone.TimeZoneConfidence;
import java.time.Duration;
import java.util.List;
@@ -73,19 +76,20 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
@NonNull ConfigurationInternal getCurrentUserConfigurationInternal();
/**
- * Returns true if the device has had an explicit time zone set.
+ * Returns the device's currently configured time zone.
*/
- boolean isDeviceTimeZoneInitialized();
+ @NonNull String getDeviceTimeZone();
/**
- * Returns the device's currently configured time zone.
+ * Returns the confidence of the device's current time zone.
*/
- String getDeviceTimeZone();
+ @TimeZoneConfidence int getDeviceTimeZoneConfidence();
/**
- * Sets the device's time zone.
+ * Sets the device's time zone and associated confidence.
*/
- void setDeviceTimeZone(@NonNull String zoneId);
+ void setDeviceTimeZoneAndConfidence(
+ @NonNull String zoneId, @TimeZoneConfidence int confidence);
/**
* Returns the time according to the elapsed realtime clock, the same as {@link
@@ -620,25 +624,32 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
@GuardedBy("this")
private void setDeviceTimeZoneIfRequired(@NonNull String newZoneId, @NonNull String cause) {
String currentZoneId = mEnvironment.getDeviceTimeZone();
-
- // Avoid unnecessary changes / intents.
- if (newZoneId.equals(currentZoneId)) {
- // No need to set the device time zone - the setting is already what we would be
- // suggesting.
+ // All manual and automatic suggestions are considered high confidence as low-quality
+ // suggestions are not currently passed on.
+ int newConfidence = TIME_ZONE_CONFIDENCE_HIGH;
+ int currentConfidence = mEnvironment.getDeviceTimeZoneConfidence();
+
+ // Avoid unnecessary changes / intents. If the newConfidence is higher than the stored value
+ // then we want to upgrade it.
+ if (newZoneId.equals(currentZoneId) && newConfidence <= currentConfidence) {
+ // No need to modify the device time zone settings.
if (DBG) {
Slog.d(LOG_TAG, "No need to change the time zone;"
+ " device is already set to newZoneId."
+ ", newZoneId=" + newZoneId
- + ", cause=" + cause);
+ + ", cause=" + cause
+ + ", currentScore=" + currentConfidence
+ + ", newConfidence=" + newConfidence);
}
return;
}
- mEnvironment.setDeviceTimeZone(newZoneId);
- String logMsg = "Set device time zone."
+ mEnvironment.setDeviceTimeZoneAndConfidence(newZoneId, newConfidence);
+ String logMsg = "Set device time zone or higher confidence."
+ ", currentZoneId=" + currentZoneId
+ ", newZoneId=" + newZoneId
- + ", cause=" + cause;
+ + ", cause=" + cause
+ + ", newConfidence=" + newConfidence;
logTimeZoneDetectorChange(logMsg);
}
@@ -710,9 +721,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
ipw.println("mCurrentConfigurationInternal=" + mCurrentConfigurationInternal);
ipw.println("[Capabilities=" + mCurrentConfigurationInternal.createCapabilitiesAndConfig()
+ "]");
- ipw.println("mEnvironment.isDeviceTimeZoneInitialized()="
- + mEnvironment.isDeviceTimeZoneInitialized());
ipw.println("mEnvironment.getDeviceTimeZone()=" + mEnvironment.getDeviceTimeZone());
+ ipw.println("mEnvironment.getDeviceTimeZoneConfidence()="
+ + mEnvironment.getDeviceTimeZoneConfidence());
ipw.println("Misc state:");
ipw.increaseIndent(); // level 2
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 42dfee375ae4..a86222ea23e2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -150,6 +150,7 @@ import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTAT
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
@@ -363,6 +364,7 @@ import com.android.internal.widget.LockSettingsInternal;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.PasswordValidationError;
import com.android.net.module.util.ProxyUtils;
+import com.android.server.AlarmManagerInternal;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.PersistentDataBlockManagerInternal;
@@ -1466,6 +1468,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mContext.getSystemService(AlarmManager.class);
}
+ AlarmManagerInternal getAlarmManagerInternal() {
+ return LocalServices.getService(AlarmManagerInternal.class);
+ }
+
ConnectivityManager getConnectivityManager() {
return mContext.getSystemService(ConnectivityManager.class);
}
@@ -12561,7 +12567,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
mInjector.binderWithCleanCallingIdentity(() ->
- mInjector.getAlarmManager().setTimeZone(timeZone));
+ mInjector.getAlarmManagerInternal()
+ .setTimeZone(timeZone, TIME_ZONE_CONFIDENCE_HIGH));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_TIME_ZONE)
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f921ccf62a3b..c4e9b4fcdffb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -762,15 +762,8 @@ public final class SystemServer implements Dumpable {
EventLog.writeEvent(EventLogTags.SYSTEM_SERVER_START,
mStartCount, mRuntimeStartUptime, mRuntimeStartElapsedTime);
- //
- // Default the timezone property to GMT if not set.
- //
- String timezoneProperty = SystemProperties.get("persist.sys.timezone");
- if (!isValidTimeZoneId(timezoneProperty)) {
- Slog.w(TAG, "persist.sys.timezone is not valid (" + timezoneProperty
- + "); setting to GMT.");
- SystemProperties.set("persist.sys.timezone", "GMT");
- }
+ // Set the device's time zone (a system property) if it is not set or is invalid.
+ SystemTimeZone.initializeTimeZoneSettingsIfRequired();
// If the system has "persist.sys.language" and friends set, replace them with
// "persist.sys.locale". Note that the default locale at this point is calculated
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index d9c622d1efb5..446317e8829f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -52,6 +52,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_ALLOW_LIST;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_COMPAT;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_NOT_APPLICABLE;
@@ -111,6 +112,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
import android.Manifest;
import android.app.ActivityManager;
@@ -323,7 +325,12 @@ public class AlarmManagerServiceTest {
}
@Override
- void setKernelTimezone(int minutesWest) {
+ void setKernelTimeZoneOffset(int utcOffsetMillis) {
+ // Do nothing.
+ }
+
+ @Override
+ void syncKernelTimeZoneOffset() {
// Do nothing.
}
@@ -3421,12 +3428,13 @@ public class AlarmManagerServiceTest {
public void setTimeZoneImpl() {
final long durationMs = 20000L;
when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn(durationMs);
- mService.setTimeZoneImpl("UTC");
+ mService.setTimeZoneImpl("UTC", TIME_ZONE_CONFIDENCE_HIGH);
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mMockContext).sendBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL),
isNull(), bundleCaptor.capture());
assertEquals(Intent.ACTION_TIMEZONE_CHANGED, intentCaptor.getValue().getAction());
+ assertEquals("UTC", intentCaptor.getValue().getStringExtra(Intent.EXTRA_TIMEZONE));
final BroadcastOptions bOptions = new BroadcastOptions(bundleCaptor.getValue());
assertEquals(durationMs, bOptions.getTemporaryAppAllowlistDuration());
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index c771998de862..3f0022de1386 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -51,6 +51,7 @@ import androidx.annotation.NonNull;
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
+import com.android.server.AlarmManagerInternal;
import com.android.server.LocalServices;
import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -225,6 +226,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
AlarmManager getAlarmManager() {return services.alarmManager;}
@Override
+ AlarmManagerInternal getAlarmManagerInternal() {
+ return services.alarmManagerInternal;
+ }
+
+ @Override
LockPatternUtils newLockPatternUtils() {
return services.lockPatternUtils;
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 171b895b93aa..0bdcac655a14 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -60,6 +60,7 @@ import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
+import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_PROFILE_OFF_DEADLINE;
import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_TURN_PROFILE_ON_NOTIFICATION;
import static com.android.server.devicepolicy.DpmMockContext.CALLER_USER_HANDLE;
@@ -4501,7 +4502,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
dpm.setTimeZone(admin1, "Asia/Shanghai");
- verify(getServices().alarmManager).setTimeZone("Asia/Shanghai");
+ verify(getServices().alarmManagerInternal)
+ .setTimeZone("Asia/Shanghai", TIME_ZONE_CONFIDENCE_HIGH);
}
@Test
@@ -4516,7 +4518,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setupProfileOwner();
configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
dpm.setTimeZone(admin1, "Asia/Shanghai");
- verify(getServices().alarmManager).setTimeZone("Asia/Shanghai");
+ verify(getServices().alarmManagerInternal)
+ .setTimeZone("Asia/Shanghai", TIME_ZONE_CONFIDENCE_HIGH);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 46cc68ffb914..cec9d50ea69c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -73,6 +73,7 @@ import android.view.IWindowManager;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
+import com.android.server.AlarmManagerInternal;
import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.UserManagerInternal;
@@ -124,6 +125,7 @@ public class MockSystemServices {
public final ConnectivityManager connectivityManager;
public final AccountManager accountManager;
public final AlarmManager alarmManager;
+ public final AlarmManagerInternal alarmManagerInternal;
public final KeyChain.KeyChainConnection keyChainConnection;
public final CrossProfileApps crossProfileApps;
public final PersistentDataBlockManagerInternal persistentDataBlockManagerInternal;
@@ -176,6 +178,7 @@ public class MockSystemServices {
connectivityManager = mock(ConnectivityManager.class);
accountManager = mock(AccountManager.class);
alarmManager = mock(AlarmManager.class);
+ alarmManagerInternal = mock(AlarmManagerInternal.class);
keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS);
crossProfileApps = mock(CrossProfileApps.class);
persistentDataBlockManagerInternal = mock(PersistentDataBlockManagerInternal.class);
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 23a9013abd96..ff838fdf9d73 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -24,6 +24,7 @@ import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_M
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
+import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGH;
import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGHEST;
import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_LOW;
@@ -44,6 +45,7 @@ import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality;
+import com.android.server.SystemTimeZone.TimeZoneConfidence;
import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion;
import org.junit.Before;
@@ -1024,7 +1026,7 @@ public class TimeZoneDetectorStrategyImplTest {
expectedDeviceTimeZoneId = geolocationTimeZoneSuggestion.getZoneIds().get(0);
script.simulateConfigurationInternalChange(expectedInternalConfig)
- .verifyTimeZoneChangedAndReset(expectedDeviceTimeZoneId);
+ .verifyTimeZoneChangedAndReset(expectedDeviceTimeZoneId, TIME_ZONE_CONFIDENCE_HIGH);
assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
manualSuggestion, telephonySuggestion, geolocationTimeZoneSuggestion,
MetricsTimeZoneDetectorState.DETECTION_MODE_GEO);
@@ -1110,6 +1112,7 @@ public class TimeZoneDetectorStrategyImplTest {
static class FakeEnvironment implements TimeZoneDetectorStrategyImpl.Environment {
private final TestState<String> mTimeZoneId = new TestState<>();
+ private int mTimeZoneConfidence;
private ConfigurationInternal mConfigurationInternal;
private @ElapsedRealtimeLong long mElapsedRealtimeMillis;
private ConfigurationChangeListener mConfigurationInternalChangeListener;
@@ -1141,18 +1144,20 @@ public class TimeZoneDetectorStrategyImplTest {
}
@Override
- public boolean isDeviceTimeZoneInitialized() {
- return mTimeZoneId.getLatest() != null;
+ public String getDeviceTimeZone() {
+ return mTimeZoneId.getLatest();
}
@Override
- public String getDeviceTimeZone() {
- return mTimeZoneId.getLatest();
+ public int getDeviceTimeZoneConfidence() {
+ return mTimeZoneConfidence;
}
@Override
- public void setDeviceTimeZone(String zoneId) {
+ public void setDeviceTimeZoneAndConfidence(
+ String zoneId, @TimeZoneConfidence int confidence) {
mTimeZoneId.set(zoneId);
+ mTimeZoneConfidence = confidence;
}
void simulateConfigurationInternalChange(ConfigurationInternal configurationInternal) {
@@ -1164,10 +1169,11 @@ public class TimeZoneDetectorStrategyImplTest {
mTimeZoneId.assertHasNotBeenSet();
}
- void assertTimeZoneChangedTo(String timeZoneId) {
+ void assertTimeZoneChangedTo(String timeZoneId, @TimeZoneConfidence int confidence) {
mTimeZoneId.assertHasBeenSet();
mTimeZoneId.assertChangeCount(1);
mTimeZoneId.assertLatestEquals(timeZoneId);
+ assertEquals(confidence, mTimeZoneConfidence);
}
void commitAllChanges() {
@@ -1280,20 +1286,22 @@ public class TimeZoneDetectorStrategyImplTest {
}
/** Verifies the device's time zone has been set and clears change tracking history. */
- Script verifyTimeZoneChangedAndReset(String zoneId) {
- mFakeEnvironment.assertTimeZoneChangedTo(zoneId);
+ Script verifyTimeZoneChangedAndReset(String zoneId, @TimeZoneConfidence int confidence) {
+ mFakeEnvironment.assertTimeZoneChangedTo(zoneId, confidence);
mFakeEnvironment.commitAllChanges();
return this;
}
Script verifyTimeZoneChangedAndReset(ManualTimeZoneSuggestion suggestion) {
- mFakeEnvironment.assertTimeZoneChangedTo(suggestion.getZoneId());
+ mFakeEnvironment.assertTimeZoneChangedTo(
+ suggestion.getZoneId(), TIME_ZONE_CONFIDENCE_HIGH);
mFakeEnvironment.commitAllChanges();
return this;
}
Script verifyTimeZoneChangedAndReset(TelephonyTimeZoneSuggestion suggestion) {
- mFakeEnvironment.assertTimeZoneChangedTo(suggestion.getZoneId());
+ mFakeEnvironment.assertTimeZoneChangedTo(
+ suggestion.getZoneId(), TIME_ZONE_CONFIDENCE_HIGH);
mFakeEnvironment.commitAllChanges();
return this;
}
@@ -1301,7 +1309,8 @@ public class TimeZoneDetectorStrategyImplTest {
Script verifyTimeZoneChangedAndReset(GeolocationTimeZoneSuggestion suggestion) {
assertEquals("Only use this method with unambiguous geo suggestions",
1, suggestion.getZoneIds().size());
- mFakeEnvironment.assertTimeZoneChangedTo(suggestion.getZoneIds().get(0));
+ mFakeEnvironment.assertTimeZoneChangedTo(
+ suggestion.getZoneIds().get(0), TIME_ZONE_CONFIDENCE_HIGH);
mFakeEnvironment.commitAllChanges();
return this;
}