Merge "Added StructStat nanosecond precision fields."
diff --git a/luni/src/main/java/android/system/StructStat.java b/luni/src/main/java/android/system/StructStat.java
index ce3548f6..a1e8729 100644
--- a/luni/src/main/java/android/system/StructStat.java
+++ b/luni/src/main/java/android/system/StructStat.java
@@ -53,15 +53,24 @@
    */
   public final long st_size; /*off_t*/
 
-  /** Time of last access. */
+  /** Seconds part of time of last access. */
   public final long st_atime; /*time_t*/
 
-  /** Time of last data modification. */
+  /** StructTimespec with time of last access. */
+  public final StructTimespec st_atim;
+
+  /** Seconds part of time of last data modification. */
   public final long st_mtime; /*time_t*/
 
-  /** Time of last status change. */
+  /** StructTimespec with time of last modification. */
+  public final StructTimespec st_mtim;
+
+  /** Seconds part of time of last status change */
   public final long st_ctime; /*time_t*/
 
+  /** StructTimespec with time of last status change. */
+  public final StructTimespec st_ctim;
+
   /**
    * A file system-specific preferred I/O block size for this object.
    * For some file system types, this may vary from file to file.
@@ -77,6 +86,17 @@
   public StructStat(long st_dev, long st_ino, int st_mode, long st_nlink, int st_uid, int st_gid,
                     long st_rdev, long st_size, long st_atime, long st_mtime, long st_ctime,
                     long st_blksize, long st_blocks) {
+    this(st_dev, st_ino, st_mode, st_nlink, st_uid, st_gid,
+        st_rdev, st_size, new StructTimespec(st_atime, 0L), new StructTimespec(st_mtime, 0L),
+        new StructTimespec(st_ctime, 0L), st_blksize, st_blocks);
+  }
+
+  /**
+   * Constructs an instance with the given field values.
+   */
+  public StructStat(long st_dev, long st_ino, int st_mode, long st_nlink, int st_uid, int st_gid,
+                    long st_rdev, long st_size, StructTimespec st_atim, StructTimespec st_mtim,
+                    StructTimespec st_ctim, long st_blksize, long st_blocks) {
     this.st_dev = st_dev;
     this.st_ino = st_ino;
     this.st_mode = st_mode;
@@ -85,9 +105,12 @@
     this.st_gid = st_gid;
     this.st_rdev = st_rdev;
     this.st_size = st_size;
-    this.st_atime = st_atime;
-    this.st_mtime = st_mtime;
-    this.st_ctime = st_ctime;
+    this.st_atime = st_atim.tv_sec;
+    this.st_mtime = st_mtim.tv_sec;
+    this.st_ctime = st_ctim.tv_sec;
+    this.st_atim = st_atim;
+    this.st_mtim = st_mtim;
+    this.st_ctim = st_ctim;
     this.st_blksize = st_blksize;
     this.st_blocks = st_blocks;
   }
diff --git a/luni/src/main/java/android/system/StructTimespec.java b/luni/src/main/java/android/system/StructTimespec.java
new file mode 100644
index 0000000..e999249
--- /dev/null
+++ b/luni/src/main/java/android/system/StructTimespec.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 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.system;
+
+import libcore.util.Objects;;
+
+/**
+ * Corresponds to C's {@code struct timespec} from {@code <time.h>}.
+ */
+public final class StructTimespec implements Comparable<StructTimespec> {
+  /** Seconds part of time of last data modification. */
+  public final long tv_sec; /*time_t*/
+
+  /** Nanoseconds (values are [0, 999999999]). */
+  public final long tv_nsec;
+
+  public StructTimespec(long tv_sec, long tv_nsec) {
+      this.tv_sec = tv_sec;
+      this.tv_nsec = tv_nsec;
+      if (tv_nsec < 0 || tv_nsec > 999_999_999) {
+          throw new IllegalArgumentException(
+                  "tv_nsec value " + tv_nsec + " is not in [0, 999999999]");
+      }
+  }
+
+  @Override
+  public int compareTo(StructTimespec other) {
+      if (tv_sec > other.tv_sec) {
+          return 1;
+      }
+      if (tv_sec < other.tv_sec) {
+          return -1;
+      }
+      if (tv_nsec > other.tv_nsec) {
+          return 1;
+      }
+      if (tv_nsec < other.tv_nsec) {
+          return -1;
+      }
+      return 0;
+  }
+
+  @Override
+  public String toString() {
+      return Objects.toString(this);
+  }
+}
diff --git a/luni/src/main/native/libcore_io_Linux.cpp b/luni/src/main/native/libcore_io_Linux.cpp
index 1e2f3a5..176300e 100644
--- a/luni/src/main/native/libcore_io_Linux.cpp
+++ b/luni/src/main/native/libcore_io_Linux.cpp
@@ -423,16 +423,23 @@
             pw_name, static_cast<jint>(pw.pw_uid), static_cast<jint>(pw.pw_gid), pw_dir, pw_shell);
 }
 
+static jobject makeStructTimespec(JNIEnv* env, const struct timespec& ts) {
+    static jmethodID ctor = env->GetMethodID(JniConstants::structTimespecClass, "<init>",
+            "(JJ)V");
+    return env->NewObject(JniConstants::structTimespecClass, ctor,
+            static_cast<jlong>(ts.tv_sec), static_cast<jlong>(ts.tv_nsec));
+}
+
 static jobject makeStructStat(JNIEnv* env, const struct stat64& sb) {
     static jmethodID ctor = env->GetMethodID(JniConstants::structStatClass, "<init>",
-            "(JJIJIIJJJJJJJ)V");
+            "(JJIJIIJJLandroid/system/StructTimespec;Landroid/system/StructTimespec;Landroid/system/StructTimespec;JJ)V");
     return env->NewObject(JniConstants::structStatClass, ctor,
             static_cast<jlong>(sb.st_dev), static_cast<jlong>(sb.st_ino),
             static_cast<jint>(sb.st_mode), static_cast<jlong>(sb.st_nlink),
             static_cast<jint>(sb.st_uid), static_cast<jint>(sb.st_gid),
             static_cast<jlong>(sb.st_rdev), static_cast<jlong>(sb.st_size),
-            static_cast<jlong>(sb.st_atime), static_cast<jlong>(sb.st_mtime),
-            static_cast<jlong>(sb.st_ctime), static_cast<jlong>(sb.st_blksize),
+            makeStructTimespec(env, sb.st_atim), makeStructTimespec(env, sb.st_mtim),
+            makeStructTimespec(env, sb.st_ctim), static_cast<jlong>(sb.st_blksize),
             static_cast<jlong>(sb.st_blocks));
 }
 
diff --git a/luni/src/test/java/libcore/io/OsTest.java b/luni/src/test/java/libcore/io/OsTest.java
index 07ce7d2..0f47d87 100644
--- a/luni/src/test/java/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/io/OsTest.java
@@ -20,6 +20,7 @@
 import android.system.NetlinkSocketAddress;
 import android.system.OsConstants;
 import android.system.PacketSocketAddress;
+import android.system.StructStat;
 import android.system.StructTimeval;
 import android.system.StructUcred;
 import android.system.UnixSocketAddress;
@@ -783,4 +784,23 @@
       assertEquals(srcSock.getLocalPort(), address.getPort());
     }
   }
+
+  public void test_fstat_times() throws Exception {
+    File file = File.createTempFile("OsTest", "fstattest");
+    FileOutputStream fos = new FileOutputStream(file);
+    StructStat structStat1 = Libcore.os.fstat(fos.getFD());
+    assertEquals(structStat1.st_mtim.tv_sec, structStat1.st_mtime);
+    assertEquals(structStat1.st_ctim.tv_sec, structStat1.st_ctime);
+    assertEquals(structStat1.st_atim.tv_sec, structStat1.st_atime);
+    Thread.sleep(100);
+    fos.write(new byte[]{1,2,3});
+    fos.flush();
+    StructStat structStat2 = Libcore.os.fstat(fos.getFD());
+    fos.close();
+
+    assertEquals(-1, structStat1.st_mtim.compareTo(structStat2.st_mtim));
+    assertEquals(-1, structStat1.st_ctim.compareTo(structStat2.st_ctim));
+    assertEquals(0, structStat1.st_atim.compareTo(structStat2.st_atim));
+  }
+
 }
diff --git a/non_openjdk_java_files.mk b/non_openjdk_java_files.mk
index ecd0ffa..29f8d0c 100644
--- a/non_openjdk_java_files.mk
+++ b/non_openjdk_java_files.mk
@@ -19,6 +19,7 @@
   luni/src/main/java/android/system/StructStat.java \
   luni/src/main/java/android/system/StructStatVfs.java \
   luni/src/main/java/android/system/StructTimeval.java \
+  luni/src/main/java/android/system/StructTimespec.java \
   luni/src/main/java/android/system/StructUcred.java \
   luni/src/main/java/android/system/StructUtsname.java \
   luni/src/main/java/android/util/MutableBoolean.java \