Merge "Command-line interface: NetworkTimeUpdateService" am: 8ac370e63f am: 7b559562f7 am: f12a783920

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2002571

Change-Id: I4c7d0a18bece446ccc828acfd3e70d91d74e902e
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 8604078..40beab3 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -265,6 +265,13 @@
         return mTimeResult;
     }
 
+    /** Clears the last received NTP. Intended for use during tests. */
+    public void clearCachedTimeResult() {
+        synchronized (this) {
+            mTimeResult = null;
+        }
+    }
+
     private static class NtpConnectionInfo {
 
         @NonNull private final String mServer;
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index fcde533..1e534b7 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -36,12 +36,15 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
 import android.os.SystemClock;
 import android.os.TimestampedValue;
 import android.provider.Settings;
 import android.util.LocalLog;
 import android.util.Log;
 import android.util.NtpTrustedTime;
+import android.util.NtpTrustedTime.TimeResult;
 import android.util.TimeUtils;
 
 import com.android.internal.util.DumpUtils;
@@ -152,6 +155,42 @@
                 }, new IntentFilter(ACTION_POLL));
     }
 
+    /**
+     * Clears the cached NTP time. For use during tests to simulate when no NTP time is available.
+     *
+     * <p>This operation takes place in the calling thread rather than the service's handler thread.
+     */
+    void clearTimeForTests() {
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.SET_TIME, "clear latest network time");
+
+        mTime.clearCachedTimeResult();
+
+        mLocalLog.log("clearTimeForTests");
+    }
+
+    /**
+     * Forces the service to refresh the NTP time.
+     *
+     * <p>This operation takes place in the calling thread rather than the service's handler thread.
+     * This method does not affect currently scheduled refreshes. If the NTP request is successful
+     * it will make an (asynchronously handled) suggestion to the time detector.
+     */
+    boolean forceRefreshForTests() {
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.SET_TIME, "force network time refresh");
+
+        boolean success = mTime.forceRefresh();
+        mLocalLog.log("forceRefreshForTests: success=" + success);
+
+        if (success) {
+            makeNetworkTimeSuggestion(mTime.getCachedTimeResult(),
+                    "Origin: NetworkTimeUpdateService: forceRefreshForTests");
+        }
+
+        return success;
+    }
+
     private void onPollNetworkTime(int event) {
         // If we don't have any default network, don't bother.
         if (mDefaultNetwork == null) return;
@@ -193,12 +232,8 @@
             resetAlarm(mPollingIntervalMs
                     - cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis));
 
-            // Suggest the time to the time detector. It may choose use it to set the system clock.
-            TimestampedValue<Long> timeSignal = new TimestampedValue<>(
-                    cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis());
-            NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
-            timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateService. event=" + event);
-            mTimeDetector.suggestNetworkTime(timeSuggestion);
+            makeNetworkTimeSuggestion(cachedNtpResult,
+                    "Origin: NetworkTimeUpdateService. event=" + event);
         } else {
             // No fresh fix; schedule retry
             mTryAgainCounter++;
@@ -217,6 +252,15 @@
         }
     }
 
+    /** Suggests the time to the time detector. It may choose use it to set the system clock. */
+    private void makeNetworkTimeSuggestion(TimeResult ntpResult, String debugInfo) {
+        TimestampedValue<Long> timeSignal = new TimestampedValue<>(
+                ntpResult.getElapsedRealtimeMillis(), ntpResult.getTimeMillis());
+        NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
+        timeSuggestion.addDebugInfo(debugInfo);
+        mTimeDetector.suggestNetworkTime(timeSuggestion);
+    }
+
     /**
      * Cancel old alarm and starts a new one for the specified interval.
      *
@@ -320,4 +364,11 @@
         mLocalLog.dump(fd, pw, args);
         pw.println();
     }
+
+    @Override
+    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+        new NetworkTimeUpdateServiceShellCommand(this).exec(
+                this, in, out, err, args, callback, resultReceiver);
+    }
 }
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java
new file mode 100644
index 0000000..dc93023
--- /dev/null
+++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/** Implements the shell command interface for {@link NetworkTimeUpdateService}. */
+class NetworkTimeUpdateServiceShellCommand extends ShellCommand {
+
+    /**
+     * The name of the service.
+     */
+    private static final String SHELL_COMMAND_SERVICE_NAME = "network_time_update_service";
+
+    /**
+     * A shell command that clears the time signal received from the network.
+     */
+    private static final String SHELL_COMMAND_CLEAR_TIME = "clear_time";
+
+    /**
+     * A shell command that forces the time signal to be refreshed from the network.
+     */
+    private static final String SHELL_COMMAND_FORCE_REFRESH = "force_refresh";
+
+    @NonNull
+    private final NetworkTimeUpdateService mNetworkTimeUpdateService;
+
+    NetworkTimeUpdateServiceShellCommand(NetworkTimeUpdateService networkTimeUpdateService) {
+        mNetworkTimeUpdateService = Objects.requireNonNull(networkTimeUpdateService);
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+
+        switch (cmd) {
+            case SHELL_COMMAND_CLEAR_TIME:
+                return runClearTime();
+            case SHELL_COMMAND_FORCE_REFRESH:
+                return runForceRefresh();
+            default: {
+                return handleDefaultCommands(cmd);
+            }
+        }
+    }
+
+    private int runClearTime() {
+        mNetworkTimeUpdateService.clearTimeForTests();
+        return 0;
+    }
+
+    private int runForceRefresh() {
+        boolean success = mNetworkTimeUpdateService.forceRefreshForTests();
+        getOutPrintWriter().println(success);
+        return 0;
+    }
+
+    @Override
+    public void onHelp() {
+        final PrintWriter pw = getOutPrintWriter();
+        pw.printf("Network Time Update Service (%s) commands:\n", SHELL_COMMAND_SERVICE_NAME);
+        pw.printf("  help\n");
+        pw.printf("    Print this help text.\n");
+        pw.printf("  %s\n", SHELL_COMMAND_CLEAR_TIME);
+        pw.printf("    Clears the latest time.\n");
+        pw.printf("  %s\n", SHELL_COMMAND_FORCE_REFRESH);
+        pw.printf("    Refreshes the latest time. Prints whether it was successful.\n");
+        pw.println();
+    }
+}