Snap for 12610777 from 199004e0d0c1c537175d3238f6a42a4016a405ef to mainline-tethering-release

Change-Id: I43489b4d2a729a4047f3955be35ee9048b02a368
diff --git a/service/java/com/android/os/profiling/DeviceConfigHelper.java b/service/java/com/android/os/profiling/DeviceConfigHelper.java
index f29de40..6a52825 100644
--- a/service/java/com/android/os/profiling/DeviceConfigHelper.java
+++ b/service/java/com/android/os/profiling/DeviceConfigHelper.java
@@ -133,6 +133,12 @@
     public static final String CLEAR_TEMPORARY_DIRECTORY_BOOT_DELAY_MS =
             "clear_temporary_directory_boot_delay_ms";
 
+    // System triggered run configs
+    public static final String SYSTEM_TRIGGERED_TRACE_MIN_PERIOD_SECONDS =
+            "system_triggered_trace_min_period_seconds";
+    public static final String SYSTEM_TRIGGERED_TRACE_MAX_PERIOD_SECONDS =
+            "system_triggered_trace_max_period_seconds";
+
     // Post Processing Configs
     public static final String PROFILING_RECHECK_DELAY_MS = "profiling_recheck_delay_ms";
 
diff --git a/service/java/com/android/os/profiling/ProfilingService.java b/service/java/com/android/os/profiling/ProfilingService.java
index e760e6a..7e1feb5 100644
--- a/service/java/com/android/os/profiling/ProfilingService.java
+++ b/service/java/com/android/os/profiling/ProfilingService.java
@@ -60,8 +60,12 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
+import java.util.Random;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
@@ -115,6 +119,10 @@
 
     private static final int PERSIST_TO_DISK_DEFAULT_FREQUENCY_MS = 30 * 60 * 1000;
 
+    // Targeting a period of around 24 hours, so set max and min to 24 +/- 6 hours, respectively.
+    private static final int DEFAULT_SYSTEM_TRIGGERED_TRACE_MIN_PERIOD_SECONDS = 18 * 60 * 60;
+    private static final int DEFAULT_SYSTEM_TRIGGERED_TRACE_MAX_PERIOD_SECONDS = 30 * 60 * 60;
+
     private final Context mContext;
     private final Object mLock = new Object();
     private final HandlerThread mHandlerThread = new HandlerThread("ProfilingService");
@@ -192,6 +200,18 @@
     @GuardedBy("mLock")
     private boolean mKeepUnredactedTrace = false;
 
+    /** Executor for scheduling system triggered profiling trace. */
+    private ScheduledExecutorService mScheduledExecutorService = null;
+
+    /** Future for the start system triggered trace. */
+    @VisibleForTesting
+    public ScheduledFuture<?> mStartSystemTriggeredTraceScheduledFuture = null;
+
+    @GuardedBy("mLock")
+    private AtomicInteger mSystemTriggeredTraceMinPeriodSeconds;
+    @GuardedBy("mLock")
+    private AtomicInteger mSystemTriggeredTraceMaxPeriodSeconds;
+
     /**
      * Package name of app being tested, or null if no app is being tested. To be used both for
      * automated testing and developer manual testing.
@@ -310,6 +330,14 @@
             mPersistFrequencyMs = new AtomicInteger(DeviceConfigHelper.getInt(
                     DeviceConfigHelper.PERSIST_TO_DISK_FREQUENCY_MS,
                     PERSIST_TO_DISK_DEFAULT_FREQUENCY_MS));
+
+            mSystemTriggeredTraceMinPeriodSeconds = new AtomicInteger(DeviceConfigHelper.getInt(
+                    DeviceConfigHelper.SYSTEM_TRIGGERED_TRACE_MIN_PERIOD_SECONDS,
+                    DEFAULT_SYSTEM_TRIGGERED_TRACE_MIN_PERIOD_SECONDS));
+
+            mSystemTriggeredTraceMaxPeriodSeconds = new AtomicInteger(DeviceConfigHelper.getInt(
+                    DeviceConfigHelper.SYSTEM_TRIGGERED_TRACE_MAX_PERIOD_SECONDS,
+                    DEFAULT_SYSTEM_TRIGGERED_TRACE_MAX_PERIOD_SECONDS));
         }
         // Now subscribe to updates on test config.
         DeviceConfig.addOnPropertiesChangedListener(DeviceConfigHelper.NAMESPACE_TESTING,
@@ -369,15 +397,24 @@
                             mPersistFrequencyMs.set(properties.getInt(
                                     DeviceConfigHelper.PERSIST_TO_DISK_FREQUENCY_MS,
                                     mPersistFrequencyMs.get()));
+
+                            mSystemTriggeredTraceMinPeriodSeconds.set(DeviceConfigHelper.getInt(
+                                    DeviceConfigHelper.SYSTEM_TRIGGERED_TRACE_MIN_PERIOD_SECONDS,
+                                    mSystemTriggeredTraceMinPeriodSeconds.get()));
+
+                            mSystemTriggeredTraceMaxPeriodSeconds.set(DeviceConfigHelper.getInt(
+                                    DeviceConfigHelper.SYSTEM_TRIGGERED_TRACE_MAX_PERIOD_SECONDS,
+                                    mSystemTriggeredTraceMaxPeriodSeconds.get()));
                         }
                     }
                 });
 
-        // Schedule initial storage cleanup after delay so as not to increase non-critical work
-        // during boot.
+        // Schedule initial storage cleanup and system triggered trace start after a delay so as not
+        // to increase non-critical or work during boot.
         getHandler().postDelayed(new Runnable() {
             @Override
             public void run() {
+                scheduleNextSystemTriggeredTraceStart();
                 maybeCleanupTemporaryDirectory();
             }
         }, mClearTemporaryDirectoryBootDelayMs);
@@ -627,6 +664,58 @@
     }
 
     /**
+     * Schedule the next start of system triggered profiling trace for a random time between min and
+     * max period.
+     */
+    @VisibleForTesting
+    public void scheduleNextSystemTriggeredTraceStart() {
+        if (!Flags.systemTriggeredProfilingNew()) {
+            // Feature disabled.
+            return;
+        }
+
+        if (mStartSystemTriggeredTraceScheduledFuture != null) {
+            // If an existing start is already scheduled, don't schedule another.
+            // This should not happen.
+            Log.e(TAG, "Attempted to schedule a system triggered trace start with one already "
+                    + "scheduled.");
+            return;
+        }
+
+        if (mScheduledExecutorService == null) {
+            mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
+        }
+
+        synchronized (mLock) {
+            // It's important that trace doesn't always run at the same time as this will bias the
+            // results, so grab a random number between min and max.
+            int scheduledDelaySeconds = mSystemTriggeredTraceMinPeriodSeconds.get()
+                    + (new Random()).nextInt(mSystemTriggeredTraceMaxPeriodSeconds.get()
+                    - mSystemTriggeredTraceMinPeriodSeconds.get());
+
+            if (DEBUG) {
+                Log.d(TAG, String.format("System triggered trace scheduled in %d seconds for params"
+                        + " min %d and max %d seconds.",
+                        scheduledDelaySeconds,
+                        mSystemTriggeredTraceMinPeriodSeconds.get(),
+                        mSystemTriggeredTraceMaxPeriodSeconds.get()));
+            }
+
+            mStartSystemTriggeredTraceScheduledFuture = mScheduledExecutorService.schedule(() -> {
+                // Start the system triggered trace.
+                startSystemTriggeredTrace();
+
+                mStartSystemTriggeredTraceScheduledFuture = null;
+
+                // In all cases, schedule again. Feature flagged off is handled earlier in this
+                // method, and all return cases in {@link #startSystemTriggeredTrace} should result
+                // in trying again at the next regularly scheduled time.
+                scheduleNextSystemTriggeredTraceStart();
+            }, scheduledDelaySeconds, TimeUnit.SECONDS);
+        }
+    }
+
+    /**
      * This is the core method that keeps the profiling flow moving.
      *
      * This is the only way that state should be set. Do not use {@link TracingSession#setState}
@@ -1374,7 +1463,8 @@
     }
 
     /** Start a trace to be used for system triggered profiling. */
-    private void startSystemTriggeredTrace() {
+    @VisibleForTesting
+    public void startSystemTriggeredTrace() {
         if (!Flags.systemTriggeredProfilingNew()) {
             // Flag disabled.
             return;
diff --git a/tests/cts/src/android/profiling/cts/ProfilingServiceTests.java b/tests/cts/src/android/profiling/cts/ProfilingServiceTests.java
index 77bb9f3..a659faf 100644
--- a/tests/cts/src/android/profiling/cts/ProfilingServiceTests.java
+++ b/tests/cts/src/android/profiling/cts/ProfilingServiceTests.java
@@ -75,6 +75,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Tests in this class are for testing the ProfilingService directly without the need to get a
@@ -1879,6 +1880,46 @@
         assertEquals(0, newTriggerTime);
     }
 
+    /**
+     * Test that scheduling for system triggered profiling trace start works correctly, configuring
+     * run delay for correct amount of time.
+     */
+    @Test
+    @EnableFlags(android.os.profiling.Flags.FLAG_SYSTEM_TRIGGERED_PROFILING_NEW)
+    public void testSystemTriggeredProfiling_Scheduling() throws Exception {
+        // Override system triggered trace start values so that the trace will be attempted to be
+        // started within the test duration
+        executeShellCmd(OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE,
+                DeviceConfigHelper.SYSTEM_TRIGGERED_TRACE_MIN_PERIOD_SECONDS, 3);
+        updateDeviceConfigAndWaitForChange(DeviceConfigHelper.NAMESPACE,
+                DeviceConfigHelper.SYSTEM_TRIGGERED_TRACE_MAX_PERIOD_SECONDS, 4);
+
+        // Cancel the already scheduled future and set to null, if applicable.
+        if (mProfilingService.mStartSystemTriggeredTraceScheduledFuture != null) {
+            mProfilingService.mStartSystemTriggeredTraceScheduledFuture.cancel(true);
+            mProfilingService.mStartSystemTriggeredTraceScheduledFuture = null;
+        }
+
+        // Schedule a start of system triggered trace.
+        mProfilingService.scheduleNextSystemTriggeredTraceStart();
+
+        // Confirm the future is scheduled and that an attempt to start the trace has not occurred
+        // yet.
+        assertNotNull(mProfilingService.mStartSystemTriggeredTraceScheduledFuture);
+        assertFalse(mProfilingService.mStartSystemTriggeredTraceScheduledFuture.isDone());
+        verify(mProfilingService, times(0)).startSystemTriggeredTrace();
+
+        // Wait for 1 second longer than the scheduled future delay so that the future can execute.
+        long delay = mProfilingService.mStartSystemTriggeredTraceScheduledFuture.getDelay(
+                TimeUnit.SECONDS);
+        sleep((delay + 1L) * 1000L);
+
+        // Finally, confirm that the future ran by confirming that an attempt to start the trace was
+        // made. We don't confirm that it actually started as we can't actually start the trace from
+        // this context.
+        verify(mProfilingService, times(1)).startSystemTriggeredTrace();
+    }
+
     private File createAndConfirmFileExists(File directory, String fileName) throws Exception {
         File file = new File(directory, fileName);
         file.createNewFile();