1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.device.collectors; 17 18 import android.os.Bundle; 19 import androidx.annotation.VisibleForTesting; 20 import android.util.Log; 21 22 import org.junit.runner.Description; 23 import org.junit.runner.Result; 24 25 import java.util.Timer; 26 import java.util.TimerTask; 27 28 /** 29 * Implementation of {@link BaseMetricListener} that allows to run a periodic collection during the 30 * instrumentation run. Implementing {@link #collect(DataRecord, Description)} as the periodic task 31 * running. It is possible to run some actions before and at the end of the periodic run using 32 * {@link #onStart(DataRecord, Description)} and {@link #onEnd(DataRecord, Result)}. 33 */ 34 public abstract class ScheduledRunMetricListener extends BaseMetricListener { 35 36 public static final String INTERVAL_ARG_KEY = "interval"; 37 private static final long DEFAULT_INTERVAL_MS = 60 * 1000l; // 1 min 38 39 private Timer mTimer; 40 ScheduledRunMetricListener()41 public ScheduledRunMetricListener() {} 42 43 @VisibleForTesting ScheduledRunMetricListener(Bundle argsBundle)44 ScheduledRunMetricListener(Bundle argsBundle) { 45 super(argsBundle); 46 } 47 48 @Override onTestRunStart(final DataRecord runData, final Description description)49 public final void onTestRunStart(final DataRecord runData, final Description description) { 50 Log.d(getTag(), "Starting"); 51 onStart(runData, description); 52 // TODO: could this be done with Handlers ? 53 mTimer = new Timer(); 54 TimerTask timerTask = 55 new TimerTask() { 56 @Override 57 public void run() { 58 try { 59 collect(runData, description); 60 } catch (InterruptedException e) { 61 mTimer.cancel(); 62 Thread.currentThread().interrupt(); 63 Log.e(getTag(), "Interrupted exception thrown from task:", e); 64 } 65 } 66 }; 67 68 mTimer.scheduleAtFixedRate(timerTask, 0, getIntervalFromArgs()); 69 } 70 71 @Override onTestRunEnd(DataRecord runData, Result result)72 public final void onTestRunEnd(DataRecord runData, Result result) { 73 if (mTimer != null) { 74 mTimer.cancel(); 75 mTimer.purge(); 76 } 77 onEnd(runData, result); 78 Log.d(getTag(), "Finished"); 79 } 80 81 /** 82 * Executed when entering this collector. 83 * 84 * @param runData the {@link DataRecord} where to put metrics. 85 * @param description the {@link Description} for the test run. 86 */ onStart(DataRecord runData, Description description)87 void onStart(DataRecord runData, Description description) { 88 // Does nothing. 89 } 90 91 /** 92 * Executed when finishing this collector. 93 * 94 * @param runData the {@link DataRecord} where to put metrics. 95 * @param result the {@link Result} of the test run about to end. 96 */ onEnd(DataRecord runData, Result result)97 void onEnd(DataRecord runData, Result result) { 98 // Does nothing. 99 } 100 101 /** 102 * Task periodically & asynchronously run during the test running. 103 * 104 * @param runData the {@link DataRecord} where to put metrics. 105 * @param description the {@link Description} of the run in progress. 106 * @throws InterruptedException 107 */ collect(DataRecord runData, Description description)108 public abstract void collect(DataRecord runData, Description description) 109 throws InterruptedException; 110 111 /** 112 * Extract the interval from the instrumentation arguments or use the default interval value. 113 */ getIntervalFromArgs()114 private long getIntervalFromArgs() { 115 String intervalValue = getArgsBundle().getString(INTERVAL_ARG_KEY); 116 long interval = 0L; 117 try { 118 interval = Long.parseLong(intervalValue); 119 } catch (NumberFormatException e) { 120 Log.e(getTag(), "Failed to parse the interval value.", e); 121 } 122 // In case invalid or no interval was specified, we use the default one. 123 if (interval <= 0l) { 124 Log.d(getTag(), 125 String.format( 126 "Using default interval %s for periodic task. %s could not be used.", 127 DEFAULT_INTERVAL_MS, interval)); 128 interval = DEFAULT_INTERVAL_MS; 129 } 130 return interval; 131 } 132 } 133