Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 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 com.android.internal.os.statsd.libstats; |
| 17 | |
| 18 | import static com.google.common.truth.Truth.assertThat; |
| 19 | |
| 20 | import android.app.StatsManager; |
| 21 | import android.content.Context; |
| 22 | import android.util.Log; |
| 23 | import android.util.StatsLog; |
| 24 | |
| 25 | import androidx.test.InstrumentationRegistry; |
| 26 | import androidx.test.filters.MediumTest; |
| 27 | import androidx.test.runner.AndroidJUnit4; |
| 28 | |
| 29 | import com.android.internal.os.StatsdConfigProto.AtomMatcher; |
| 30 | import com.android.internal.os.StatsdConfigProto.FieldFilter; |
| 31 | import com.android.internal.os.StatsdConfigProto.GaugeMetric; |
Tej Singh | dea94cd | 2020-03-26 22:20:19 -0700 | [diff] [blame] | 32 | import com.android.internal.os.StatsdConfigProto.PullAtomPackages; |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 33 | import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher; |
| 34 | import com.android.internal.os.StatsdConfigProto.StatsdConfig; |
| 35 | import com.android.internal.os.StatsdConfigProto.TimeUnit; |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 36 | import com.android.internal.os.statsd.protos.TestAtoms; |
| 37 | import com.android.os.AtomsProto.Atom; |
| 38 | |
| 39 | import org.junit.After; |
| 40 | import org.junit.Before; |
| 41 | import org.junit.Test; |
| 42 | import org.junit.runner.RunWith; |
| 43 | |
| 44 | import java.util.List; |
| 45 | |
| 46 | /** |
| 47 | * Test puller registration. |
| 48 | */ |
| 49 | @MediumTest |
| 50 | @RunWith(AndroidJUnit4.class) |
| 51 | public class LibStatsPullTests { |
| 52 | private static final String LOG_TAG = LibStatsPullTests.class.getSimpleName(); |
| 53 | private static final int SHORT_SLEEP_MILLIS = 250; |
| 54 | private static final int LONG_SLEEP_MILLIS = 1_000; |
| 55 | private Context mContext; |
| 56 | private static final int PULL_ATOM_TAG = 150030; |
| 57 | private static final int APP_BREADCRUMB_LABEL = 3; |
| 58 | private static int sPullReturnValue; |
| 59 | private static long sConfigId; |
| 60 | private static long sPullLatencyMillis; |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 61 | private static long sPullTimeoutMillis; |
| 62 | private static long sCoolDownMillis; |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 63 | private static int sAtomsPerPull; |
| 64 | |
| 65 | static { |
| 66 | System.loadLibrary("statspull_testhelper"); |
| 67 | } |
| 68 | |
| 69 | /** |
| 70 | * Setup the tests. Initialize shared data. |
| 71 | */ |
| 72 | @Before |
| 73 | public void setup() { |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 74 | mContext = InstrumentationRegistry.getTargetContext(); |
| 75 | assertThat(InstrumentationRegistry.getInstrumentation()).isNotNull(); |
| 76 | sPullReturnValue = StatsManager.PULL_SUCCESS; |
| 77 | sPullLatencyMillis = 0; |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 78 | sPullTimeoutMillis = 10_000L; |
| 79 | sCoolDownMillis = 1_000L; |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 80 | sAtomsPerPull = 1; |
| 81 | } |
| 82 | |
| 83 | /** |
| 84 | * Teardown the tests. |
| 85 | */ |
| 86 | @After |
| 87 | public void tearDown() throws Exception { |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 88 | clearStatsPuller(PULL_ATOM_TAG); |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 89 | StatsManager statsManager = (StatsManager) mContext.getSystemService( |
| 90 | Context.STATS_MANAGER); |
| 91 | statsManager.removeConfig(sConfigId); |
| 92 | } |
| 93 | |
| 94 | /** |
| 95 | * Tests adding a puller callback and that pulls complete successfully. |
| 96 | */ |
| 97 | @Test |
| 98 | public void testPullAtomCallbackRegistration() throws Exception { |
| 99 | StatsManager statsManager = (StatsManager) mContext.getSystemService( |
| 100 | Context.STATS_MANAGER); |
| 101 | // Upload a config that captures that pulled atom. |
| 102 | createAndAddConfigToStatsd(statsManager); |
| 103 | |
| 104 | // Add the puller. |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 105 | setStatsPuller(PULL_ATOM_TAG, sPullTimeoutMillis, sCoolDownMillis, sPullReturnValue, |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 106 | sPullLatencyMillis, sAtomsPerPull); |
| 107 | Thread.sleep(SHORT_SLEEP_MILLIS); |
| 108 | StatsLog.logStart(APP_BREADCRUMB_LABEL); |
| 109 | // Let the current bucket finish. |
| 110 | Thread.sleep(LONG_SLEEP_MILLIS); |
| 111 | List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId); |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 112 | clearStatsPuller(PULL_ATOM_TAG); |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 113 | assertThat(data.size()).isEqualTo(1); |
| 114 | TestAtoms.PullCallbackAtomWrapper atomWrapper = null; |
| 115 | try { |
| 116 | atomWrapper = TestAtoms.PullCallbackAtomWrapper.parser() |
| 117 | .parseFrom(data.get(0).toByteArray()); |
| 118 | } catch (Exception e) { |
| 119 | Log.e(LOG_TAG, "Failed to parse primitive atoms"); |
| 120 | } |
| 121 | assertThat(atomWrapper).isNotNull(); |
| 122 | assertThat(atomWrapper.hasPullCallbackAtom()).isTrue(); |
| 123 | TestAtoms.PullCallbackAtom atom = |
| 124 | atomWrapper.getPullCallbackAtom(); |
| 125 | assertThat(atom.getLongVal()).isEqualTo(1); |
| 126 | } |
| 127 | |
| 128 | /** |
| 129 | * Tests that a failed pull is skipped. |
| 130 | */ |
| 131 | @Test |
| 132 | public void testPullAtomCallbackFailure() throws Exception { |
| 133 | StatsManager statsManager = (StatsManager) mContext.getSystemService( |
| 134 | Context.STATS_MANAGER); |
| 135 | createAndAddConfigToStatsd(statsManager); |
| 136 | sPullReturnValue = StatsManager.PULL_SKIP; |
| 137 | // Add the puller. |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 138 | setStatsPuller(PULL_ATOM_TAG, sPullTimeoutMillis, sCoolDownMillis, sPullReturnValue, |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 139 | sPullLatencyMillis, sAtomsPerPull); |
| 140 | Thread.sleep(SHORT_SLEEP_MILLIS); |
| 141 | StatsLog.logStart(APP_BREADCRUMB_LABEL); |
| 142 | // Let the current bucket finish. |
| 143 | Thread.sleep(LONG_SLEEP_MILLIS); |
| 144 | List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId); |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 145 | clearStatsPuller(PULL_ATOM_TAG); |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 146 | assertThat(data.size()).isEqualTo(0); |
| 147 | } |
| 148 | |
| 149 | /** |
| 150 | * Tests that a pull that times out is skipped. |
| 151 | */ |
| 152 | @Test |
| 153 | public void testPullAtomCallbackTimeout() throws Exception { |
| 154 | StatsManager statsManager = (StatsManager) mContext.getSystemService( |
| 155 | Context.STATS_MANAGER); |
| 156 | createAndAddConfigToStatsd(statsManager); |
| 157 | // The puller will sleep for 1.5 sec. |
| 158 | sPullLatencyMillis = 1_500; |
| 159 | // 1 second timeout |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 160 | sPullTimeoutMillis = 1_000; |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 161 | |
| 162 | // Add the puller. |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 163 | setStatsPuller(PULL_ATOM_TAG, sPullTimeoutMillis, sCoolDownMillis, sPullReturnValue, |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 164 | sPullLatencyMillis, sAtomsPerPull); |
| 165 | Thread.sleep(SHORT_SLEEP_MILLIS); |
| 166 | StatsLog.logStart(APP_BREADCRUMB_LABEL); |
| 167 | // Let the current bucket finish and the pull timeout. |
| 168 | Thread.sleep(sPullLatencyMillis * 2); |
| 169 | List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId); |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 170 | clearStatsPuller(PULL_ATOM_TAG); |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 171 | assertThat(data.size()).isEqualTo(0); |
| 172 | } |
| 173 | |
| 174 | /** |
| 175 | * Tests that 2 pulls in quick succession use the cache instead of pulling again. |
| 176 | */ |
| 177 | @Test |
| 178 | public void testPullAtomCallbackCache() throws Exception { |
| 179 | StatsManager statsManager = (StatsManager) mContext.getSystemService( |
| 180 | Context.STATS_MANAGER); |
| 181 | createAndAddConfigToStatsd(statsManager); |
| 182 | |
| 183 | // Set the cooldown to 10 seconds |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 184 | sCoolDownMillis = 10_000L; |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 185 | // Add the puller. |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 186 | setStatsPuller(PULL_ATOM_TAG, sPullTimeoutMillis, sCoolDownMillis, sPullReturnValue, |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 187 | sPullLatencyMillis, sAtomsPerPull); |
| 188 | |
| 189 | Thread.sleep(SHORT_SLEEP_MILLIS); |
| 190 | StatsLog.logStart(APP_BREADCRUMB_LABEL); |
| 191 | // Pull from cache. |
| 192 | StatsLog.logStart(APP_BREADCRUMB_LABEL); |
| 193 | Thread.sleep(LONG_SLEEP_MILLIS); |
| 194 | List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId); |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 195 | clearStatsPuller(PULL_ATOM_TAG); |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 196 | assertThat(data.size()).isEqualTo(2); |
| 197 | for (int i = 0; i < data.size(); i++) { |
| 198 | TestAtoms.PullCallbackAtomWrapper atomWrapper = null; |
| 199 | try { |
| 200 | atomWrapper = TestAtoms.PullCallbackAtomWrapper.parser() |
| 201 | .parseFrom(data.get(i).toByteArray()); |
| 202 | } catch (Exception e) { |
| 203 | Log.e(LOG_TAG, "Failed to parse primitive atoms"); |
| 204 | } |
| 205 | assertThat(atomWrapper).isNotNull(); |
| 206 | assertThat(atomWrapper.hasPullCallbackAtom()).isTrue(); |
| 207 | TestAtoms.PullCallbackAtom atom = |
| 208 | atomWrapper.getPullCallbackAtom(); |
| 209 | assertThat(atom.getLongVal()).isEqualTo(1); |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | /** |
| 214 | * Tests that a pull that returns 1000 stats events works properly. |
| 215 | */ |
| 216 | @Test |
| 217 | public void testPullAtomCallbackStress() throws Exception { |
| 218 | StatsManager statsManager = (StatsManager) mContext.getSystemService( |
| 219 | Context.STATS_MANAGER); |
| 220 | // Upload a config that captures that pulled atom. |
| 221 | createAndAddConfigToStatsd(statsManager); |
| 222 | sAtomsPerPull = 1000; |
| 223 | // Add the puller. |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 224 | setStatsPuller(PULL_ATOM_TAG, sPullTimeoutMillis, sCoolDownMillis, sPullReturnValue, |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 225 | sPullLatencyMillis, sAtomsPerPull); |
| 226 | |
| 227 | Thread.sleep(SHORT_SLEEP_MILLIS); |
| 228 | StatsLog.logStart(APP_BREADCRUMB_LABEL); |
| 229 | // Let the current bucket finish. |
| 230 | Thread.sleep(LONG_SLEEP_MILLIS); |
| 231 | List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId); |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 232 | clearStatsPuller(PULL_ATOM_TAG); |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 233 | assertThat(data.size()).isEqualTo(sAtomsPerPull); |
| 234 | |
| 235 | for (int i = 0; i < data.size(); i++) { |
| 236 | TestAtoms.PullCallbackAtomWrapper atomWrapper = null; |
| 237 | try { |
| 238 | atomWrapper = TestAtoms.PullCallbackAtomWrapper.parser() |
| 239 | .parseFrom(data.get(i).toByteArray()); |
| 240 | } catch (Exception e) { |
| 241 | Log.e(LOG_TAG, "Failed to parse primitive atoms"); |
| 242 | } |
| 243 | assertThat(atomWrapper).isNotNull(); |
| 244 | assertThat(atomWrapper.hasPullCallbackAtom()).isTrue(); |
| 245 | TestAtoms.PullCallbackAtom atom = |
| 246 | atomWrapper.getPullCallbackAtom(); |
| 247 | assertThat(atom.getLongVal()).isEqualTo(1); |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | private void createAndAddConfigToStatsd(StatsManager statsManager) throws Exception { |
| 252 | sConfigId = System.currentTimeMillis(); |
| 253 | long triggerMatcherId = sConfigId + 10; |
| 254 | long pullerMatcherId = sConfigId + 11; |
| 255 | long metricId = sConfigId + 100; |
| 256 | StatsdConfig config = StatsConfigUtils.getSimpleTestConfig(sConfigId) |
| 257 | .addAtomMatcher( |
| 258 | StatsConfigUtils.getAppBreadcrumbMatcher(triggerMatcherId, |
| 259 | APP_BREADCRUMB_LABEL)) |
| 260 | .addAtomMatcher(AtomMatcher.newBuilder() |
| 261 | .setId(pullerMatcherId) |
| 262 | .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder() |
| 263 | .setAtomId(PULL_ATOM_TAG)) |
| 264 | ) |
| 265 | .addGaugeMetric(GaugeMetric.newBuilder() |
| 266 | .setId(metricId) |
| 267 | .setWhat(pullerMatcherId) |
| 268 | .setTriggerEvent(triggerMatcherId) |
| 269 | .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true)) |
| 270 | .setBucket(TimeUnit.CTS) |
| 271 | .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES) |
| 272 | .setMaxNumGaugeAtomsPerBucket(1000) |
| 273 | ) |
Tej Singh | dea94cd | 2020-03-26 22:20:19 -0700 | [diff] [blame] | 274 | .addPullAtomPackages(PullAtomPackages.newBuilder() |
| 275 | .setAtomId(PULL_ATOM_TAG) |
| 276 | .addPackages(LibStatsPullTests.class.getPackage().getName())) |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 277 | .build(); |
| 278 | statsManager.addConfig(sConfigId, config.toByteArray()); |
| 279 | assertThat(StatsConfigUtils.verifyValidConfigExists(statsManager, sConfigId)).isTrue(); |
| 280 | } |
| 281 | |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 282 | private native void setStatsPuller(int atomTag, long timeoutMillis, long coolDownMillis, |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 283 | int pullReturnVal, long latencyMillis, int atomPerPull); |
| 284 | |
Tej Singh | 16daf38 | 2020-03-13 18:42:40 -0700 | [diff] [blame] | 285 | private native void clearStatsPuller(int atomTag); |
Tej Singh | e20bdcf | 2020-01-15 16:12:07 -0800 | [diff] [blame] | 286 | } |
| 287 | |