diff options
33 files changed, 1188 insertions, 291 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000000..03af56d64062 --- /dev/null +++ b/.clang-format @@ -0,0 +1,13 @@ +BasedOnStyle: Google + +AccessModifierOffset: -4 +AlignOperands: false +AllowShortFunctionsOnASingleLine: Inline +AlwaysBreakBeforeMultilineStrings: false +ColumnLimit: 100 +CommentPragmas: NOLINT:.* +ConstructorInitializerIndentWidth: 6 +ContinuationIndentWidth: 8 +IndentWidth: 4 +PenaltyBreakBeforeFirstCallParameter: 100000 +SpacesBeforeTrailingComments: 1 diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 68311176a783..9abb308534df 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,3 +1,15 @@ +[Builtin Hooks] +clang_format = true + +[Builtin Hooks Options] +# Only turn on clang-format check for the following subfolders. +clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp + cmds/hid/ + cmds/input/ + core/jni/ + libs/input/ + services/core/jni/ + [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} diff --git a/StubLibraries.bp b/StubLibraries.bp index 26478d3bad26..b999a10d0a3f 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -299,6 +299,15 @@ java_defaults { compile_dex: true, } +java_defaults { + name: "android_stubs_dists_default", + dist: { + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android.jar", + }, +} + java_library_static { name: "android_stubs_current", srcs: [ ":api-stubs-docs" ], @@ -308,20 +317,54 @@ java_library_static { java_library_static { name: "android_system_stubs_current", srcs: [ ":system-api-stubs-docs" ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], + dist: { + dir: "apistubs/android/system", + }, + dists: [ + { + // Legacy dist path + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android_system.jar", + }, + ], } java_library_static { name: "android_test_stubs_current", srcs: [ ":test-api-stubs-docs" ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], + dist: { + dir: "apistubs/android/test", + }, + dists: [ + { + // Legacy dist path + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android_test.jar", + }, + ], } java_library_static { name: "android_module_lib_stubs_current", srcs: [ ":module-lib-api-stubs-docs-non-updatable" ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], libs: ["sdk_system_29_android"], + dist: { + dir: "apistubs/android/module-lib", + }, } java_library_static { diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index 683c74737a2d..38e3b39f8cfc 100755 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -19,14 +19,13 @@ package android.text.format; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.icu.text.DateFormatSymbols; import android.icu.text.DateTimePatternGenerator; import android.provider.Settings; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.SpannedString; -import libcore.icu.LocaleData; - import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; @@ -286,8 +285,10 @@ public class DateFormat { */ @UnsupportedAppUsage public static String getTimeFormatString(Context context, int userHandle) { - final LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale); - return is24HourFormat(context, userHandle) ? d.timeFormat_Hm : d.timeFormat_hm; + DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance( + context.getResources().getConfiguration().locale); + return is24HourFormat(context, userHandle) ? dtpg.getBestPattern("Hm") + : dtpg.getBestPattern("hm"); } /** @@ -474,7 +475,8 @@ public class DateFormat { SpannableStringBuilder s = new SpannableStringBuilder(inFormat); int count; - LocaleData localeData = LocaleData.get(Locale.getDefault()); + DateFormatSymbols dfs = getIcuDateFormatSymbols(Locale.getDefault()); + String[] amPm = dfs.getAmPmStrings(); int len = inFormat.length(); @@ -496,14 +498,14 @@ public class DateFormat { switch (c) { case 'A': case 'a': - replacement = localeData.amPm[inDate.get(Calendar.AM_PM) - Calendar.AM]; + replacement = amPm[inDate.get(Calendar.AM_PM) - Calendar.AM]; break; case 'd': replacement = zeroPad(inDate.get(Calendar.DATE), count); break; case 'c': case 'E': - replacement = getDayOfWeekString(localeData, + replacement = getDayOfWeekString(dfs, inDate.get(Calendar.DAY_OF_WEEK), count, c); break; case 'K': // hour in am/pm (0-11) @@ -531,8 +533,7 @@ public class DateFormat { break; case 'L': case 'M': - replacement = getMonthString(localeData, - inDate.get(Calendar.MONTH), count, c); + replacement = getMonthString(dfs, inDate.get(Calendar.MONTH), count, c); break; case 'm': replacement = zeroPad(inDate.get(Calendar.MINUTE), count); @@ -565,25 +566,29 @@ public class DateFormat { } } - private static String getDayOfWeekString(LocaleData ld, int day, int count, int kind) { + private static String getDayOfWeekString(DateFormatSymbols dfs, int day, int count, int kind) { boolean standalone = (kind == 'c'); + int context = standalone ? DateFormatSymbols.STANDALONE : DateFormatSymbols.FORMAT; + final int width; if (count == 5) { - return standalone ? ld.tinyStandAloneWeekdayNames[day] : ld.tinyWeekdayNames[day]; + width = DateFormatSymbols.NARROW; } else if (count == 4) { - return standalone ? ld.longStandAloneWeekdayNames[day] : ld.longWeekdayNames[day]; + width = DateFormatSymbols.WIDE; } else { - return standalone ? ld.shortStandAloneWeekdayNames[day] : ld.shortWeekdayNames[day]; + width = DateFormatSymbols.ABBREVIATED; } + return dfs.getWeekdays(context, width)[day]; } - private static String getMonthString(LocaleData ld, int month, int count, int kind) { + private static String getMonthString(DateFormatSymbols dfs, int month, int count, int kind) { boolean standalone = (kind == 'L'); + int monthContext = standalone ? DateFormatSymbols.STANDALONE : DateFormatSymbols.FORMAT; if (count == 5) { - return standalone ? ld.tinyStandAloneMonthNames[month] : ld.tinyMonthNames[month]; + return dfs.getMonths(monthContext, DateFormatSymbols.NARROW)[month]; } else if (count == 4) { - return standalone ? ld.longStandAloneMonthNames[month] : ld.longMonthNames[month]; + return dfs.getMonths(monthContext, DateFormatSymbols.WIDE)[month]; } else if (count == 3) { - return standalone ? ld.shortStandAloneMonthNames[month] : ld.shortMonthNames[month]; + return dfs.getMonths(monthContext, DateFormatSymbols.ABBREVIATED)[month]; } else { // Calendar.JANUARY == 0, so add 1 to month. return zeroPad(month+1, count); @@ -678,4 +683,16 @@ public class DateFormat { private static String zeroPad(int inValue, int inMinDigits) { return String.format(Locale.getDefault(), "%0" + inMinDigits + "d", inValue); } + + /** + * We use Gregorian calendar for date formats in android.text.format and various UI widget + * historically. It's a utility method to get an {@link DateFormatSymbols} instance. Note that + * {@link DateFormatSymbols} has cache, and external cache is not needed unless same instance is + * requested repeatedly in the performance critical code. + * + * @hide + */ + public static DateFormatSymbols getIcuDateFormatSymbols(Locale locale) { + return new DateFormatSymbols(android.icu.util.GregorianCalendar.class, locale); + } } diff --git a/core/java/android/text/format/DateIntervalFormat.java b/core/java/android/text/format/DateIntervalFormat.java new file mode 100644 index 000000000000..de9ec7ab9ea9 --- /dev/null +++ b/core/java/android/text/format/DateIntervalFormat.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2013 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 android.text.format; + +import static android.text.format.DateUtils.FORMAT_SHOW_TIME; +import static android.text.format.DateUtils.FORMAT_UTC; + +import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; + +import android.icu.util.Calendar; +import android.icu.util.ULocale; +import android.util.LruCache; + +import com.android.internal.annotations.VisibleForTesting; + +import java.text.FieldPosition; +import java.util.TimeZone; + +/** + * A wrapper of {@link android.icu.text.DateIntervalFormat} used by {@link DateUtilsBridge}. + * + * @hide + */ +@VisibleForTesting(visibility = PACKAGE) +public final class DateIntervalFormat { + + private static final LruCache<String, android.icu.text.DateIntervalFormat> CACHED_FORMATTERS = + new LruCache<>(8); + + private DateIntervalFormat() { + } + + /** + * Format a date range. + */ + @VisibleForTesting(visibility = PACKAGE) + public static String formatDateRange(long startMs, long endMs, int flags, String olsonId) { + if ((flags & FORMAT_UTC) != 0) { + olsonId = "UTC"; + } + // We create a java.util.TimeZone here to use libcore's data and libcore's olson ID / + // pseudo-tz logic. + TimeZone tz = (olsonId != null) ? TimeZone.getTimeZone(olsonId) : TimeZone.getDefault(); + android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz); + ULocale icuLocale = ULocale.getDefault(); + return formatDateRange(icuLocale, icuTimeZone, startMs, endMs, flags); + } + + /** + * Format a date range. This is our slightly more sensible internal API. + * A truly sane replacement would take a skeleton instead of int flags. + */ + @VisibleForTesting(visibility = PACKAGE) + public static String formatDateRange(ULocale icuLocale, android.icu.util.TimeZone icuTimeZone, + long startMs, long endMs, int flags) { + Calendar startCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, startMs); + Calendar endCalendar; + if (startMs == endMs) { + endCalendar = startCalendar; + } else { + endCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, endMs); + } + + // Special handling when the range ends at midnight: + // - If we're not showing times, and the range is non-empty, we fudge the end date so we + // don't count the day that's about to start. + // - If we are showing times, and the range ends at exactly 00:00 of the day following + // its start (which can be thought of as 24:00 the same day), we fudge the end date so we + // don't show the dates --- unless the start is anything displayed as 00:00, in which case + // we include both dates to disambiguate. + // This is not the behavior of icu4j's DateIntervalFormat, but it's the required behavior + // of Android's DateUtils.formatDateRange. + if (isExactlyMidnight(endCalendar)) { + boolean showTime = (flags & FORMAT_SHOW_TIME) == FORMAT_SHOW_TIME; + boolean endsDayAfterStart = DateUtilsBridge.dayDistance(startCalendar, endCalendar) + == 1; + if ((!showTime && startMs != endMs) + || (endsDayAfterStart + && !DateUtilsBridge.isDisplayMidnightUsingSkeleton(startCalendar))) { + endCalendar.add(Calendar.DAY_OF_MONTH, -1); + } + } + + String skeleton = DateUtilsBridge.toSkeleton(startCalendar, endCalendar, flags); + synchronized (CACHED_FORMATTERS) { + android.icu.text.DateIntervalFormat formatter = + getFormatter(skeleton, icuLocale, icuTimeZone); + return formatter.format(startCalendar, endCalendar, new StringBuffer(), + new FieldPosition(0)).toString(); + } + } + + private static android.icu.text.DateIntervalFormat getFormatter(String skeleton, ULocale locale, + android.icu.util.TimeZone icuTimeZone) { + String key = skeleton + "\t" + locale + "\t" + icuTimeZone; + android.icu.text.DateIntervalFormat formatter = CACHED_FORMATTERS.get(key); + if (formatter != null) { + return formatter; + } + formatter = android.icu.text.DateIntervalFormat.getInstance(skeleton, locale); + formatter.setTimeZone(icuTimeZone); + CACHED_FORMATTERS.put(key, formatter); + return formatter; + } + + private static boolean isExactlyMidnight(Calendar c) { + return c.get(Calendar.HOUR_OF_DAY) == 0 + && c.get(Calendar.MINUTE) == 0 + && c.get(Calendar.SECOND) == 0 + && c.get(Calendar.MILLISECOND) == 0; + } +} diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index b0253a0af7a7..31464575186b 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -20,6 +20,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.icu.text.DateFormatSymbols; import android.icu.text.MeasureFormat; import android.icu.text.MeasureFormat.FormatWidth; import android.icu.util.Measure; @@ -27,9 +28,6 @@ import android.icu.util.MeasureUnit; import com.android.internal.R; -import libcore.icu.DateIntervalFormat; -import libcore.icu.LocaleData; - import java.io.IOException; import java.util.Calendar; import java.util.Date; @@ -201,17 +199,23 @@ public class DateUtils */ @Deprecated public static String getDayOfWeekString(int dayOfWeek, int abbrev) { - LocaleData d = LocaleData.get(Locale.getDefault()); - String[] names; + DateFormatSymbols dfs = DateFormatSymbols.getInstance(); + final int width; switch (abbrev) { - case LENGTH_LONG: names = d.longWeekdayNames; break; - case LENGTH_MEDIUM: names = d.shortWeekdayNames; break; - case LENGTH_SHORT: names = d.shortWeekdayNames; break; // TODO - case LENGTH_SHORTER: names = d.shortWeekdayNames; break; // TODO - case LENGTH_SHORTEST: names = d.tinyWeekdayNames; break; - default: names = d.shortWeekdayNames; break; + case LENGTH_LONG: + width = DateFormatSymbols.WIDE; + break; + case LENGTH_SHORTEST: + width = DateFormatSymbols.NARROW; + break; + case LENGTH_MEDIUM: + case LENGTH_SHORT: // TODO + case LENGTH_SHORTER: // TODO + default: + width = DateFormatSymbols.ABBREVIATED; + break; } - return names[dayOfWeek]; + return dfs.getWeekdays(DateFormatSymbols.FORMAT, width)[dayOfWeek]; } /** @@ -223,7 +227,8 @@ public class DateUtils */ @Deprecated public static String getAMPMString(int ampm) { - return LocaleData.get(Locale.getDefault()).amPm[ampm - Calendar.AM]; + String[] amPm = DateFormat.getIcuDateFormatSymbols(Locale.getDefault()).getAmPmStrings(); + return amPm[ampm - Calendar.AM]; } /** @@ -239,17 +244,23 @@ public class DateUtils */ @Deprecated public static String getMonthString(int month, int abbrev) { - LocaleData d = LocaleData.get(Locale.getDefault()); - String[] names; + DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(Locale.getDefault()); + final int width; switch (abbrev) { - case LENGTH_LONG: names = d.longMonthNames; break; - case LENGTH_MEDIUM: names = d.shortMonthNames; break; - case LENGTH_SHORT: names = d.shortMonthNames; break; - case LENGTH_SHORTER: names = d.shortMonthNames; break; - case LENGTH_SHORTEST: names = d.tinyMonthNames; break; - default: names = d.shortMonthNames; break; + case LENGTH_LONG: + width = DateFormatSymbols.WIDE; + break; + case LENGTH_SHORTEST: + width = DateFormatSymbols.NARROW; + break; + case LENGTH_MEDIUM: + case LENGTH_SHORT: + case LENGTH_SHORTER: + default: + width = DateFormatSymbols.ABBREVIATED; + break; } - return names[month]; + return dfs.getMonths(DateFormatSymbols.FORMAT, width)[month]; } /** diff --git a/core/java/android/text/format/DateUtilsBridge.java b/core/java/android/text/format/DateUtilsBridge.java index 370d999abf3e..92ec9cf6d736 100644 --- a/core/java/android/text/format/DateUtilsBridge.java +++ b/core/java/android/text/format/DateUtilsBridge.java @@ -16,6 +16,20 @@ package android.text.format; +import static android.text.format.DateUtils.FORMAT_12HOUR; +import static android.text.format.DateUtils.FORMAT_24HOUR; +import static android.text.format.DateUtils.FORMAT_ABBREV_ALL; +import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; +import static android.text.format.DateUtils.FORMAT_ABBREV_TIME; +import static android.text.format.DateUtils.FORMAT_ABBREV_WEEKDAY; +import static android.text.format.DateUtils.FORMAT_NO_MONTH_DAY; +import static android.text.format.DateUtils.FORMAT_NO_YEAR; +import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_TIME; +import static android.text.format.DateUtils.FORMAT_SHOW_WEEKDAY; +import static android.text.format.DateUtils.FORMAT_SHOW_YEAR; + import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import android.icu.util.Calendar; @@ -33,24 +47,6 @@ import com.android.internal.annotations.VisibleForTesting; */ @VisibleForTesting(visibility = PACKAGE) public final class DateUtilsBridge { - // These are all public API in DateUtils. There are others, but they're either for use with - // other methods (like FORMAT_ABBREV_RELATIVE), don't internationalize (like FORMAT_CAP_AMPM), - // or have never been implemented anyway. - public static final int FORMAT_SHOW_TIME = 0x00001; - public static final int FORMAT_SHOW_WEEKDAY = 0x00002; - public static final int FORMAT_SHOW_YEAR = 0x00004; - public static final int FORMAT_NO_YEAR = 0x00008; - public static final int FORMAT_SHOW_DATE = 0x00010; - public static final int FORMAT_NO_MONTH_DAY = 0x00020; - public static final int FORMAT_12HOUR = 0x00040; - public static final int FORMAT_24HOUR = 0x00080; - public static final int FORMAT_UTC = 0x02000; - public static final int FORMAT_ABBREV_TIME = 0x04000; - public static final int FORMAT_ABBREV_WEEKDAY = 0x08000; - public static final int FORMAT_ABBREV_MONTH = 0x10000; - public static final int FORMAT_NUMERIC_DATE = 0x20000; - public static final int FORMAT_ABBREV_RELATIVE = 0x40000; - public static final int FORMAT_ABBREV_ALL = 0x80000; /** * Creates an immutable ICU timezone backed by the specified libcore timezone data. At the time diff --git a/core/java/android/text/format/RelativeDateTimeFormatter.java b/core/java/android/text/format/RelativeDateTimeFormatter.java index c5bca172873a..9096469699c1 100644 --- a/core/java/android/text/format/RelativeDateTimeFormatter.java +++ b/core/java/android/text/format/RelativeDateTimeFormatter.java @@ -16,14 +16,14 @@ package android.text.format; -import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_ALL; -import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_MONTH; -import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_RELATIVE; -import static android.text.format.DateUtilsBridge.FORMAT_NO_YEAR; -import static android.text.format.DateUtilsBridge.FORMAT_NUMERIC_DATE; -import static android.text.format.DateUtilsBridge.FORMAT_SHOW_DATE; -import static android.text.format.DateUtilsBridge.FORMAT_SHOW_TIME; -import static android.text.format.DateUtilsBridge.FORMAT_SHOW_YEAR; +import static android.text.format.DateUtils.FORMAT_ABBREV_ALL; +import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; +import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE; +import static android.text.format.DateUtils.FORMAT_NO_YEAR; +import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_TIME; +import static android.text.format.DateUtils.FORMAT_SHOW_YEAR; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java index cd541f2b829f..c71dfbbafd40 100644 --- a/core/java/android/text/format/TimeFormatter.java +++ b/core/java/android/text/format/TimeFormatter.java @@ -21,11 +21,11 @@ package android.text.format; import android.content.res.Resources; +import android.icu.text.DateFormatSymbols; +import android.icu.text.DecimalFormatSymbols; import com.android.i18n.timezone.ZoneInfoData; -import libcore.icu.LocaleData; - import java.nio.CharBuffer; import java.time.Instant; import java.time.LocalDateTime; @@ -52,15 +52,17 @@ class TimeFormatter { private static final int DAYSPERNYEAR = 365; /** - * The Locale for which the cached LocaleData and formats have been loaded. + * The Locale for which the cached symbols and formats have been loaded. */ private static Locale sLocale; - private static LocaleData sLocaleData; + private static DateFormatSymbols sDateFormatSymbols; + private static DecimalFormatSymbols sDecimalFormatSymbols; private static String sTimeOnlyFormat; private static String sDateOnlyFormat; private static String sDateTimeFormat; - private final LocaleData localeData; + private final DateFormatSymbols dateFormatSymbols; + private final DecimalFormatSymbols decimalFormatSymbols; private final String dateTimeFormat; private final String timeOnlyFormat; private final String dateOnlyFormat; @@ -74,7 +76,8 @@ class TimeFormatter { if (sLocale == null || !(locale.equals(sLocale))) { sLocale = locale; - sLocaleData = LocaleData.get(locale); + sDateFormatSymbols = DateFormat.getIcuDateFormatSymbols(locale); + sDecimalFormatSymbols = DecimalFormatSymbols.getInstance(locale); Resources r = Resources.getSystem(); sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day); @@ -82,10 +85,11 @@ class TimeFormatter { sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time); } + this.dateFormatSymbols = sDateFormatSymbols; + this.decimalFormatSymbols = sDecimalFormatSymbols; this.dateTimeFormat = sDateTimeFormat; this.timeOnlyFormat = sTimeOnlyFormat; this.dateOnlyFormat = sDateOnlyFormat; - localeData = sLocaleData; } } @@ -167,12 +171,12 @@ class TimeFormatter { } private String localizeDigits(String s) { - if (localeData.zeroDigit == '0') { + if (decimalFormatSymbols.getZeroDigit() == '0') { return s; } int length = s.length(); - int offsetToLocalizedDigits = localeData.zeroDigit - '0'; + int offsetToLocalizedDigits = decimalFormatSymbols.getZeroDigit() - '0'; StringBuilder result = new StringBuilder(length); for (int i = 0; i < length; ++i) { char ch = s.charAt(i); @@ -215,35 +219,44 @@ class TimeFormatter { char currentChar = formatBuffer.get(formatBuffer.position()); switch (currentChar) { case 'A': - modifyAndAppend((wallTime.getWeekDay() < 0 - || wallTime.getWeekDay() >= DAYSPERWEEK) - ? "?" : localeData.longWeekdayNames[wallTime.getWeekDay() + 1], + modifyAndAppend( + (wallTime.getWeekDay() < 0 || wallTime.getWeekDay() >= DAYSPERWEEK) + ? "?" + : dateFormatSymbols.getWeekdays(DateFormatSymbols.FORMAT, + DateFormatSymbols.WIDE)[wallTime.getWeekDay() + 1], modifier); return false; case 'a': - modifyAndAppend((wallTime.getWeekDay() < 0 - || wallTime.getWeekDay() >= DAYSPERWEEK) - ? "?" : localeData.shortWeekdayNames[wallTime.getWeekDay() + 1], + modifyAndAppend( + (wallTime.getWeekDay() < 0 || wallTime.getWeekDay() >= DAYSPERWEEK) + ? "?" + : dateFormatSymbols.getWeekdays(DateFormatSymbols.FORMAT, + DateFormatSymbols.ABBREVIATED)[wallTime.getWeekDay() + 1], modifier); return false; case 'B': if (modifier == '-') { - modifyAndAppend((wallTime.getMonth() < 0 - || wallTime.getMonth() >= MONSPERYEAR) - ? "?" - : localeData.longStandAloneMonthNames[wallTime.getMonth()], + modifyAndAppend( + (wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR) + ? "?" + : dateFormatSymbols.getMonths(DateFormatSymbols.STANDALONE, + DateFormatSymbols.WIDE)[wallTime.getMonth()], modifier); } else { - modifyAndAppend((wallTime.getMonth() < 0 - || wallTime.getMonth() >= MONSPERYEAR) - ? "?" : localeData.longMonthNames[wallTime.getMonth()], + modifyAndAppend( + (wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR) + ? "?" + : dateFormatSymbols.getMonths(DateFormatSymbols.FORMAT, + DateFormatSymbols.WIDE)[wallTime.getMonth()], modifier); } return false; case 'b': case 'h': modifyAndAppend((wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR) - ? "?" : localeData.shortMonthNames[wallTime.getMonth()], + ? "?" + : dateFormatSymbols.getMonths(DateFormatSymbols.FORMAT, + DateFormatSymbols.ABBREVIATED)[wallTime.getMonth()], modifier); return false; case 'C': @@ -310,12 +323,14 @@ class TimeFormatter { outputBuilder.append('\n'); return false; case 'p': - modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1] - : localeData.amPm[0], modifier); + modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) + ? dateFormatSymbols.getAmPmStrings()[1] + : dateFormatSymbols.getAmPmStrings()[0], modifier); return false; case 'P': - modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1] - : localeData.amPm[0], FORCE_LOWER_CASE); + modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) + ? dateFormatSymbols.getAmPmStrings()[1] + : dateFormatSymbols.getAmPmStrings()[0], FORCE_LOWER_CASE); return false; case 'R': formatInternal("%H:%M", wallTime, zoneInfoData); diff --git a/core/java/android/text/method/NumberKeyListener.java b/core/java/android/text/method/NumberKeyListener.java index d40015ee17a8..2b038dd11348 100644 --- a/core/java/android/text/method/NumberKeyListener.java +++ b/core/java/android/text/method/NumberKeyListener.java @@ -29,8 +29,6 @@ import android.text.format.DateFormat; import android.view.KeyEvent; import android.view.View; -import libcore.icu.LocaleData; - import java.util.Collection; import java.util.Locale; @@ -228,7 +226,7 @@ public abstract class NumberKeyListener extends BaseKeyListener if (locale == null) { return false; } - final String[] amPm = LocaleData.get(locale).amPm; + final String[] amPm = DateFormat.getIcuDateFormatSymbols(locale).getAmPmStrings(); for (int i = 0; i < amPm.length; i++) { for (int j = 0; j < amPm[i].length(); j++) { final char ch = amPm[i].charAt(j); diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java index 1b899dbf6d03..33e64f4d37e9 100644 --- a/core/java/android/widget/CalendarViewLegacyDelegate.java +++ b/core/java/android/widget/CalendarViewLegacyDelegate.java @@ -38,8 +38,6 @@ import android.view.ViewGroup; import com.android.internal.R; -import libcore.icu.LocaleData; - import java.util.Locale; /** @@ -264,7 +262,7 @@ class CalendarViewLegacyDelegate extends CalendarView.AbstractCalendarViewDelega mShowWeekNumber = a.getBoolean(R.styleable.CalendarView_showWeekNumber, DEFAULT_SHOW_WEEK_NUMBER); mFirstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek, - LocaleData.get(Locale.getDefault()).firstDayOfWeek); + Calendar.getInstance().getFirstDayOfWeek()); final String minDate = a.getString(R.styleable.CalendarView_minDate); if (!CalendarView.parseDate(minDate, mMinDate)) { CalendarView.parseDate(DEFAULT_MIN_DATE, mMinDate); diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java index 67fef13d23f2..7de2bd10482f 100644 --- a/core/java/android/widget/DayPickerView.java +++ b/core/java/android/widget/DayPickerView.java @@ -33,10 +33,6 @@ import com.android.internal.R; import com.android.internal.widget.ViewPager; import com.android.internal.widget.ViewPager.OnPageChangeListener; -import libcore.icu.LocaleData; - -import java.util.Locale; - class DayPickerView extends ViewGroup { private static final int DEFAULT_LAYOUT = R.layout.day_picker_content_material; private static final int DEFAULT_START_YEAR = 1900; @@ -86,7 +82,7 @@ class DayPickerView extends ViewGroup { attrs, a, defStyleAttr, defStyleRes); final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek, - LocaleData.get(Locale.getDefault()).firstDayOfWeek); + Calendar.getInstance().getFirstDayOfWeek()); final String minDate = a.getString(R.styleable.CalendarView_minDate); final String maxDate = a.getString(R.styleable.CalendarView_maxDate); diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index e4a34b943657..68ac2f684ef0 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -34,6 +34,7 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.icu.text.DecimalFormatSymbols; import android.os.Build; import android.os.Bundle; import android.text.InputFilter; @@ -61,8 +62,6 @@ import android.view.inputmethod.InputMethodManager; import com.android.internal.R; -import libcore.icu.LocaleData; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -209,7 +208,7 @@ public class NumberPicker extends LinearLayout { } private static char getZeroDigit(Locale locale) { - return LocaleData.get(locale).zeroDigit; + return DecimalFormatSymbols.getInstance(locale).getZeroDigit(); } private java.util.Formatter createFormatter(Locale locale) { diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java index 80de6fc65f90..8b74add8df0a 100644 --- a/core/java/android/widget/SimpleMonthView.java +++ b/core/java/android/widget/SimpleMonthView.java @@ -27,6 +27,7 @@ import android.graphics.Paint.Align; import android.graphics.Paint.Style; import android.graphics.Rect; import android.graphics.Typeface; +import android.icu.text.DateFormatSymbols; import android.icu.text.DisplayContext; import android.icu.text.SimpleDateFormat; import android.icu.util.Calendar; @@ -49,8 +50,6 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import com.android.internal.R; import com.android.internal.widget.ExploreByTouchHelper; -import libcore.icu.LocaleData; - import java.text.NumberFormat; import java.util.Locale; @@ -193,7 +192,8 @@ class SimpleMonthView extends View { private void updateDayOfWeekLabels() { // Use tiny (e.g. single-character) weekday names from ICU. The indices // for this list correspond to Calendar days, e.g. SUNDAY is index 1. - final String[] tinyWeekdayNames = LocaleData.get(mLocale).tinyWeekdayNames; + final String[] tinyWeekdayNames = DateFormatSymbols.getInstance(mLocale) + .getWeekdays(DateFormatSymbols.FORMAT, DateFormatSymbols.NARROW); for (int i = 0; i < DAYS_IN_WEEK; i++) { mDayOfWeekLabels[i] = tinyWeekdayNames[(mWeekStart + i - 1) % DAYS_IN_WEEK + 1]; } diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java index 88a67625d615..7804fd1f9f3c 100644 --- a/core/java/android/widget/TextClock.java +++ b/core/java/android/widget/TextClock.java @@ -30,6 +30,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.TypedArray; import android.database.ContentObserver; +import android.icu.text.DateTimePatternGenerator; import android.net.Uri; import android.os.Handler; import android.os.SystemClock; @@ -43,8 +44,6 @@ import android.view.inspector.InspectableProperty; import com.android.internal.R; -import libcore.icu.LocaleData; - import java.util.Calendar; import java.util.TimeZone; @@ -259,14 +258,11 @@ public class TextClock extends TextView { } private void init() { - if (mFormat12 == null || mFormat24 == null) { - LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale); - if (mFormat12 == null) { - mFormat12 = ld.timeFormat_hm; - } - if (mFormat24 == null) { - mFormat24 = ld.timeFormat_Hm; - } + if (mFormat12 == null) { + mFormat12 = getBestDateTimePattern("hm"); + } + if (mFormat24 == null) { + mFormat24 = getBestDateTimePattern("Hm"); } createTime(mTimeZone); @@ -508,13 +504,11 @@ public class TextClock extends TextView { private void chooseFormat() { final boolean format24Requested = is24HourModeEnabled(); - LocaleData ld = LocaleData.get(getContext().getResources().getConfiguration().locale); - if (format24Requested) { - mFormat = abc(mFormat24, mFormat12, ld.timeFormat_Hm); + mFormat = abc(mFormat24, mFormat12, getBestDateTimePattern("Hm")); mDescFormat = abc(mDescFormat24, mDescFormat12, mFormat); } else { - mFormat = abc(mFormat12, mFormat24, ld.timeFormat_hm); + mFormat = abc(mFormat12, mFormat24, getBestDateTimePattern("hm")); mDescFormat = abc(mDescFormat12, mDescFormat24, mFormat); } @@ -527,6 +521,12 @@ public class TextClock extends TextView { } } + private String getBestDateTimePattern(String skeleton) { + DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance( + getContext().getResources().getConfiguration().locale); + return dtpg.getBestPattern(skeleton); + } + /** * Returns a if not null, else return b if not null, else return c. */ diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java index 51b18473f1ac..1c219eb95479 100644 --- a/core/java/android/widget/TimePicker.java +++ b/core/java/android/widget/TimePicker.java @@ -24,9 +24,11 @@ import android.annotation.Widget; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.TypedArray; +import android.icu.text.DateFormatSymbols; import android.icu.util.Calendar; import android.os.Parcel; import android.os.Parcelable; +import android.text.format.DateFormat; import android.util.AttributeSet; import android.util.Log; import android.util.MathUtils; @@ -39,8 +41,6 @@ import android.view.inspector.InspectableProperty; import com.android.internal.R; -import libcore.icu.LocaleData; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Locale; @@ -421,11 +421,13 @@ public class TimePicker extends FrameLayout { static String[] getAmPmStrings(Context context) { final Locale locale = context.getResources().getConfiguration().locale; - final LocaleData d = LocaleData.get(locale); + DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(locale); + String[] amPm = dfs.getAmPmStrings(); + String[] narrowAmPm = dfs.getAmpmNarrowStrings(); final String[] result = new String[2]; - result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0]; - result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1]; + result[0] = amPm[0].length() > 4 ? narrowAmPm[0] : amPm[0]; + result[1] = amPm[1].length() > 4 ? narrowAmPm[1] : amPm[1]; return result; } diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java index 83c86d5ce36b..bd2fa5965bc9 100644 --- a/core/java/android/widget/TimePickerSpinnerDelegate.java +++ b/core/java/android/widget/TimePickerSpinnerDelegate.java @@ -35,8 +35,6 @@ import android.view.inputmethod.InputMethodManager; import com.android.internal.R; -import libcore.icu.LocaleData; - import java.util.Calendar; /** @@ -143,7 +141,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); // Get the localized am/pm strings and use them in the spinner. - mAmPmStrings = getAmPmStrings(context); + mAmPmStrings = TimePicker.getAmPmStrings(context); // am/pm final View amPmView = mDelegator.findViewById(R.id.amPm); @@ -574,12 +572,4 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { target.setContentDescription(mContext.getString(contDescResId)); } } - - public static String[] getAmPmStrings(Context context) { - String[] result = new String[2]; - LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale); - result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0]; - result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1]; - return result; - } } diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index 37365111b0c5..bcb0460ace0f 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -371,7 +371,7 @@ public class PointerLocationView extends View implements InputDeviceListener, } if (haveLast) { canvas.drawLine(lastX, lastY, x, y, mPathPaint); - final Paint paint = ps.mTraceCurrent[i] ? mCurrentPointPaint : mPaint; + final Paint paint = ps.mTraceCurrent[i - 1] ? mCurrentPointPaint : mPaint; canvas.drawPoint(lastX, lastY, paint); drawn = true; } diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java index fa1d56f2e68d..a3434e885012 100644 --- a/core/tests/coretests/src/android/text/format/DateFormatTest.java +++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.icu.text.DateFormatSymbols; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; @@ -60,6 +61,15 @@ public class DateFormatTest { } @Test + public void testgetIcuDateFormatSymbols() { + DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(Locale.US); + assertEquals("AM", dfs.getAmPmStrings()[0]); + assertEquals("PM", dfs.getAmPmStrings()[1]); + assertEquals("a", dfs.getAmpmNarrowStrings()[0]); + assertEquals("p", dfs.getAmpmNarrowStrings()[1]); + } + + @Test public void testGetDateFormatOrder() { // lv and fa use differing orders depending on whether you're using numeric or // textual months. diff --git a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java new file mode 100644 index 000000000000..0f17d27048f3 --- /dev/null +++ b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java @@ -0,0 +1,671 @@ +/* + * Copyright (C) 2013 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 android.text.format; + +import static android.icu.util.TimeZone.GMT_ZONE; +import static android.icu.util.ULocale.ENGLISH; +import static android.text.format.DateIntervalFormat.formatDateRange; +import static android.text.format.DateUtils.FORMAT_12HOUR; +import static android.text.format.DateUtils.FORMAT_24HOUR; +import static android.text.format.DateUtils.FORMAT_ABBREV_ALL; +import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; +import static android.text.format.DateUtils.FORMAT_ABBREV_TIME; +import static android.text.format.DateUtils.FORMAT_ABBREV_WEEKDAY; +import static android.text.format.DateUtils.FORMAT_NO_MONTH_DAY; +import static android.text.format.DateUtils.FORMAT_NO_YEAR; +import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_TIME; +import static android.text.format.DateUtils.FORMAT_SHOW_WEEKDAY; +import static android.text.format.DateUtils.FORMAT_SHOW_YEAR; +import static android.text.format.DateUtils.FORMAT_UTC; + +import static org.junit.Assert.assertEquals; + +import android.icu.util.Calendar; +import android.icu.util.TimeZone; +import android.icu.util.ULocale; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.function.BiFunction; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class DateIntervalFormatTest { + private static final long MINUTE = 60 * 1000; + private static final long HOUR = 60 * MINUTE; + private static final long DAY = 24 * HOUR; + private static final long MONTH = 31 * DAY; + private static final long YEAR = 12 * MONTH; + + // These are the old CTS tests for DateIntervalFormat.formatDateRange. + @Test + public void test_formatDateInterval() throws Exception { + TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); + + Calendar c = Calendar.getInstance(tz, ULocale.US); + c.set(Calendar.MONTH, Calendar.JANUARY); + c.set(Calendar.DAY_OF_MONTH, 19); + c.set(Calendar.HOUR_OF_DAY, 3); + c.set(Calendar.MINUTE, 30); + c.set(Calendar.SECOND, 15); + c.set(Calendar.MILLISECOND, 0); + long timeWithCurrentYear = c.getTimeInMillis(); + + c.set(Calendar.YEAR, 2009); + long fixedTime = c.getTimeInMillis(); + + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + long onTheHour = c.getTimeInMillis(); + + long noonDuration = (8 * 60 + 30) * 60 * 1000 - 15 * 1000; + long midnightDuration = (3 * 60 + 30) * 60 * 1000 + 15 * 1000; + + ULocale de_DE = new ULocale("de", "DE"); + ULocale en_US = new ULocale("en", "US"); + ULocale es_ES = new ULocale("es", "ES"); + ULocale es_US = new ULocale("es", "US"); + + assertEquals("Monday", + formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_WEEKDAY)); + assertEquals("January 19", + formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, + FORMAT_SHOW_DATE)); + assertEquals("3:30 AM", formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_SHOW_TIME)); + assertEquals("January 19, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_SHOW_YEAR)); + assertEquals("January 19", + formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, FORMAT_NO_YEAR)); + assertEquals("January", + formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, + FORMAT_NO_MONTH_DAY)); + assertEquals("3:30 AM", + formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_12HOUR | FORMAT_SHOW_TIME)); + assertEquals("03:30", + formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_24HOUR | FORMAT_SHOW_TIME)); + assertEquals("3:30 AM", formatDateRange(en_US, tz, fixedTime, fixedTime, + FORMAT_12HOUR /*| FORMAT_CAP_AMPM*/ | FORMAT_SHOW_TIME)); + assertEquals("12:00 PM", + formatDateRange(en_US, tz, fixedTime + noonDuration, fixedTime + noonDuration, + FORMAT_12HOUR | FORMAT_SHOW_TIME)); + assertEquals("12:00 PM", + formatDateRange(en_US, tz, fixedTime + noonDuration, fixedTime + noonDuration, + FORMAT_12HOUR | FORMAT_SHOW_TIME /*| FORMAT_CAP_NOON*/)); + assertEquals("12:00 PM", + formatDateRange(en_US, tz, fixedTime + noonDuration, fixedTime + noonDuration, + FORMAT_12HOUR /*| FORMAT_NO_NOON*/ | FORMAT_SHOW_TIME)); + assertEquals("12:00 AM", formatDateRange(en_US, tz, fixedTime - midnightDuration, + fixedTime - midnightDuration, + FORMAT_12HOUR | FORMAT_SHOW_TIME /*| FORMAT_NO_MIDNIGHT*/)); + assertEquals("3:30 AM", + formatDateRange(en_US, tz, fixedTime, fixedTime, FORMAT_SHOW_TIME | FORMAT_UTC)); + assertEquals("3 AM", formatDateRange(en_US, tz, onTheHour, onTheHour, + FORMAT_SHOW_TIME | FORMAT_ABBREV_TIME)); + assertEquals("Mon", formatDateRange(en_US, tz, fixedTime, fixedTime + HOUR, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_WEEKDAY)); + assertEquals("Jan 19", + formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, + FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH)); + assertEquals("Jan 19", + formatDateRange(en_US, tz, timeWithCurrentYear, timeWithCurrentYear + HOUR, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + + assertEquals("1/19/2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * HOUR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("1/19/2009 – 1/22/2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("1/19/2009 – 4/22/2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("1/19/2009 – 2/9/2012", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + + assertEquals("19.1.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + HOUR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19.–22.01.2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19.01. – 22.04.2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19.01.2009 – 09.02.2012", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + + assertEquals("19/1/2009", formatDateRange(es_US, tz, fixedTime, fixedTime + HOUR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19/1/2009–22/1/2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19/1/2009–22/4/2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19/1/2009–9/2/2012", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + + assertEquals("19/1/2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + HOUR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19/1/2009–22/1/2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19/1/2009–22/4/2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + assertEquals("19/1/2009–9/2/2012", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE)); + + // These are some random other test cases I came up with. + + assertEquals("January 19 – 22, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, 0)); + assertEquals("Jan 19 – 22, 2009", formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("Mon, Jan 19 – Thu, Jan 22, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("Monday, January 19 – Thursday, January 22, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY)); + + assertEquals("January 19 – April 22, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, 0)); + assertEquals("Jan 19 – Apr 22, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("Mon, Jan 19 – Wed, Apr 22, 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("January – April 2009", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY)); + + assertEquals("Jan 19, 2009 – Feb 9, 2012", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("Jan 2009 – Feb 2012", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL)); + assertEquals("January 19, 2009 – February 9, 2012", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, 0)); + assertEquals("Monday, January 19, 2009 – Thursday, February 9, 2012", + formatDateRange(en_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY)); + + // The same tests but for de_DE. + + assertEquals("19.–22. Januar 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, 0)); + assertEquals("19.–22. Jan. 2009", formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("Mo., 19. – Do., 22. Jan. 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("Montag, 19. – Donnerstag, 22. Januar 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY)); + + assertEquals("19. Januar – 22. April 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, 0)); + assertEquals("19. Jan. – 22. Apr. 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("Mo., 19. Jan. – Mi., 22. Apr. 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("Januar–April 2009", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY)); + + assertEquals("19. Jan. 2009 – 9. Feb. 2012", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("Jan. 2009 – Feb. 2012", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL)); + assertEquals("19. Januar 2009 – 9. Februar 2012", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, 0)); + assertEquals("Montag, 19. Januar 2009 – Donnerstag, 9. Februar 2012", + formatDateRange(de_DE, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY)); + + // The same tests but for es_US. + + assertEquals("19–22 de enero de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0)); + assertEquals("19–22 de ene. de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("lun., 19 de ene. – jue., 22 de ene. de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY)); + + assertEquals("19 de enero–22 de abril de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0)); + assertEquals("19 de ene. – 22 de abr. 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("lun., 19 de ene. – mié., 22 de abr. de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("enero–abril de 2009", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY)); + + assertEquals("19 de ene. de 2009 – 9 de feb. de 2012", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("ene. de 2009 – feb. de 2012", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL)); + assertEquals("19 de enero de 2009–9 de febrero de 2012", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, 0)); + assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012", + formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY)); + + // The same tests but for es_ES. + + assertEquals("19–22 de enero de 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0)); + assertEquals("19–22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("lun., 19 ene. – jue., 22 ene. 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY)); + + assertEquals("19 de enero–22 de abril de 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0)); + assertEquals("19 ene. – 22 abr. 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("lun., 19 ene. – mié., 22 abr. 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, + FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); + assertEquals("enero–abril de 2009", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY)); + + assertEquals("19 ene. 2009 – 9 feb. 2012", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); + assertEquals("ene. 2009 – feb. 2012", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, + FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL)); + assertEquals("19 de enero de 2009–9 de febrero de 2012", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0)); + assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012", + formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY)); + } + + // http://b/8862241 - we should be able to format dates past 2038. + // See also http://code.google.com/p/android/issues/detail?id=13050. + @Test + public void test8862241() throws Exception { + ULocale l = ULocale.US; + TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); + Calendar c = Calendar.getInstance(tz, l); + c.clear(); + c.set(2042, Calendar.JANUARY, 19, 3, 30); + long jan_19_2042 = c.getTimeInMillis(); + c.set(2046, Calendar.OCTOBER, 4, 3, 30); + long oct_4_2046 = c.getTimeInMillis(); + int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL; + assertEquals("Jan 19, 2042 – Oct 4, 2046", + formatDateRange(l, tz, jan_19_2042, oct_4_2046, flags)); + } + + // http://b/10089890 - we should take the given time zone into account. + @Test + public void test10089890() throws Exception { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles"); + int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL | FORMAT_SHOW_TIME | FORMAT_24HOUR; + + // The Unix epoch is UTC, so 0 is 1970-01-01T00:00Z... + assertEquals("Jan 1, 1970, 00:00 – Jan 2, 1970, 00:00", + formatDateRange(l, utc, 0, DAY + 1, flags)); + // But MTV is hours behind, so 0 was still the afternoon of the previous day... + assertEquals("Dec 31, 1969, 16:00 – Jan 1, 1970, 16:00", + formatDateRange(l, pacific, 0, DAY, flags)); + } + + // http://b/10318326 - we can drop the minutes in a 12-hour time if they're zero, + // but not if we're using the 24-hour clock. That is: "4 PM" is reasonable, "16" is not. + @Test + public void test10318326() throws Exception { + long midnight = 0; + long teaTime = 16 * HOUR; + + int time12 = FORMAT_12HOUR | FORMAT_SHOW_TIME; + int time24 = FORMAT_24HOUR | FORMAT_SHOW_TIME; + int abbr12 = time12 | FORMAT_ABBREV_ALL; + int abbr24 = time24 | FORMAT_ABBREV_ALL; + + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + + // Full length on-the-hour times. + assertEquals("00:00", formatDateRange(l, utc, midnight, midnight, time24)); + assertEquals("12:00 AM", formatDateRange(l, utc, midnight, midnight, time12)); + assertEquals("16:00", formatDateRange(l, utc, teaTime, teaTime, time24)); + assertEquals("4:00 PM", formatDateRange(l, utc, teaTime, teaTime, time12)); + + // Abbreviated on-the-hour times. + assertEquals("00:00", formatDateRange(l, utc, midnight, midnight, abbr24)); + assertEquals("12 AM", formatDateRange(l, utc, midnight, midnight, abbr12)); + assertEquals("16:00", formatDateRange(l, utc, teaTime, teaTime, abbr24)); + assertEquals("4 PM", formatDateRange(l, utc, teaTime, teaTime, abbr12)); + + // Abbreviated on-the-hour ranges. + assertEquals("00:00 – 16:00", formatDateRange(l, utc, midnight, teaTime, abbr24)); + assertEquals("12 AM – 4 PM", formatDateRange(l, utc, midnight, teaTime, abbr12)); + + // Abbreviated mixed ranges. + assertEquals("00:00 – 16:01", formatDateRange(l, utc, midnight, teaTime + MINUTE, abbr24)); + assertEquals("12:00 AM – 4:01 PM", + formatDateRange(l, utc, midnight, teaTime + MINUTE, abbr12)); + } + + // http://b/10560853 - when the time is not displayed, an end time 0 ms into the next day is + // considered to belong to the previous day. + @Test + public void test10560853_when_time_not_displayed() throws Exception { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + + long midnight = 0; + long midnightNext = 1 * DAY; + + int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY; + + // An all-day event runs until 0 milliseconds into the next day, but is formatted as if it's + // just the first day. + assertEquals("Thursday, January 1, 1970", + formatDateRange(l, utc, midnight, midnightNext, flags)); + + // Run one millisecond over, though, and you're into the next day. + long nextMorning = 1 * DAY + 1; + assertEquals("Thursday, January 1 – Friday, January 2, 1970", + formatDateRange(l, utc, midnight, nextMorning, flags)); + + // But the same reasoning applies for that day. + long nextMidnight = 2 * DAY; + assertEquals("Thursday, January 1 – Friday, January 2, 1970", + formatDateRange(l, utc, midnight, nextMidnight, flags)); + } + + // http://b/10560853 - when the start and end times are otherwise on the same day, + // an end time 0 ms into the next day is considered to belong to the previous day. + @Test + public void test10560853_for_single_day_events() throws Exception { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + + int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE; + + assertEquals("January 1, 1970, 22:00 – 00:00", + formatDateRange(l, utc, 22 * HOUR, 24 * HOUR, flags)); + assertEquals("January 1, 1970, 22:00 – January 2, 1970, 00:30", + formatDateRange(l, utc, 22 * HOUR, 24 * HOUR + 30 * MINUTE, flags)); + } + + // The fix for http://b/10560853 didn't work except for the day around the epoch, which was + // all the unit test checked! + @Test + public void test_single_day_events_later_than_epoch() throws Exception { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + + int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE; + + Calendar c = Calendar.getInstance(utc, l); + c.clear(); + c.set(1980, Calendar.JANUARY, 1, 0, 0); + long jan_1_1980 = c.getTimeInMillis(); + assertEquals("January 1, 1980, 22:00 – 00:00", + formatDateRange(l, utc, jan_1_1980 + 22 * HOUR, jan_1_1980 + 24 * HOUR, flags)); + assertEquals("January 1, 1980, 22:00 – January 2, 1980, 00:30", + formatDateRange(l, utc, jan_1_1980 + 22 * HOUR, + jan_1_1980 + 24 * HOUR + 30 * MINUTE, flags)); + } + + // The fix for http://b/10560853 didn't work except for UTC, which was + // all the unit test checked! + @Test + public void test_single_day_events_not_in_UTC() throws Exception { + ULocale l = ULocale.US; + TimeZone pacific = TimeZone.getTimeZone("America/Los_Angeles"); + + int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR | FORMAT_SHOW_DATE; + + Calendar c = Calendar.getInstance(pacific, l); + c.clear(); + c.set(1980, Calendar.JANUARY, 1, 0, 0); + long jan_1_1980 = c.getTimeInMillis(); + assertEquals("January 1, 1980, 22:00 – 00:00", + formatDateRange(l, pacific, jan_1_1980 + 22 * HOUR, jan_1_1980 + 24 * HOUR, flags)); + + c.set(1980, Calendar.JULY, 1, 0, 0); + long jul_1_1980 = c.getTimeInMillis(); + assertEquals("July 1, 1980, 22:00 – 00:00", + formatDateRange(l, pacific, jul_1_1980 + 22 * HOUR, jul_1_1980 + 24 * HOUR, flags)); + } + + // http://b/10209343 - even if the caller didn't explicitly ask us to include the year, + // we should do so for years other than the current year. + @Test + public void test10209343_when_not_this_year() { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + + int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_TIME | FORMAT_24HOUR; + + assertEquals("Thursday, January 1, 1970, 00:00", formatDateRange(l, utc, 0L, 0L, flags)); + + long t1833 = ((long) Integer.MIN_VALUE + Integer.MIN_VALUE) * 1000L; + assertEquals("Sunday, November 24, 1833, 17:31", + formatDateRange(l, utc, t1833, t1833, flags)); + + long t1901 = Integer.MIN_VALUE * 1000L; + assertEquals("Friday, December 13, 1901, 20:45", + formatDateRange(l, utc, t1901, t1901, flags)); + + long t2038 = Integer.MAX_VALUE * 1000L; + assertEquals("Tuesday, January 19, 2038, 03:14", + formatDateRange(l, utc, t2038, t2038, flags)); + + long t2106 = (2L + Integer.MAX_VALUE + Integer.MAX_VALUE) * 1000L; + assertEquals("Sunday, February 7, 2106, 06:28", + formatDateRange(l, utc, t2106, t2106, flags)); + } + + // http://b/10209343 - for the current year, we should honor the FORMAT_SHOW_YEAR flags. + @Test + public void test10209343_when_this_year() { + // Construct a date in the current year (whenever the test happens to be run). + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + Calendar c = Calendar.getInstance(utc, l); + c.set(Calendar.MONTH, Calendar.FEBRUARY); + c.set(Calendar.DAY_OF_MONTH, 10); + c.set(Calendar.HOUR_OF_DAY, 0); + long thisYear = c.getTimeInMillis(); + + // You don't get the year if it's this year... + assertEquals("February 10", formatDateRange(l, utc, thisYear, thisYear, FORMAT_SHOW_DATE)); + + // ...unless you explicitly ask for it. + assertEquals(String.format("February 10, %d", c.get(Calendar.YEAR)), + formatDateRange(l, utc, thisYear, thisYear, FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR)); + + // ...or it's not actually this year... + Calendar c2 = (Calendar) c.clone(); + c2.set(Calendar.YEAR, 1980); + long oldYear = c2.getTimeInMillis(); + assertEquals("February 10, 1980", + formatDateRange(l, utc, oldYear, oldYear, FORMAT_SHOW_DATE)); + + // (But you can disable that!) + assertEquals("February 10", + formatDateRange(l, utc, oldYear, oldYear, FORMAT_SHOW_DATE | FORMAT_NO_YEAR)); + + // ...or the start and end years aren't the same... + assertEquals(String.format("February 10, 1980 – February 10, %d", c.get(Calendar.YEAR)), + formatDateRange(l, utc, oldYear, thisYear, FORMAT_SHOW_DATE)); + + // (And you can't avoid that --- icu4c steps in and overrides you.) + assertEquals(String.format("February 10, 1980 – February 10, %d", c.get(Calendar.YEAR)), + formatDateRange(l, utc, oldYear, thisYear, FORMAT_SHOW_DATE | FORMAT_NO_YEAR)); + } + + // http://b/8467515 - yet another y2k38 bug report. + @Test + public void test8467515() throws Exception { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH + | FORMAT_ABBREV_WEEKDAY; + long t; + + Calendar calendar = Calendar.getInstance(utc, l); + calendar.clear(); + + calendar.set(2038, Calendar.JANUARY, 19, 12, 0, 0); + t = calendar.getTimeInMillis(); + assertEquals("Tue, Jan 19, 2038", formatDateRange(l, utc, t, t, flags)); + + calendar.set(1900, Calendar.JANUARY, 1, 0, 0, 0); + t = calendar.getTimeInMillis(); + assertEquals("Mon, Jan 1, 1900", formatDateRange(l, utc, t, t, flags)); + } + + // http://b/12004664 + @Test + public void test12004664() throws Exception { + TimeZone utc = TimeZone.getTimeZone("UTC"); + Calendar c = Calendar.getInstance(utc, ULocale.US); + c.clear(); + c.set(Calendar.YEAR, 1980); + c.set(Calendar.MONTH, Calendar.FEBRUARY); + c.set(Calendar.DAY_OF_MONTH, 10); + c.set(Calendar.HOUR_OF_DAY, 0); + long thisYear = c.getTimeInMillis(); + + int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_YEAR; + assertEquals("Sunday, February 10, 1980", + formatDateRange(new ULocale("en", "US"), utc, thisYear, thisYear, flags)); + + // If we supported non-Gregorian calendars, this is what that we'd expect for these + // ULocales. + // This is really the correct behavior, but since java.util.Calendar currently only supports + // the Gregorian calendar, we want to deliberately force icu4c to agree, otherwise we'd have + // a mix of calendars throughout an app's UI depending on whether Java or native code + // formatted + // the date. + // assertEquals("یکشنبه ۲۱ بهمن ۱۳۵۸ ه.ش.", formatDateRange(new ULocale("fa"), utc, + // thisYear, thisYear, flags)); + // assertEquals("AP ۱۳۵۸ سلواغه ۲۱, یکشنبه", formatDateRange(new ULocale("ps"), utc, + // thisYear, thisYear, flags)); + // assertEquals("วันอาทิตย์ 10 กุมภาพันธ์ 2523", formatDateRange(new ULocale("th"), utc, + // thisYear, thisYear, flags)); + + // For now, here are the localized Gregorian strings instead... + assertEquals("یکشنبه ۱۰ فوریهٔ ۱۹۸۰", + formatDateRange(new ULocale("fa"), utc, thisYear, thisYear, flags)); + assertEquals("يونۍ د ۱۹۸۰ د فبروري ۱۰", + formatDateRange(new ULocale("ps"), utc, thisYear, thisYear, flags)); + assertEquals("วันอาทิตย์ที่ 10 กุมภาพันธ์ ค.ศ. 1980", + formatDateRange(new ULocale("th"), utc, thisYear, thisYear, flags)); + } + + // http://b/13234532 + @Test + public void test13234532() throws Exception { + ULocale l = ULocale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + + int flags = FORMAT_SHOW_TIME | FORMAT_ABBREV_ALL | FORMAT_12HOUR; + + assertEquals("10 – 11 AM", formatDateRange(l, utc, 10 * HOUR, 11 * HOUR, flags)); + assertEquals("11 AM – 1 PM", formatDateRange(l, utc, 11 * HOUR, 13 * HOUR, flags)); + assertEquals("2 – 3 PM", formatDateRange(l, utc, 14 * HOUR, 15 * HOUR, flags)); + } + + // http://b/20708022 + @Test + public void testEndOfDayOnLastDayOfMonth() throws Exception { + final ULocale locale = new ULocale("en"); + final TimeZone timeZone = TimeZone.getTimeZone("UTC"); + + assertEquals("11:00 PM – 12:00 AM", formatDateRange(locale, timeZone, + 1430434800000L, 1430438400000L, FORMAT_SHOW_TIME)); + } + + // http://b/68847519 + @Test + public void testEndAtMidnight_dateAndTime() { + BiFunction<Long, Long, String> fmt = (from, to) -> formatDateRange( + ENGLISH, GMT_ZONE, from, to, FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_24HOUR); + // If we're showing times and the end-point is midnight the following day, we want the + // behaviour of suppressing the date for the end... + assertEquals("February 27, 2007, 04:00 – 00:00", fmt.apply(1172548800000L, 1172620800000L)); + // ...unless the start-point is also midnight, in which case we need dates to disambiguate. + assertEquals("February 27, 2007, 00:00 – February 28, 2007, 00:00", + fmt.apply(1172534400000L, 1172620800000L)); + // We want to show the date if the end-point is a millisecond after midnight the following + // day, or if it is exactly midnight the day after that. + assertEquals("February 27, 2007, 04:00 – February 28, 2007, 00:00", + fmt.apply(1172548800000L, 1172620800001L)); + assertEquals("February 27, 2007, 04:00 – March 1, 2007, 00:00", + fmt.apply(1172548800000L, 1172707200000L)); + // We want to show the date if the start-point is anything less than a minute after + // midnight, + // since that gets displayed as midnight... + assertEquals("February 27, 2007, 00:00 – February 28, 2007, 00:00", + fmt.apply(1172534459999L, 1172620800000L)); + // ...but not if it is exactly one minute after midnight. + assertEquals("February 27, 2007, 00:01 – 00:00", fmt.apply(1172534460000L, 1172620800000L)); + } + + // http://b/68847519 + @Test + public void testEndAtMidnight_dateOnly() { + BiFunction<Long, Long, String> fmt = (from, to) -> formatDateRange( + ENGLISH, GMT_ZONE, from, to, FORMAT_SHOW_DATE); + // If we're only showing dates and the end-point is midnight of any day, we want the + // behaviour of showing an end date one earlier. So if the end-point is March 2, 2007 00:00, + // show March 1, 2007 instead (whether the start-point is midnight or not). + assertEquals("February 27 – March 1, 2007", fmt.apply(1172534400000L, 1172793600000L)); + assertEquals("February 27 – March 1, 2007", fmt.apply(1172548800000L, 1172793600000L)); + // We want to show the true date if the end-point is a millisecond after midnight. + assertEquals("February 27 – March 2, 2007", fmt.apply(1172534400000L, 1172793600001L)); + + // 2006-02-27 00:00:00.000 GMT - 2007-03-02 00:00:00.000 GMT + assertEquals("February 27, 2006 – March 1, 2007", + fmt.apply(1140998400000L, 1172793600000L)); + + // Spans a leap year's Feb 29th. + assertEquals("February 27 – March 1, 2004", fmt.apply(1077840000000L, 1078185600000L)); + } +} diff --git a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java index d9ba8fb81d3c..4b3b5735b4f3 100644 --- a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java +++ b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java @@ -16,11 +16,11 @@ package android.text.format; -import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_ALL; -import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_RELATIVE; -import static android.text.format.DateUtilsBridge.FORMAT_NO_YEAR; -import static android.text.format.DateUtilsBridge.FORMAT_NUMERIC_DATE; -import static android.text.format.DateUtilsBridge.FORMAT_SHOW_YEAR; +import static android.text.format.DateUtils.FORMAT_ABBREV_ALL; +import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE; +import static android.text.format.DateUtils.FORMAT_NO_YEAR; +import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE; +import static android.text.format.DateUtils.FORMAT_SHOW_YEAR; import static android.text.format.RelativeDateTimeFormatter.DAY_IN_MILLIS; import static android.text.format.RelativeDateTimeFormatter.HOUR_IN_MILLIS; import static android.text.format.RelativeDateTimeFormatter.MINUTE_IN_MILLIS; diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index 8699cb491dff..9b45d7f04e61 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -317,7 +317,7 @@ key 366 DVR # key 367 "KEY_MHP" # key 368 "KEY_LANGUAGE" # key 369 "KEY_TITLE" -# key 370 "KEY_SUBTITLE" +key 370 CAPTIONS # key 371 "KEY_ANGLE" # key 372 "KEY_ZOOM" # key 373 "KEY_MODE" @@ -352,7 +352,7 @@ key 397 CALENDAR key 402 CHANNEL_UP key 403 CHANNEL_DOWN # key 404 "KEY_FIRST" -# key 405 "KEY_LAST" +key 405 LAST_CHANNEL # key 406 "KEY_AB" # key 407 "KEY_NEXT" # key 408 "KEY_RESTART" diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp new file mode 100644 index 000000000000..77ef8dfb9725 --- /dev/null +++ b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp @@ -0,0 +1,46 @@ +// 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. + +cc_fuzz { + name: "resourcefile_fuzzer", + srcs: [ + "resourcefile_fuzzer.cpp", + ], + host_supported: true, + corpus: ["corpus/*"], + static_libs: ["libgmock"], + target: { + android: { + shared_libs:[ + "libandroidfw", + "libbase", + "libcutils", + "libutils", + "libziparchive", + "libui", + ], + }, + host: { + static_libs: [ + "libandroidfw", + "libbase", + "libcutils", + "libutils", + "libziparchive", + "liblog", + "libz", + ], + }, + }, +} diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/corpus/resources.arsc b/libs/androidfw/fuzz/resourcefile_fuzzer/corpus/resources.arsc Binary files differnew file mode 100644 index 000000000000..3cf2ea733d28 --- /dev/null +++ b/libs/androidfw/fuzz/resourcefile_fuzzer/corpus/resources.arsc diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp new file mode 100644 index 000000000000..96d44ab8e45c --- /dev/null +++ b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <string> +#include <memory> + +#include <androidfw/ApkAssets.h> +#include <androidfw/LoadedArsc.h> +#include <androidfw/StringPiece.h> + +#include <fuzzer/FuzzedDataProvider.h> + +using android::ApkAssets; +using android::LoadedArsc; +using android::StringPiece; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(StringPiece(reinterpret_cast<const char*>(data), size)); + + return 0; +}
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java index 0b6996365372..121f5492a5ab 100644 --- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java +++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java @@ -38,7 +38,10 @@ public class LicenseHtmlLoaderCompat extends AsyncLoaderCompat<File> { "/odm/etc/NOTICE.xml.gz", "/oem/etc/NOTICE.xml.gz", "/product/etc/NOTICE.xml.gz", - "/product_services/etc/NOTICE.xml.gz"}; + "/system_ext/etc/NOTICE.xml.gz", + "/vendor_dlkm/etc/NOTICE.xml.gz", + "/odm_dlkm/etc/NOTICE.xml.gz", + }; static final String NOTICE_HTML_FILE_NAME = "NOTICE.html"; private final Context mContext; diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 3b48c1d12f40..f247166d1a97 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -198,6 +198,9 @@ <!-- Permission needed to test tcp keepalive offload. --> <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" /> + <!-- Permission needed for CTS test - UnsupportedErrorDialogTests --> + <uses-permission android:name="android.permission.RESET_APP_ERRORS" /> + <!-- Permission needed to run keyguard manager tests in CTS --> <uses-permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS" /> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 371de7439f8f..3a42cd84369d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -23,6 +23,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.TypedArray; import android.graphics.Rect; +import android.icu.text.DateTimePatternGenerator; import android.os.Bundle; import android.os.Handler; import android.os.Parcelable; @@ -54,8 +55,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController.Configurati import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; -import libcore.icu.LocaleData; - import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; @@ -384,15 +383,16 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C private final CharSequence getSmallTime() { Context context = getContext(); boolean is24 = DateFormat.is24HourFormat(context, mCurrentUserId); - LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale); + DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance( + context.getResources().getConfiguration().locale); final char MAGIC1 = '\uEF00'; final char MAGIC2 = '\uEF01'; SimpleDateFormat sdf; String format = mShowSeconds - ? is24 ? d.timeFormat_Hms : d.timeFormat_hms - : is24 ? d.timeFormat_Hm : d.timeFormat_hm; + ? is24 ? dtpg.getBestPattern("Hms") : dtpg.getBestPattern("hms") + : is24 ? dtpg.getBestPattern("Hm") : dtpg.getBestPattern("hm"); if (!format.equals(mClockFormatString)) { mContentDescriptionFormat = new SimpleDateFormat(format); /* diff --git a/packages/WAPPushManager/AndroidManifest.xml b/packages/WAPPushManager/AndroidManifest.xml index 14e6e91e3a25..a75fb2d47bbd 100644 --- a/packages/WAPPushManager/AndroidManifest.xml +++ b/packages/WAPPushManager/AndroidManifest.xml @@ -23,6 +23,8 @@ <permission android:name="com.android.smspush.WAPPUSH_MANAGER_BIND" android:protectionLevel="signatureOrSystem" /> + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> + <original-package android:name="com.android.smspush" /> <application android:allowClearUserData="false"> diff --git a/services/Android.bp b/services/Android.bp index 581edceea1cb..a8c155c6fcbb 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -140,7 +140,14 @@ droidstubs { java_library { name: "android_system_server_stubs_current", + defaults: ["android_stubs_dists_default"], srcs: [":services-stubs.sources"], installable: false, static_libs: ["android_module_lib_stubs_current"], + sdk_version: "none", + system_modules: "none", + java_version: "1.8", + dist: { + dir: "apistubs/android/system-server", + }, } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 5f9d1d801441..25c8a3e6eafb 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -23,7 +23,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.hidl.manager.V1_0.IServiceManager; import android.os.Binder; -import android.os.Build; import android.os.Debug; import android.os.Handler; import android.os.IPowerManager; @@ -32,10 +31,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; -import android.system.ErrnoException; -import android.system.Os; -import android.system.OsConstants; -import android.system.StructRlimit; import android.util.EventLog; import android.util.Log; import android.util.Slog; @@ -48,13 +43,8 @@ import com.android.server.wm.SurfaceAnimationThread; import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -132,7 +122,6 @@ public class Watchdog extends Thread { int mPhonePid; IActivityController mController; boolean mAllowRestart = true; - final OpenFdMonitor mOpenFdMonitor; /** * Used for checking status of handle threads and scheduling monitor callbacks. @@ -337,8 +326,6 @@ public class Watchdog extends Thread { // Initialize monitor for Binder threads. addMonitor(new BinderThreadMonitor()); - mOpenFdMonitor = OpenFdMonitor.create(); - // See the notes on DEFAULT_TIMEOUT. assert DB || DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS; @@ -560,41 +547,31 @@ public class Watchdog extends Thread { timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start); } - boolean fdLimitTriggered = false; - if (mOpenFdMonitor != null) { - fdLimitTriggered = mOpenFdMonitor.monitor(); - } - - if (!fdLimitTriggered) { - final int waitState = evaluateCheckerCompletionLocked(); - if (waitState == COMPLETED) { - // The monitors have returned; reset - waitedHalf = false; - continue; - } else if (waitState == WAITING) { - // still waiting but within their configured intervals; back off and recheck - continue; - } else if (waitState == WAITED_HALF) { - if (!waitedHalf) { - Slog.i(TAG, "WAITED_HALF"); - // We've waited half the deadlock-detection interval. Pull a stack - // trace and wait another half. - ArrayList<Integer> pids = new ArrayList<Integer>(); - pids.add(Process.myPid()); - ActivityManagerService.dumpStackTraces(pids, null, null, + final int waitState = evaluateCheckerCompletionLocked(); + if (waitState == COMPLETED) { + // The monitors have returned; reset + waitedHalf = false; + continue; + } else if (waitState == WAITING) { + // still waiting but within their configured intervals; back off and recheck + continue; + } else if (waitState == WAITED_HALF) { + if (!waitedHalf) { + Slog.i(TAG, "WAITED_HALF"); + // We've waited half the deadlock-detection interval. Pull a stack + // trace and wait another half. + ArrayList<Integer> pids = new ArrayList<Integer>(); + pids.add(Process.myPid()); + ActivityManagerService.dumpStackTraces(pids, null, null, getInterestingNativePids()); - waitedHalf = true; - } - continue; + waitedHalf = true; } - - // something is overdue! - blockedCheckers = getBlockedCheckersLocked(); - subject = describeCheckersLocked(blockedCheckers); - } else { - blockedCheckers = Collections.emptyList(); - subject = "Open FD high water mark reached"; + continue; } + + // something is overdue! + blockedCheckers = getBlockedCheckersLocked(); + subject = describeCheckersLocked(blockedCheckers); allowRestart = mAllowRestart; } @@ -688,94 +665,4 @@ public class Watchdog extends Thread { Slog.w(TAG, "Failed to write to /proc/sysrq-trigger", e); } } - - public static final class OpenFdMonitor { - /** - * Number of FDs below the soft limit that we trigger a runtime restart at. This was - * chosen arbitrarily, but will need to be at least 6 in order to have a sufficient number - * of FDs in reserve to complete a dump. - */ - private static final int FD_HIGH_WATER_MARK = 12; - - private final File mDumpDir; - private final File mFdHighWaterMark; - - public static OpenFdMonitor create() { - // Only run the FD monitor on debuggable builds (such as userdebug and eng builds). - if (!Build.IS_DEBUGGABLE) { - return null; - } - - final StructRlimit rlimit; - try { - rlimit = android.system.Os.getrlimit(OsConstants.RLIMIT_NOFILE); - } catch (ErrnoException errno) { - Slog.w(TAG, "Error thrown from getrlimit(RLIMIT_NOFILE)", errno); - return null; - } - - // The assumption we're making here is that FD numbers are allocated (more or less) - // sequentially, which is currently (and historically) true since open is currently - // specified to always return the lowest-numbered non-open file descriptor for the - // current process. - // - // We do this to avoid having to enumerate the contents of /proc/self/fd in order to - // count the number of descriptors open in the process. - final File fdThreshold = new File("/proc/self/fd/" + (rlimit.rlim_cur - FD_HIGH_WATER_MARK)); - return new OpenFdMonitor(new File("/data/anr"), fdThreshold); - } - - OpenFdMonitor(File dumpDir, File fdThreshold) { - mDumpDir = dumpDir; - mFdHighWaterMark = fdThreshold; - } - - /** - * Dumps open file descriptors and their full paths to a temporary file in {@code mDumpDir}. - */ - private void dumpOpenDescriptors() { - // We cannot exec lsof to get more info about open file descriptors because a newly - // forked process will not have the permissions to readlink. Instead list all open - // descriptors from /proc/pid/fd and resolve them. - List<String> dumpInfo = new ArrayList<>(); - String fdDirPath = String.format("/proc/%d/fd/", Process.myPid()); - File[] fds = new File(fdDirPath).listFiles(); - if (fds == null) { - dumpInfo.add("Unable to list " + fdDirPath); - } else { - for (File f : fds) { - String fdSymLink = f.getAbsolutePath(); - String resolvedPath = ""; - try { - resolvedPath = Os.readlink(fdSymLink); - } catch (ErrnoException ex) { - resolvedPath = ex.getMessage(); - } - dumpInfo.add(fdSymLink + "\t" + resolvedPath); - } - } - - // Dump the fds & paths to a temp file. - try { - File dumpFile = File.createTempFile("anr_fd_", "", mDumpDir); - Path out = Paths.get(dumpFile.getAbsolutePath()); - Files.write(out, dumpInfo, StandardCharsets.UTF_8); - } catch (IOException ex) { - Slog.w(TAG, "Unable to write open descriptors to file: " + ex); - } - } - - /** - * @return {@code true} if the high water mark was breached and a dump was written, - * {@code false} otherwise. - */ - public boolean monitor() { - if (mFdHighWaterMark.exists()) { - dumpOpenDescriptors(); - return true; - } - - return false; - } - } } diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp index 17d0bbfad171..566e61e1a14f 100644 --- a/services/robotests/Android.bp +++ b/services/robotests/Android.bp @@ -43,6 +43,7 @@ android_robolectric_test { // Include the testing libraries libs: [ "platform-test-annotations", + "services.backup", "testng", ], diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index b75f9a15f66f..a605af4dbc34 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -3163,6 +3163,17 @@ public class CarrierConfigManager { "5g_icon_display_secondary_grace_period_string"; /** + * Whether device reset all of NR timers when device camped on a network that haven't 5G + * capability and RRC currently in IDLE state. + * + * The default value is false; + * + * @hide + */ + public static final String KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL = + "nr_timers_reset_if_non_endc_and_rrc_idle_bool"; + + /** * Controls time in milliseconds until DcTracker reevaluates 5G connection state. * @hide */ @@ -4230,6 +4241,7 @@ public class CarrierConfigManager { + "not_restricted_rrc_con:5G"); sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, ""); sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, ""); + sDefaults.putBoolean(KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL, false); /* Default value is 1 hour. */ sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000); sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_BOOL, false); |