diff options
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; } |