Patch submitted at https://bugzilla.gnome.org/show_bug.cgi?id=771304 diff -u -r ../glib-2.54.2/glib/gtimezone.c ./glib/gtimezone.c --- ../glib-2.54.2/glib/gtimezone.c 2017-07-14 01:03:39.000000000 +0200 +++ ./glib/gtimezone.c 2017-12-09 02:03:46.797229494 +0100 @@ -43,6 +43,13 @@ #include #endif +#ifdef __ANDROID__ +#include +#include +#include +#include +#endif + /** * SECTION:timezone * @title: GTimeZone @@ -392,7 +399,105 @@ gtz->transitions = NULL; } -#ifdef G_OS_UNIX +#ifdef __ANDROID__ +/* Android does not have /etc/localtime but uses a system property for the + the current timezone. There are no files under /usr/share/zoneinfo, + instead a single /system/usr/share/zoneinfo/tzdata which are all zoneinfo + files compiled together with the following tool: + https://android.googlesource.com/platform/external/icu/+/master/tools/ZoneCompactor.java */ +static GBytes* +zone_info_android (const gchar *identifier) +{ + struct tzdata_header + { + char version[12]; + uint32_t index_offset; + uint32_t data_offset; + uint32_t zonetab_offset; + } __attribute__((packed)) header; + + struct tzdata_index_entry + { + char name[40]; + uint32_t offset; + uint32_t length; + uint32_t unused; + } __attribute__((packed)) entry; + + char sys_timezone[PROP_VALUE_MAX]; + + if (identifier == NULL) + { + if (__system_property_get("persist.sys.timezone", sys_timezone) < 1) + { + g_warning("__system_property_get(\"persist.sys.timezone\") failed\n"); + return NULL; + } + identifier = sys_timezone; + } + + int tzdata_fd = open("/system/usr/share/zoneinfo/tzdata", O_RDONLY); + if (tzdata_fd < 0) + { + g_warning("Failed opening tzdata"); + return NULL; + } + + if (read(tzdata_fd, &header, sizeof(header)) < (ssize_t) sizeof(header)) + { + g_warning("Failed reading tzdata header"); + goto error; + } + + header.index_offset = htonl(header.index_offset); + header.data_offset = htonl(header.data_offset); + + uint32_t current_offset = header.index_offset; + while (current_offset < header.data_offset) + { + if (read(tzdata_fd, &entry, sizeof(entry)) < (ssize_t) sizeof(entry)) + { + g_warning("Failed reading tzdata index entry"); + goto error; + } + + if (strcmp(entry.name, identifier) == 0) + { + entry.offset = htonl(entry.offset); + entry.length = htonl(entry.length); + if (entry.length == 0) + { + g_warning("Invalid tzdata entry with length zero"); + goto error; + } + + if (lseek(tzdata_fd, header.data_offset + entry.offset, SEEK_SET) == -1) + { + g_warning("Failed seeking to tzdata entry"); + goto error; + } + + guint8* data = g_malloc(entry.length); + if (read(tzdata_fd, data, entry.length) < entry.length) + { + g_warning("Failed reading tzdata entry"); + g_free(data); + goto error; + } + + close(tzdata_fd); + return g_bytes_new_take(data, entry.length); + } + } + +error: + close(tzdata_fd); + + return NULL; +} + +#elif defined(G_OS_UNIX) + static GBytes* zone_info_unix (const gchar *identifier) { @@ -436,6 +541,10 @@ return zoneinfo; } +#endif + +#ifdef G_OS_UNIX + static void init_zone_from_iana_info (GTimeZone *gtz, GBytes *zoneinfo) { @@ -1387,7 +1496,11 @@ if (tz->t_info == NULL) { #ifdef G_OS_UNIX +# ifdef __ANDROID__ + GBytes *zoneinfo = zone_info_android (identifier); +# else GBytes *zoneinfo = zone_info_unix (identifier); +# endif if (!zoneinfo) zone_for_constant_offset (tz, "UTC"); else